Implement typestate checking for move-mode args. Un-XFAIL compile-fail/move-arg.rs.
This commit is contained in:
parent
f7749b1608
commit
b2cac5afa3
@ -1093,14 +1093,45 @@ fn locals_to_bindings(locals : &(@local)[]) -> binding[] {
|
||||
ivec::map(local_to_bindings, locals)
|
||||
}
|
||||
|
||||
fn anon_bindings(es : &(@expr)[]) -> binding[] {
|
||||
fn expr_to_initializer(e : &@expr) -> initializer {
|
||||
{op: init_assign, expr: e}
|
||||
fn callee_modes(fcx: &fn_ctxt, callee: node_id) -> ty::mode[] {
|
||||
let ty = ty::type_autoderef(fcx.ccx.tcx,
|
||||
ty::node_id_to_type(fcx.ccx.tcx, callee));
|
||||
alt ty::struct(fcx.ccx.tcx, ty) {
|
||||
ty::ty_fn(_, args, _, _, _)
|
||||
| ty::ty_native_fn(_, args, _) {
|
||||
let modes = ~[];
|
||||
for arg: ty::arg in args {
|
||||
modes += ~[arg.mode];
|
||||
}
|
||||
ret modes;
|
||||
}
|
||||
_ {
|
||||
// Shouldn't happen; callee should be ty_fn.
|
||||
fcx.ccx.tcx.sess.bug("non-fn callee type in callee_modes: "
|
||||
+ util::ppaux::ty_to_str(fcx.ccx.tcx, ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn callee_arg_init_ops(fcx: &fn_ctxt, callee: node_id) -> init_op[] {
|
||||
fn mode_to_op(m: &ty::mode) -> init_op {
|
||||
alt m {
|
||||
ty::mo_move. { init_move }
|
||||
_ { init_assign }
|
||||
}
|
||||
}
|
||||
ret ivec::map(fn (e : &@expr) -> binding {
|
||||
{lhs: ~[],
|
||||
rhs: some(expr_to_initializer(e)) } },
|
||||
es);
|
||||
ivec::map(mode_to_op, callee_modes(fcx, callee))
|
||||
}
|
||||
|
||||
fn anon_bindings(ops: &init_op[], es : &(@expr)[]) -> binding[] {
|
||||
let bindings: binding[] = ~[];
|
||||
let i = 0;
|
||||
for op: init_op in ops {
|
||||
bindings += ~[{lhs: ~[],
|
||||
rhs: some({op:op, expr: es.(i)})}];
|
||||
i += 1;
|
||||
}
|
||||
ret bindings;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -316,6 +316,18 @@ fn handle_var(fcx: &fn_ctxt, rslt: &pre_and_post, id: node_id, name: ident) {
|
||||
}
|
||||
}
|
||||
|
||||
fn forget_args_moved_in(fcx: &fn_ctxt, parent: &@expr,
|
||||
modes: &ty::mode[],
|
||||
operands: &(@expr)[]) {
|
||||
let i = 0;
|
||||
for mode: ty::mode in modes {
|
||||
if mode == ty::mo_move {
|
||||
forget_in_postcond(fcx, parent.id, operands.(i).id);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fills in annotations as a side effect. Does not rebuild the expr */
|
||||
fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
|
||||
let enclosing = fcx.enclosing;
|
||||
@ -336,6 +348,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
|
||||
require(i, expr_pp(fcx.ccx, e));
|
||||
}
|
||||
|
||||
forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
|
||||
operands);
|
||||
|
||||
/* if this is a failing call, its postcondition sets everything */
|
||||
alt controlflow_expr(fcx.ccx, operator) {
|
||||
@ -347,6 +361,8 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
|
||||
let /* copy */args = operands;
|
||||
args += ~[operator];
|
||||
find_pre_post_exprs(fcx, args, e.id);
|
||||
forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
|
||||
operands);
|
||||
}
|
||||
expr_vec(args, _, _) { find_pre_post_exprs(fcx, args, e.id); }
|
||||
expr_path(p) {
|
||||
@ -544,14 +560,21 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
|
||||
|
||||
expr_bind(operator, maybe_args) {
|
||||
let args = ~[];
|
||||
for expr_opt: option::t[@expr] in maybe_args {
|
||||
let cmodes = callee_modes(fcx, operator.id);
|
||||
let modes = ~[];
|
||||
let i = 0;
|
||||
for expr_opt: option::t[@expr] in maybe_args {
|
||||
alt expr_opt {
|
||||
none. {/* no-op */ }
|
||||
some(expr) { args += ~[expr]; }
|
||||
some(expr) {
|
||||
modes += ~[cmodes.(i)];
|
||||
args += ~[expr];
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
args += ~[operator]; /* ??? order of eval? */
|
||||
|
||||
forget_args_moved_in(fcx, e, modes, args);
|
||||
find_pre_post_exprs(fcx, args, e.id);
|
||||
}
|
||||
expr_break. { clear_pp(expr_pp(fcx.ccx, e)); }
|
||||
|
@ -45,9 +45,6 @@ fn handle_move_or_copy(fcx: &fn_ctxt, post: &poststate, rhs_path: &path,
|
||||
// not a local -- do nothing
|
||||
}
|
||||
}
|
||||
if (init_op == init_move) {
|
||||
forget_in_poststate(fcx, post, rhs_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
|
||||
@ -67,10 +64,14 @@ fn seq_states(fcx: &fn_ctxt, pres: &prestate, bindings: &binding[])
|
||||
handle_move_or_copy(fcx, post, p, an_init.expr.id, i,
|
||||
an_init.op);
|
||||
}
|
||||
_ {}
|
||||
_ { }
|
||||
}
|
||||
set_in_poststate_ident(fcx, i.node, i.ident, post);
|
||||
}
|
||||
// Forget the RHS if we just moved it.
|
||||
if an_init.op == init_move {
|
||||
forget_in_poststate(fcx, post, an_init.expr.id);
|
||||
}
|
||||
}
|
||||
none {
|
||||
for i: inst in b.lhs {
|
||||
@ -162,16 +163,24 @@ fn find_pre_post_state_two(fcx: &fn_ctxt, pres: &prestate, lhs: &@expr,
|
||||
}
|
||||
|
||||
fn find_pre_post_state_call(fcx: &fn_ctxt, pres: &prestate, a: &@expr,
|
||||
id: node_id, bs: &(@expr)[], cf: controlflow) ->
|
||||
bool {
|
||||
id: node_id, ops: &init_op[], bs: &(@expr)[],
|
||||
cf: controlflow) -> bool {
|
||||
let changed = find_pre_post_state_expr(fcx, pres, a);
|
||||
ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id, bs, cf)
|
||||
if ivec::len(bs) != ivec::len(ops) {
|
||||
fcx.ccx.tcx.sess.span_bug(a.span,
|
||||
#fmt("mismatched arg lengths: \
|
||||
%u exprs vs. %u ops",
|
||||
ivec::len(bs), ivec::len(ops)));
|
||||
}
|
||||
ret find_pre_post_state_exprs(fcx, expr_poststate(fcx.ccx, a), id,
|
||||
ops, bs, cf)
|
||||
|| changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_exprs(fcx: &fn_ctxt, pres: &prestate, id: node_id,
|
||||
es: &(@expr)[], cf: controlflow) -> bool {
|
||||
let rs = seq_states(fcx, pres, anon_bindings(es));
|
||||
ops: &init_op[], es: &(@expr)[],
|
||||
cf: controlflow) -> bool {
|
||||
let rs = seq_states(fcx, pres, anon_bindings(ops, es));
|
||||
let changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres);
|
||||
/* if this is a failing call, it sets everything as initialized */
|
||||
alt cf {
|
||||
@ -304,23 +313,39 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
|
||||
|
||||
alt e.node {
|
||||
expr_vec(elts, _, _) {
|
||||
ret find_pre_post_state_exprs(fcx, pres, e.id, elts, return);
|
||||
ret find_pre_post_state_exprs(fcx, pres, e.id,
|
||||
ivec::init_elt(init_assign,
|
||||
ivec::len(elts)),
|
||||
elts, return);
|
||||
}
|
||||
expr_call(operator, operands) {
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id,
|
||||
callee_arg_init_ops(fcx, operator.id),
|
||||
operands,
|
||||
controlflow_expr(fcx.ccx, operator));
|
||||
}
|
||||
expr_spawn(_, _, operator, operands) {
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id, operands,
|
||||
return);
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id,
|
||||
callee_arg_init_ops(fcx, operator.id),
|
||||
operands, return);
|
||||
}
|
||||
expr_bind(operator, maybe_args) {
|
||||
let args = ~[];
|
||||
for a_opt: option::t[@expr] in maybe_args {
|
||||
alt a_opt { none. {/* no-op */ } some(a) { args += ~[a]; } }
|
||||
let callee_ops = callee_arg_init_ops(fcx, operator.id);
|
||||
let ops = ~[];
|
||||
let i = 0;
|
||||
for a_opt: option::t[@expr] in maybe_args {
|
||||
alt a_opt {
|
||||
none. {/* no-op */ }
|
||||
some(a) {
|
||||
ops += ~[callee_ops.(i)];
|
||||
args += ~[a];
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id, args, return);
|
||||
ret find_pre_post_state_call(fcx, pres, operator, e.id, ops, args,
|
||||
return);
|
||||
}
|
||||
expr_path(_) { ret pure_exp(fcx.ccx, e.id, pres); }
|
||||
expr_log(_, ex) {
|
||||
@ -347,7 +372,10 @@ fn find_pre_post_state_expr(fcx: &fn_ctxt, pres: &prestate, e: @expr) ->
|
||||
}
|
||||
expr_rec(fields, maybe_base) {
|
||||
let changed =
|
||||
find_pre_post_state_exprs(fcx, pres, e.id, field_exprs(fields),
|
||||
find_pre_post_state_exprs(fcx, pres, e.id,
|
||||
ivec::init_elt(init_assign,
|
||||
ivec::len(fields)),
|
||||
field_exprs(fields),
|
||||
return);
|
||||
alt maybe_base {
|
||||
none. {/* do nothing */ }
|
||||
|
@ -1,7 +1,3 @@
|
||||
// xfail-stage0
|
||||
// xfail-stage1
|
||||
// xfail-stage2
|
||||
// xfail-stage3
|
||||
// error-pattern: Unsatisfied precondition constraint
|
||||
fn test(foo: -int) {
|
||||
assert (foo == 10);
|
||||
|
Loading…
x
Reference in New Issue
Block a user