2011-06-15 11:19:50 -07:00
|
|
|
|
2011-07-05 11:48:19 +02:00
|
|
|
import syntax::ast;
|
|
|
|
import ast::method;
|
|
|
|
import ast::item;
|
|
|
|
import ast::item_fn;
|
|
|
|
import ast::_fn;
|
|
|
|
import ast::obj_field;
|
|
|
|
import ast::_obj;
|
|
|
|
import ast::stmt;
|
|
|
|
import ast::ident;
|
|
|
|
import ast::fn_ident;
|
|
|
|
import ast::node_id;
|
|
|
|
import ast::def_id;
|
|
|
|
import ast::local_def;
|
|
|
|
import ast::ty_param;
|
|
|
|
import ast::crate;
|
|
|
|
import ast::return;
|
|
|
|
import ast::noreturn;
|
|
|
|
import ast::expr;
|
2011-07-12 11:26:14 -07:00
|
|
|
import syntax::visit;
|
2011-07-05 11:48:19 +02:00
|
|
|
import syntax::codemap::span;
|
2011-05-14 19:02:30 -07:00
|
|
|
import middle::ty::type_is_nil;
|
|
|
|
import middle::ty::ret_ty_of_fn;
|
|
|
|
import tstate::ann::ts_ann;
|
|
|
|
import tstate::ann::empty_poststate;
|
|
|
|
import tstate::ann::true_precond;
|
|
|
|
import tstate::ann::true_postcond;
|
|
|
|
import tstate::ann::false_postcond;
|
|
|
|
import tstate::ann::precond;
|
|
|
|
import tstate::ann::postcond;
|
|
|
|
import tstate::ann::poststate;
|
|
|
|
import tstate::ann::prestate;
|
|
|
|
import tstate::ann::implies;
|
|
|
|
import tstate::ann::ann_precond;
|
|
|
|
import tstate::ann::ann_prestate;
|
|
|
|
import std::option;
|
|
|
|
import std::option::t;
|
|
|
|
import std::option::some;
|
|
|
|
import std::option::none;
|
2011-06-30 00:18:41 -07:00
|
|
|
import aux::*;
|
2011-07-05 11:48:19 +02:00
|
|
|
import syntax::print::pprust::ty_to_str;
|
2011-05-18 15:43:05 -07:00
|
|
|
import util::common::log_stmt_err;
|
2011-05-14 19:02:30 -07:00
|
|
|
import bitvectors::promises;
|
|
|
|
import annotate::annotate_crate;
|
|
|
|
import collect_locals::mk_f_to_fn_info;
|
2011-05-18 16:59:34 -07:00
|
|
|
import pre_post_conditions::fn_pre_post;
|
2011-05-14 19:02:30 -07:00
|
|
|
import states::find_pre_post_state_fn;
|
|
|
|
|
2011-06-30 00:18:41 -07:00
|
|
|
fn check_unused_vars(&fn_ctxt fcx) {
|
|
|
|
// FIXME: could be more efficient
|
|
|
|
for (norm_constraint c in constraints(fcx)) {
|
|
|
|
alt (c.c.node.c) {
|
|
|
|
case (ninit(?v)) {
|
|
|
|
if (!vec_contains(fcx.enclosing.used_vars,
|
|
|
|
c.c.node.id)) {
|
|
|
|
fcx.ccx.tcx.sess.span_warn(c.c.span,
|
|
|
|
"Unused variable " + v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case (_) { /* ignore pred constraints */ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
fn check_states_expr(&@expr e, &fn_ctxt fcx, &visit::vt[fn_ctxt] v) {
|
|
|
|
visit::visit_expr(e, fcx, v);
|
|
|
|
|
2011-06-15 11:19:50 -07:00
|
|
|
let precond prec = expr_precond(fcx.ccx, e);
|
2011-05-18 15:43:05 -07:00
|
|
|
let prestate pres = expr_prestate(fcx.ccx, e);
|
2011-06-17 19:07:23 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
log_err("check_states_expr:");
|
|
|
|
util::common::log_expr_err(*e);
|
|
|
|
log_err("prec = ");
|
2011-06-27 18:12:37 -07:00
|
|
|
log_tritv_err(fcx, prec);
|
2011-06-17 19:07:23 -07:00
|
|
|
log_err("pres = ");
|
2011-06-27 18:12:37 -07:00
|
|
|
log_tritv_err(fcx, pres);
|
2011-06-17 19:07:23 -07:00
|
|
|
*/
|
|
|
|
|
2011-05-18 15:43:05 -07:00
|
|
|
if (!implies(pres, prec)) {
|
|
|
|
auto s = "";
|
2011-05-26 16:02:25 -07:00
|
|
|
auto diff = first_difference_string(fcx, prec, pres);
|
2011-06-15 11:19:50 -07:00
|
|
|
s +=
|
|
|
|
"Unsatisfied precondition constraint (for example, " + diff +
|
|
|
|
") for expression:\n";
|
2011-07-05 11:48:19 +02:00
|
|
|
s += syntax::print::pprust::expr_to_str(e);
|
2011-06-15 11:19:50 -07:00
|
|
|
s += "\nPrecondition:\n";
|
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
|
|
|
s += tritv_to_str(fcx, prec);
|
2011-06-15 11:19:50 -07:00
|
|
|
s += "\nPrestate:\n";
|
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
|
|
|
s += tritv_to_str(fcx, pres);
|
2011-06-18 22:41:20 -07:00
|
|
|
fcx.ccx.tcx.sess.span_fatal(e.span, s);
|
2011-05-18 15:43:05 -07:00
|
|
|
}
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
fn check_states_stmt(&@stmt s, &fn_ctxt fcx, &visit::vt[fn_ctxt] v) {
|
|
|
|
visit::visit_stmt(s, fcx, v);
|
|
|
|
|
2011-06-17 19:07:23 -07:00
|
|
|
auto a = stmt_to_ann(fcx.ccx, *s);
|
2011-06-15 11:19:50 -07:00
|
|
|
let precond prec = ann_precond(a);
|
|
|
|
let prestate pres = ann_prestate(a);
|
2011-05-18 15:43:05 -07:00
|
|
|
|
2011-06-17 19:07:23 -07:00
|
|
|
/*
|
2011-05-18 15:43:05 -07:00
|
|
|
log_err("check_states_stmt:");
|
2011-06-17 19:07:23 -07:00
|
|
|
log_stmt_err(*s);
|
2011-05-18 15:43:05 -07:00
|
|
|
log_err("prec = ");
|
2011-06-27 18:12:37 -07:00
|
|
|
log_tritv_err(fcx, prec);
|
2011-05-18 15:43:05 -07:00
|
|
|
log_err("pres = ");
|
2011-06-27 18:12:37 -07:00
|
|
|
log_tritv_err(fcx, pres);
|
2011-05-18 15:43:05 -07:00
|
|
|
*/
|
2011-06-17 19:07:23 -07:00
|
|
|
|
2011-05-18 15:43:05 -07:00
|
|
|
if (!implies(pres, prec)) {
|
|
|
|
auto ss = "";
|
2011-05-26 16:02:25 -07:00
|
|
|
auto diff = first_difference_string(fcx, prec, pres);
|
2011-06-15 11:19:50 -07:00
|
|
|
ss +=
|
|
|
|
"Unsatisfied precondition constraint (for example, " + diff +
|
|
|
|
") for statement:\n";
|
2011-07-05 11:48:19 +02:00
|
|
|
ss += syntax::print::pprust::stmt_to_str(*s);
|
2011-06-15 11:19:50 -07:00
|
|
|
ss += "\nPrecondition:\n";
|
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
|
|
|
ss += tritv_to_str(fcx, prec);
|
2011-06-15 11:19:50 -07:00
|
|
|
ss += "\nPrestate: \n";
|
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
|
|
|
ss += tritv_to_str(fcx, pres);
|
2011-06-18 22:41:20 -07:00
|
|
|
fcx.ccx.tcx.sess.span_fatal(s.span, ss);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-30 09:00:44 -07:00
|
|
|
fn check_states_against_conditions(&fn_ctxt fcx, &_fn f,
|
2011-07-06 19:00:00 -07:00
|
|
|
&ast::ty_param[] tps,
|
2011-06-30 00:18:41 -07:00
|
|
|
node_id id, &span sp, &fn_ident i) {
|
2011-06-17 19:07:23 -07:00
|
|
|
/* Postorder traversal instead of pre is important
|
|
|
|
because we want the smallest possible erroneous statement
|
|
|
|
or expression. */
|
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
auto visitor = visit::default_visitor[fn_ctxt]();
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
visitor = @rec(visit_stmt=check_states_stmt,
|
|
|
|
visit_expr=check_states_expr,
|
|
|
|
visit_fn=do_nothing
|
|
|
|
with *visitor);
|
|
|
|
visit::visit_fn(f, tps, sp, i, id, fcx, visit::mk_vt(visitor));
|
2011-06-17 19:07:23 -07:00
|
|
|
|
2011-06-30 00:18:41 -07:00
|
|
|
/* Check that the return value is initialized */
|
2011-06-17 19:07:23 -07:00
|
|
|
auto post = aux::block_poststate(fcx.ccx, f.body);
|
2011-06-13 18:10:33 -07:00
|
|
|
let aux::constr_ ret_c = rec(id=fcx.id, c=aux::ninit(fcx.name));
|
2011-06-17 19:07:23 -07:00
|
|
|
if (f.proto == ast::proto_fn && !promises(fcx, post, ret_c) &&
|
2011-06-19 22:41:21 +02:00
|
|
|
!type_is_nil(fcx.ccx.tcx, ret_ty_of_fn(fcx.ccx.tcx, id)) &&
|
2011-06-17 19:07:23 -07:00
|
|
|
f.decl.cf == return) {
|
2011-06-15 11:19:50 -07:00
|
|
|
fcx.ccx.tcx.sess.span_note(f.body.span,
|
|
|
|
"In function " + fcx.name +
|
|
|
|
", not all control paths \
|
|
|
|
return a value");
|
2011-06-18 22:41:20 -07:00
|
|
|
fcx.ccx.tcx.sess.span_fatal(f.decl.output.span,
|
2011-06-15 11:19:50 -07:00
|
|
|
"see declared return type of '" +
|
|
|
|
ty_to_str(*f.decl.output) + "'");
|
2011-06-17 19:07:23 -07:00
|
|
|
} else if (f.decl.cf == noreturn) {
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-05-20 19:50:29 -07:00
|
|
|
// check that this really always fails
|
|
|
|
// the fcx.id bit means "returns" for a returning fn,
|
2011-06-01 18:10:10 -07:00
|
|
|
// "diverges" for a non-returning fn
|
2011-06-17 19:07:23 -07:00
|
|
|
if (!promises(fcx, post, ret_c)) {
|
2011-06-18 22:41:20 -07:00
|
|
|
fcx.ccx.tcx.sess.span_fatal(f.body.span,
|
2011-06-15 11:19:50 -07:00
|
|
|
"In non-returning function " + fcx.name
|
2011-06-16 16:55:46 -07:00
|
|
|
+
|
|
|
|
", some control paths may \
|
2011-06-15 11:19:50 -07:00
|
|
|
return to the caller");
|
2011-05-20 19:50:29 -07:00
|
|
|
}
|
|
|
|
}
|
2011-06-30 00:18:41 -07:00
|
|
|
|
|
|
|
/* Finally, check for unused variables */
|
|
|
|
check_unused_vars(fcx);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-07-06 19:00:00 -07:00
|
|
|
fn check_fn_states(&fn_ctxt fcx, &_fn f, &ast::ty_param[] tps,
|
2011-06-30 00:18:41 -07:00
|
|
|
node_id id, &span sp, &fn_ident i) {
|
2011-05-14 19:02:30 -07:00
|
|
|
/* Compute the pre- and post-states for this function */
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-06-25 17:29:50 +02:00
|
|
|
// Fixpoint iteration
|
|
|
|
while (find_pre_post_state_fn(fcx, f)) {}
|
|
|
|
|
2011-05-14 19:02:30 -07:00
|
|
|
/* Now compare each expr's pre-state to its precondition
|
|
|
|
and post-state to its postcondition */
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-06-30 00:18:41 -07:00
|
|
|
check_states_against_conditions(fcx, f, tps, id, sp, i);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
fn fn_states(&_fn f, &ast::ty_param[] tps,
|
|
|
|
&span sp, &fn_ident i, node_id id, &crate_ctxt ccx,
|
|
|
|
&visit::vt[crate_ctxt] v) {
|
|
|
|
visit::visit_fn(f, tps, sp, i, id, ccx, v);
|
2011-05-14 19:02:30 -07:00
|
|
|
/* Look up the var-to-bit-num map for this function */
|
2011-07-12 11:26:14 -07:00
|
|
|
|
2011-05-14 19:02:30 -07:00
|
|
|
assert (ccx.fm.contains_key(id));
|
|
|
|
auto f_info = ccx.fm.get(id);
|
2011-06-24 15:11:22 -07:00
|
|
|
auto name = option::from_maybe("anon", i);
|
|
|
|
auto fcx = rec(enclosing=f_info, id=id, name=name, ccx=ccx);
|
2011-06-30 00:18:41 -07:00
|
|
|
check_fn_states(fcx, f, tps, id, sp, i);
|
2011-05-14 19:02:30 -07:00
|
|
|
}
|
|
|
|
|
2011-06-15 11:19:50 -07:00
|
|
|
fn check_crate(ty::ctxt cx, @crate crate) {
|
2011-05-19 15:47:15 -07:00
|
|
|
let crate_ctxt ccx = new_crate_ctxt(cx);
|
2011-05-14 19:02:30 -07:00
|
|
|
/* Build the global map from function id to var-to-bit-num-map */
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-05-14 19:02:30 -07:00
|
|
|
mk_f_to_fn_info(ccx, crate);
|
2011-05-18 15:43:05 -07:00
|
|
|
/* Add a blank ts_ann for every statement (and expression) */
|
2011-05-14 19:02:30 -07:00
|
|
|
|
2011-06-15 11:19:50 -07:00
|
|
|
annotate_crate(ccx, *crate);
|
2011-05-14 19:02:30 -07:00
|
|
|
/* Compute the pre and postcondition for every subexpression */
|
2011-06-15 11:19:50 -07:00
|
|
|
|
2011-07-12 11:26:14 -07:00
|
|
|
auto vtor = visit::default_visitor[crate_ctxt]();
|
|
|
|
vtor = @rec(visit_fn=fn_pre_post with *vtor);
|
|
|
|
visit::visit_crate(*crate, ccx, visit::mk_vt(vtor));
|
|
|
|
|
2011-05-18 18:04:07 -07:00
|
|
|
/* Check the pre- and postcondition against the pre- and poststate
|
|
|
|
for every expression */
|
2011-07-12 11:26:14 -07:00
|
|
|
vtor = @rec(visit_fn=fn_states with *vtor);
|
|
|
|
visit::visit_crate(*crate, ccx, visit::mk_vt(vtor));
|
2011-05-14 19:02:30 -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:
|
|
|
|
//
|