Skip to content

Commit 4f792f2

Browse files
committed
Track spans for each local variable in typestate
This lets us print better messages in situations with name shadowing.
1 parent 250643c commit 4f792f2

File tree

9 files changed

+186
-103
lines changed

9 files changed

+186
-103
lines changed

src/comp/middle/tstate/annotate.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ fn collect_ids_decl(&@decl d, @vec[uint] res) -> () {
9191
}
9292
}
9393

94-
fn node_ids_in_fn(&_fn f, &ident i, &def_id d, &ann a, @vec[uint] res) -> () {
94+
fn node_ids_in_fn(&_fn f, &span sp, &ident i, &def_id d, &ann a,
95+
@vec[uint] res) -> () {
9596
auto collect_ids = walk::default_visitor();
9697
collect_ids = rec(visit_expr_pre = bind collect_ids_expr(_,res),
9798
visit_block_pre = bind collect_ids_block(_,res),
9899
visit_stmt_pre = bind collect_ids_stmt(_,res),
99100
visit_decl_pre = bind collect_ids_decl(_,res)
100101
with collect_ids);
101-
walk::walk_fn(collect_ids, f, i, d, a);
102+
walk::walk_fn(collect_ids, f, sp, i, d, a);
102103
}
103104

104105
fn init_vecs(&crate_ctxt ccx, @vec[uint] node_ids, uint len) -> () {
@@ -108,23 +109,24 @@ fn init_vecs(&crate_ctxt ccx, @vec[uint] node_ids, uint len) -> () {
108109
}
109110
}
110111

111-
fn visit_fn(&crate_ctxt ccx, uint num_locals, &_fn f, &ident i,
112-
&def_id d, &ann a) -> () {
112+
fn visit_fn(&crate_ctxt ccx, uint num_locals, &_fn f,
113+
&span sp, &ident i, &def_id d, &ann a) -> () {
113114
let vec[uint] node_ids_ = [];
114115
let @vec[uint] node_ids = @node_ids_;
115-
node_ids_in_fn(f, i, d, a, node_ids);
116+
node_ids_in_fn(f, sp, i, d, a, node_ids);
116117
init_vecs(ccx, node_ids, num_locals);
117118
}
118119

119-
fn annotate_in_fn(&crate_ctxt ccx, &_fn f, &ident i, &def_id f_id, &ann a)
120+
fn annotate_in_fn(&crate_ctxt ccx, &_fn f, &span sp, &ident i,
121+
&def_id f_id, &ann a)
120122
-> () {
121123
auto f_info = get_fn_info(ccx, f_id);
122-
visit_fn(ccx, num_locals(f_info), f, i, f_id, a);
124+
visit_fn(ccx, num_locals(f_info), f, sp, i, f_id, a);
123125
}
124126

125127
fn annotate_crate(&crate_ctxt ccx, &crate crate) -> () {
126128
auto do_ann = walk::default_visitor();
127-
do_ann = rec(visit_fn_pre = bind annotate_in_fn(ccx,_,_,_,_)
129+
do_ann = rec(visit_fn_pre = bind annotate_in_fn(ccx,_,_,_,_,_)
128130
with do_ann);
129131
walk::walk_crate(do_ann, crate);
130132
}

src/comp/middle/tstate/auxiliary.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import front::ast::expr_path;
1818
import front::ast::ident;
1919
import front::ast::controlflow;
2020
import front::ast::ann;
21-
import front::ast::ts_ann;
2221
import front::ast::stmt;
2322
import front::ast::expr;
2423
import front::ast::block;
@@ -54,26 +53,60 @@ import tstate::ann::extend_prestate;
5453
import tstate::ann::extend_poststate;
5554
import tstate::ann::set_precondition;
5655
import tstate::ann::set_postcondition;
56+
import tstate::ann::ts_ann;
57+
58+
import util::common::istr;
5759

5860
/* logging funs */
5961

60-
fn bitv_to_str(fn_info enclosing, bitv::t v) -> str {
62+
fn def_id_to_str(def_id d) -> str {
63+
ret (istr(d._0) + "," + istr(d._1));
64+
}
65+
66+
fn bitv_to_str(fn_ctxt fcx, bitv::t v) -> str {
6167
auto s = "";
68+
auto comma = false;
6269

63-
for each (@tup(def_id, tup(uint, ident)) p in enclosing.vars.items()) {
64-
if (bitv::get(v, p._1._0)) {
65-
s += " " + p._1._1 + " ";
66-
}
70+
for each (@tup(def_id, var_info) p in fcx.enclosing.vars.items()) {
71+
if (bitv::get(v, p._1.bit_num)) {
72+
s += (if (comma) { ", " } else { comma = true; "" })
73+
+ p._1.name + " [" + fcx.ccx.tcx.sess.span_str(p._1.sp) + "]";
74+
}
6775
}
6876
ret s;
6977
}
7078

71-
fn log_bitv(fn_info enclosing, bitv::t v) {
72-
log(bitv_to_str(enclosing, v));
79+
fn log_bitv(fn_ctxt fcx, bitv::t v) {
80+
log(bitv_to_str(fcx, v));
81+
}
82+
83+
fn first_difference_string(&fn_ctxt fcx, &bitv::t expected,
84+
&bitv::t actual) -> str {
85+
let str s = "";
86+
auto done = false;
87+
for each (@tup(def_id, var_info) p in fcx.enclosing.vars.items()) {
88+
if (!done) {
89+
if (bitv::get(expected, p._1.bit_num) &&
90+
!bitv::get(actual, p._1.bit_num)) {
91+
92+
/*
93+
for fun, try either:
94+
* "ret s" after the assignment to s
95+
or
96+
* using break here
97+
*/
98+
s = (p._1.name + " ["
99+
+ fcx.ccx.tcx.sess.span_str(p._1.sp) + "]");
100+
101+
done = true;
102+
}
103+
}
104+
}
105+
ret s;
73106
}
74107

75-
fn log_bitv_err(fn_info enclosing, bitv::t v) {
76-
log_err(bitv_to_str(enclosing, v));
108+
fn log_bitv_err(fn_ctxt fcx, bitv::t v) {
109+
log_err(bitv_to_str(fcx, v));
77110
}
78111

79112
fn tos (vec[uint] v) -> str {
@@ -152,8 +185,10 @@ fn print_idents(vec[ident] idents) -> () {
152185
/**********************************************************************/
153186
/* mapping from variable name (def_id is assumed to be for a local
154187
variable in a given function) to bit number
155-
(also remembers the ident for error-logging purposes) */
156-
type var_info = tup(uint, ident);
188+
(also remembers the ident and span for error-logging purposes) */
189+
type var_info = rec(uint bit_num,
190+
ident name,
191+
span sp);
157192
type fn_info = rec(@std::map::hashmap[def_id, var_info] vars,
158193
controlflow cf);
159194

src/comp/middle/tstate/bitvectors.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import tstate::ann::clear_in_poststate;
3333

3434
fn bit_num(def_id v, fn_info m) -> uint {
3535
assert (m.vars.contains_key(v));
36-
ret m.vars.get(v)._0;
36+
ret m.vars.get(v).bit_num;
3737
}
3838

3939
fn promises(&poststate p, def_id v, fn_info m) -> bool {
@@ -44,26 +44,26 @@ fn promises(&poststate p, def_id v, fn_info m) -> bool {
4444
// return the precondition for evaluating each expr in order.
4545
// So, if e0's post is {x} and e1's pre is {x, y, z}, the entire
4646
// precondition shouldn't include x.
47-
fn seq_preconds(fn_info enclosing, vec[pre_and_post] pps) -> precond {
47+
fn seq_preconds(fn_ctxt fcx, vec[pre_and_post] pps) -> precond {
4848
let uint sz = len[pre_and_post](pps);
49-
let uint num_vars = num_locals(enclosing);
49+
let uint num_vars = num_locals(fcx.enclosing);
5050

5151
if (sz >= 1u) {
5252
auto first = pps.(0);
5353
assert (pps_len(first) == num_vars);
54-
let precond rest = seq_preconds(enclosing,
54+
let precond rest = seq_preconds(fcx,
5555
slice[pre_and_post](pps, 1u, sz));
5656
difference(rest, first.postcondition);
5757
auto res = clone(first.precondition);
5858
union(res, rest);
5959

6060
log("seq_preconds:");
6161
log("first.postcondition =");
62-
log_bitv(enclosing, first.postcondition);
62+
log_bitv(fcx, first.postcondition);
6363
log("rest =");
64-
log_bitv(enclosing, rest);
64+
log_bitv(fcx, rest);
6565
log("returning");
66-
log_bitv(enclosing, res);
66+
log_bitv(fcx, res);
6767

6868
ret res;
6969
}
@@ -118,14 +118,14 @@ fn intersect_postconds(&vec[postcond] pcs) -> postcond {
118118
fn gen(&fn_ctxt fcx, &ann a, def_id id) -> bool {
119119
log "gen";
120120
assert (fcx.enclosing.vars.contains_key(id));
121-
let uint i = (fcx.enclosing.vars.get(id))._0;
121+
let uint i = (fcx.enclosing.vars.get(id)).bit_num;
122122
ret set_in_postcond(i, (ann_to_ts_ann(fcx.ccx, a)).conditions);
123123
}
124124

125125
fn declare_var(&fn_info enclosing, def_id id, prestate pre)
126126
-> prestate {
127127
assert (enclosing.vars.contains_key(id));
128-
let uint i = (enclosing.vars.get(id))._0;
128+
let uint i = (enclosing.vars.get(id)).bit_num;
129129
auto res = clone(pre);
130130
relax_prestate(i, res);
131131
ret res;
@@ -134,14 +134,14 @@ fn declare_var(&fn_info enclosing, def_id id, prestate pre)
134134
fn gen_poststate(&fn_ctxt fcx, &ann a, def_id id) -> bool {
135135
log "gen_poststate";
136136
assert (fcx.enclosing.vars.contains_key(id));
137-
let uint i = (fcx.enclosing.vars.get(id))._0;
137+
let uint i = (fcx.enclosing.vars.get(id)).bit_num;
138138
ret set_in_poststate(i, (ann_to_ts_ann(fcx.ccx, a)).states);
139139
}
140140

141141
fn kill_poststate(&fn_ctxt fcx, &ann a, def_id id) -> bool {
142142
log "kill_poststate";
143143
assert (fcx.enclosing.vars.contains_key(id));
144-
let uint i = (fcx.enclosing.vars.get(id))._0;
144+
let uint i = (fcx.enclosing.vars.get(id)).bit_num;
145145
ret clear_in_poststate(i, (ann_to_ts_ann(fcx.ccx, a)).states);
146146
}
147147

src/comp/middle/tstate/ck.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import aux::stmt_to_ann;
5353
import aux::num_locals;
5454
import aux::fixed_point_states;
5555
import aux::bitv_to_str;
56+
import aux::first_difference_string;
5657

5758
import util::common::ty_to_str;
5859
import util::common::log_stmt_err;
@@ -70,12 +71,14 @@ fn check_states_expr(&fn_ctxt fcx, @expr e) -> () {
7071

7172
if (!implies(pres, prec)) {
7273
auto s = "";
73-
s += ("Unsatisfied precondition constraint for expression:\n");
74+
auto diff = first_difference_string(fcx, prec, pres);
75+
s += ("Unsatisfied precondition constraint (for example, "
76+
+ diff + ") for expression:\n");
7477
s += util::common::expr_to_str(e);
7578
s += ("\nPrecondition:\n");
76-
s += bitv_to_str(fcx.enclosing, prec);
79+
s += bitv_to_str(fcx, prec);
7780
s += ("\nPrestate:\n");
78-
s += bitv_to_str(fcx.enclosing, pres);
81+
s += bitv_to_str(fcx, pres);
7982
fcx.ccx.tcx.sess.span_err(e.span, s);
8083
}
8184
}
@@ -96,12 +99,14 @@ fn check_states_stmt(&fn_ctxt fcx, &stmt s) -> () {
9699

97100
if (!implies(pres, prec)) {
98101
auto ss = "";
99-
ss += ("Unsatisfied precondition constraint for statement:\n");
102+
auto diff = first_difference_string(fcx, prec, pres);
103+
ss += ("Unsatisfied precondition constraint (for example, "
104+
+ diff + ") for statement:\n");
100105
ss += util::common::stmt_to_str(s);
101106
ss += ("\nPrecondition:\n");
102-
ss += bitv_to_str(fcx.enclosing, prec);
107+
ss += bitv_to_str(fcx, prec);
103108
ss += ("\nPrestate: \n");
104-
ss += bitv_to_str(fcx.enclosing, pres);
109+
ss += bitv_to_str(fcx, pres);
105110
fcx.ccx.tcx.sess.span_err(s.span, ss);
106111
}
107112
}
@@ -162,7 +167,8 @@ fn check_fn_states(&fn_ctxt fcx, &_fn f, &ann a) -> () {
162167
check_states_against_conditions(fcx, f, a);
163168
}
164169

165-
fn fn_states(&crate_ctxt ccx, &_fn f, &ident i, &def_id id, &ann a) -> () {
170+
fn fn_states(&crate_ctxt ccx, &_fn f, &span sp, &ident i,
171+
&def_id id, &ann a) -> () {
166172
/* Look up the var-to-bit-num map for this function */
167173
assert (ccx.fm.contains_key(id));
168174
auto f_info = ccx.fm.get(id);
@@ -171,6 +177,8 @@ fn fn_states(&crate_ctxt ccx, &_fn f, &ident i, &def_id id, &ann a) -> () {
171177
check_fn_states(fcx, f, a);
172178
}
173179

180+
181+
174182
fn check_crate(ty::ctxt cx, @crate crate) -> () {
175183
let crate_ctxt ccx = new_crate_ctxt(cx);
176184

@@ -182,14 +190,14 @@ fn check_crate(ty::ctxt cx, @crate crate) -> () {
182190

183191
/* Compute the pre and postcondition for every subexpression */
184192
auto do_pre_post = walk::default_visitor();
185-
do_pre_post = rec(visit_fn_pre = bind fn_pre_post(ccx,_,_,_,_)
193+
do_pre_post = rec(visit_fn_pre = bind fn_pre_post(ccx,_,_,_,_,_)
186194
with do_pre_post);
187195
walk::walk_crate(do_pre_post, *crate);
188196

189197
/* Check the pre- and postcondition against the pre- and poststate
190198
for every expression */
191199
auto do_states = walk::default_visitor();
192-
do_states = rec(visit_fn_pre = bind fn_states(ccx,_,_,_,_)
200+
do_states = rec(visit_fn_pre = bind fn_states(ccx,_,_,_,_,_)
193201
with do_states);
194202
walk::walk_crate(do_states, *crate);
195203
}

src/comp/middle/tstate/collect_locals.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import front::ast::ident;
2323
import middle::walk::walk_crate;
2424
import middle::walk::walk_fn;
2525
import middle::walk::ast_visitor;
26+
import front::ast::span;
2627

2728
import aux::fn_info;
2829
import aux::var_info;
@@ -31,39 +32,44 @@ import aux::crate_ctxt;
3132
import util::common::new_def_hash;
3233
import util::common::uistr;
3334

35+
type identifier = rec(ident name, def_id id, span sp);
36+
3437
fn var_is_local(def_id v, fn_info m) -> bool {
3538
ret (m.vars.contains_key(v));
3639
}
3740

38-
fn collect_local(&@vec[tup(ident, def_id)] vars, &@decl d) -> () {
41+
fn collect_local(&@vec[identifier] vars, &@decl d) -> () {
3942
alt (d.node) {
4043
case (decl_local(?loc)) {
4144
log("collect_local: pushing " + loc.ident);
42-
vec::push[tup(ident, def_id)](*vars, tup(loc.ident, loc.id));
45+
vec::push[identifier](*vars, rec(name=loc.ident,
46+
id=loc.id,
47+
sp=d.span));
4348
}
4449
case (_) { ret; }
4550
}
4651
}
4752

48-
fn find_locals(&_fn f, &ident i, &def_id d, &ann a)
49-
-> @vec[tup(ident,def_id)] {
50-
auto res = @vec::alloc[tup(ident,def_id)](0u);
53+
fn find_locals(&_fn f, &span sp, &ident i, &def_id d, &ann a)
54+
-> @vec[identifier] {
55+
auto res = @vec::alloc[identifier](0u);
5156
auto visitor = walk::default_visitor();
5257
visitor = rec(visit_decl_pre=bind collect_local(res,_) with visitor);
53-
walk_fn(visitor, f, i, d, a);
58+
walk_fn(visitor, f, sp, i, d, a);
5459
ret res;
5560
}
5661

5762

58-
fn add_var(def_id v, ident nm, uint next, fn_info tbl) -> uint {
63+
fn add_var(def_id v, span sp, ident nm, uint next, fn_info tbl) -> uint {
5964
log(nm + " |-> " + util::common::uistr(next));
60-
tbl.vars.insert(v, tup(next,nm));
65+
tbl.vars.insert(v, rec(bit_num=next, name=nm, sp=sp));
6166
ret (next + 1u);
6267
}
6368

6469
/* builds a table mapping each local var defined in f
6570
to a bit number in the precondition/postcondition vectors */
66-
fn mk_fn_info(&crate_ctxt ccx, &_fn f, &ident f_name, &def_id f_id, &ann a)
71+
fn mk_fn_info(&crate_ctxt ccx, &_fn f, &span f_sp,
72+
&ident f_name, &def_id f_id, &ann a)
6773
-> () {
6874
auto res = rec(vars=@new_def_hash[var_info](),
6975
cf=f.decl.cf);
@@ -73,15 +79,15 @@ fn mk_fn_info(&crate_ctxt ccx, &_fn f, &ident f_name, &def_id f_id, &ann a)
7379
/* ignore args, which we know are initialized;
7480
just collect locally declared vars */
7581

76-
let @vec[tup(ident,def_id)] locals = find_locals(f, f_name, f_id, a);
77-
for (tup(ident,def_id) p in *locals) {
78-
next = add_var(p._1, p._0, next, res);
82+
let @vec[identifier] locals = find_locals(f, f_sp, f_name, f_id, a);
83+
for (identifier p in *locals) {
84+
next = add_var(p.id, p.sp, p.name, next, res);
7985
}
8086
/* add a pseudo-entry for the function's return value
8187
we can safely use the function's name itself for this purpose */
82-
add_var(f_id, f_name, next, res);
88+
add_var(f_id, f_sp, f_name, next, res);
8389

84-
log(f_name + " has " + uistr(vec::len[tup(ident, def_id)](*locals))
90+
log(f_name + " has " + uistr(vec::len[identifier](*locals))
8591
+ " locals");
8692

8793
ccx.fm.insert(f_id, res);
@@ -92,7 +98,7 @@ fn mk_fn_info(&crate_ctxt ccx, &_fn f, &ident f_name, &def_id f_id, &ann a)
9298
to bit number) */
9399
fn mk_f_to_fn_info(&crate_ctxt ccx, @crate c) -> () {
94100
let ast_visitor vars_visitor = walk::default_visitor();
95-
vars_visitor = rec(visit_fn_pre=bind mk_fn_info(ccx,_,_,_,_)
101+
vars_visitor = rec(visit_fn_pre=bind mk_fn_info(ccx,_,_,_,_,_)
96102
with vars_visitor);
97103

98104
walk_crate(vars_visitor, *c);

0 commit comments

Comments
 (0)