Skip to content

Commit 182c413

Browse files
committed
Propagate constraints through copy, move, and swap
Assignments and moves with a simple local variable reference on the RHS now propagate any typestate constraints the RHS was involved in to the LHS. Swaps where both sides are local variables exchange the constraints. This was a pain in the butt and I'm still not proud of the resulting code. Needs refactoring like whoa.
1 parent be6febb commit 182c413

File tree

3 files changed

+442
-133
lines changed

3 files changed

+442
-133
lines changed

Diff for: src/comp/middle/tstate/auxiliary.rs

+258-23
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import std::vec;
12
import std::ivec;
3+
import std::int::str;
24
import std::str;
35
import std::option;
4-
import std::option::none;
5-
import std::option::some;
6-
import std::option::maybe;
6+
import std::option::*;
77
import std::int;
88
import std::uint;
99
import syntax::ast::*;
@@ -26,19 +26,32 @@ import tstate::ann::empty_states;
2626
import tstate::ann::pps_len;
2727
import tstate::ann::set_prestate;
2828
import tstate::ann::set_poststate;
29+
import tstate::ann::set_in_poststate_;
2930
import tstate::ann::extend_prestate;
3031
import tstate::ann::extend_poststate;
3132
import tstate::ann::set_precondition;
3233
import tstate::ann::set_postcondition;
34+
import tstate::ann::set_in_postcond_;
3335
import tstate::ann::ts_ann;
3436
import tstate::ann::clear_in_postcond;
3537
import tstate::ann::clear_in_poststate;
3638
import tstate::ann::clear_in_poststate_;
3739
import tritv::*;
40+
import bitvectors::promises_;
3841

3942
import syntax::print::pprust::constr_args_to_str;
43+
import syntax::print::pprust::constr_arg_to_str;
4044
import syntax::print::pprust::lit_to_str;
4145

46+
// Used to communicate which operands should be invalidated
47+
// to helper functions
48+
tag oper_type {
49+
oper_move;
50+
oper_swap;
51+
oper_assign;
52+
oper_assign_op;
53+
oper_pure;
54+
}
4255

4356
/* logging funs */
4457
fn def_id_to_str(def_id d) -> str {
@@ -195,6 +208,8 @@ type pred_desc_ = rec((@constr_arg_use)[] args, uint bit_num);
195208
196209
type pred_desc = spanned[pred_desc_];
197210
211+
// FIXME: Should be node_id, since we can only talk
212+
// about locals.
198213
type constr_arg_use = constr_arg_general[tup(ident, def_id)];
199214
200215
tag constraint {
@@ -564,9 +579,13 @@ fn expr_to_constr_arg(ty::ctxt tcx, &@expr e) -> @constr_arg_use {
564579
}
565580

566581
fn exprs_to_constr_args(ty::ctxt tcx, &(@expr)[] args)
567-
-> (@constr_arg_use)[] {
582+
-> (@constr_arg_use)[] {
568583
auto f = bind expr_to_constr_arg(tcx, _);
569-
ret ivec::map(f, args);
584+
let (@constr_arg_use)[] rslt = ~[];
585+
for (@expr e in args) {
586+
rslt += ~[f(e)];
587+
}
588+
rslt
570589
}
571590

572591
fn expr_to_constr(ty::ctxt tcx, &@expr e) -> constr {
@@ -602,14 +621,9 @@ fn expr_to_constr(ty::ctxt tcx, &@expr e) -> constr {
602621
}
603622

604623
fn pred_desc_to_str(&pred_desc p) -> str {
605-
// FIXME: Remove this vec->ivec conversion.
606-
let (@constr_arg_use)[] cau_ivec = ~[];
607-
for (@constr_arg_use cau in p.node.args) {
608-
cau_ivec += ~[cau];
609-
}
610-
611-
ret "<" + uint::str(p.node.bit_num) + ", " +
612-
constr_args_to_str(std::util::fst[ident, def_id], cau_ivec) + ">";
624+
"<" + uint::str(p.node.bit_num) + ", " +
625+
constr_args_to_str(std::util::fst[ident, def_id],
626+
p.node.args) + ">"
613627
}
614628

615629
fn substitute_constr_args(&ty::ctxt cx, &(@expr)[] actuals,
@@ -621,8 +635,6 @@ fn substitute_constr_args(&ty::ctxt cx, &(@expr)[] actuals,
621635
ret npred(c.node.path, rslt);
622636
}
623637

624-
type subst = tup(arg, @expr)[];
625-
626638
fn substitute_arg(&ty::ctxt cx, &(@expr)[] actuals, @constr_arg a) ->
627639
@constr_arg_use {
628640
auto num_actuals = ivec::len(actuals);
@@ -640,6 +652,144 @@ fn substitute_arg(&ty::ctxt cx, &(@expr)[] actuals, @constr_arg a) ->
640652
}
641653
}
642654

655+
fn pred_desc_matches(&(constr_arg_general_[tup(ident, def_id)])[] pattern,
656+
&pred_desc desc) -> bool {
657+
auto i = 0u;
658+
for (@constr_arg_use c in desc.node.args) {
659+
auto n = pattern.(i);
660+
alt (c.node) {
661+
case (carg_ident(?p)) {
662+
alt (n) {
663+
case (carg_ident(?q)) {
664+
if (p._1 != q._1) {
665+
ret false;
666+
}
667+
}
668+
case (_) { ret false; }
669+
}
670+
}
671+
case (carg_base) {
672+
if (n != carg_base) {
673+
ret false;
674+
}
675+
}
676+
case (carg_lit(?l)) {
677+
alt (n) {
678+
case (carg_lit(?m)) {
679+
if (!lit_eq(l, m)) {
680+
ret false;
681+
}
682+
}
683+
case (_) { ret false; }
684+
}
685+
}
686+
}
687+
i += 1u;
688+
}
689+
ret true;
690+
}
691+
692+
fn find_instance_(&(constr_arg_general_[tup(ident, def_id)])[] pattern,
693+
&pred_desc[] descs) -> option::t[uint] {
694+
for (pred_desc d in descs) {
695+
if (pred_desc_matches(pattern, d)) {
696+
ret some(d.node.bit_num);
697+
}
698+
}
699+
ret none;
700+
}
701+
702+
type inst = tup(ident, def_id);
703+
type subst = tup(inst, inst)[];
704+
705+
fn find_instances(&fn_ctxt fcx, &subst subst, &constraint c)
706+
-> vec[tup(uint, uint)] {
707+
708+
let vec[tup(uint, uint)] rslt = [];
709+
if (ivec::len(subst) == 0u) {
710+
ret rslt;
711+
}
712+
713+
alt (c) {
714+
case (cinit(_,_,_)) { /* this is dealt with separately */ }
715+
case (cpred(?p, ?descs)) {
716+
for (pred_desc d in *descs) {
717+
if (args_mention(d.node.args, find_in_subst_bool, subst)) {
718+
auto old_bit_num = d.node.bit_num;
719+
auto new = replace(subst, d);
720+
alt (find_instance_(new, *descs)) {
721+
case (some(?d1)) {
722+
rslt += [tup(old_bit_num, d1)];
723+
}
724+
case (_) { }
725+
}
726+
}
727+
}
728+
}
729+
}
730+
rslt
731+
}
732+
733+
fn find_in_subst(def_id id, &subst s) -> option::t[inst] {
734+
for (tup(inst, inst) p in s) {
735+
if (id == p._0._1) {
736+
ret some(p._1);
737+
}
738+
}
739+
ret none;
740+
}
741+
742+
fn find_in_subst_bool(&subst s, def_id id) -> bool {
743+
is_some(find_in_subst(id, s))
744+
}
745+
746+
fn insts_to_str(&(constr_arg_general_[inst])[] stuff) -> str {
747+
auto rslt = "<";
748+
for (constr_arg_general_[inst] i in stuff) {
749+
rslt += " " + alt(i) {
750+
case (carg_ident(?p)) { p._0 }
751+
case (carg_base) { "*" }
752+
case (carg_lit(_)) { "[lit]" } } + " ";
753+
}
754+
rslt += ">";
755+
rslt
756+
}
757+
758+
fn replace(subst subst, pred_desc d) -> (constr_arg_general_[inst])[] {
759+
let (constr_arg_general_[inst])[] rslt = ~[];
760+
for (@constr_arg_use c in d.node.args) {
761+
alt (c.node) {
762+
case (carg_ident(?p)) {
763+
alt (find_in_subst(p._1, subst)) {
764+
case (some(?new)) {
765+
rslt += ~[carg_ident(new)];
766+
}
767+
case (_) {
768+
rslt += ~[c.node];
769+
}
770+
}
771+
}
772+
case (_) {
773+
// log_err "##";
774+
rslt += ~[c.node];
775+
}
776+
}
777+
}
778+
779+
/*
780+
for (constr_arg_general_[tup(ident, def_id)] p in rslt) {
781+
alt (p) {
782+
case (carg_ident(?p)) {
783+
log_err p._0;
784+
}
785+
case (_) {}
786+
}
787+
}
788+
*/
789+
790+
ret rslt;
791+
}
792+
643793
fn path_to_ident(&ty::ctxt cx, &path p) -> ident {
644794
alt (ivec::last(p.node.idents)) {
645795
case (none) { cx.sess.span_fatal(p.span, "Malformed path"); }
@@ -684,6 +834,56 @@ fn local_node_id_to_def_id(&fn_ctxt fcx, &node_id i) -> option::t[def_id] {
684834
}
685835
}
686836

837+
fn copy_in_postcond(&fn_ctxt fcx, node_id parent_exp, inst dest, inst src,
838+
oper_type ty) {
839+
auto post = node_id_to_ts_ann(fcx.ccx, parent_exp).conditions.
840+
postcondition;
841+
copy_in_poststate_two(fcx, post, post, dest, src, ty);
842+
}
843+
844+
// FIXME refactor
845+
fn copy_in_poststate(&fn_ctxt fcx, &poststate post, inst dest, inst src,
846+
oper_type ty) {
847+
copy_in_poststate_two(fcx, post, post, dest, src, ty);
848+
}
849+
850+
// In target_post, set the bits corresponding to copies of any
851+
// constraints mentioning src that are set in src_post, with
852+
// dest substituted for src.
853+
// (This doesn't create any new constraints. If a new, substituted
854+
// constraint isn't already in the bit vector, it's ignored.)
855+
fn copy_in_poststate_two(&fn_ctxt fcx, &poststate src_post,
856+
&poststate target_post, inst dest, inst src,
857+
oper_type ty) {
858+
auto subst;
859+
alt (ty) {
860+
case (oper_swap) {
861+
subst = ~[tup(dest, src),
862+
tup(src, dest)];
863+
}
864+
case (oper_assign_op) {
865+
ret; // Don't do any propagation
866+
}
867+
case (_) {
868+
subst = ~[tup(src, dest)];
869+
}
870+
}
871+
872+
for each (@tup(node_id, constraint) p in
873+
fcx.enclosing.constrs.items()) {
874+
// replace any occurrences of the src def_id with the
875+
// dest def_id
876+
auto instances = find_instances(fcx, subst, p._1);
877+
878+
for (tup(uint,uint) p in instances) {
879+
if (promises_(p._0, src_post)) {
880+
set_in_poststate_(p._1, target_post);
881+
}
882+
}
883+
}
884+
}
885+
886+
687887
/* FIXME should refactor this better */
688888
fn forget_in_postcond(&fn_ctxt fcx, node_id parent_exp, node_id dead_v) {
689889
// In the postcondition given by parent_exp, clear the bits
@@ -757,13 +957,20 @@ fn forget_in_poststate_still_init(&fn_ctxt fcx, &poststate p, node_id dead_v)
757957
ret changed;
758958
}
759959

760-
fn constraint_mentions(&fn_ctxt fcx, &norm_constraint c, &def_id v) -> bool {
960+
fn any_eq(&(def_id)[] v, def_id d) -> bool {
961+
for (def_id i in v) {
962+
if (i == d) { ret true; }
963+
}
964+
false
965+
}
966+
967+
fn constraint_mentions(&fn_ctxt fcx, &norm_constraint c, def_id v) -> bool {
761968
ret (alt (c.c.node.c) {
762969
case (ninit(_)) {
763970
v == local_def(c.c.node.id)
764971
}
765972
case (npred(_, ?args)) {
766-
args_mention(args, v)
973+
args_mention(args, any_eq, ~[v])
767974
}
768975
});
769976
}
@@ -775,20 +982,42 @@ fn non_init_constraint_mentions(&fn_ctxt fcx, &norm_constraint c,
775982
false
776983
}
777984
case (npred(_, ?args)) {
778-
args_mention(args, v)
985+
args_mention(args, any_eq, ~[v])
779986
}
780987
});
781988
}
782989

783-
784-
fn args_mention(&(@constr_arg_use)[] args, &def_id v) -> bool {
785-
fn mentions(&def_id v, &@constr_arg_use a) -> bool {
990+
fn args_mention[T](&(@constr_arg_use)[] args, fn(&(T)[], def_id) -> bool q,
991+
&(T)[] s) -> bool {
992+
/*
993+
FIXME
994+
The following version causes an assertion in trans to fail
995+
(something about type_is_tup_like)
996+
fn mentions[T](&(T)[] s, &fn(&(T)[], def_id) -> bool q,
997+
&@constr_arg_use a) -> bool {
786998
alt (a.node) {
787-
case (carg_ident(?p1)) { p1._1 == v }
999+
case (carg_ident(?p1)) {
1000+
auto res = q(s, p1._1);
1001+
log_err (res);
1002+
res
1003+
}
7881004
case (_) { false }
7891005
}
7901006
}
791-
ret ivec::any[@constr_arg_use](bind mentions(v,_), args);
1007+
ret ivec::any(bind mentions(s,q,_), args);
1008+
*/
1009+
1010+
for (@constr_arg_use a in args) {
1011+
alt (a.node) {
1012+
case (carg_ident(?p1)) {
1013+
if (q(s, p1._1)) {
1014+
ret true;
1015+
}
1016+
}
1017+
case (_) {}
1018+
}
1019+
}
1020+
ret false;
7921021
}
7931022

7941023
fn use_var(&fn_ctxt fcx, &node_id v) {
@@ -803,6 +1032,12 @@ fn vec_contains(&@mutable (node_id[]) v, &node_id i) -> bool {
8031032
ret false;
8041033
}
8051034

1035+
fn op_to_oper_ty(init_op io) -> oper_type {
1036+
alt (io) {
1037+
case (init_move) { oper_move }
1038+
case (_) { oper_assign }
1039+
}
1040+
}
8061041
//
8071042
// Local Variables:
8081043
// mode: rust

0 commit comments

Comments
 (0)