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 std::bitv;
|
|
|
|
|
|
|
|
export t;
|
|
|
|
export create_tritv;
|
|
|
|
export tritv_clone;
|
|
|
|
export tritv_set;
|
|
|
|
export to_vec;
|
|
|
|
export trit;
|
|
|
|
export dont_care;
|
|
|
|
export ttrue;
|
|
|
|
export tfalse;
|
|
|
|
export tritv_get;
|
|
|
|
export tritv_set_all;
|
|
|
|
export tritv_difference;
|
|
|
|
export tritv_union;
|
|
|
|
export tritv_intersect;
|
|
|
|
export tritv_copy;
|
|
|
|
export tritv_clear;
|
|
|
|
export tritv_doesntcare;
|
|
|
|
|
|
|
|
/* for a fixed index:
|
|
|
|
10 = "this constraint may or may not be true after execution"
|
|
|
|
01 = "this constraint is definitely true"
|
|
|
|
00 = "this constraint is definitely false"
|
|
|
|
11 should never appear
|
|
|
|
FIXME: typestate precondition (uncertain and val must
|
|
|
|
have the same length; 11 should never appear in a given position)
|
|
|
|
*/
|
|
|
|
|
|
|
|
type t = rec(bitv::t uncertain, bitv::t val, uint nbits);
|
|
|
|
tag trit {
|
|
|
|
ttrue;
|
|
|
|
tfalse;
|
|
|
|
dont_care;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_tritv(uint len) -> t {
|
|
|
|
ret rec(uncertain=bitv::create(len, true),
|
|
|
|
val=bitv::create(len, false),
|
|
|
|
nbits=len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn trit_minus(trit a, trit b) -> trit {
|
|
|
|
/* 2 - anything = 2
|
|
|
|
1 - 1 = 2
|
|
|
|
1 - 0 is an error
|
|
|
|
1 - 2 = 1
|
|
|
|
0 - 1 is an error
|
|
|
|
0 - anything else - 0
|
|
|
|
*/
|
|
|
|
alt (a) {
|
|
|
|
case (dont_care) { dont_care }
|
|
|
|
case (ttrue) {
|
|
|
|
alt (b) {
|
|
|
|
case (ttrue) { dont_care }
|
|
|
|
case (tfalse) { ttrue } /* internally contradictory, but
|
|
|
|
I guess it'll get flagged? */
|
|
|
|
case (dont_care) { ttrue }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (tfalse) {
|
|
|
|
alt (b) {
|
|
|
|
case (ttrue) { tfalse } /* see above comment */
|
|
|
|
case (_) { tfalse }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn trit_or(trit a, trit b) -> trit {
|
|
|
|
alt (a) {
|
|
|
|
case (dont_care) { b }
|
|
|
|
case (ttrue) { ttrue }
|
|
|
|
case (tfalse) {
|
|
|
|
alt (b) {
|
|
|
|
case (ttrue) { dont_care } /* FIXME: ?????? */
|
|
|
|
case (_) { tfalse }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: not sure about this
|
|
|
|
fn trit_and(trit a, trit b) -> trit {
|
|
|
|
alt (a) {
|
|
|
|
case (dont_care) { dont_care }
|
|
|
|
case (ttrue) {
|
|
|
|
alt (b) {
|
|
|
|
case (dont_care) { dont_care }
|
|
|
|
case (ttrue) { ttrue }
|
|
|
|
case (tfalse) { tfalse } // FIXME: ???
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (tfalse) { tfalse }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_difference(&t p1, &t p2) -> bool {
|
|
|
|
let uint i = 0u;
|
|
|
|
assert (p1.nbits == p2.nbits);
|
|
|
|
let uint sz = p1.nbits;
|
|
|
|
auto changed = false;
|
|
|
|
while (i < sz) {
|
|
|
|
auto old = tritv_get(p1, i);
|
|
|
|
auto new = trit_minus(old, tritv_get(p2, i));
|
|
|
|
changed = changed || (old != new);
|
|
|
|
tritv_set(i, p1, new);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
ret changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_union(&t p1, &t p2) -> bool {
|
|
|
|
let uint i = 0u;
|
|
|
|
assert (p1.nbits == p2.nbits);
|
|
|
|
let uint sz = p1.nbits;
|
|
|
|
auto changed = false;
|
|
|
|
while (i < sz) {
|
|
|
|
auto old = tritv_get(p1, i);
|
|
|
|
auto new = trit_or(old, tritv_get(p2, i));
|
|
|
|
changed = changed || (old != new);
|
|
|
|
tritv_set(i, p1, new);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
ret changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_intersect(&t p1, &t p2) -> bool {
|
|
|
|
let uint i = 0u;
|
|
|
|
assert (p1.nbits == p2.nbits);
|
|
|
|
let uint sz = p1.nbits;
|
|
|
|
auto changed = false;
|
|
|
|
while (i < sz) {
|
|
|
|
auto old = tritv_get(p1, i);
|
|
|
|
auto new = trit_and(old, tritv_get(p2, i));
|
|
|
|
changed = changed || (old != new);
|
|
|
|
tritv_set(i, p1, new);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
ret changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_get(&t v, uint i) -> trit {
|
|
|
|
auto b1 = bitv::get(v.uncertain, i);
|
|
|
|
auto b2 = bitv::get(v.val, i);
|
|
|
|
assert (! (b1 && b2));
|
|
|
|
if (b1) { dont_care }
|
|
|
|
else if (b2) { ttrue }
|
|
|
|
else { tfalse}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_set(uint i, &t v, trit t) -> bool {
|
|
|
|
auto old = tritv_get(v, i);
|
|
|
|
alt (t) {
|
|
|
|
case (dont_care) {
|
|
|
|
bitv::set(v.uncertain, i, true);
|
|
|
|
bitv::set(v.val, i, false);
|
|
|
|
}
|
|
|
|
case (ttrue) {
|
|
|
|
bitv::set(v.uncertain, i, false);
|
|
|
|
bitv::set(v.val, i, true);
|
|
|
|
}
|
|
|
|
case (tfalse) {
|
|
|
|
bitv::set(v.uncertain, i, false);
|
|
|
|
bitv::set(v.val, i, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret (old != t);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_copy(&t target, &t source) -> bool {
|
|
|
|
let uint i = 0u;
|
|
|
|
assert (target.nbits == source.nbits);
|
|
|
|
auto changed = false;
|
|
|
|
auto old;
|
|
|
|
auto new;
|
|
|
|
while (i < target.nbits) {
|
|
|
|
old = bitv::get(target.uncertain, i);
|
|
|
|
new = bitv::get(source.uncertain, i);
|
|
|
|
bitv::set(target.uncertain, i, new);
|
|
|
|
changed = changed || (old != new);
|
|
|
|
old = bitv::get(target.val, i);
|
|
|
|
new = bitv::get(source.val, i);
|
|
|
|
bitv::set(target.val, i, new);
|
|
|
|
changed = changed || (old != new);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
ret changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_set_all(&t v) {
|
|
|
|
let uint i = 0u;
|
|
|
|
while (i < v.nbits) {
|
|
|
|
tritv_set(i, v, ttrue);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_clear(&t v) {
|
|
|
|
let uint i = 0u;
|
|
|
|
while (i < v.nbits) {
|
|
|
|
tritv_set(i, v, dont_care);
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_clone(&t v) -> t {
|
|
|
|
ret rec(uncertain=bitv::clone(v.uncertain),
|
|
|
|
val=bitv::clone(v.val),
|
|
|
|
nbits=v.nbits);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tritv_doesntcare(&t v) -> bool {
|
|
|
|
let uint i = 0u;
|
|
|
|
while (i < v.nbits) {
|
|
|
|
if (tritv_get(v, i) != dont_care) {
|
|
|
|
ret false;
|
|
|
|
}
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
ret true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_vec(&t v) -> vec[uint] {
|
|
|
|
let uint i = 0u;
|
2011-06-24 19:04:08 +02:00
|
|
|
let vec[uint] rslt = [];
|
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
|
|
|
while (i < v.nbits) {
|
2011-06-24 19:04:08 +02:00
|
|
|
rslt += [alt (tritv_get(v, i)) {
|
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
|
|
|
case (dont_care) { 2u }
|
|
|
|
case (ttrue) { 1u }
|
|
|
|
case (tfalse) { 0u } }];
|
|
|
|
i += 1u;
|
|
|
|
}
|
2011-06-24 19:04:08 +02:00
|
|
|
ret rslt;
|
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
|
|
|
}
|
|
|
|
//
|
|
|
|
// 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:
|
|
|
|
//
|