提交 0190ebfe 编写于 作者: T Tim Chevalier 提交者: Graydon Hoare

Support all expression forms in typestate

Added support for self_method, cont, chan, port, recv, send, be,
do_while, spawn, and ext; handled break and cont correctly.
(However, there are no non-xfailed test cases for ext or spawn in
stage0 currently.)

Although the standard library compiles and all test cases pass with
typestate enabled, I left typestate checking disabled as rustc
terminates abnormally when building the standard library if so,
even though it does generate code correctly.
上级 7c4f8cb4
...@@ -106,6 +106,7 @@ ...@@ -106,6 +106,7 @@
import util.common.log_block; import util.common.log_block;
import util.common.log_stmt_err; import util.common.log_stmt_err;
import util.common.log_block_err; import util.common.log_block_err;
import util.common.has_nonlocal_exits;
import util.common.decl_lhs; import util.common.decl_lhs;
import util.typestate_ann; import util.typestate_ann;
import util.typestate_ann.ts_ann; import util.typestate_ann.ts_ann;
...@@ -466,6 +467,9 @@ fn expr_ann(&expr e) -> ann { ...@@ -466,6 +467,9 @@ fn expr_ann(&expr e) -> ann {
case (expr_cont(?a)) { case (expr_cont(?a)) {
ret a; ret a;
} }
case (expr_self_method(_, ?a)) {
ret a;
}
} }
} }
...@@ -896,6 +900,11 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -896,6 +900,11 @@ fn pp_one(&@expr e) -> pre_and_post {
_vec.push[@expr](args, operator); _vec.push[@expr](args, operator);
find_pre_post_exprs(fm, enclosing, args, a); find_pre_post_exprs(fm, enclosing, args, a);
} }
case (expr_spawn(_, _, ?operator, ?operands, ?a)) {
auto args = _vec.clone[@expr](operands);
_vec.push[@expr](args, operator);
find_pre_post_exprs(fm, enclosing, args, a);
}
case (expr_vec(?args, _, ?a)) { case (expr_vec(?args, _, ?a)) {
find_pre_post_exprs(fm, enclosing, args, a); find_pre_post_exprs(fm, enclosing, args, a);
} }
...@@ -923,10 +932,19 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -923,10 +932,19 @@ fn pp_one(&@expr e) -> pre_and_post {
// Otherwise, variable is global, so it must be initialized // Otherwise, variable is global, so it must be initialized
set_pre_and_post(a, res); set_pre_and_post(a, res);
} }
case (expr_self_method(?v, ?a)) {
/* v is a method of the enclosing obj, so it must be
initialized, right? */
set_pre_and_post(a, empty_pre_post(num_local_vars));
}
case(expr_log(_, ?arg, ?a)) { case(expr_log(_, ?arg, ?a)) {
find_pre_post_expr(fm, enclosing, *arg); find_pre_post_expr(fm, enclosing, *arg);
set_pre_and_post(a, expr_pp(*arg)); set_pre_and_post(a, expr_pp(*arg));
} }
case (expr_chan(?arg, ?a)) {
find_pre_post_expr(fm, enclosing, *arg);
set_pre_and_post(a, expr_pp(*arg));
}
case(expr_put(?opt, ?a)) { case(expr_put(?opt, ?a)) {
alt (opt) { alt (opt) {
case (some[@expr](?arg)) { case (some[@expr](?arg)) {
...@@ -963,6 +981,22 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -963,6 +981,22 @@ fn pp_one(&@expr e) -> pre_and_post {
} }
} }
} }
case (expr_recv(?lhs, ?rhs, ?a)) {
alt (lhs.node) {
case (expr_path(?p, some[def](def_local(?d_id)), ?a_lhs)) {
find_pre_post_expr(fm, enclosing, *rhs);
set_pre_and_post(a, expr_pp(*rhs));
log("gen:");
log_expr(e);
gen(enclosing, a, d_id);
}
case (_) {
// doesn't check that lhs is an lval, but
// that's probably ok
find_pre_post_exprs(fm, enclosing, vec(lhs, rhs), a);
}
}
}
case (expr_assign_op(_, ?lhs, ?rhs, ?a)) { case (expr_assign_op(_, ?lhs, ?rhs, ?a)) {
/* Different from expr_assign in that the lhs *must* /* Different from expr_assign in that the lhs *must*
already be initialized */ already be initialized */
...@@ -987,6 +1021,11 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -987,6 +1021,11 @@ fn pp_one(&@expr e) -> pre_and_post {
} }
} }
} }
case (expr_be(?e, ?a)) {
find_pre_post_expr(fm, enclosing, *e);
set_pre_and_post(a, rec(precondition=expr_prestate(*e),
postcondition=false_postcond(num_local_vars)));
}
case (expr_if(?antec, ?conseq, ?maybe_alt, ?a)) { case (expr_if(?antec, ?conseq, ?maybe_alt, ?a)) {
find_pre_post_expr(fm, enclosing, *antec); find_pre_post_expr(fm, enclosing, *antec);
find_pre_post_block(fm, enclosing, conseq); find_pre_post_block(fm, enclosing, conseq);
...@@ -1028,6 +1067,9 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -1028,6 +1067,9 @@ fn pp_one(&@expr e) -> pre_and_post {
FIXME */ FIXME */
find_pre_post_exprs(fm, enclosing, vec(l, r), a); find_pre_post_exprs(fm, enclosing, vec(l, r), a);
} }
case (expr_send(?l, ?r, ?a)) {
find_pre_post_exprs(fm, enclosing, vec(l, r), a);
}
case (expr_unary(_,?operand,?a)) { case (expr_unary(_,?operand,?a)) {
find_pre_post_expr(fm, enclosing, *operand); find_pre_post_expr(fm, enclosing, *operand);
set_pre_and_post(a, expr_pp(*operand)); set_pre_and_post(a, expr_pp(*operand));
...@@ -1048,6 +1090,24 @@ fn pp_one(&@expr e) -> pre_and_post { ...@@ -1048,6 +1090,24 @@ fn pp_one(&@expr e) -> pre_and_post {
intersect_postconds(vec(expr_postcond(*test), intersect_postconds(vec(expr_postcond(*test),
block_postcond(body))))); block_postcond(body)))));
} }
case (expr_do_while(?body, ?test, ?a)) {
find_pre_post_block(fm, enclosing, body);
find_pre_post_expr(fm, enclosing, *test);
auto loop_postcond = union_postconds(num_local_vars,
vec(block_postcond(body), expr_postcond(*test)));
/* conservative approximination: if the body
could break or cont, the test may never be executed */
if (has_nonlocal_exits(body)) {
loop_postcond = empty_poststate(num_local_vars);
}
set_pre_and_post(a,
rec(precondition=seq_preconds(num_local_vars,
vec(block_pp(body),
expr_pp(*test))),
postcondition=loop_postcond));
}
case (expr_for(?d, ?index, ?body, ?a)) { case (expr_for(?d, ?index, ?body, ?a)) {
find_pre_post_loop(fm, enclosing, d, index, body, a); find_pre_post_loop(fm, enclosing, d, index, body, a);
} }
...@@ -1104,10 +1164,15 @@ fn combine_pp(pre_and_post antec, ...@@ -1104,10 +1164,15 @@ fn combine_pp(pre_and_post antec,
case (expr_break(?a)) { case (expr_break(?a)) {
set_pre_and_post(a, empty_pre_post(num_local_vars)); set_pre_and_post(a, empty_pre_post(num_local_vars));
} }
case(_) { case (expr_cont(?a)) {
log_err("this sort of expr isn't implemented!"); set_pre_and_post(a, empty_pre_post(num_local_vars));
log_expr_err(e); }
fail; case (expr_port(?a)) {
set_pre_and_post(a, empty_pre_post(num_local_vars));
}
case (expr_ext(_, _, _, ?expanded, ?a)) {
find_pre_post_expr(fm, enclosing, *expanded);
set_pre_and_post(a, expr_pp(*expanded));
} }
} }
} }
...@@ -1181,6 +1246,22 @@ fn find_pre_post_stmt(fn_info_map fm, &fn_info enclosing, &ast.stmt s) ...@@ -1181,6 +1246,22 @@ fn find_pre_post_stmt(fn_info_map fm, &fn_info enclosing, &ast.stmt s)
fn find_pre_post_block(&fn_info_map fm, &fn_info enclosing, block b) fn find_pre_post_block(&fn_info_map fm, &fn_info enclosing, block b)
-> () { -> () {
/* Want to say that if there is a break or cont in this
block, then that invalidates the poststate upheld by
any of the stmts after it.
Given that the typechecker has run, we know any break will be in
a block that forms a loop body. So that's ok. There'll never be an
expr_break outside a loop body, therefore, no expr_break outside a block.
*/
/* Conservative approximation for now: This says that if a block contains
*any* breaks or conts, then its postcondition doesn't promise anything.
This will mean that:
x = 0;
break;
won't have a postcondition that says x is initialized, but that's ok.
*/
auto nv = num_locals(enclosing); auto nv = num_locals(enclosing);
fn do_one_(fn_info_map fm, fn_info i, &@stmt s) -> () { fn do_one_(fn_info_map fm, fn_info i, &@stmt s) -> () {
...@@ -1214,7 +1295,11 @@ fn get_pp_expr(&@expr e) -> pre_and_post { ...@@ -1214,7 +1295,11 @@ fn get_pp_expr(&@expr e) -> pre_and_post {
/* A block may be empty, so this next line ensures that the postconds /* A block may be empty, so this next line ensures that the postconds
vector is non-empty. */ vector is non-empty. */
_vec.push[postcond](postconds, block_precond); _vec.push[postcond](postconds, block_precond);
auto block_postcond = union_postconds(nv, postconds); auto block_postcond = empty_poststate(nv);
/* conservative approximation */
if (! has_nonlocal_exits(b)) {
block_postcond = union_postconds(nv, postconds);
}
set_pre_and_post(b.node.a, rec(precondition=block_precond, set_pre_and_post(b.node.a, rec(precondition=block_precond,
postcondition=block_postcond)); postcondition=block_postcond));
} }
...@@ -1406,6 +1491,12 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1406,6 +1491,12 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
expr_poststate(*operator), a, operands) expr_poststate(*operator), a, operands)
|| changed); || changed);
} }
case (expr_spawn(_, _, ?operator, ?operands, ?a)) {
changed = find_pre_post_state_expr(fm, enclosing, pres, operator);
ret(find_pre_post_state_exprs(fm, enclosing,
expr_poststate(*operator), a, operands)
|| changed);
}
case (expr_bind(?operator, ?maybe_args, ?a)) { case (expr_bind(?operator, ?maybe_args, ?a)) {
changed = find_pre_post_state_expr(fm, enclosing, pres, operator) changed = find_pre_post_state_expr(fm, enclosing, pres, operator)
|| changed; || changed;
...@@ -1423,6 +1514,19 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1423,6 +1514,19 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
changed = extend_poststate_ann(a, expr_poststate(*e)) || changed; changed = extend_poststate_ann(a, expr_poststate(*e)) || changed;
ret changed; ret changed;
} }
case (expr_chan(?e, ?a)) {
changed = find_pre_post_state_expr(fm, enclosing, pres, e);
changed = extend_prestate_ann(a, pres) || changed;
changed = extend_poststate_ann(a, expr_poststate(*e)) || changed;
ret changed;
}
case (expr_ext(_, _, _, ?expanded, ?a)) {
changed = find_pre_post_state_expr(fm, enclosing, pres, expanded);
changed = extend_prestate_ann(a, pres) || changed;
changed = extend_poststate_ann(a, expr_poststate(*expanded))
|| changed;
ret changed;
}
case (expr_put(?maybe_e, ?a)) { case (expr_put(?maybe_e, ?a)) {
alt (maybe_e) { alt (maybe_e) {
case (some[@expr](?arg)) { case (some[@expr](?arg)) {
...@@ -1486,6 +1590,32 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1486,6 +1590,32 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
} }
ret changed; ret changed;
} }
case (expr_recv(?lhs, ?rhs, ?a)) {
extend_prestate_ann(a, pres);
alt (lhs.node) {
case (expr_path(?p, some[def](def_local(?d_id)), ?a_lhs)) {
// receive to local var
changed = pure_exp(a_lhs, pres) || changed;
changed = find_pre_post_state_expr(fm, enclosing, pres, rhs)
|| changed;
changed = extend_poststate_ann(a, expr_poststate(*rhs))
|| changed;
changed = gen_poststate(enclosing, a, d_id) || changed;
}
case (_) {
// receive to something that must already have been init'd
changed = find_pre_post_state_expr(fm, enclosing, pres, lhs)
|| changed;
changed = find_pre_post_state_expr(fm, enclosing,
expr_poststate(*lhs), rhs) || changed;
changed = extend_poststate_ann(a, expr_poststate(*rhs))
|| changed;
}
}
ret changed;
}
case (expr_ret(?maybe_ret_val, ?a)) { case (expr_ret(?maybe_ret_val, ?a)) {
changed = extend_prestate_ann(a, pres) || changed; changed = extend_prestate_ann(a, pres) || changed;
set_poststate_ann(a, false_postcond(num_local_vars)); set_poststate_ann(a, false_postcond(num_local_vars));
...@@ -1498,6 +1628,12 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1498,6 +1628,12 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
} }
ret changed; ret changed;
} }
case (expr_be(?e, ?a)) {
changed = extend_prestate_ann(a, pres) || changed;
set_poststate_ann(a, false_postcond(num_local_vars));
changed = find_pre_post_state_expr(fm, enclosing, pres, e) || changed;
ret changed;
}
case (expr_if(?antec, ?conseq, ?maybe_alt, ?a)) { case (expr_if(?antec, ?conseq, ?maybe_alt, ?a)) {
changed = extend_prestate_ann(a, pres) || changed; changed = extend_prestate_ann(a, pres) || changed;
changed = find_pre_post_state_expr(fm, enclosing, pres, antec) changed = find_pre_post_state_expr(fm, enclosing, pres, antec)
...@@ -1529,6 +1665,15 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1529,6 +1665,15 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
changed = extend_poststate_ann(a, expr_poststate(*r)) || changed; changed = extend_poststate_ann(a, expr_poststate(*r)) || changed;
ret changed; ret changed;
} }
case (expr_send(?l, ?r, ?a)) {
changed = extend_prestate_ann(a, pres) || changed;
changed = find_pre_post_state_expr(fm, enclosing, pres, l)
|| changed;
changed = find_pre_post_state_expr(fm,
enclosing, expr_poststate(*l), r) || changed;
changed = extend_poststate_ann(a, expr_poststate(*r)) || changed;
ret changed;
}
case (expr_assign_op(?op, ?lhs, ?rhs, ?a)) { case (expr_assign_op(?op, ?lhs, ?rhs, ?a)) {
/* quite similar to binary -- should abstract this */ /* quite similar to binary -- should abstract this */
changed = extend_prestate_ann(a, pres) || changed; changed = extend_prestate_ann(a, pres) || changed;
...@@ -1558,6 +1703,27 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1558,6 +1703,27 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
block_poststate(body)))) || changed; block_poststate(body)))) || changed;
ret changed; ret changed;
} }
case (expr_do_while(?body, ?test, ?a)) {
changed = extend_prestate_ann(a, pres) || changed;
changed = find_pre_post_state_block(fm, enclosing, pres, body)
|| changed;
changed = find_pre_post_state_expr(fm, enclosing,
block_poststate(body), test) || changed;
/* conservative approximination: if the body of the loop
could break or cont, we revert to the prestate
(TODO: could treat cont differently from break, since
if there's a cont, the test will execute) */
if (has_nonlocal_exits(body)) {
changed = set_poststate_ann(a, pres) || changed;
}
else {
changed = extend_poststate_ann(a, expr_poststate(*test))
|| changed;
}
ret changed;
}
case (expr_for(?d, ?index, ?body, ?a)) { case (expr_for(?d, ?index, ?body, ?a)) {
ret find_pre_post_state_loop(fm, enclosing, pres, d, index, body, a); ret find_pre_post_state_loop(fm, enclosing, pres, d, index, body, a);
} }
...@@ -1634,13 +1800,16 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, ...@@ -1634,13 +1800,16 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing,
case (expr_break(?a)) { case (expr_break(?a)) {
ret pure_exp(a, pres); ret pure_exp(a, pres);
} }
case (_) { case (expr_cont(?a)) {
log_err("find_pre_post_state_expr: implement this case!"); ret pure_exp(a, pres);
log_expr_err(*e); }
fail; case (expr_port(?a)) {
ret pure_exp(a, pres);
}
case (expr_self_method(_, ?a)) {
ret pure_exp(a, pres);
} }
} }
} }
fn find_pre_post_state_stmt(&fn_info_map fm, &fn_info enclosing, fn find_pre_post_state_stmt(&fn_info_map fm, &fn_info enclosing,
...@@ -1733,6 +1902,8 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing, ...@@ -1733,6 +1902,8 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing,
&prestate pres0, &block b) &prestate pres0, &block b)
-> bool { -> bool {
/* FIXME handle non-local exits */
auto changed = false; auto changed = false;
auto num_local_vars = num_locals(enclosing); auto num_local_vars = num_locals(enclosing);
...@@ -1756,6 +1927,20 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing, ...@@ -1756,6 +1927,20 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing,
post = expr_poststate(*e); post = expr_poststate(*e);
} }
} }
/*
log_err("block:");
log_block_err(b);
log_err("has non-local exits?");
log_err(has_nonlocal_exits(b));
*/
/* conservative approximation: if a block contains a break
or cont, we assume nothing about the poststate */
if (has_nonlocal_exits(b)) {
post = pres0;
}
set_prestate_ann(@b.node.a, pres0); set_prestate_ann(@b.node.a, pres0);
set_poststate_ann(b.node.a, post); set_poststate_ann(b.node.a, post);
...@@ -1802,7 +1987,7 @@ fn check_states_expr(fn_info enclosing, &expr e) -> () { ...@@ -1802,7 +1987,7 @@ fn check_states_expr(fn_info enclosing, &expr e) -> () {
let prestate pres = expr_prestate(e); let prestate pres = expr_prestate(e);
if (!implies(pres, prec)) { if (!implies(pres, prec)) {
log_err("check_states_expr: unsatisfied precondition for "); log_err("check_states_expr: Unsatisfied precondition constraint for ");
log_expr_err(e); log_expr_err(e);
log_err("Precondition: "); log_err("Precondition: ");
log_bitv_err(enclosing, prec); log_bitv_err(enclosing, prec);
...@@ -1831,7 +2016,8 @@ fn check_states_stmt(fn_info enclosing, &stmt s) -> () { ...@@ -1831,7 +2016,8 @@ fn check_states_stmt(fn_info enclosing, &stmt s) -> () {
*/ */
if (!implies(pres, prec)) { if (!implies(pres, prec)) {
log_err("check_states_stmt: unsatisfied precondition for "); log_err("check_states_stmt: "
+ "Unsatisfied precondition constraint for ");
log_stmt_err(s); log_stmt_err(s);
log_err("Precondition: "); log_err("Precondition: ");
log_bitv_err(enclosing, prec); log_bitv_err(enclosing, prec);
......
import std.map;
import std.map.hashmap;
import std._uint; import std._uint;
import std._int; import std._int;
import std._vec; import std._vec;
...@@ -5,6 +7,9 @@ ...@@ -5,6 +7,9 @@
import front.ast; import front.ast;
import util.typestate_ann.ts_ann; import util.typestate_ann.ts_ann;
import middle.fold;
import middle.fold.respan;
import std.io.stdout; import std.io.stdout;
import std.io.str_writer; import std.io.str_writer;
import std.io.string_writer; import std.io.string_writer;
...@@ -16,6 +21,7 @@ ...@@ -16,6 +21,7 @@
type filename = str; type filename = str;
type span = rec(uint lo, uint hi); type span = rec(uint lo, uint hi);
type spanned[T] = rec(T node, span span); type spanned[T] = rec(T node, span span);
type flag = hashmap[str, ()];
tag ty_mach { tag ty_mach {
ty_i8; ty_i8;
...@@ -222,6 +228,32 @@ fn decl_lhs(@ast.decl d) -> ast.def_id { ...@@ -222,6 +228,32 @@ fn decl_lhs(@ast.decl d) -> ast.def_id {
} }
} }
fn has_nonlocal_exits(&ast.block b) -> bool {
/* overkill, but just passing around a mutable bool doesn't seem
to work in rustboot */
auto has_exits = new_str_hash[()]();
fn set_break(&flag f, &span sp, ast.ann a) -> @ast.expr {
f.insert("foo", ());
ret @respan(sp, ast.expr_break(a));
}
fn set_cont(&flag f, &span sp, ast.ann a) -> @ast.expr {
f.insert("foo", ());
ret @respan(sp, ast.expr_cont(a));
}
fn check_b(&flag f) -> bool {
ret (f.size() == 0u);
}
auto fld0 = fold.new_identity_fold[flag]();
fld0 = @rec(fold_expr_break = bind set_break(_,_,_),
fold_expr_cont = bind set_cont(_,_,_),
keep_going = bind check_b(_) with *fld0);
fold.fold_block[flag](has_exits, fld0, b);
ret (has_exits.size() > 0u);
}
// //
// Local Variables: // Local Variables:
// mode: rust // mode: rust
......
// xfail-boot
// xfail-stage0
// error-pattern:Unsatisfied precondition
fn foo() -> int {
let int x;
let int i;
do {
i = 0;
break;
x = 0;
} while ((x = 0) != 0);
log(x);
ret 17;
}
fn main() {
log(foo());
}
// xfail-boot
// xfail-stage0
// error-pattern:Unsatisfied precondition
fn foo() -> int {
let int x;
let int i;
do {
i = 0;
break;
x = 0;
} while (1 != 2);
log(x);
ret 17;
}
fn main() {
log(foo());
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册