Skip to content

Commit 7fc89f5

Browse files
committed
Restructure the "checking" pass in typestate
I noticed that typestate was being lazier than it should be, because it was only checking typestate for statements and top-level expression (that is, the expression in a stmt_expr, but not any subexpressions). So I rewrote the checks in tstate/ck.rs to use walk, which exposed a few bugs in typestate that I fixed. Also added some more test cases for if-check.
1 parent a4eb663 commit 7fc89f5

File tree

9 files changed

+383
-120
lines changed

9 files changed

+383
-120
lines changed

src/comp/middle/tstate/ann.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,22 @@ fn extend_poststate(&poststate p, &poststate new) -> bool {
168168
ret bitv::union(p, new);
169169
}
170170

171-
172171
// Clears the given bit in p
173172
fn relax_prestate(uint i, &prestate p) -> bool {
174173
auto was_set = bitv::get(p, i);
175174
bitv::set(p, i, false);
176175
ret was_set;
177176
}
178177

178+
// Clears the given bit in p
179+
fn relax_poststate(uint i, &poststate p) -> bool {
180+
ret relax_prestate(i, p);
181+
}
182+
183+
// Clears the given bit in p
184+
fn relax_precond(uint i, &precond p) {
185+
relax_prestate(i, p);
186+
}
179187

180188
// Clears all the bits in p
181189
fn clear(&precond p) { bitv::clear(p); }

src/comp/middle/tstate/auxiliary.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,18 @@ fn stmt_poststate(&crate_ctxt ccx, &stmt s) -> poststate {
358358
ret stmt_states(ccx, s).poststate;
359359
}
360360

361+
fn block_precond(&crate_ctxt ccx, &block b) -> precond {
362+
ret block_pp(ccx, b).precondition;
363+
}
364+
361365
fn block_postcond(&crate_ctxt ccx, &block b) -> postcond {
362366
ret block_pp(ccx, b).postcondition;
363367
}
364368

369+
fn block_prestate(&crate_ctxt ccx, &block b) -> prestate {
370+
ret block_states(ccx, b).prestate;
371+
}
372+
365373
fn block_poststate(&crate_ctxt ccx, &block b) -> poststate {
366374
ret block_states(ccx, b).poststate;
367375
}
@@ -402,11 +410,15 @@ fn set_pre_and_post(&crate_ctxt ccx, &ann a, &precond pre, &postcond post) {
402410
fn copy_pre_post(&crate_ctxt ccx, &ann a, &@expr sub) {
403411
log "set_pre_and_post";
404412
auto p = expr_pp(ccx, sub);
405-
auto t = ann_to_ts_ann(ccx, a);
406-
set_precondition(t, p.precondition);
407-
set_postcondition(t, p.postcondition);
413+
copy_pre_post_(ccx, a, p.precondition, p.postcondition);
408414
}
409415

416+
fn copy_pre_post_(&crate_ctxt ccx, &ann a, &prestate pre, &poststate post) {
417+
log "set_pre_and_post";
418+
auto t = ann_to_ts_ann(ccx, a);
419+
set_precondition(t, pre);
420+
set_postcondition(t, post);
421+
}
410422

411423
/* sets all bits to *1* */
412424
fn set_postcond_false(&crate_ctxt ccx, &ann a) {
@@ -548,7 +560,9 @@ fn expr_to_constr_arg(ty::ctxt tcx, &@expr e) -> @constr_arg_use {
548560
}
549561
case (expr_lit(?l, _)) { ret @respan(e.span, carg_lit(l)); }
550562
case (_) {
551-
tcx.sess.bug("exprs_to_constr_args: ill-formed pred arg");
563+
tcx.sess.span_err(e.span,
564+
"Arguments to constrained functions must be "
565+
+ "literals or local variables");
552566
}
553567
}
554568
}
@@ -626,6 +640,11 @@ fn path_to_ident(&ty::ctxt cx, &path p) -> ident {
626640
case (some(?i)) { ret i; }
627641
}
628642
}
643+
644+
tag if_ty {
645+
if_check;
646+
plain_if;
647+
}
629648
//
630649
// Local Variables:
631650
// mode: rust

src/comp/middle/tstate/bitvectors.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ import aux::npred;
1515
import aux::pred_desc;
1616
import aux::match_args;
1717
import aux::constr_;
18+
import aux::block_precond;
19+
import aux::stmt_precond;
20+
import aux::expr_precond;
21+
import aux::block_prestate;
22+
import aux::expr_prestate;
23+
import aux::stmt_prestate;
1824
import tstate::aux::ann_to_ts_ann;
1925
import tstate::ann::pre_and_post;
2026
import tstate::ann::precond;
2127
import tstate::ann::postcond;
2228
import tstate::ann::prestate;
2329
import tstate::ann::poststate;
2430
import tstate::ann::relax_prestate;
31+
import tstate::ann::relax_precond;
32+
import tstate::ann::relax_poststate;
2533
import tstate::ann::pps_len;
2634
import tstate::ann::true_precond;
2735
import tstate::ann::empty_prestate;
@@ -136,9 +144,46 @@ fn gen(&fn_ctxt fcx, &ann a, &constr_ c) -> bool {
136144
fn declare_var(&fn_ctxt fcx, &constr_ c, prestate pre) -> prestate {
137145
auto res = clone(pre);
138146
relax_prestate(bit_num(fcx, c), res);
147+
// idea is this is scoped
148+
relax_poststate(bit_num(fcx, c), res);
139149
ret res;
140150
}
141151

152+
fn relax_precond_block_non_recursive(&fn_ctxt fcx, uint i, &block b) {
153+
relax_precond(i, block_precond(fcx.ccx, b));
154+
}
155+
156+
fn relax_precond_expr(&fn_ctxt fcx, uint i, &@expr e) {
157+
relax_precond(i, expr_precond(fcx.ccx, e));
158+
}
159+
160+
fn relax_precond_stmt(&fn_ctxt fcx, uint i, &@stmt s) {
161+
relax_precond(i, stmt_precond(fcx.ccx, *s));
162+
}
163+
164+
fn relax_precond_block(&fn_ctxt fcx, uint i, &block b) {
165+
relax_precond_block_non_recursive(fcx, i, b);
166+
// FIXME: should use visit instead
167+
// could at least generalize this pattern
168+
// (also seen in ck::check_states_against_conditions)
169+
let @mutable bool keepgoing = @mutable true;
170+
171+
fn quit(@mutable bool keepgoing, &@item i) {
172+
*keepgoing = false;
173+
}
174+
fn kg(@mutable bool keepgoing) -> bool { ret *keepgoing; }
175+
176+
auto v = rec(visit_block_pre = bind
177+
relax_precond_block_non_recursive(fcx, i, _),
178+
visit_expr_pre = bind relax_precond_expr(fcx, i, _),
179+
visit_stmt_pre = bind relax_precond_stmt(fcx, i, _),
180+
visit_item_pre=bind quit(keepgoing, _),
181+
keep_going=bind kg(keepgoing)
182+
183+
with walk::default_visitor());
184+
walk::walk_block(v, b);
185+
}
186+
142187
fn gen_poststate(&fn_ctxt fcx, &ann a, &constr_ c) -> bool {
143188
log "gen_poststate";
144189
ret set_in_poststate(bit_num(fcx, c), ann_to_ts_ann(fcx.ccx, a).states);

src/comp/middle/tstate/ck.rs

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,19 @@ import collect_locals::mk_f_to_fn_info;
6161
import pre_post_conditions::fn_pre_post;
6262
import states::find_pre_post_state_fn;
6363

64-
fn check_states_expr(&fn_ctxt fcx, @expr e) {
64+
fn check_states_expr(&fn_ctxt fcx, &@expr e) {
6565
let precond prec = expr_precond(fcx.ccx, e);
6666
let prestate pres = expr_prestate(fcx.ccx, e);
67+
68+
/*
69+
log_err("check_states_expr:");
70+
util::common::log_expr_err(*e);
71+
log_err("prec = ");
72+
log_bitv_err(fcx, prec);
73+
log_err("pres = ");
74+
log_bitv_err(fcx, pres);
75+
*/
76+
6777
if (!implies(pres, prec)) {
6878
auto s = "";
6979
auto diff = first_difference_string(fcx, prec, pres);
@@ -79,26 +89,27 @@ fn check_states_expr(&fn_ctxt fcx, @expr e) {
7989
}
8090
}
8191

82-
fn check_states_stmt(&fn_ctxt fcx, &stmt s) {
83-
auto a = stmt_to_ann(fcx.ccx, s);
92+
fn check_states_stmt(&fn_ctxt fcx, &@stmt s) {
93+
auto a = stmt_to_ann(fcx.ccx, *s);
8494
let precond prec = ann_precond(a);
8595
let prestate pres = ann_prestate(a);
8696

87-
/*
97+
/*
8898
log_err("check_states_stmt:");
89-
log_stmt_err(s);
99+
log_stmt_err(*s);
90100
log_err("prec = ");
91-
log_bitv_err(fcx.enclosing, prec);
101+
log_bitv_err(fcx, prec);
92102
log_err("pres = ");
93-
log_bitv_err(fcx.enclosing, pres);
103+
log_bitv_err(fcx, pres);
94104
*/
105+
95106
if (!implies(pres, prec)) {
96107
auto ss = "";
97108
auto diff = first_difference_string(fcx, prec, pres);
98109
ss +=
99110
"Unsatisfied precondition constraint (for example, " + diff +
100111
") for statement:\n";
101-
ss += pretty::pprust::stmt_to_str(s);
112+
ss += pretty::pprust::stmt_to_str(*s);
102113
ss += "\nPrecondition:\n";
103114
ss += bitv_to_str(fcx, prec);
104115
ss += "\nPrestate: \n";
@@ -107,42 +118,50 @@ fn check_states_stmt(&fn_ctxt fcx, &stmt s) {
107118
}
108119
}
109120

110-
fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, &ann a) {
111-
auto enclosing = fcx.enclosing;
112-
auto nv = num_constraints(enclosing);
113-
auto post = @mutable empty_poststate(nv);
114-
fn do_one_(fn_ctxt fcx, &@stmt s, @mutable poststate post) {
115-
check_states_stmt(fcx, *s);
116-
*post = stmt_poststate(fcx.ccx, *s);
121+
fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, &ann a,
122+
&span sp, &ident i, &def_id d) {
123+
/* Postorder traversal instead of pre is important
124+
because we want the smallest possible erroneous statement
125+
or expression. */
126+
127+
let @mutable bool keepgoing = @mutable true;
128+
129+
/* TODO probably should use visit instead */
130+
131+
fn quit(@mutable bool keepgoing, &@ast::item i) {
132+
*keepgoing = false;
117133
}
118-
auto do_one = bind do_one_(fcx, _, post);
119-
vec::map[@stmt, ()](do_one, f.body.node.stmts);
120-
fn do_inner_(fn_ctxt fcx, &@expr e, @mutable poststate post) {
121-
check_states_expr(fcx, e);
122-
*post = expr_poststate(fcx.ccx, e);
134+
fn kg(@mutable bool keepgoing) -> bool {
135+
ret *keepgoing;
123136
}
124-
auto do_inner = bind do_inner_(fcx, _, post);
125-
option::map[@expr, ()](do_inner, f.body.node.expr);
126-
auto cf = fcx.enclosing.cf;
127-
/* Finally, check that the return value is initialized */
128137

138+
auto v = rec (visit_stmt_post=bind check_states_stmt(fcx, _),
139+
visit_expr_post=bind check_states_expr(fcx, _),
140+
visit_item_pre=bind quit(keepgoing, _),
141+
keep_going=bind kg(keepgoing)
142+
with walk::default_visitor());
143+
144+
walk::walk_fn(v, f, sp, i, d, a);
145+
146+
/* Finally, check that the return value is initialized */
147+
auto post = aux::block_poststate(fcx.ccx, f.body);
129148
let aux::constr_ ret_c = rec(id=fcx.id, c=aux::ninit(fcx.name));
130-
if (f.proto == ast::proto_fn && !promises(fcx, { *post }, ret_c) &&
149+
if (f.proto == ast::proto_fn && !promises(fcx, post, ret_c) &&
131150
!type_is_nil(fcx.ccx.tcx, ret_ty_of_fn(fcx.ccx.tcx, a)) &&
132-
cf == return) {
151+
f.decl.cf == return) {
133152
fcx.ccx.tcx.sess.span_note(f.body.span,
134153
"In function " + fcx.name +
135154
", not all control paths \
136155
return a value");
137156
fcx.ccx.tcx.sess.span_err(f.decl.output.span,
138157
"see declared return type of '" +
139158
ty_to_str(*f.decl.output) + "'");
140-
} else if (cf == noreturn) {
159+
} else if (f.decl.cf == noreturn) {
141160

142161
// check that this really always fails
143162
// the fcx.id bit means "returns" for a returning fn,
144163
// "diverges" for a non-returning fn
145-
if (!promises(fcx, { *post }, ret_c)) {
164+
if (!promises(fcx, post, ret_c)) {
146165
fcx.ccx.tcx.sess.span_err(f.body.span,
147166
"In non-returning function " + fcx.name
148167
+
@@ -152,15 +171,16 @@ fn check_states_against_conditions(&fn_ctxt fcx, &_fn f, &ann a) {
152171
}
153172
}
154173

155-
fn check_fn_states(&fn_ctxt fcx, &_fn f, &ann a) {
174+
fn check_fn_states(&fn_ctxt fcx, &_fn f, &ann a, &span sp, &ident i,
175+
&def_id d) {
156176
/* Compute the pre- and post-states for this function */
157177

158178
auto g = find_pre_post_state_fn;
159179
fixed_point_states(fcx, g, f);
160180
/* Now compare each expr's pre-state to its precondition
161181
and post-state to its postcondition */
162182

163-
check_states_against_conditions(fcx, f, a);
183+
check_states_against_conditions(fcx, f, a, sp, i, d);
164184
}
165185

166186
fn fn_states(&crate_ctxt ccx, &_fn f, &span sp, &ident i, &def_id id,
@@ -170,7 +190,7 @@ fn fn_states(&crate_ctxt ccx, &_fn f, &span sp, &ident i, &def_id id,
170190
assert (ccx.fm.contains_key(id));
171191
auto f_info = ccx.fm.get(id);
172192
auto fcx = rec(enclosing=f_info, id=id, name=i, ccx=ccx);
173-
check_fn_states(fcx, f, a);
193+
check_fn_states(fcx, f, a, sp, i, id);
174194
}
175195

176196
fn check_crate(ty::ctxt cx, @crate crate) {
@@ -185,15 +205,15 @@ fn check_crate(ty::ctxt cx, @crate crate) {
185205

186206
auto do_pre_post = walk::default_visitor();
187207
do_pre_post =
188-
rec(visit_fn_pre=bind fn_pre_post(ccx, _, _, _, _, _)
208+
rec(visit_fn_post=bind fn_pre_post(ccx, _, _, _, _, _)
189209
with do_pre_post);
190210
walk::walk_crate(do_pre_post, *crate);
191211
/* Check the pre- and postcondition against the pre- and poststate
192212
for every expression */
193213

194214
auto do_states = walk::default_visitor();
195215
do_states =
196-
rec(visit_fn_pre=bind fn_states(ccx, _, _, _, _, _) with do_states);
216+
rec(visit_fn_post=bind fn_states(ccx, _, _, _, _, _) with do_states);
197217
walk::walk_crate(do_states, *crate);
198218
}
199219
//

0 commit comments

Comments
 (0)