提交 b2cac5af 编写于 作者: G Graydon Hoare

Implement typestate checking for move-mode args. Un-XFAIL compile-fail/move-arg.rs.

上级 f7749b16
......@@ -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 }
}
}
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 ivec::map(fn (e : &@expr) -> binding {
{lhs: ~[],
rhs: some(expr_to_initializer(e)) } },
es);
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 combine_pp(antec: pre_and_post, fcx: fn_ctxt, pp: &pre_and_post,
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 */ }
......
// xfail-stage0
// xfail-stage1
// xfail-stage2
// xfail-stage3
// error-pattern: Unsatisfied precondition constraint
fn test(foo: -int) {
assert (foo == 10);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册