diff --git a/src/comp/middle/typestate_check.rs b/src/comp/middle/typestate_check.rs index 04aeebdf37ca4fee132eccca5d376bceb4715314..693521778ddd5f3fc6dc55552d7cfb64f339315d 100644 --- a/src/comp/middle/typestate_check.rs +++ b/src/comp/middle/typestate_check.rs @@ -106,6 +106,7 @@ import util.common.log_block; import util.common.log_stmt_err; import util.common.log_block_err; +import util.common.has_nonlocal_exits; import util.common.decl_lhs; import util.typestate_ann; import util.typestate_ann.ts_ann; @@ -466,6 +467,9 @@ fn expr_ann(&expr e) -> ann { case (expr_cont(?a)) { ret a; } + case (expr_self_method(_, ?a)) { + ret a; + } } } @@ -896,6 +900,11 @@ fn pp_one(&@expr e) -> pre_and_post { _vec.push[@expr](args, operator); 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)) { find_pre_post_exprs(fm, enclosing, args, a); } @@ -923,10 +932,19 @@ fn pp_one(&@expr e) -> pre_and_post { // Otherwise, variable is global, so it must be initialized 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)) { find_pre_post_expr(fm, enclosing, *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)) { alt (opt) { case (some[@expr](?arg)) { @@ -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)) { /* Different from expr_assign in that the lhs *must* already be initialized */ @@ -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)) { find_pre_post_expr(fm, enclosing, *antec); find_pre_post_block(fm, enclosing, conseq); @@ -1028,6 +1067,9 @@ fn pp_one(&@expr e) -> pre_and_post { FIXME */ 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)) { find_pre_post_expr(fm, enclosing, *operand); set_pre_and_post(a, expr_pp(*operand)); @@ -1048,6 +1090,24 @@ fn pp_one(&@expr e) -> pre_and_post { intersect_postconds(vec(expr_postcond(*test), 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)) { find_pre_post_loop(fm, enclosing, d, index, body, a); } @@ -1104,10 +1164,15 @@ fn combine_pp(pre_and_post antec, case (expr_break(?a)) { set_pre_and_post(a, empty_pre_post(num_local_vars)); } - case(_) { - log_err("this sort of expr isn't implemented!"); - log_expr_err(e); - fail; + case (expr_cont(?a)) { + set_pre_and_post(a, empty_pre_post(num_local_vars)); + } + 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) 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); 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 { /* A block may be empty, so this next line ensures that the postconds vector is non-empty. */ _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, postcondition=block_postcond)); } @@ -1406,6 +1491,12 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, expr_poststate(*operator), a, operands) || 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)) { changed = find_pre_post_state_expr(fm, enclosing, pres, operator) || changed; @@ -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; 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)) { alt (maybe_e) { case (some[@expr](?arg)) { @@ -1486,6 +1590,32 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, } 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)) { changed = extend_prestate_ann(a, pres) || changed; 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, } 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)) { changed = extend_prestate_ann(a, pres) || changed; 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, changed = extend_poststate_ann(a, expr_poststate(*r)) || 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)) { /* quite similar to binary -- should abstract this */ changed = extend_prestate_ann(a, pres) || changed; @@ -1558,6 +1703,27 @@ fn find_pre_post_state_expr(&fn_info_map fm, &fn_info enclosing, block_poststate(body)))) || 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)) { 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, case (expr_break(?a)) { ret pure_exp(a, pres); } - case (_) { - log_err("find_pre_post_state_expr: implement this case!"); - log_expr_err(*e); - fail; + case (expr_cont(?a)) { + ret pure_exp(a, pres); + } + 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, @@ -1733,6 +1902,8 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing, &prestate pres0, &block b) -> bool { + /* FIXME handle non-local exits */ + auto changed = false; auto num_local_vars = num_locals(enclosing); @@ -1756,6 +1927,20 @@ fn find_pre_post_state_block(&fn_info_map fm, &fn_info enclosing, 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_poststate_ann(b.node.a, post); @@ -1802,7 +1987,7 @@ fn check_states_expr(fn_info enclosing, &expr e) -> () { let prestate pres = expr_prestate(e); 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_err("Precondition: "); log_bitv_err(enclosing, prec); @@ -1831,7 +2016,8 @@ fn check_states_stmt(fn_info enclosing, &stmt s) -> () { */ 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_err("Precondition: "); log_bitv_err(enclosing, prec); diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs index d2ed72a0ff3411603d6ee9272d7b82ab81494261..fd0d688bab8e8c60f977dba02ebf2c117f5a2aff 100644 --- a/src/comp/util/common.rs +++ b/src/comp/util/common.rs @@ -1,3 +1,5 @@ +import std.map; +import std.map.hashmap; import std._uint; import std._int; import std._vec; @@ -5,6 +7,9 @@ import front.ast; import util.typestate_ann.ts_ann; +import middle.fold; +import middle.fold.respan; + import std.io.stdout; import std.io.str_writer; import std.io.string_writer; @@ -16,6 +21,7 @@ type filename = str; type span = rec(uint lo, uint hi); type spanned[T] = rec(T node, span span); +type flag = hashmap[str, ()]; tag ty_mach { ty_i8; @@ -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: // mode: rust diff --git a/src/test/compile-fail/break-uninit.rs b/src/test/compile-fail/break-uninit.rs new file mode 100644 index 0000000000000000000000000000000000000000..e9085dd25e8239f187ab7a5347cd90ec048eabe4 --- /dev/null +++ b/src/test/compile-fail/break-uninit.rs @@ -0,0 +1,22 @@ +// 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()); +} diff --git a/src/test/compile-fail/break-uninit2.rs b/src/test/compile-fail/break-uninit2.rs new file mode 100644 index 0000000000000000000000000000000000000000..8ef83f08cf5ec0a0abca93a51c2964b1e5c36dec --- /dev/null +++ b/src/test/compile-fail/break-uninit2.rs @@ -0,0 +1,22 @@ +// 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()); +}