diff --git a/src/comp/middle/tstate/ann.rs b/src/comp/middle/tstate/ann.rs index a1b5508e2a2d..a894dde11a64 100644 --- a/src/comp/middle/tstate/ann.rs +++ b/src/comp/middle/tstate/ann.rs @@ -1,36 +1,36 @@ import front::ast::ident; import std::vec; -import std::bitv; +import tritv::*; +type precond = t; + +/* 2 means "this constraint may or may not be true after execution" + 1 means "this constraint is definitely true after execution" + 0 means "this constraint is definitely false after execution" */ +type postcond = t; + + +/* 2 means "don't know about this constraint" + 1 means "this constraint is definitely true before entry" + 0 means "this constraint is definitely false on entry" */ +type prestate = t; + + +/* similar to postcond */ +type poststate = t; + + +/* 1 means "this variable is definitely initialized" + 0 means "don't know whether this variable is + initialized" */ /* - This says: this expression requires the idents in
 to be initialized,
-   and given the precondition, it guarantees that the idents in  are
-   initialized.
+   This says: this expression requires the constraints whose value is 1 in
+   
 to be true, and given the precondition, it guarantees that the
+   constraints in  whose values are 1 are true, and that the constraints
+   in  whose values are 0 are false.
  */
-type precond = bitv::t;
-
-
-/* 1 means "this variable must be initialized"
-  0 means "don't care about this variable" */
-type postcond = bitv::t;
-
-
-/* 1 means "this variable is initialized"
-  0 means "don't know about this variable */
-type prestate = bitv::t;
-
-
-/* 1 means "this variable is definitely initialized"
-  0 means "don't know whether this variable is
-  initialized" */
-type poststate = bitv::t;
-
-
-/* 1 means "this variable is definitely initialized"
-  0 means "don't know whether this variable is
-  initialized" */
 
 /* named thus so as not to confuse with prestate and poststate */
 type pre_and_post = @rec(precond precondition, postcond postcondition);
@@ -44,7 +44,7 @@ type pre_and_post_state = rec(prestate prestate, poststate poststate);
 type ts_ann = @rec(pre_and_post conditions, pre_and_post_state states);
 
 fn true_precond(uint num_vars) -> precond {
-    be bitv::create(num_vars, false);
+    be create_tritv(num_vars);
 }
 
 fn true_postcond(uint num_vars) -> postcond { be true_precond(num_vars); }
@@ -54,7 +54,9 @@ fn empty_prestate(uint num_vars) -> prestate { be true_precond(num_vars); }
 fn empty_poststate(uint num_vars) -> poststate { be true_precond(num_vars); }
 
 fn false_postcond(uint num_vars) -> postcond {
-    be bitv::create(num_vars, true);
+    auto res = create_tritv(num_vars);
+    tritv_set_all(res);
+    ret res;
 }
 
 fn empty_pre_post(uint num_vars) -> pre_and_post {
@@ -77,12 +79,16 @@ fn get_pre(&pre_and_post p) -> precond { ret p.precondition; }
 fn get_post(&pre_and_post p) -> postcond { ret p.postcondition; }
 
 fn difference(&precond p1, &precond p2) -> bool {
-    be bitv::difference(p1, p2);
+    ret tritv_difference(p1, p2);
 }
 
-fn union(&precond p1, &precond p2) -> bool { be bitv::union(p1, p2); }
+fn union(&precond p1, &precond p2) -> bool {
+    ret tritv_union(p1, p2);
+}
 
-fn intersect(&precond p1, &precond p2) -> bool { be bitv::intersect(p1, p2); }
+fn intersect(&precond p1, &precond p2) -> bool {
+    ret tritv_intersect(p1, p2);
+}
 
 fn pps_len(&pre_and_post p) -> uint {
     // gratuitous check
@@ -93,86 +99,88 @@ fn pps_len(&pre_and_post p) -> uint {
 
 fn require(uint i, &pre_and_post p) {
     // sets the ith bit in p's pre
-
-    bitv::set(p.precondition, i, true);
+    tritv_set(i, p.precondition, ttrue);
 }
 
 fn require_and_preserve(uint i, &pre_and_post p) {
     // sets the ith bit in p's pre and post
-
-    bitv::set(p.precondition, i, true);
-    bitv::set(p.postcondition, i, true);
+    tritv_set(i, p.precondition, ttrue);
+    tritv_set(i, p.postcondition, ttrue);
 }
 
 fn set_in_postcond(uint i, &pre_and_post p) -> bool {
     // sets the ith bit in p's post
-
-    auto was_set = bitv::get(p.postcondition, i);
-    bitv::set(p.postcondition, i, true);
-    ret !was_set;
+    auto was_set = tritv_get(p.postcondition, i);
+    tritv_set(i, p.postcondition, ttrue);
+    ret was_set != ttrue;
 }
 
 fn set_in_poststate(uint i, &pre_and_post_state s) -> bool {
     // sets the ith bit in p's post
-
-    auto was_set = bitv::get(s.poststate, i);
-    bitv::set(s.poststate, i, true);
-    ret !was_set;
+    auto was_set = tritv_get(s.poststate, i);
+    tritv_set(i, s.poststate, ttrue);
+    ret was_set != ttrue;
 }
 
 fn clear_in_poststate(uint i, &pre_and_post_state s) -> bool {
     // sets the ith bit in p's post
-
-    auto was_set = bitv::get(s.poststate, i);
-    bitv::set(s.poststate, i, false);
-    ret was_set;
+    auto was_set = tritv_get(s.poststate, i);
+    tritv_set(i, s.poststate, tfalse);
+    ret was_set != tfalse;
 }
 
+fn clear_in_postcond(uint i, &pre_and_post s) -> bool {
+    // sets the ith bit in p's post
+    auto was_set = tritv_get(s.postcondition, i);
+    tritv_set(i, s.postcondition, tfalse);
+    ret was_set != tfalse;
+}
 
 // Sets all the bits in a's precondition to equal the
 // corresponding bit in p's precondition.
 fn set_precondition(ts_ann a, &precond p) {
-    bitv::copy(a.conditions.precondition, p);
+    tritv_copy(a.conditions.precondition, p);
 }
 
 
 // Sets all the bits in a's postcondition to equal the
 // corresponding bit in p's postcondition.
 fn set_postcondition(ts_ann a, &postcond p) {
-    bitv::copy(a.conditions.postcondition, p);
+    tritv_copy(a.conditions.postcondition, p);
 }
 
 
 // Sets all the bits in a's prestate to equal the
 // corresponding bit in p's prestate.
 fn set_prestate(ts_ann a, &prestate p) -> bool {
-    ret bitv::copy(a.states.prestate, p);
+    ret tritv_copy(a.states.prestate, p);
 }
 
 
 // Sets all the bits in a's postcondition to equal the
 // corresponding bit in p's postcondition.
 fn set_poststate(ts_ann a, &poststate p) -> bool {
-    ret bitv::copy(a.states.poststate, p);
+    ret tritv_copy(a.states.poststate, p);
 }
 
 
 // Set all the bits in p that are set in new
 fn extend_prestate(&prestate p, &poststate new) -> bool {
-    ret bitv::union(p, new);
+    ret tritv_union(p, new);
 }
 
 
 // Set all the bits in p that are set in new
 fn extend_poststate(&poststate p, &poststate new) -> bool {
-    ret bitv::union(p, new);
+    ret tritv_union(p, new);
 }
 
-// Clears the given bit in p
+// Sets the given bit in p to "don't care"
+// FIXME: is this correct?
 fn relax_prestate(uint i, &prestate p) -> bool {
-    auto was_set = bitv::get(p, i);
-    bitv::set(p, i, false);
-    ret was_set;
+    auto was_set = tritv_get(p, i);
+    tritv_set(i, p, dont_care);
+    ret was_set != dont_care;
 }
 
 // Clears the given bit in p
@@ -185,12 +193,11 @@ fn relax_precond(uint i, &precond p) {
     relax_prestate(i, p);
 }
 
-// Clears all the bits in p
-fn clear(&precond p) { bitv::clear(p); }
+// Sets all the bits in p to "don't care"
+fn clear(&precond p) { tritv_clear(p); }
 
-
-// Sets all the bits in p
-fn set(&precond p) { bitv::set_all(p); }
+// Sets all the bits in p to true
+fn set(&precond p) { tritv_set_all(p); }
 
 fn ann_precond(&ts_ann a) -> precond { ret a.conditions.precondition; }
 
@@ -203,16 +210,17 @@ fn pp_clone(&pre_and_post p) -> pre_and_post {
              postcondition=clone(p.postcondition));
 }
 
-fn clone(prestate p) -> prestate { ret bitv::clone(p); }
+fn clone(prestate p) -> prestate { ret tritv_clone(p); }
 
 
 // returns true if a implies b
 // that is, returns true except if for some bits c and d,
-// c = 1 and d = 0
-fn implies(bitv::t a, bitv::t b) -> bool {
-    auto tmp = bitv::clone(b);
-    bitv::difference(tmp, a);
-    ret bitv::is_false(tmp);
+// c = 1 and d = either 0 or "don't know"
+// FIXME: is this correct?
+fn implies(t a, t b) -> bool {
+    auto tmp = tritv_clone(b);
+    tritv_difference(tmp, a);
+    ret tritv_doesntcare(tmp);
 }
 //
 // Local Variables:
diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs
index d2f068ba5dc6..a21db0407b28 100644
--- a/src/comp/middle/tstate/auxiliary.rs
+++ b/src/comp/middle/tstate/auxiliary.rs
@@ -1,5 +1,3 @@
-
-import std::bitv;
 import std::str;
 import std::vec;
 import std::vec::len;
@@ -39,6 +37,10 @@ import tstate::ann::extend_poststate;
 import tstate::ann::set_precondition;
 import tstate::ann::set_postcondition;
 import tstate::ann::ts_ann;
+import tstate::ann::clear_in_postcond;
+import tstate::ann::clear_in_poststate;
+import tritv::*;
+
 import pretty::ppaux::constr_args_to_str;
 import pretty::ppaux::lit_to_str;
 
@@ -53,7 +55,7 @@ fn comma_str(vec[@constr_arg_use] args) -> str {
         if (comma) { res += ", "; } else { comma = true; }
         alt (a.node) {
             case (carg_base) { res += "*"; }
-            case (carg_ident(?i)) { res += i; }
+            case (carg_ident(?i)) { res += i._0; }
             case (carg_lit(?l)) { res += lit_to_str(l); }
         }
     }
@@ -72,29 +74,33 @@ fn constraint_to_str(&ty::ctxt tcx, &constr c) -> str {
     }
 }
 
-fn bitv_to_str(fn_ctxt fcx, bitv::t v) -> str {
+fn tritv_to_str(fn_ctxt fcx, &tritv::t v) -> str {
     auto s = "";
     auto comma = false;
     for (norm_constraint p in constraints(fcx)) {
-        if (bitv::get(v, p.bit_num)) {
-            s +=
-                if (comma) { ", " } else { comma = true; "" } +
-                    aux::constraint_to_str(fcx.ccx.tcx, p.c);
+        alt (tritv_get(v, p.bit_num)) {
+            case (dont_care) { }
+            case (?t) {
+                s +=
+                    if (comma) { ", " } else { comma = true; "" } +
+                    if (t == tfalse) { "!" } else { "" } +                  
+                    constraint_to_str(fcx.ccx.tcx, p.c);
+            }
         }
     }
     ret s;
 }
 
-fn log_bitv(&fn_ctxt fcx, &bitv::t v) { log bitv_to_str(fcx, v); }
+fn log_tritv(&fn_ctxt fcx, &tritv::t v) { log tritv_to_str(fcx, v); }
 
-fn first_difference_string(&fn_ctxt fcx, &bitv::t expected, &bitv::t actual)
+fn first_difference_string(&fn_ctxt fcx, &tritv::t expected, &tritv::t actual)
    -> str {
     let str s = "";
     auto done = false;
     for (norm_constraint c in constraints(fcx)) {
         if (!done) {
-            if (bitv::get(expected, c.bit_num) &&
-                    !bitv::get(actual, c.bit_num)) {
+            if (tritv_get(expected, c.bit_num) == ttrue &&
+                tritv_get(actual, c.bit_num) != ttrue) {
                 /*
                   FIXME
                   for fun, try either:
@@ -111,11 +117,13 @@ fn first_difference_string(&fn_ctxt fcx, &bitv::t expected, &bitv::t actual)
     ret s;
 }
 
-fn log_bitv_err(fn_ctxt fcx, bitv::t v) { log_err bitv_to_str(fcx, v); }
+fn log_tritv_err(fn_ctxt fcx, tritv::t v) { log_err tritv_to_str(fcx, v); }
 
 fn tos(vec[uint] v) -> str {
     auto res = "";
-    for (uint i in v) { if (i == 0u) { res += "0"; } else { res += "1"; } }
+    for (uint i in v) { if (i == 0u) { res += "0"; } 
+        else if (i == 1u) { res += "1"; }
+        else { res += "?"; } }
     ret res;
 }
 
@@ -124,8 +132,8 @@ fn log_cond(vec[uint] v) { log tos(v); }
 fn log_cond_err(vec[uint] v) { log_err tos(v); }
 
 fn log_pp(&pre_and_post pp) {
-    auto p1 = bitv::to_vec(pp.precondition);
-    auto p2 = bitv::to_vec(pp.postcondition);
+    auto p1 = tritv::to_vec(pp.precondition);
+    auto p2 = tritv::to_vec(pp.postcondition);
     log "pre:";
     log_cond(p1);
     log "post:";
@@ -133,8 +141,8 @@ fn log_pp(&pre_and_post pp) {
 }
 
 fn log_pp_err(&pre_and_post pp) {
-    auto p1 = bitv::to_vec(pp.precondition);
-    auto p2 = bitv::to_vec(pp.postcondition);
+    auto p1 = tritv::to_vec(pp.precondition);
+    auto p2 = tritv::to_vec(pp.postcondition);
     log_err "pre:";
     log_cond_err(p1);
     log_err "post:";
@@ -142,8 +150,8 @@ fn log_pp_err(&pre_and_post pp) {
 }
 
 fn log_states(&pre_and_post_state pp) {
-    auto p1 = bitv::to_vec(pp.prestate);
-    auto p2 = bitv::to_vec(pp.poststate);
+    auto p1 = tritv::to_vec(pp.prestate);
+    auto p2 = tritv::to_vec(pp.poststate);
     log "prestate:";
     log_cond(p1);
     log "poststate:";
@@ -151,8 +159,8 @@ fn log_states(&pre_and_post_state pp) {
 }
 
 fn log_states_err(&pre_and_post_state pp) {
-    auto p1 = bitv::to_vec(pp.prestate);
-    auto p2 = bitv::to_vec(pp.poststate);
+    auto p1 = tritv::to_vec(pp.prestate);
+    auto p2 = tritv::to_vec(pp.poststate);
     log_err "prestate:";
     log_cond_err(p1);
     log_err "poststate:";
@@ -202,7 +210,7 @@ type pred_desc_ = rec(vec[@constr_arg_use] args, uint bit_num);
 
 type pred_desc = spanned[pred_desc_];
 
-type constr_arg_use = constr_arg_general[ident];
+type constr_arg_use = constr_arg_general[tup(ident, def_id)];
 
 tag constraint {
     cinit(uint, span, ident);
@@ -377,12 +385,6 @@ fn block_poststate(&crate_ctxt ccx, &block b) -> poststate {
     ret block_states(ccx, b).poststate;
 }
 
-
-/* sets the pre_and_post for an ann */
-fn with_pp(&crate_ctxt ccx, node_id id, pre_and_post p) {
-    add_node(ccx, id, @rec(conditions=p, states=empty_states(pps_len(p))));
-}
-
 fn set_prestate_ann(&crate_ctxt ccx, node_id id, &prestate pre) -> bool {
     log "set_prestate_ann";
     ret set_prestate(node_id_to_ts_ann(ccx, id), pre);
@@ -519,17 +521,16 @@ fn constraints(&fn_ctxt fcx) -> vec[norm_constraint] {
     ret res;
 }
 
-
-// FIXME:
-// this probably doesn't handle name shadowing well (or at all)
-// variables should really always be id'd by def_id and not ident
 fn match_args(&fn_ctxt fcx, vec[pred_desc] occs, vec[@constr_arg_use] occ) ->
    uint {
     log "match_args: looking at " +
-            pretty::ppaux::constr_args_to_str(std::util::id[str], occ);
+        pretty::ppaux::constr_args_to_str(std::util::fst[ident, def_id], occ);
     for (pred_desc pd in occs) {
         log "match_args: candidate " + pred_desc_to_str(pd);
-        if (ty::args_eq(str::eq, pd.node.args, occ)) { ret pd.node.bit_num; }
+        fn eq(&tup(ident, def_id) p, &tup(ident, def_id) q) -> bool {
+            ret p._1 == q._1;
+        }
+        if (ty::args_eq(eq, pd.node.args, occ)) { ret pd.node.bit_num; }
     }
     fcx.ccx.tcx.sess.bug("match_args: no match for occurring args");
 }
@@ -549,11 +550,20 @@ fn node_id_for_constr(ty::ctxt tcx, node_id t) -> node_id {
 fn expr_to_constr_arg(ty::ctxt tcx, &@expr e) -> @constr_arg_use {
     alt (e.node) {
         case (expr_path(?p)) {
-            if (vec::len(p.node.idents) == 1u) {
-                ret @respan(p.span, carg_ident[ident](p.node.idents.(0)));
-            } else {
-                tcx.sess.bug("exprs_to_constr_args: non-local variable " +
+            alt (tcx.def_map.find(e.id)) {
+                case (some(def_local(?l_id))) {
+                    ret @respan(p.span, carg_ident(tup(p.node.idents.(0),
+                                                       l_id)));
+                }
+                case (some(def_arg(?a_id))) {
+                    ret @respan(p.span, carg_ident(tup(p.node.idents.(0),
+                                                       a_id)));
+                }
+                case (_) {
+                    tcx.sess.bug("exprs_to_constr_args: non-local variable " +
                                  "as pred arg");
+                        
+                }
             }
         }
         case (expr_lit(?l)) { ret @respan(e.span, carg_lit(l)); }
@@ -601,8 +611,8 @@ fn expr_to_constr(ty::ctxt tcx, &@expr e) -> constr {
 
 fn pred_desc_to_str(&pred_desc p) -> str {
     ret "<" + uistr(p.node.bit_num) + ", " +
-            pretty::ppaux::constr_args_to_str(std::util::id[str], p.node.args)
-            + ">";
+        pretty::ppaux::constr_args_to_str(std::util::fst[ident, def_id],
+                                          p.node.args) + ">";
 }
 
 fn substitute_constr_args(&ty::ctxt cx, &vec[@expr] actuals,
@@ -644,6 +654,77 @@ tag if_ty {
     if_check;
     plain_if;
 }
+
+fn local_node_id_to_def_id(&fn_ctxt fcx, &span sp, &node_id i) -> def_id {
+    alt (fcx.ccx.tcx.def_map.find(i)) {
+        case (some(def_local(?d_id))) {
+            ret d_id;
+        }
+        case (some (def_arg(?a_id))) {
+            ret a_id;
+        }
+        case (some(_)) {
+            fcx.ccx.tcx.sess.span_fatal(sp, "local_node_id_to_def_id: id \
+               isn't a local");
+        }
+        case (none) {
+            // should really be bug. span_bug()?
+            fcx.ccx.tcx.sess.span_fatal(sp, "local_node_id_to_def_id: id \
+               is unbound");
+        }
+    }
+}
+
+fn forget_in_postcond(&fn_ctxt fcx, &span dead_sp,
+                      node_id parent_exp, node_id dead_v) {
+    // In the postcondition given by parent_exp, clear the bits
+    // for any constraints mentioning dead_v
+    auto d_id = local_node_id_to_def_id(fcx, dead_sp, dead_v);
+    for (norm_constraint c in constraints(fcx)) {
+        if (constraint_mentions(fcx, c, d_id)) {
+            clear_in_postcond(c.bit_num,
+              node_id_to_ts_ann(fcx.ccx, parent_exp).conditions);
+        }
+    }
+}
+
+fn forget_in_poststate(&fn_ctxt fcx, &span dead_sp, 
+                       node_id parent_exp, node_id dead_v) -> bool {
+    // In the poststate given by parent_exp, clear the bits
+    // for any constraints mentioning dead_v
+    auto d_id = local_node_id_to_def_id(fcx, dead_sp, dead_v);
+    auto changed = false;
+    for (norm_constraint c in constraints(fcx)) {
+        if (constraint_mentions(fcx, c, d_id)) {
+            changed = clear_in_poststate(c.bit_num,
+              node_id_to_ts_ann(fcx.ccx, parent_exp).states) || changed;
+        }
+    }
+    ret changed;
+}
+
+fn constraint_mentions(&fn_ctxt fcx, &norm_constraint c, &def_id v) -> bool {
+    ret (alt (c.c.node.c) {
+            case (ninit(_)) {
+                v == local_def(c.c.node.id)
+            }
+            case (npred(_, ?args)) {
+                args_mention(args, v)
+            }
+        });
+}
+
+
+fn args_mention(&vec[@constr_arg_use] args, &def_id v) -> bool {
+    fn mentions(&def_id v, &@constr_arg_use a) -> bool {
+        alt (a.node) {
+            case (carg_ident(?p1)) { p1._1 == v }
+            case (_)               { false }
+        }
+    }
+    ret util::common::any[@constr_arg_use](bind mentions(v,_), args);
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/comp/middle/tstate/bitvectors.rs b/src/comp/middle/tstate/bitvectors.rs
index 3850986a4f8f..d29e57c7d301 100644
--- a/src/comp/middle/tstate/bitvectors.rs
+++ b/src/comp/middle/tstate/bitvectors.rs
@@ -1,12 +1,11 @@
-
-import std::bitv;
+import front::ast::*;
 import std::vec;
 import std::vec::len;
 import std::vec::slice;
-import front::ast::*;
 import aux::fn_ctxt;
 import aux::fn_info;
-import aux::log_bitv;
+import aux::log_tritv;
+import aux::log_tritv_err;
 import aux::num_constraints;
 import aux::cinit;
 import aux::cpred;
@@ -40,6 +39,7 @@ import tstate::ann::clone;
 import tstate::ann::set_in_postcond;
 import tstate::ann::set_in_poststate;
 import tstate::ann::clear_in_poststate;
+import tritv::*;
 
 fn bit_num(&fn_ctxt fcx, &constr_ c) -> uint {
     assert (fcx.enclosing.constrs.contains_key(c.id));
@@ -67,57 +67,81 @@ fn bit_num(&fn_ctxt fcx, &constr_ c) -> uint {
 }
 
 fn promises(&fn_ctxt fcx, &poststate p, &constr_ c) -> bool {
-    ret bitv::get(p, bit_num(fcx, c));
+    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 }
+    }
+}
+
+// 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));
+    }
+}
 
 // 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.
-fn seq_preconds(fn_ctxt fcx, vec[pre_and_post] pps) -> precond {
-    let uint sz = len[pre_and_post](pps);
+fn seq_preconds(&fn_ctxt fcx, &vec[pre_and_post] pps) -> precond {
+    let uint sz = len(pps);
     let uint num_vars = num_constraints(fcx.enclosing);
+
+    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;
+        }
+    }
+
     if (sz >= 1u) {
         auto first = pps.(0);
         assert (pps_len(first) == num_vars);
-        let precond rest =
-            seq_preconds(fcx, slice[pre_and_post](pps, 1u, sz));
-        difference(rest, first.postcondition);
-        auto res = clone(first.precondition);
-        union(res, rest);
-        log "seq_preconds:";
-        log "first.postcondition =";
-        log_bitv(fcx, first.postcondition);
-        log "rest =";
-        log_bitv(fcx, rest);
-        log "returning";
-        log_bitv(fcx, res);
-        ret res;
+        ret seq_preconds_go(fcx, slice(pps, 1u, sz), first);
     } else { ret true_precond(num_vars); }
 }
 
-
-/* works on either postconds or preconds
- should probably rethink the whole type synonym situation */
-fn union_postconds_go(&postcond first, &vec[postcond] rest) -> postcond {
-    auto sz = vec::len[postcond](rest);
-    if (sz > 0u) {
-        auto other = rest.(0);
-        union(first, other);
-        union_postconds_go(first,
-                           slice[postcond](rest, 1u, len[postcond](rest)));
-    }
-    ret first;
-}
-
-fn union_postconds(uint nv, &vec[postcond] pcs) -> postcond {
-    if (len[postcond](pcs) > 0u) {
-        ret union_postconds_go(bitv::clone(pcs.(0)), pcs);
-    } else { ret empty_prestate(nv); }
-}
-
-
 /* Gee, maybe we could use foldl or something */
 fn intersect_postconds_go(&postcond first, &vec[postcond] rest) -> postcond {
     auto sz = vec::len[postcond](rest);
@@ -133,7 +157,7 @@ fn intersect_postconds_go(&postcond first, &vec[postcond] rest) -> postcond {
 
 fn intersect_postconds(&vec[postcond] pcs) -> postcond {
     assert (len[postcond](pcs) > 0u);
-    ret intersect_postconds_go(bitv::clone(pcs.(0)), pcs);
+    ret intersect_postconds_go(tritv_clone(pcs.(0)), pcs);
 }
 
 fn gen(&fn_ctxt fcx, node_id id, &constr_ c) -> bool {
diff --git a/src/comp/middle/tstate/ck.rs b/src/comp/middle/tstate/ck.rs
index 27933ee8eab1..444880e02cac 100644
--- a/src/comp/middle/tstate/ck.rs
+++ b/src/comp/middle/tstate/ck.rs
@@ -51,11 +51,11 @@ import aux::stmt_poststate;
 import aux::stmt_to_ann;
 import aux::num_constraints;
 import aux::fixed_point_states;
-import aux::bitv_to_str;
+import aux::tritv_to_str;
 import aux::first_difference_string;
 import pretty::pprust::ty_to_str;
 import util::common::log_stmt_err;
-import aux::log_bitv_err;
+import aux::log_tritv_err;
 import bitvectors::promises;
 import annotate::annotate_crate;
 import collect_locals::mk_f_to_fn_info;
@@ -83,9 +83,9 @@ fn check_states_expr(&fn_ctxt fcx, &@expr e) {
                 ") for expression:\n";
         s += pretty::pprust::expr_to_str(e);
         s += "\nPrecondition:\n";
-        s += bitv_to_str(fcx, prec);
+        s += tritv_to_str(fcx, prec);
         s += "\nPrestate:\n";
-        s += bitv_to_str(fcx, pres);
+        s += tritv_to_str(fcx, pres);
         fcx.ccx.tcx.sess.span_fatal(e.span, s);
     }
 }
@@ -112,9 +112,9 @@ fn check_states_stmt(&fn_ctxt fcx, &@stmt s) {
                 ") for statement:\n";
         ss += pretty::pprust::stmt_to_str(*s);
         ss += "\nPrecondition:\n";
-        ss += bitv_to_str(fcx, prec);
+        ss += tritv_to_str(fcx, prec);
         ss += "\nPrestate: \n";
-        ss += bitv_to_str(fcx, pres);
+        ss += tritv_to_str(fcx, pres);
         fcx.ccx.tcx.sess.span_fatal(s.span, ss);
     }
 }
diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs
index 8c7cb5067a7c..91d1f82f366d 100644
--- a/src/comp/middle/tstate/pre_post_conditions.rs
+++ b/src/comp/middle/tstate/pre_post_conditions.rs
@@ -47,18 +47,23 @@ import aux::expr_to_constr;
 import aux::if_ty;
 import aux::if_check;
 import aux::plain_if;
+import aux::forget_in_postcond;
 
 import aux::constraints_expr;
 import aux::substitute_constr_args;
 import aux::ninit;
 import aux::npred;
 import aux::path_to_ident;
-import bitvectors::seq_preconds;
-import bitvectors::union_postconds;
-import bitvectors::intersect_postconds;
 import bitvectors::bit_num;
-import bitvectors::gen;
+import bitvectors::promises;
+import bitvectors::seq_preconds;
+import bitvectors::seq_postconds;
+import bitvectors::intersect_postconds;
+import bitvectors::declare_var;
+import bitvectors::gen_poststate;
+import bitvectors::kill_poststate;
 import bitvectors::relax_precond_block;
+import bitvectors::gen;
 import front::ast::*;
 import util::common::new_int_hash;
 import util::common::new_def_hash;
@@ -153,9 +158,7 @@ fn find_pre_post_exprs(&fn_ctxt fcx, &vec[@expr] args, node_id id) {
     auto pps = vec::map[@expr, pre_and_post](g, args);
     auto h = get_post;
     set_pre_and_post(fcx.ccx, id, seq_preconds(fcx, pps),
-                     union_postconds(nv,
-                                     vec::map[pre_and_post,
-                                              postcond](h, pps)));
+                     seq_postconds(fcx, vec::map(h, pps)));
 }
 
 fn find_pre_post_loop(&fn_ctxt fcx, &@local l, &@expr index, &block body,
@@ -212,9 +215,8 @@ fn join_then_else(&fn_ctxt fcx, &@expr antec, &block conseq,
                              [expr_pp(fcx.ccx, antec),
                               expr_pp(fcx.ccx, altern)]);
             auto postcond_false_case =
-                union_postconds(num_local_vars,
-                                [expr_postcond(fcx.ccx, antec),
-                                 expr_postcond(fcx.ccx, altern)]);
+                seq_postconds(fcx, [expr_postcond(fcx.ccx, antec),
+                                    expr_postcond(fcx.ccx, altern)]);
 
             /* Be sure to set the bit for the check condition here,
              so that it's *not* set in the alternative. */
@@ -230,14 +232,12 @@ fn join_then_else(&fn_ctxt fcx, &@expr antec, &block conseq,
                              [expr_pp(fcx.ccx, antec),
                               block_pp(fcx.ccx, conseq)]);
             auto postcond_true_case =
-                union_postconds(num_local_vars,
-                                [expr_postcond(fcx.ccx, antec),
-                                 block_postcond(fcx.ccx, conseq)]);
+                seq_postconds(fcx, [expr_postcond(fcx.ccx, antec),
+                                    block_postcond(fcx.ccx, conseq)]);
 
             auto precond_res =
-                union_postconds(num_local_vars,
-                                [precond_true_case,
-                                 precond_false_case]);
+                seq_postconds(fcx, [precond_true_case,
+                                    precond_false_case]);
             auto postcond_res =
                 intersect_postconds([postcond_true_case,
                                      postcond_false_case]);
@@ -274,7 +274,7 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
     auto num_local_vars = num_constraints(enclosing);
     fn do_rand_(fn_ctxt fcx, &@expr e) { find_pre_post_expr(fcx, e); }
     log "find_pre_post_expr (num_constraints =" + uistr(num_local_vars) +
-            "):";
+        "):";
     log_expr(*e);
     alt (e.node) {
         case (expr_call(?operator, ?operands)) {
@@ -288,13 +288,13 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
             auto pp = expr_pp(fcx.ccx, e);
             for (@ty::constr_def c in constraints_expr(fcx.ccx.tcx, operator))
                 {
-                auto i =
-                    bit_num(fcx,
-                            rec(id=c.node.id._1,
-                                c=substitute_constr_args(fcx.ccx.tcx,
-                                                         operands, c)));
-                require(i, pp);
-            }
+                    auto i =
+                        bit_num(fcx,
+                                rec(id=c.node.id._1,
+                                    c=substitute_constr_args(fcx.ccx.tcx,
+                                                             operands, c)));
+                    require(i, pp);
+                }
 
             /* if this is a failing call, its postcondition sets everything */
             alt (controlflow_expr(fcx.ccx, operator)) {
@@ -359,17 +359,17 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
             find_pre_post_exprs(fcx, es, e.id);
         }
         case (expr_move(?lhs, ?rhs)) {
-            // FIXME: this needs to deinitialize the rhs
             alt (lhs.node) {
                 case (expr_path(?p)) {
                     gen_if_local(fcx, lhs, rhs, e.id, lhs.id, p);
                 }
                 case (_) { find_pre_post_exprs(fcx, [lhs, rhs], e.id); }
             }
+            forget_in_postcond(fcx, rhs.span, e.id, rhs.id);
         }
         case (expr_swap(?lhs, ?rhs)) {
             // Both sides must already be initialized
-
+            
             find_pre_post_exprs(fcx, [lhs, rhs], e.id);
         }
         case (expr_assign(?lhs, ?rhs)) {
@@ -455,9 +455,8 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
             find_pre_post_block(fcx, body);
             find_pre_post_expr(fcx, test);
             auto loop_postcond =
-                union_postconds(num_local_vars,
-                                [block_postcond(fcx.ccx, body),
-                                 expr_postcond(fcx.ccx, test)]);
+                seq_postconds(fcx, [block_postcond(fcx.ccx, body),
+                                    expr_postcond(fcx.ccx, test)]);
             /* conservative approximination: if the body
                could break or cont, the test may never be executed */
 
@@ -566,6 +565,9 @@ fn find_pre_post_stmt(&fn_ctxt fcx, &stmt s) {
                 case (decl_local(?alocal)) {
                     alt (alocal.node.init) {
                         case (some(?an_init)) {
+                            /* LHS always becomes initialized,
+                             whether or not this is a move */
+
                             find_pre_post_expr(fcx, an_init.expr);
                             copy_pre_post(fcx.ccx, alocal.node.id, 
                                           an_init.expr);
@@ -576,6 +578,15 @@ fn find_pre_post_stmt(&fn_ctxt fcx, &stmt s) {
                             gen(fcx, id,
                                 rec(id=alocal.node.id, 
                                     c=ninit(alocal.node.ident)));
+                            
+                            alt (an_init.op) {
+                                case (init_move) {
+                                    forget_in_postcond(fcx, an_init.expr.span,
+                                                       id, an_init.expr.id);
+                                }
+                                case (_) { /* nothing gets deinitialized */ } 
+                            }
+
                         }
                         case (none) {
                             clear_pp(node_id_to_ts_ann(fcx.ccx,
@@ -643,6 +654,7 @@ fn find_pre_post_block(&fn_ctxt fcx, block b) {
     plus_option[pre_and_post](pps,
                               option::map[@expr,
                                           pre_and_post](g, b.node.expr));
+
     auto block_precond = seq_preconds(fcx, pps);
     auto h = get_post;
     auto postconds = vec::map[pre_and_post, postcond](h, pps);
@@ -654,7 +666,7 @@ fn find_pre_post_block(&fn_ctxt fcx, block b) {
     /* conservative approximation */
 
     if (!has_nonlocal_exits(b)) {
-        block_postcond = union_postconds(nv, postconds);
+        block_postcond = seq_postconds(fcx, postconds);
     }
     set_pre_and_post(fcx.ccx, b.node.id, block_precond, block_postcond);
 }
diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs
index 2efcb672794e..1637924d0a59 100644
--- a/src/comp/middle/tstate/states.rs
+++ b/src/comp/middle/tstate/states.rs
@@ -1,5 +1,3 @@
-
-import std::bitv;
 import std::vec;
 import std::vec::plus_option;
 import std::vec::cat_options;
@@ -15,7 +13,6 @@ import tstate::ann::postcond;
 import tstate::ann::empty_pre_post;
 import tstate::ann::empty_poststate;
 import tstate::ann::require_and_preserve;
-import tstate::ann::union;
 import tstate::ann::intersect;
 import tstate::ann::empty_prestate;
 import tstate::ann::prestate;
@@ -48,8 +45,8 @@ import aux::extend_poststate_ann;
 import aux::set_prestate_ann;
 import aux::set_poststate_ann;
 import aux::pure_exp;
-import aux::log_bitv;
-import aux::log_bitv_err;
+import aux::log_tritv;
+import aux::log_tritv_err;
 import aux::stmt_to_ann;
 import aux::log_states;
 import aux::log_states_err;
@@ -63,9 +60,12 @@ import aux::path_to_ident;
 import aux::if_ty;
 import aux::if_check;
 import aux::plain_if;
+import aux::forget_in_poststate;
+import tritv::tritv_clone;
+import tritv::tritv_set;
+import tritv::ttrue;
 
 import bitvectors::seq_preconds;
-import bitvectors::union_postconds;
 import bitvectors::intersect_postconds;
 import bitvectors::declare_var;
 import bitvectors::bit_num;
@@ -193,8 +193,8 @@ fn join_then_else(&fn_ctxt fcx, &@expr antec, &block conseq,
             alt (chk) {
                 case (if_check) {
                     let aux::constr c = expr_to_constr(fcx.ccx.tcx, antec);
-                    conseq_prestate = bitv::clone(conseq_prestate);
-                    bitv::set(conseq_prestate, bit_num(fcx, c.node), true);
+                    conseq_prestate = tritv_clone(conseq_prestate);
+                    tritv_set(bit_num(fcx, c.node), conseq_prestate, ttrue);
                 }
                 case (_) {}
             }
@@ -352,8 +352,7 @@ fn find_pre_post_state_expr(&fn_ctxt fcx, &prestate pres, @expr e) -> bool {
             ret changed;
         }
         case (expr_move(?lhs, ?rhs)) {
-            // FIXME: this needs to deinitialize the rhs
-
+        
             extend_prestate_ann(fcx.ccx, e.id, pres);
             alt (lhs.node) {
                 case (expr_path(?p)) {
@@ -362,9 +361,8 @@ fn find_pre_post_state_expr(&fn_ctxt fcx, &prestate pres, @expr e) -> bool {
                     changed = pure_exp(fcx.ccx, lhs.id, pres) || changed;
                     changed = find_pre_post_state_expr
                         (fcx, pres, rhs) || changed;
-                    changed = extend_poststate_ann
-                        (fcx.ccx, e.id, expr_poststate(fcx.ccx, rhs)) ||
-                        changed;
+                    // not extending e's poststate,
+                    // because rhs is getting deinit'd anyway
                     changed = gen_if_local(fcx, lhs.id, e.id, p) || changed;
                 }
                 case (_) {
@@ -381,6 +379,9 @@ fn find_pre_post_state_expr(&fn_ctxt fcx, &prestate pres, @expr e) -> bool {
                         || changed;
                 }
             }
+
+            changed = forget_in_poststate(fcx, rhs.span, e.id, rhs.id)
+                || changed;
             ret changed;
         }
         case (expr_assign(?lhs, ?rhs)) {
@@ -725,25 +726,42 @@ fn find_pre_post_state_stmt(&fn_ctxt fcx, &prestate pres, @stmt s) -> bool {
                                 find_pre_post_state_expr(fcx, pres,
                                                          an_init.expr)
                                 || changed;
-                            changed =
-                                extend_poststate(stmt_ann.states.poststate,
-                                                 expr_poststate(fcx.ccx,
-                                                                an_init.expr))
-                                    || changed;
+
+
+                            /* FIXME less copypasta */
+                            alt (an_init.op) {
+                                case (init_move) {
+                                    changed = forget_in_poststate(fcx,
+                                      an_init.expr.span, id, an_init.expr.id)
+                                        || changed;
+                /* Safe to forget rhs's poststate here 'cause it's a var. */
+                                }
+                                case (_) { /* nothing gets deinitialized */ 
+                                    changed =
+                                      extend_poststate(
+                                        stmt_ann.states.poststate,
+                                        expr_poststate(fcx.ccx, an_init.expr))
+                                        || changed;
+                                } 
+                            }
+
                             changed =
                                 gen_poststate(fcx, id,
                                               rec(id=alocal.node.id,
                                                   c=ninit(alocal.node.ident)))
                                 || changed;
-                            log "Summary: stmt = ";
-                            log_stmt(*s);
-                            log "prestate = ";
-                            log bitv::to_str(stmt_ann.states.prestate);
-                            log_bitv(fcx, stmt_ann.states.prestate);
-                            log "poststate =";
-                            log_bitv(fcx, stmt_ann.states.poststate);
-                            log "changed =";
-                            log changed;
+                            
+                            /*
+                            log_err "Summary: stmt = ";
+                            log_stmt_err(*s);
+                            log_err "prestate = ";
+                            log_tritv_err(fcx, stmt_ann.states.prestate);
+                            log_err "poststate =";
+                            log_tritv_err(fcx, stmt_ann.states.poststate);
+                            log_err "changed =";
+                            log_err changed;
+                            */
+
                             ret changed;
                         }
                         case (none) {
@@ -778,17 +796,18 @@ fn find_pre_post_state_stmt(&fn_ctxt fcx, &prestate pres, @stmt s) -> bool {
             changed =
                 extend_poststate(stmt_ann.states.poststate,
                                  expr_poststate(fcx.ccx, ex)) || changed;
+            
             /*
-              log("Summary: stmt = ");
-              log_stmt(*s);
-              log("prestate = ");
-              log(bitv::to_str(stmt_ann.states.prestate));
-              log_bitv(enclosing, stmt_ann.states.prestate);
-              log("poststate =");
-              log(bitv::to_str(stmt_ann.states.poststate));
-              log_bitv(enclosing, stmt_ann.states.poststate);
-              log("changed =");
-              log(changed);
+              log_err("Summary: stmt = ");
+              log_stmt_err(*s);
+              log_err("prestate = ");
+              //              log_err(bitv::to_str(stmt_ann.states.prestate));
+              log_tritv_err(fcx, stmt_ann.states.prestate);
+              log_err("poststate =");
+              //   log_err(bitv::to_str(stmt_ann.states.poststate));
+              log_tritv_err(fcx, stmt_ann.states.poststate);
+              log_err("changed =");
+              log_err(changed);
             */
 
             ret changed;
@@ -833,9 +852,9 @@ fn find_pre_post_state_block(&fn_ctxt fcx, &prestate pres0, &block b) ->
     log_err "poststate = ";
     log_states_err(block_states(fcx.ccx, b));
     log_err "pres0:";
-    log_bitv_err(fcx, pres0);
+    log_tritv_err(fcx, pres0);
     log_err "post:";
-    log_bitv_err(fcx, post);
+    log_tritv_err(fcx, post);
     */
 
     ret changed;
@@ -844,7 +863,7 @@ fn find_pre_post_state_block(&fn_ctxt fcx, &prestate pres0, &block b) ->
 fn find_pre_post_state_fn(&fn_ctxt fcx, &_fn f) -> bool {
     auto num_local_vars = num_constraints(fcx.enclosing);
     auto changed =
-        find_pre_post_state_block(fcx, empty_prestate(num_local_vars),
+        find_pre_post_state_block(fcx, block_prestate(fcx.ccx, f.body),
                                   f.body);
     // Treat the tail expression as a return statement
 
diff --git a/src/comp/middle/tstate/tritv.rs b/src/comp/middle/tstate/tritv.rs
new file mode 100644
index 000000000000..bf4a08ad9863
--- /dev/null
+++ b/src/comp/middle/tstate/tritv.rs
@@ -0,0 +1,246 @@
+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;
+  let vec[uint] res = [];
+  while (i < v.nbits) {
+    res += [alt (tritv_get(v, i)) {
+        case (dont_care) { 2u }
+        case (ttrue)     { 1u }
+        case (tfalse)    { 0u } }];
+    i += 1u;
+  }
+  ret res;
+}
+//
+// 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:
+//
diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc
index 565b436d4045..3a96e03f0c51 100644
--- a/src/comp/rustc.rc
+++ b/src/comp/rustc.rc
@@ -33,6 +33,7 @@ mod middle {
         mod pre_post_conditions;
         mod states;
         mod ann;
+        mod tritv;
     }
 }
 
diff --git a/src/comp/util/common.rs b/src/comp/util/common.rs
index 801dda99de4c..87a1908f8e06 100644
--- a/src/comp/util/common.rs
+++ b/src/comp/util/common.rs
@@ -252,6 +252,15 @@ fn is_hex_digit(char c) -> bool {
 }
 
 fn is_bin_digit(char c) -> bool { ret c == '0' || c == '1'; }
+
+// FIXME move to vec
+fn any[T](&fn(&T) -> bool f, &vec[T] v) -> bool {
+    for (T t in v) {
+        if (f(t)) { ret true; } 
+    }
+    ret false;
+}
+
 //
 // Local Variables:
 // mode: rust
diff --git a/src/test/compile-fail/use-after-move.rs b/src/test/compile-fail/use-after-move.rs
new file mode 100644
index 000000000000..6c26279c581a
--- /dev/null
+++ b/src/test/compile-fail/use-after-move.rs
@@ -0,0 +1,6 @@
+// error-pattern: Unsatisfied precondition constraint (for example, init(x
+fn main() {
+  auto x = @5;
+  auto y <- x;
+  log *x;
+}
\ No newline at end of file