Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
import front::ast::*;
|
2011-05-17 20:41:41 +02:00
|
|
|
import std::vec;
|
|
|
|
import std::vec::len;
|
|
|
|
import std::vec::slice;
|
2011-05-14 19:02:30 -07:00
|
|
|
import aux::fn_ctxt;
|
|
|
|
import aux::fn_info;
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
import aux::log_tritv;
|
|
|
|
import aux::log_tritv_err;
|
2011-06-01 18:10:10 -07:00
|
|
|
import aux::num_constraints;
|
|
|
|
import aux::cinit;
|
|
|
|
import aux::cpred;
|
2011-06-13 18:10:33 -07:00
|
|
|
import aux::ninit;
|
|
|
|
import aux::npred;
|
2011-06-01 18:10:10 -07:00
|
|
|
import aux::pred_desc;
|
|
|
|
import aux::match_args;
|
2011-06-13 18:10:33 -07:00
|
|
|
import aux::constr_;
|
2011-06-17 19:07:23 -07:00
|
|
|
import aux::block_precond;
|
|
|
|
import aux::stmt_precond;
|
|
|
|
import aux::expr_precond;
|
|
|
|
import aux::block_prestate;
|
|
|
|
import aux::expr_prestate;
|
|
|
|
import aux::stmt_prestate;
|
2011-06-19 22:41:21 +02:00
|
|
|
import tstate::aux::node_id_to_ts_ann;
|
2011-05-14 19:02:30 -07:00
|
|
|
import tstate::ann::pre_and_post;
|
|
|
|
import tstate::ann::precond;
|
|
|
|
import tstate::ann::postcond;
|
|
|
|
import tstate::ann::prestate;
|
|
|
|
import tstate::ann::poststate;
|
|
|
|
import tstate::ann::relax_prestate;
|
2011-06-17 19:07:23 -07:00
|
|
|
import tstate::ann::relax_precond;
|
|
|
|
import tstate::ann::relax_poststate;
|
2011-05-14 19:02:30 -07:00
|
|
|
import tstate::ann::pps_len;
|
|
|
|
import tstate::ann::true_precond;
|
|
|
|
import tstate::ann::empty_prestate;
|
|
|
|
import tstate::ann::difference;
|
|
|
|
import tstate::ann::union;
|
|
|
|
import tstate::ann::intersect;
|
|
|
|
import tstate::ann::clone;
|
|
|
|
import tstate::ann::set_in_postcond;
|
|
|
|
import tstate::ann::set_in_poststate;
|
2011-05-20 19:50:29 -07:00
|
|
|
import tstate::ann::clear_in_poststate;
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
import tritv::*;
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-06-13 18:10:33 -07:00
|
|
|
fn bit_num(&fn_ctxt fcx, &constr_ c) -> uint {
|
|
|
|
assert (fcx.enclosing.constrs.contains_key(c.id));
|
|
|
|
auto res = fcx.enclosing.constrs.get(c.id);
|
|
|
|
alt (c.c) {
|
|
|
|
case (ninit(_)) {
|
2011-06-01 18:10:10 -07:00
|
|
|
alt (res) {
|
2011-06-15 11:19:50 -07:00
|
|
|
case (cinit(?n, _, _)) { ret n; }
|
2011-06-01 18:10:10 -07:00
|
|
|
case (_) {
|
|
|
|
fcx.ccx.tcx.sess.bug("bit_num: asked for init constraint,"
|
2011-06-15 11:19:50 -07:00
|
|
|
+ " found a pred constraint");
|
2011-06-01 18:10:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-13 18:10:33 -07:00
|
|
|
case (npred(_, ?args)) {
|
2011-06-01 18:10:10 -07:00
|
|
|
alt (res) {
|
2011-06-15 11:19:50 -07:00
|
|
|
case (cpred(_, ?descs)) { ret match_args(fcx, *descs, args); }
|
2011-06-01 18:10:10 -07:00
|
|
|
case (_) {
|
|
|
|
fcx.ccx.tcx.sess.bug("bit_num: asked for pred constraint,"
|
2011-06-15 11:19:50 -07:00
|
|
|
+ " found an init constraint");
|
2011-06-01 18:10:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-06-13 18:10:33 -07:00
|
|
|
fn promises(&fn_ctxt fcx, &poststate p, &constr_ c) -> bool {
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
ret tritv_get(p, bit_num(fcx, c)) == ttrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// v "happens after" u
|
|
|
|
fn seq_trit(trit u, trit v) -> trit {
|
|
|
|
alt (v) {
|
|
|
|
case (ttrue) { ttrue }
|
|
|
|
case (tfalse) { tfalse }
|
|
|
|
case (dont_care) { u }
|
|
|
|
}
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
// idea: q "happens after" p -- so if something is
|
|
|
|
// 1 in q and 0 in p, it's 1 in the result; however,
|
|
|
|
// if it's 0 in q and 1 in p, it's 0 in the result
|
|
|
|
fn seq_tritv(&postcond p, &postcond q) {
|
|
|
|
auto i = 0u;
|
|
|
|
assert (p.nbits == q.nbits);
|
|
|
|
while (i < p.nbits) {
|
|
|
|
tritv_set(i, p, seq_trit(tritv_get(p, i), tritv_get(q, i)));
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn seq_postconds(&fn_ctxt fcx, &vec[postcond] ps) -> postcond {
|
|
|
|
auto sz = vec::len(ps);
|
|
|
|
if (sz >= 1u) {
|
|
|
|
auto prev = tritv_clone(ps.(0));
|
|
|
|
for (postcond p in slice(ps, 1u, sz)) {
|
|
|
|
seq_tritv(prev, p);
|
|
|
|
}
|
|
|
|
ret prev;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret ann::empty_poststate(num_constraints(fcx.enclosing));
|
|
|
|
}
|
|
|
|
}
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-05-14 19:02:30 -07:00
|
|
|
// Given a list of pres and posts for exprs e0 ... en,
|
|
|
|
// return the precondition for evaluating each expr in order.
|
|
|
|
// So, if e0's post is {x} and e1's pre is {x, y, z}, the entire
|
|
|
|
// precondition shouldn't include x.
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
fn seq_preconds(&fn_ctxt fcx, &vec[pre_and_post] pps) -> precond {
|
|
|
|
let uint sz = len(pps);
|
2011-06-15 11:19:50 -07:00
|
|
|
let uint num_vars = num_constraints(fcx.enclosing);
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
|
|
|
|
fn seq_preconds_go(&fn_ctxt fcx, &vec[pre_and_post] pps,
|
|
|
|
&pre_and_post first)
|
|
|
|
-> precond {
|
|
|
|
let uint sz = len(pps);
|
|
|
|
if (sz >= 1u) {
|
|
|
|
auto second = pps.(0);
|
|
|
|
assert (pps_len(second) == num_constraints(fcx.enclosing));
|
|
|
|
auto second_pre = clone(second.precondition);
|
|
|
|
difference(second_pre, first.postcondition);
|
|
|
|
auto next_first = clone(first.precondition);
|
|
|
|
union(next_first, second_pre);
|
|
|
|
auto next_first_post = clone(first.postcondition);
|
|
|
|
seq_tritv(next_first_post, second.postcondition);
|
|
|
|
ret seq_preconds_go(fcx, slice(pps, 1u, sz),
|
|
|
|
@rec(precondition=next_first,
|
|
|
|
postcondition=next_first_post));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret first.precondition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 11:19:50 -07:00
|
|
|
if (sz >= 1u) {
|
|
|
|
auto first = pps.(0);
|
|
|
|
assert (pps_len(first) == num_vars);
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
ret seq_preconds_go(fcx, slice(pps, 1u, sz), first);
|
2011-06-15 11:19:50 -07:00
|
|
|
} else { ret true_precond(num_vars); }
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Gee, maybe we could use foldl or something */
|
|
|
|
fn intersect_postconds_go(&postcond first, &vec[postcond] rest) -> postcond {
|
2011-06-15 11:19:50 -07:00
|
|
|
auto sz = vec::len[postcond](rest);
|
|
|
|
if (sz > 0u) {
|
|
|
|
auto other = rest.(0);
|
|
|
|
intersect(first, other);
|
|
|
|
intersect_postconds_go(first,
|
|
|
|
slice[postcond](rest, 1u,
|
|
|
|
len[postcond](rest)));
|
|
|
|
}
|
|
|
|
ret first;
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn intersect_postconds(&vec[postcond] pcs) -> postcond {
|
2011-06-15 11:19:50 -07:00
|
|
|
assert (len[postcond](pcs) > 0u);
|
Compute typestate properly for move
typestate now drops constraints correctly in the post-state of
a move expression or a declaration whose op is a move. It doesn't
yet drop constraints mentioning variables that get updated.
To do this, I had to change typestate to use trit-vectors instead
of bit-vectors, because for every constraint, there are three
possible values: known-to-be-false (e.g. after x <- y, init(y) is
known-to-be-false), known-to-be-true, and unknown. Before, we
conflated known-to-be-false with unknown. But move requires them
to be treated differently. Consider:
(program a)
(a1) x = 1;
(a2) y <- x;
(a3) log x;
(program b)
(b1) x = 1;
(b2) y <- z;
(b3) log x;
With only two values, the postcondition of statement a2 for
constraint init(x) is the same as that of b2: 0. But in (a2)'s
postcondition, init(x) *must* be false, but in (b2)'s condition,
it's just whatever it was in the postcondition of the preceding statement.
2011-06-22 21:26:34 -07:00
|
|
|
ret intersect_postconds_go(tritv_clone(pcs.(0)), pcs);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn gen(&fn_ctxt fcx, node_id id, &constr_ c) -> bool {
|
2011-06-15 11:19:50 -07:00
|
|
|
ret set_in_postcond(bit_num(fcx, c),
|
2011-06-19 22:41:21 +02:00
|
|
|
node_id_to_ts_ann(fcx.ccx, id).conditions);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-06-13 18:10:33 -07:00
|
|
|
fn declare_var(&fn_ctxt fcx, &constr_ c, prestate pre) -> prestate {
|
2011-05-14 19:02:30 -07:00
|
|
|
auto res = clone(pre);
|
2011-06-13 18:10:33 -07:00
|
|
|
relax_prestate(bit_num(fcx, c), res);
|
2011-06-17 19:07:23 -07:00
|
|
|
// idea is this is scoped
|
|
|
|
relax_poststate(bit_num(fcx, c), res);
|
2011-05-14 19:02:30 -07:00
|
|
|
ret res;
|
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn relax_precond_block_non_recursive(&fn_ctxt fcx, node_id i, &block b) {
|
|
|
|
relax_precond(i as uint, block_precond(fcx.ccx, b));
|
2011-06-17 19:07:23 -07:00
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn relax_precond_expr(&fn_ctxt fcx, node_id i, &@expr e) {
|
|
|
|
relax_precond(i as uint, expr_precond(fcx.ccx, e));
|
2011-06-17 19:07:23 -07:00
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn relax_precond_stmt(&fn_ctxt fcx, node_id i, &@stmt s) {
|
|
|
|
relax_precond(i as uint, stmt_precond(fcx.ccx, *s));
|
2011-06-17 19:07:23 -07:00
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn relax_precond_block(&fn_ctxt fcx, node_id i, &block b) {
|
2011-06-17 19:07:23 -07:00
|
|
|
relax_precond_block_non_recursive(fcx, i, b);
|
|
|
|
// FIXME: should use visit instead
|
|
|
|
// could at least generalize this pattern
|
|
|
|
// (also seen in ck::check_states_against_conditions)
|
|
|
|
let @mutable bool keepgoing = @mutable true;
|
|
|
|
|
|
|
|
fn quit(@mutable bool keepgoing, &@item i) {
|
|
|
|
*keepgoing = false;
|
|
|
|
}
|
|
|
|
fn kg(@mutable bool keepgoing) -> bool { ret *keepgoing; }
|
|
|
|
|
|
|
|
auto v = rec(visit_block_pre = bind
|
|
|
|
relax_precond_block_non_recursive(fcx, i, _),
|
|
|
|
visit_expr_pre = bind relax_precond_expr(fcx, i, _),
|
|
|
|
visit_stmt_pre = bind relax_precond_stmt(fcx, i, _),
|
|
|
|
visit_item_pre=bind quit(keepgoing, _),
|
|
|
|
keep_going=bind kg(keepgoing)
|
|
|
|
|
|
|
|
with walk::default_visitor());
|
|
|
|
walk::walk_block(v, b);
|
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn gen_poststate(&fn_ctxt fcx, node_id id, &constr_ c) -> bool {
|
2011-06-15 11:19:50 -07:00
|
|
|
log "gen_poststate";
|
2011-06-19 22:41:21 +02:00
|
|
|
ret set_in_poststate(bit_num(fcx, c),
|
|
|
|
node_id_to_ts_ann(fcx.ccx, id).states);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-06-19 22:41:21 +02:00
|
|
|
fn kill_poststate(&fn_ctxt fcx, node_id id, &constr_ c) -> bool {
|
2011-06-15 11:19:50 -07:00
|
|
|
log "kill_poststate";
|
2011-06-19 22:41:21 +02:00
|
|
|
ret clear_in_poststate(bit_num(fcx, c),
|
|
|
|
node_id_to_ts_ann(fcx.ccx, id).states);
|
2011-05-20 19:50:29 -07:00
|
|
|
}
|
2011-06-01 18:10:10 -07:00
|
|
|
//
|
|
|
|
// Local Variables:
|
|
|
|
// mode: rust
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
|
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
|
|
// End:
|
|
|
|
//
|