rustc: Remove middle/tstate
I feel like I've done this before
This commit is contained in:
parent
f5be40384f
commit
a51a561852
@ -1,76 +0,0 @@
|
||||
|
||||
use syntax::ast::*;
|
||||
use syntax::visit;
|
||||
use syntax::codemap::span;
|
||||
use syntax::print::pprust::stmt_to_str;
|
||||
use aux::{num_constraints, get_fn_info, crate_ctxt, add_node};
|
||||
use ann::empty_ann;
|
||||
use pat_util::pat_binding_ids;
|
||||
|
||||
fn collect_ids_expr(e: @expr, rs: @mut ~[node_id]) { vec::push(*rs, e.id); }
|
||||
|
||||
fn collect_ids_block(b: blk, rs: @mut ~[node_id]) {
|
||||
vec::push(*rs, b.node.id);
|
||||
}
|
||||
|
||||
fn collect_ids_stmt(s: @stmt, rs: @mut ~[node_id]) {
|
||||
match s.node {
|
||||
stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) {
|
||||
debug!("node_id %s", int::str(id));
|
||||
debug!("%s", stmt_to_str(*s));
|
||||
vec::push(*rs, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_ids_local(tcx: ty::ctxt, l: @local, rs: @mut ~[node_id]) {
|
||||
vec::push_all(*rs, pat_binding_ids(tcx.def_map, l.node.pat));
|
||||
}
|
||||
|
||||
fn node_ids_in_fn(tcx: ty::ctxt, body: blk, rs: @mut ~[node_id]) {
|
||||
let collect_ids =
|
||||
visit::mk_simple_visitor(@{visit_expr: |a| collect_ids_expr(a, rs),
|
||||
visit_block: |a| collect_ids_block(a, rs),
|
||||
visit_stmt: |a| collect_ids_stmt(a, rs),
|
||||
visit_local: |a|
|
||||
collect_ids_local(tcx, a, rs)
|
||||
with *visit::default_simple_visitor()});
|
||||
collect_ids.visit_block(body, (), collect_ids);
|
||||
}
|
||||
|
||||
fn init_vecs(ccx: crate_ctxt, node_ids: ~[node_id], len: uint) {
|
||||
for node_ids.each |i| {
|
||||
log(debug, int::str(i) + ~" |-> " + uint::str(len));
|
||||
add_node(ccx, *i, empty_ann(len));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_fn(ccx: crate_ctxt, num_constraints: uint, body: blk) {
|
||||
let node_ids: @mut ~[node_id] = @mut ~[];
|
||||
node_ids_in_fn(ccx.tcx, body, node_ids);
|
||||
let node_id_vec = *node_ids;
|
||||
init_vecs(ccx, node_id_vec, num_constraints);
|
||||
}
|
||||
|
||||
fn annotate_in_fn(ccx: crate_ctxt, _fk: visit::fn_kind, _decl: fn_decl,
|
||||
body: blk, _sp: span, id: node_id) {
|
||||
let f_info = get_fn_info(ccx, id);
|
||||
visit_fn(ccx, num_constraints(f_info), body);
|
||||
}
|
||||
|
||||
fn annotate_crate(ccx: crate_ctxt, crate: crate) {
|
||||
let do_ann =
|
||||
visit::mk_simple_visitor(
|
||||
@{visit_fn: |a,b,c,d,e| annotate_in_fn(ccx, a, b, c, d, e)
|
||||
with *visit::default_simple_visitor()});
|
||||
visit::visit_crate(crate, (), do_ann);
|
||||
}
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,988 +0,0 @@
|
||||
use option::*;
|
||||
use pat_util::*;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::*;
|
||||
use syntax::{visit, codemap};
|
||||
use codemap::span;
|
||||
use std::map::{hashmap, int_hash};
|
||||
use syntax::print::pprust::path_to_str;
|
||||
use tstate::ann::{pre_and_post, pre_and_post_state, empty_ann, prestate,
|
||||
poststate, precond, postcond,
|
||||
set_prestate, set_poststate, set_in_poststate_,
|
||||
extend_prestate, extend_poststate, set_precondition,
|
||||
set_postcondition, ts_ann,
|
||||
clear_in_postcond,
|
||||
clear_in_poststate_};
|
||||
use driver::session::session;
|
||||
use dvec::{dvec, extensions};
|
||||
use tritv::{trit, tfalse, ttrue, dont_care, t};
|
||||
|
||||
use syntax::print::pprust::{constr_args_to_str, lit_to_str};
|
||||
|
||||
// Used to communicate which operands should be invalidated
|
||||
// to helper functions
|
||||
enum oper_type {
|
||||
oper_move,
|
||||
oper_swap,
|
||||
oper_assign,
|
||||
oper_assign_op,
|
||||
oper_pure,
|
||||
}
|
||||
|
||||
/* logging funs */
|
||||
fn def_id_to_str(d: def_id) -> ~str {
|
||||
return int::str(d.crate) + ~"," + int::str(d.node);
|
||||
}
|
||||
|
||||
fn comma_str(args: ~[@constr_arg_use]) -> ~str {
|
||||
let mut rslt = ~"";
|
||||
let mut comma = false;
|
||||
for args.each |a| {
|
||||
if comma { rslt += ~", "; } else { comma = true; }
|
||||
match a.node {
|
||||
carg_base { rslt += ~"*"; }
|
||||
carg_ident(i) { rslt += *i.ident; }
|
||||
carg_lit(l) { rslt += lit_to_str(l); }
|
||||
}
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
fn constraint_to_str(tcx: ty::ctxt, c: sp_constr) -> ~str {
|
||||
return fmt!("%s(%s) - arising from %s",
|
||||
path_to_str(c.node.path),
|
||||
comma_str(c.node.args),
|
||||
codemap::span_to_str(c.span, tcx.sess.codemap));
|
||||
}
|
||||
|
||||
fn tritv_to_str(fcx: fn_ctxt, v: tritv::t) -> ~str {
|
||||
let mut s = ~"";
|
||||
let mut comma = false;
|
||||
for constraints(fcx).each |p| {
|
||||
match v.get(p.bit_num) {
|
||||
dont_care { }
|
||||
tt {
|
||||
s +=
|
||||
if comma { ~", " } else { comma = true; ~"" } +
|
||||
if tt == tfalse { ~"!" } else { ~"" } +
|
||||
constraint_to_str(fcx.ccx.tcx, p.c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
fn log_tritv(fcx: fn_ctxt, v: tritv::t) {
|
||||
log(debug, tritv_to_str(fcx, v));
|
||||
}
|
||||
|
||||
fn first_difference_string(fcx: fn_ctxt, expected: tritv::t, actual: tritv::t)
|
||||
-> ~str {
|
||||
let mut s = ~"";
|
||||
for constraints(fcx).each |c| {
|
||||
if expected.get(c.bit_num) == ttrue &&
|
||||
actual.get(c.bit_num) != ttrue {
|
||||
s = constraint_to_str(fcx.ccx.tcx, c.c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
fn log_tritv_err(fcx: fn_ctxt, v: tritv::t) {
|
||||
log(error, tritv_to_str(fcx, v));
|
||||
}
|
||||
|
||||
fn tos(v: ~[uint]) -> ~str {
|
||||
let mut rslt = ~"";
|
||||
for v.each |i| {
|
||||
if i == 0u {
|
||||
rslt += ~"0";
|
||||
} else if i == 1u { rslt += ~"1"; } else { rslt += ~"?"; }
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
fn log_cond(v: ~[uint]) { log(debug, tos(v)); }
|
||||
|
||||
fn log_cond_err(v: ~[uint]) { log(error, tos(v)); }
|
||||
|
||||
fn log_pp(pp: pre_and_post) {
|
||||
let p1 = pp.precondition.to_vec();
|
||||
let p2 = pp.postcondition.to_vec();
|
||||
debug!("pre:");
|
||||
log_cond(p1);
|
||||
debug!("post:");
|
||||
log_cond(p2);
|
||||
}
|
||||
|
||||
fn log_pp_err(pp: pre_and_post) {
|
||||
let p1 = pp.precondition.to_vec();
|
||||
let p2 = pp.postcondition.to_vec();
|
||||
error!("pre:");
|
||||
log_cond_err(p1);
|
||||
error!("post:");
|
||||
log_cond_err(p2);
|
||||
}
|
||||
|
||||
fn log_states(pp: pre_and_post_state) {
|
||||
let p1 = pp.prestate.to_vec();
|
||||
let p2 = pp.poststate.to_vec();
|
||||
debug!("prestate:");
|
||||
log_cond(p1);
|
||||
debug!("poststate:");
|
||||
log_cond(p2);
|
||||
}
|
||||
|
||||
fn log_states_err(pp: pre_and_post_state) {
|
||||
let p1 = pp.prestate.to_vec();
|
||||
let p2 = pp.poststate.to_vec();
|
||||
error!("prestate:");
|
||||
log_cond_err(p1);
|
||||
error!("poststate:");
|
||||
log_cond_err(p2);
|
||||
}
|
||||
|
||||
fn print_ident(i: ident) { log(debug, ~" " + *i + ~" "); }
|
||||
|
||||
fn print_idents(&idents: ~[ident]) {
|
||||
if vec::len::<ident>(idents) == 0u { return; }
|
||||
log(debug, ~"an ident: " + *vec::pop::<ident>(idents));
|
||||
print_idents(idents);
|
||||
}
|
||||
|
||||
|
||||
/* data structures */
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/* Two different data structures represent constraints in different
|
||||
contexts: constraint and norm_constraint.
|
||||
|
||||
constraint gets used to record constraints in a table keyed by def_ids. Each
|
||||
constraint has a single operator but a list of possible argument lists, and
|
||||
thus represents several constraints at once, one for each possible argument
|
||||
list.
|
||||
|
||||
norm_constraint, in contrast, gets used when handling an instance of a
|
||||
constraint rather than a definition of a constraint. It has only a single
|
||||
argument list.
|
||||
|
||||
The representation of constraints, where multiple instances of the
|
||||
same predicate are collapsed into one entry in the table, makes it
|
||||
easier to look up a specific instance.
|
||||
|
||||
Both types are in constrast with the constraint type defined in
|
||||
syntax::ast, which is for predicate constraints only, and is what
|
||||
gets generated by the parser. aux and ast share the same type
|
||||
to represent predicate *arguments* however. This type
|
||||
(constr_arg_general) is parameterized (see comments in syntax::ast).
|
||||
|
||||
Both types store an ident and span, for error-logging purposes.
|
||||
*/
|
||||
type pred_args_ = {args: ~[@constr_arg_use], bit_num: uint};
|
||||
|
||||
type pred_args = spanned<pred_args_>;
|
||||
|
||||
// The attached node ID is the *defining* node ID
|
||||
// for this local.
|
||||
type constr_arg_use = spanned<constr_arg_general_<inst>>;
|
||||
|
||||
/* Predicate constraints refer to the truth value of a predicate on variables
|
||||
(definitely false, maybe true, or definitely true). The <path> field (and the
|
||||
<def_id> field in the npred constructor) names a user-defined function that
|
||||
may be the operator in a "check" expression in the source. */
|
||||
|
||||
type constraint = {
|
||||
path: @path,
|
||||
// FIXME (#2539): really only want it to be mut during
|
||||
// collect_locals. freeze it after that.
|
||||
descs: @dvec<pred_args>
|
||||
};
|
||||
|
||||
type tsconstr = {
|
||||
path: @path,
|
||||
def_id: def_id,
|
||||
args: ~[@constr_arg_use]
|
||||
};
|
||||
|
||||
type sp_constr = spanned<tsconstr>;
|
||||
|
||||
type norm_constraint = {bit_num: uint, c: sp_constr};
|
||||
|
||||
type constr_map = std::map::hashmap<def_id, constraint>;
|
||||
|
||||
/* Contains stuff that has to be computed up front */
|
||||
/* For easy access, the fn_info stores two special constraints for each
|
||||
function. So we need context. And so it seems clearer to just have separate
|
||||
constraints. */
|
||||
type fn_info =
|
||||
/* list, accumulated during pre/postcondition
|
||||
computation, of all local variables that may be
|
||||
used */
|
||||
// Doesn't seem to work without the @ -- bug
|
||||
{constrs: constr_map,
|
||||
num_constraints: uint,
|
||||
cf: ret_style,
|
||||
used_vars: @mut ~[node_id],
|
||||
ignore: bool};
|
||||
|
||||
/* mapping from node ID to typestate annotation */
|
||||
type node_ann_table = @mut ~[mut ts_ann];
|
||||
|
||||
|
||||
/* mapping from function name to fn_info map */
|
||||
type fn_info_map = std::map::hashmap<node_id, fn_info>;
|
||||
|
||||
type fn_ctxt =
|
||||
{enclosing: fn_info, id: node_id, name: ident, ccx: crate_ctxt};
|
||||
|
||||
type crate_ctxt = {tcx: ty::ctxt, node_anns: node_ann_table, fm: fn_info_map};
|
||||
|
||||
fn get_fn_info(ccx: crate_ctxt, id: node_id) -> fn_info {
|
||||
assert (ccx.fm.contains_key(id));
|
||||
return ccx.fm.get(id);
|
||||
}
|
||||
|
||||
fn add_node(ccx: crate_ctxt, i: node_id, a: ts_ann) {
|
||||
let sz = vec::len(*ccx.node_anns);
|
||||
if sz <= i as uint {
|
||||
vec::grow(*ccx.node_anns, (i as uint) - sz + 1u, empty_ann(0u));
|
||||
}
|
||||
ccx.node_anns[i] = a;
|
||||
}
|
||||
|
||||
fn get_ts_ann(ccx: crate_ctxt, i: node_id) -> Option<ts_ann> {
|
||||
if i as uint < vec::len(*ccx.node_anns) {
|
||||
return some::<ts_ann>(ccx.node_anns[i]);
|
||||
} else { return none::<ts_ann>; }
|
||||
}
|
||||
|
||||
|
||||
/********* utils ********/
|
||||
fn node_id_to_ts_ann(ccx: crate_ctxt, id: node_id) -> ts_ann {
|
||||
match get_ts_ann(ccx, id) {
|
||||
none {
|
||||
error!("node_id_to_ts_ann: no ts_ann for node_id %d", id);
|
||||
fail;
|
||||
}
|
||||
Some(tt) { return tt; }
|
||||
}
|
||||
}
|
||||
|
||||
fn node_id_to_poststate(ccx: crate_ctxt, id: node_id) -> poststate {
|
||||
debug!("node_id_to_poststate");
|
||||
return node_id_to_ts_ann(ccx, id).states.poststate;
|
||||
}
|
||||
|
||||
fn stmt_to_ann(ccx: crate_ctxt, s: stmt) -> ts_ann {
|
||||
debug!("stmt_to_ann");
|
||||
match s.node {
|
||||
stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) {
|
||||
return node_id_to_ts_ann(ccx, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* fails if e has no annotation */
|
||||
fn expr_states(ccx: crate_ctxt, e: @expr) -> pre_and_post_state {
|
||||
debug!("expr_states");
|
||||
return node_id_to_ts_ann(ccx, e.id).states;
|
||||
}
|
||||
|
||||
|
||||
/* fails if e has no annotation */
|
||||
fn expr_pp(ccx: crate_ctxt, e: @expr) -> pre_and_post {
|
||||
debug!("expr_pp");
|
||||
return node_id_to_ts_ann(ccx, e.id).conditions;
|
||||
}
|
||||
|
||||
fn stmt_pp(ccx: crate_ctxt, s: stmt) -> pre_and_post {
|
||||
return stmt_to_ann(ccx, s).conditions;
|
||||
}
|
||||
|
||||
|
||||
/* fails if b has no annotation */
|
||||
fn block_pp(ccx: crate_ctxt, b: blk) -> pre_and_post {
|
||||
debug!("block_pp");
|
||||
return node_id_to_ts_ann(ccx, b.node.id).conditions;
|
||||
}
|
||||
|
||||
fn clear_pp(pp: pre_and_post) {
|
||||
ann::clear(pp.precondition);
|
||||
ann::clear(pp.postcondition);
|
||||
}
|
||||
|
||||
fn clear_precond(ccx: crate_ctxt, id: node_id) {
|
||||
let pp = node_id_to_ts_ann(ccx, id);
|
||||
ann::clear(pp.conditions.precondition);
|
||||
}
|
||||
|
||||
fn block_states(ccx: crate_ctxt, b: blk) -> pre_and_post_state {
|
||||
debug!("block_states");
|
||||
return node_id_to_ts_ann(ccx, b.node.id).states;
|
||||
}
|
||||
|
||||
fn stmt_states(ccx: crate_ctxt, s: stmt) -> pre_and_post_state {
|
||||
return stmt_to_ann(ccx, s).states;
|
||||
}
|
||||
|
||||
fn expr_precond(ccx: crate_ctxt, e: @expr) -> precond {
|
||||
return expr_pp(ccx, e).precondition;
|
||||
}
|
||||
|
||||
fn expr_postcond(ccx: crate_ctxt, e: @expr) -> postcond {
|
||||
return expr_pp(ccx, e).postcondition;
|
||||
}
|
||||
|
||||
fn expr_prestate(ccx: crate_ctxt, e: @expr) -> prestate {
|
||||
return expr_states(ccx, e).prestate;
|
||||
}
|
||||
|
||||
fn expr_poststate(ccx: crate_ctxt, e: @expr) -> poststate {
|
||||
return expr_states(ccx, e).poststate;
|
||||
}
|
||||
|
||||
fn stmt_precond(ccx: crate_ctxt, s: stmt) -> precond {
|
||||
return stmt_pp(ccx, s).precondition;
|
||||
}
|
||||
|
||||
fn stmt_postcond(ccx: crate_ctxt, s: stmt) -> postcond {
|
||||
return stmt_pp(ccx, s).postcondition;
|
||||
}
|
||||
|
||||
fn states_to_poststate(ss: pre_and_post_state) -> poststate {
|
||||
return ss.poststate;
|
||||
}
|
||||
|
||||
fn stmt_prestate(ccx: crate_ctxt, s: stmt) -> prestate {
|
||||
return stmt_states(ccx, s).prestate;
|
||||
}
|
||||
|
||||
fn stmt_poststate(ccx: crate_ctxt, s: stmt) -> poststate {
|
||||
return stmt_states(ccx, s).poststate;
|
||||
}
|
||||
|
||||
fn block_precond(ccx: crate_ctxt, b: blk) -> precond {
|
||||
return block_pp(ccx, b).precondition;
|
||||
}
|
||||
|
||||
fn block_postcond(ccx: crate_ctxt, b: blk) -> postcond {
|
||||
return block_pp(ccx, b).postcondition;
|
||||
}
|
||||
|
||||
fn block_prestate(ccx: crate_ctxt, b: blk) -> prestate {
|
||||
return block_states(ccx, b).prestate;
|
||||
}
|
||||
|
||||
fn block_poststate(ccx: crate_ctxt, b: blk) -> poststate {
|
||||
return block_states(ccx, b).poststate;
|
||||
}
|
||||
|
||||
fn set_prestate_ann(ccx: crate_ctxt, id: node_id, pre: prestate) -> bool {
|
||||
debug!("set_prestate_ann");
|
||||
return set_prestate(node_id_to_ts_ann(ccx, id), pre);
|
||||
}
|
||||
|
||||
fn extend_prestate_ann(ccx: crate_ctxt, id: node_id, pre: prestate) -> bool {
|
||||
debug!("extend_prestate_ann");
|
||||
return extend_prestate(node_id_to_ts_ann(ccx, id).states.prestate, pre);
|
||||
}
|
||||
|
||||
fn set_poststate_ann(ccx: crate_ctxt, id: node_id, post: poststate) -> bool {
|
||||
debug!("set_poststate_ann");
|
||||
return set_poststate(node_id_to_ts_ann(ccx, id), post);
|
||||
}
|
||||
|
||||
fn extend_poststate_ann(ccx: crate_ctxt, id: node_id, post: poststate) ->
|
||||
bool {
|
||||
debug!("extend_poststate_ann");
|
||||
return extend_poststate(
|
||||
node_id_to_ts_ann(ccx, id).states.poststate, post);
|
||||
}
|
||||
|
||||
fn set_pre_and_post(ccx: crate_ctxt, id: node_id, pre: precond,
|
||||
post: postcond) {
|
||||
debug!("set_pre_and_post");
|
||||
let tt = node_id_to_ts_ann(ccx, id);
|
||||
set_precondition(tt, pre);
|
||||
set_postcondition(tt, post);
|
||||
}
|
||||
|
||||
fn copy_pre_post(ccx: crate_ctxt, id: node_id, sub: @expr) {
|
||||
debug!("set_pre_and_post");
|
||||
let p = expr_pp(ccx, sub);
|
||||
copy_pre_post_(ccx, id, p.precondition, p.postcondition);
|
||||
}
|
||||
|
||||
fn copy_pre_post_(ccx: crate_ctxt, id: node_id, pre: prestate,
|
||||
post: poststate) {
|
||||
debug!("set_pre_and_post");
|
||||
let tt = node_id_to_ts_ann(ccx, id);
|
||||
set_precondition(tt, pre);
|
||||
set_postcondition(tt, post);
|
||||
}
|
||||
|
||||
/* sets all bits to *1* */
|
||||
fn set_postcond_false(ccx: crate_ctxt, id: node_id) {
|
||||
let p = node_id_to_ts_ann(ccx, id);
|
||||
ann::set(p.conditions.postcondition);
|
||||
}
|
||||
|
||||
fn pure_exp(ccx: crate_ctxt, id: node_id, p: prestate) -> bool {
|
||||
return set_prestate_ann(ccx, id, p) | set_poststate_ann(ccx, id, p);
|
||||
}
|
||||
|
||||
fn num_constraints(m: fn_info) -> uint { return m.num_constraints; }
|
||||
|
||||
fn new_crate_ctxt(cx: ty::ctxt) -> crate_ctxt {
|
||||
let na: ~[mut ts_ann] = ~[mut];
|
||||
return {tcx: cx, node_anns: @mut na, fm: int_hash::<fn_info>()};
|
||||
}
|
||||
|
||||
/* Use e's type to determine whether it returns.
|
||||
If it has a function type with a ! annotation,
|
||||
the answer is noreturn. */
|
||||
fn controlflow_expr(ccx: crate_ctxt, e: @expr) -> ret_style {
|
||||
match ty::get(ty::node_id_to_type(ccx.tcx, e.id)).struct {
|
||||
ty::ty_fn(f) { return f.ret_style; }
|
||||
_ { return return_val; }
|
||||
}
|
||||
}
|
||||
|
||||
fn constraints_expr(cx: ty::ctxt, e: @expr) -> ~[@ty::constr] {
|
||||
match ty::get(ty::node_id_to_type(cx, e.id)).struct {
|
||||
ty::ty_fn(f) { return f.constraints; }
|
||||
_ { return ~[]; }
|
||||
}
|
||||
}
|
||||
|
||||
fn node_id_to_def_strict(cx: ty::ctxt, id: node_id) -> def {
|
||||
match cx.def_map.find(id) {
|
||||
none {
|
||||
error!("node_id_to_def: node_id %d has no def", id);
|
||||
fail;
|
||||
}
|
||||
Some(d) { return d; }
|
||||
}
|
||||
}
|
||||
|
||||
fn node_id_to_def(ccx: crate_ctxt, id: node_id) -> Option<def> {
|
||||
return ccx.tcx.def_map.find(id);
|
||||
}
|
||||
|
||||
fn norm_a_constraint(id: def_id, c: constraint) -> ~[norm_constraint] {
|
||||
let mut rslt: ~[norm_constraint] = ~[];
|
||||
for (*c.descs).each |pd| {
|
||||
vec::push(rslt,
|
||||
{bit_num: pd.node.bit_num,
|
||||
c: respan(pd.span, {path: c.path,
|
||||
def_id: id,
|
||||
args: pd.node.args})});
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
|
||||
// Tried to write this as an iterator, but I got a
|
||||
// non-exhaustive match in trans.
|
||||
fn constraints(fcx: fn_ctxt) -> ~[norm_constraint] {
|
||||
let mut rslt: ~[norm_constraint] = ~[];
|
||||
for fcx.enclosing.constrs.each |key, val| {
|
||||
vec::push_all(rslt, norm_a_constraint(key, val));
|
||||
};
|
||||
return rslt;
|
||||
}
|
||||
|
||||
// FIXME (#2539): Would rather take an immutable vec as an argument,
|
||||
// should freeze it at some earlier point.
|
||||
fn match_args(fcx: fn_ctxt, occs: @dvec<pred_args>,
|
||||
occ: ~[@constr_arg_use]) -> uint {
|
||||
debug!("match_args: looking at %s",
|
||||
constr_args_to_str(fn@(i: inst) -> ~str { *i.ident }, occ));
|
||||
for (*occs).each |pd| {
|
||||
log(debug,
|
||||
~"match_args: candidate " + pred_args_to_str(pd));
|
||||
fn eq(p: inst, q: inst) -> bool { return p.node == q.node; }
|
||||
if ty::args_eq(eq, pd.node.args, occ) { return pd.node.bit_num; }
|
||||
}
|
||||
fcx.ccx.tcx.sess.bug(~"match_args: no match for occurring args");
|
||||
}
|
||||
|
||||
fn def_id_for_constr(tcx: ty::ctxt, t: node_id) -> def_id {
|
||||
match tcx.def_map.find(t) {
|
||||
none {
|
||||
tcx.sess.bug(~"node_id_for_constr: bad node_id " + int::str(t));
|
||||
}
|
||||
Some(def_fn(i, _)) { return i; }
|
||||
_ { tcx.sess.bug(~"node_id_for_constr: pred is not a function"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_to_constr_arg(tcx: ty::ctxt, e: @expr) -> @constr_arg_use {
|
||||
match e.node {
|
||||
expr_path(p) {
|
||||
match tcx.def_map.find(e.id) {
|
||||
Some(def_local(nid, _)) | Some(def_arg(nid, _)) |
|
||||
Some(def_binding(nid, _)) | Some(def_upvar(nid, _, _, _)) {
|
||||
return @respan(p.span,
|
||||
carg_ident({ident: p.idents[0], node: nid}));
|
||||
}
|
||||
Some(what) {
|
||||
tcx.sess.span_bug(e.span,
|
||||
fmt!("exprs_to_constr_args: non-local variable %? \
|
||||
as pred arg", what));
|
||||
}
|
||||
none {
|
||||
tcx.sess.span_bug(e.span,
|
||||
~"exprs_to_constr_args: unbound id as pred arg");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_lit(l) { return @respan(e.span, carg_lit(l)); }
|
||||
_ {
|
||||
tcx.sess.span_fatal(e.span,
|
||||
~"arguments to constrained functions must be " +
|
||||
~"literals or local variables");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exprs_to_constr_args(tcx: ty::ctxt,
|
||||
args: ~[@expr]) -> ~[@constr_arg_use] {
|
||||
let f = |a| expr_to_constr_arg(tcx, a);
|
||||
let mut rslt: ~[@constr_arg_use] = ~[];
|
||||
for args.each |e| { vec::push(rslt, f(e)); }
|
||||
rslt
|
||||
}
|
||||
|
||||
fn expr_to_constr(tcx: ty::ctxt, e: @expr) -> sp_constr {
|
||||
match e.node {
|
||||
expr_call(operator, args, _) {
|
||||
match operator.node {
|
||||
expr_path(p) {
|
||||
return respan(e.span,
|
||||
{path: p,
|
||||
def_id: def_id_for_constr(tcx, operator.id),
|
||||
args: exprs_to_constr_args(tcx, args)});
|
||||
}
|
||||
_ {
|
||||
tcx.sess.span_bug(operator.span,
|
||||
~"ill-formed operator in predicate");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ {
|
||||
tcx.sess.span_bug(e.span, ~"ill-formed predicate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pred_args_to_str(p: pred_args) -> ~str {
|
||||
~"<" + uint::str(p.node.bit_num) + ~", " +
|
||||
constr_args_to_str(fn@(i: inst) -> ~str {return *i.ident; },
|
||||
p.node.args)
|
||||
+ ~">"
|
||||
}
|
||||
|
||||
fn substitute_constr_args(cx: ty::ctxt, actuals: ~[@expr], c: @ty::constr) ->
|
||||
tsconstr {
|
||||
let mut rslt: ~[@constr_arg_use] = ~[];
|
||||
for c.node.args.each |a| {
|
||||
vec::push(rslt, substitute_arg(cx, actuals, a));
|
||||
}
|
||||
return {path: c.node.path,
|
||||
def_id: c.node.id,
|
||||
args: rslt};
|
||||
}
|
||||
|
||||
fn substitute_arg(cx: ty::ctxt, actuals: ~[@expr], a: @constr_arg) ->
|
||||
@constr_arg_use {
|
||||
let num_actuals = vec::len(actuals);
|
||||
match a.node {
|
||||
carg_ident(i) {
|
||||
if i < num_actuals {
|
||||
return expr_to_constr_arg(cx, actuals[i]);
|
||||
} else {
|
||||
cx.sess.span_fatal(a.span, ~"constraint argument out of bounds");
|
||||
}
|
||||
}
|
||||
carg_base { return @respan(a.span, carg_base); }
|
||||
carg_lit(l) { return @respan(a.span, carg_lit(l)); }
|
||||
}
|
||||
}
|
||||
|
||||
fn pred_args_matches(pattern: ~[constr_arg_general_<inst>],
|
||||
desc: pred_args) ->
|
||||
bool {
|
||||
let mut i = 0u;
|
||||
for desc.node.args.each |c| {
|
||||
let n = pattern[i];
|
||||
match c.node {
|
||||
carg_ident(p) {
|
||||
match n {
|
||||
carg_ident(q) { if p.node != q.node { return false; } }
|
||||
_ { return false; }
|
||||
}
|
||||
}
|
||||
carg_base { if n != carg_base { return false; } }
|
||||
carg_lit(l) {
|
||||
match n {
|
||||
carg_lit(m) { if !const_eval::lit_eq(l, m) { return false; } }
|
||||
_ { return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1u;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn find_instance_(pattern: ~[constr_arg_general_<inst>],
|
||||
descs: ~[pred_args]) ->
|
||||
Option<uint> {
|
||||
for descs.each |d| {
|
||||
if pred_args_matches(pattern, d) { return Some(d.node.bit_num); }
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
type inst = {ident: ident, node: node_id};
|
||||
|
||||
enum dest {
|
||||
local_dest(inst), // RHS is assigned to a local variable
|
||||
call // RHS is passed to a function
|
||||
}
|
||||
|
||||
type subst = ~[{from: inst, to: inst}];
|
||||
|
||||
fn find_instances(_fcx: fn_ctxt, subst: subst,
|
||||
c: constraint) -> ~[{from: uint, to: uint}] {
|
||||
|
||||
if vec::len(subst) == 0u { return ~[]; }
|
||||
let mut res = ~[];
|
||||
do (*c.descs).swap |v| {
|
||||
let v <- vec::from_mut(v);
|
||||
for v.each |d| {
|
||||
if args_mention(d.node.args, find_in_subst_bool, subst) {
|
||||
let old_bit_num = d.node.bit_num;
|
||||
let newv = replace(subst, d);
|
||||
match find_instance_(newv, v) {
|
||||
Some(d1) {vec::push(res, {from: old_bit_num, to: d1})}
|
||||
_ {}
|
||||
}
|
||||
} else {}
|
||||
}
|
||||
vec::to_mut(v)
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
fn find_in_subst(id: node_id, s: subst) -> Option<inst> {
|
||||
for s.each |p| {
|
||||
if id == p.from.node { return Some(p.to); }
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
fn find_in_subst_bool(s: subst, id: node_id) -> bool {
|
||||
is_some(find_in_subst(id, s))
|
||||
}
|
||||
|
||||
fn insts_to_str(stuff: ~[constr_arg_general_<inst>]) -> ~str {
|
||||
let mut rslt = ~"<";
|
||||
for stuff.each |i| {
|
||||
rslt +=
|
||||
~" " +
|
||||
match i {
|
||||
carg_ident(p) { *p.ident }
|
||||
carg_base { ~"*" }
|
||||
carg_lit(_) { ~"~[lit]" }
|
||||
} + ~" ";
|
||||
}
|
||||
rslt += ~">";
|
||||
rslt
|
||||
}
|
||||
|
||||
fn replace(subst: subst, d: pred_args) -> ~[constr_arg_general_<inst>] {
|
||||
let mut rslt: ~[constr_arg_general_<inst>] = ~[];
|
||||
for d.node.args.each |c| {
|
||||
match c.node {
|
||||
carg_ident(p) {
|
||||
match find_in_subst(p.node, subst) {
|
||||
Some(newv) { vec::push(rslt, carg_ident(newv)); }
|
||||
_ { vec::push(rslt, c.node); }
|
||||
}
|
||||
}
|
||||
_ {
|
||||
vec::push(rslt, c.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rslt;
|
||||
}
|
||||
|
||||
enum if_ty { if_check, plain_if, }
|
||||
|
||||
fn for_constraints_mentioning(fcx: fn_ctxt, id: node_id,
|
||||
f: fn(norm_constraint)) {
|
||||
for constraints(fcx).each |c| {
|
||||
if constraint_mentions(fcx, c, id) { f(c); }
|
||||
};
|
||||
}
|
||||
|
||||
fn local_node_id_to_def_id_strict(fcx: fn_ctxt, sp: span, i: node_id) ->
|
||||
def_id {
|
||||
match local_node_id_to_def(fcx, i) {
|
||||
Some(def_local(nid, _)) | Some(def_arg(nid, _)) |
|
||||
Some(def_upvar(nid, _, _)) {
|
||||
return local_def(nid);
|
||||
}
|
||||
Some(_) {
|
||||
fcx.ccx.tcx.sess.span_fatal(sp,
|
||||
~"local_node_id_to_def_id: id \
|
||||
isn't a local");
|
||||
}
|
||||
none {
|
||||
// should really be bug. span_bug()?
|
||||
fcx.ccx.tcx.sess.span_fatal(sp,
|
||||
~"local_node_id_to_def_id: id \
|
||||
is unbound");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn local_node_id_to_def(fcx: fn_ctxt, i: node_id) -> Option<def> {
|
||||
fcx.ccx.tcx.def_map.find(i)
|
||||
}
|
||||
|
||||
fn local_node_id_to_def_id(fcx: fn_ctxt, i: node_id) -> Option<def_id> {
|
||||
match local_node_id_to_def(fcx, i) {
|
||||
Some(def_local(nid, _)) | Some(def_arg(nid, _)) |
|
||||
Some(def_binding(nid, _)) | Some(def_upvar(nid, _, _)) {
|
||||
Some(local_def(nid))
|
||||
}
|
||||
_ { none }
|
||||
}
|
||||
}
|
||||
|
||||
fn local_node_id_to_local_def_id(fcx: fn_ctxt, i: node_id) ->
|
||||
Option<node_id> {
|
||||
match local_node_id_to_def_id(fcx, i) {
|
||||
Some(did) { Some(did.node) }
|
||||
_ { none }
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_in_postcond(fcx: fn_ctxt, parent_exp: node_id, dest: inst, src: inst,
|
||||
ty: oper_type) {
|
||||
let post =
|
||||
node_id_to_ts_ann(fcx.ccx, parent_exp).conditions.postcondition;
|
||||
copy_in_poststate_two(fcx, post, post, dest, src, ty);
|
||||
}
|
||||
|
||||
fn copy_in_poststate(fcx: fn_ctxt, post: poststate, dest: inst, src: inst,
|
||||
ty: oper_type) {
|
||||
copy_in_poststate_two(fcx, post, post, dest, src, ty);
|
||||
}
|
||||
|
||||
// In target_post, set the bits corresponding to copies of any
|
||||
// constraints mentioning src that are set in src_post, with
|
||||
// dest substituted for src.
|
||||
// (This doesn't create any new constraints. If a new, substituted
|
||||
// constraint isn't already in the bit vector, it's ignored.)
|
||||
fn copy_in_poststate_two(fcx: fn_ctxt, src_post: poststate,
|
||||
target_post: poststate, dest: inst, src: inst,
|
||||
ty: oper_type) {
|
||||
let mut subst;
|
||||
match ty {
|
||||
oper_swap { subst = ~[{from: dest, to: src}, {from: src, to: dest}]; }
|
||||
oper_assign_op {
|
||||
return; // Don't do any propagation
|
||||
}
|
||||
_ { subst = ~[{from: src, to: dest}]; }
|
||||
}
|
||||
|
||||
|
||||
for fcx.enclosing.constrs.each_value |val| {
|
||||
// replace any occurrences of the src def_id with the
|
||||
// dest def_id
|
||||
let insts = find_instances(fcx, subst, val);
|
||||
for insts.each |p| {
|
||||
if bitvectors::promises_(p.from, src_post) {
|
||||
set_in_poststate_(p.to, target_post);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn forget_in_postcond(fcx: fn_ctxt, parent_exp: node_id, dead_v: node_id) {
|
||||
// In the postcondition given by parent_exp, clear the bits
|
||||
// for any constraints mentioning dead_v
|
||||
let d = local_node_id_to_local_def_id(fcx, dead_v);
|
||||
do option::iter(d) |d_id| {
|
||||
do for_constraints_mentioning(fcx, d_id) |c| {
|
||||
debug!("clearing constraint %u %s",
|
||||
c.bit_num,
|
||||
constraint_to_str(fcx.ccx.tcx, c.c));
|
||||
clear_in_postcond(c.bit_num,
|
||||
node_id_to_ts_ann(fcx.ccx,
|
||||
parent_exp).conditions);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn forget_in_poststate(fcx: fn_ctxt, p: poststate, dead_v: node_id) -> bool {
|
||||
// In the poststate given by parent_exp, clear the bits
|
||||
// for any constraints mentioning dead_v
|
||||
let d = local_node_id_to_local_def_id(fcx, dead_v);
|
||||
let mut changed = false;
|
||||
do option::iter(d) |d_id| {
|
||||
do for_constraints_mentioning(fcx, d_id) |c| {
|
||||
changed |= clear_in_poststate_(c.bit_num, p);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn any_eq(v: ~[node_id], d: node_id) -> bool {
|
||||
for v.each |i| { if i == d { return true; } }
|
||||
false
|
||||
}
|
||||
|
||||
fn constraint_mentions(_fcx: fn_ctxt, c: norm_constraint, v: node_id) ->
|
||||
bool {
|
||||
return args_mention(c.c.node.args, any_eq, ~[v]);
|
||||
}
|
||||
|
||||
fn args_mention<T>(args: ~[@constr_arg_use],
|
||||
q: fn(~[T], node_id) -> bool,
|
||||
s: ~[T]) -> bool {
|
||||
|
||||
for args.each |a| {
|
||||
match a.node {
|
||||
carg_ident(p1) { if q(s, p1.node) { return true; } } _ { }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn use_var(fcx: fn_ctxt, v: node_id) {
|
||||
vec::push(*fcx.enclosing.used_vars, v);
|
||||
}
|
||||
|
||||
fn op_to_oper_ty(io: init_op) -> oper_type {
|
||||
match io { init_move { oper_move } _ { oper_assign } }
|
||||
}
|
||||
|
||||
// default function visitor
|
||||
fn do_nothing<T>(_fk: visit::fn_kind, _decl: fn_decl, _body: blk,
|
||||
_sp: span, _id: node_id,
|
||||
_t: T, _v: visit::vt<T>) {
|
||||
}
|
||||
|
||||
|
||||
fn args_to_constr_args(tcx: ty::ctxt, args: ~[arg],
|
||||
indices: ~[@sp_constr_arg<uint>])
|
||||
-> ~[@constr_arg_use] {
|
||||
let mut actuals: ~[@constr_arg_use] = ~[];
|
||||
let num_args = vec::len(args);
|
||||
for indices.each |a| {
|
||||
vec::push(
|
||||
actuals,
|
||||
@respan(a.span,
|
||||
match a.node {
|
||||
carg_base { carg_base }
|
||||
carg_ident(i) {
|
||||
if i < num_args {
|
||||
carg_ident({ident: args[i].ident,
|
||||
node: args[i].id})
|
||||
} else {
|
||||
tcx.sess.span_bug(a.span,
|
||||
~"index out of bounds in \
|
||||
constraint arg");
|
||||
}
|
||||
}
|
||||
carg_lit(l) { carg_lit(l) }
|
||||
}));
|
||||
}
|
||||
return actuals;
|
||||
}
|
||||
|
||||
fn ast_constr_to_ts_constr(tcx: ty::ctxt, args: ~[arg], c: @constr) ->
|
||||
tsconstr {
|
||||
let tconstr = ty::ast_constr_to_constr(tcx, c);
|
||||
return {path: tconstr.node.path,
|
||||
def_id: tconstr.node.id,
|
||||
args: args_to_constr_args(tcx, args, tconstr.node.args)};
|
||||
}
|
||||
|
||||
fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: ~[arg], c: @constr) ->
|
||||
sp_constr {
|
||||
let tconstr = ast_constr_to_ts_constr(tcx, args, c);
|
||||
return respan(c.span, tconstr);
|
||||
}
|
||||
|
||||
type binding = {lhs: ~[dest], rhs: Option<initializer>};
|
||||
|
||||
fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding {
|
||||
let mut lhs = ~[];
|
||||
do pat_bindings(tcx.def_map, loc.node.pat) |_bm, p_id, _s, name| {
|
||||
vec::push(lhs, local_dest({ident: path_to_ident(name), node: p_id}));
|
||||
};
|
||||
{lhs: lhs, rhs: loc.node.init}
|
||||
}
|
||||
|
||||
fn locals_to_bindings(tcx: ty::ctxt, locals: ~[@local]) -> ~[binding] {
|
||||
let mut rslt = ~[];
|
||||
for locals.each |loc| { vec::push(rslt, local_to_bindings(tcx, loc)); }
|
||||
return rslt;
|
||||
}
|
||||
|
||||
fn callee_modes(fcx: fn_ctxt, callee: node_id) -> ~[mode] {
|
||||
let ty = ty::type_autoderef(fcx.ccx.tcx,
|
||||
ty::node_id_to_type(fcx.ccx.tcx, callee));
|
||||
match ty::get(ty).struct {
|
||||
ty::ty_fn(ref fn_ty) {
|
||||
return fn_ty.sig.inputs.map(|input| input.mode);
|
||||
}
|
||||
_ {
|
||||
// Shouldn't happen; callee should be ty_fn.
|
||||
fcx.ccx.tcx.sess.bug(~"non-fn callee type in callee_modes: " +
|
||||
util::ppaux::ty_to_str(fcx.ccx.tcx, ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn callee_arg_init_ops(fcx: fn_ctxt, callee: node_id) -> ~[init_op] {
|
||||
do vec::map(callee_modes(fcx, callee)) |m| {
|
||||
match ty::resolved_mode(fcx.ccx.tcx, m) {
|
||||
by_move { init_move }
|
||||
by_copy | by_ref | by_val | by_mutbl_ref { init_assign }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_bindings(ops: ~[init_op], es: ~[@expr]) -> ~[binding] {
|
||||
let mut bindings: ~[binding] = ~[];
|
||||
let mut i = 0u;
|
||||
for ops.each |op| {
|
||||
vec::push(bindings,
|
||||
{lhs: ~[call], rhs: Some({op: op, expr: es[i]})});
|
||||
i += 1u;
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,153 +0,0 @@
|
||||
|
||||
use syntax::ast;
|
||||
use ast::{stmt, fn_ident, node_id, crate, return_val, noreturn, expr};
|
||||
use syntax::{visit, print};
|
||||
use syntax::codemap::span;
|
||||
use middle::ty;
|
||||
use tstate::ann::{precond, prestate,
|
||||
implies, ann_precond, ann_prestate};
|
||||
use aux::*;
|
||||
|
||||
use util::ppaux::ty_to_str;
|
||||
use bitvectors::*;
|
||||
use annotate::annotate_crate;
|
||||
use collect_locals::mk_f_to_fn_info;
|
||||
use pre_post_conditions::fn_pre_post;
|
||||
use states::find_pre_post_state_fn;
|
||||
use syntax::print::pprust::expr_to_str;
|
||||
use driver::session::session;
|
||||
use std::map::hashmap;
|
||||
|
||||
fn check_states_expr(e: @expr, fcx: fn_ctxt, v: visit::vt<fn_ctxt>) {
|
||||
visit::visit_expr(e, fcx, v);
|
||||
|
||||
let prec: precond = expr_precond(fcx.ccx, e);
|
||||
let pres: prestate = expr_prestate(fcx.ccx, e);
|
||||
|
||||
if !implies(pres, prec) {
|
||||
let mut s = ~"";
|
||||
let diff = first_difference_string(fcx, prec, pres);
|
||||
s +=
|
||||
~"unsatisfied precondition constraint (for example, " + diff +
|
||||
~") for expression:\n";
|
||||
s += syntax::print::pprust::expr_to_str(e);
|
||||
s += ~"\nprecondition:\n";
|
||||
s += tritv_to_str(fcx, prec);
|
||||
s += ~"\nprestate:\n";
|
||||
s += tritv_to_str(fcx, pres);
|
||||
fcx.ccx.tcx.sess.span_fatal(e.span, s);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_states_stmt(s: @stmt, fcx: fn_ctxt, v: visit::vt<fn_ctxt>) {
|
||||
visit::visit_stmt(s, fcx, v);
|
||||
|
||||
let a = stmt_to_ann(fcx.ccx, *s);
|
||||
let prec: precond = ann_precond(a);
|
||||
let pres: prestate = ann_prestate(a);
|
||||
|
||||
debug!("check_states_stmt:");
|
||||
log(debug, print::pprust::stmt_to_str(*s));
|
||||
debug!("prec = ");
|
||||
log_tritv(fcx, prec);
|
||||
debug!("pres = ");
|
||||
log_tritv(fcx, pres);
|
||||
|
||||
if !implies(pres, prec) {
|
||||
let mut ss = ~"";
|
||||
let diff = first_difference_string(fcx, prec, pres);
|
||||
ss +=
|
||||
~"unsatisfied precondition constraint (for example, " + diff +
|
||||
~") for statement:\n";
|
||||
ss += syntax::print::pprust::stmt_to_str(*s);
|
||||
ss += ~"\nprecondition:\n";
|
||||
ss += tritv_to_str(fcx, prec);
|
||||
ss += ~"\nprestate: \n";
|
||||
ss += tritv_to_str(fcx, pres);
|
||||
fcx.ccx.tcx.sess.span_fatal(s.span, ss);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_states_against_conditions(fcx: fn_ctxt,
|
||||
fk: visit::fn_kind,
|
||||
f_decl: ast::fn_decl,
|
||||
f_body: ast::blk,
|
||||
sp: span,
|
||||
id: node_id) {
|
||||
/* Postorder traversal instead of pre is important
|
||||
because we want the smallest possible erroneous statement
|
||||
or expression. */
|
||||
let visitor = visit::mk_vt(
|
||||
@{visit_stmt: check_states_stmt,
|
||||
visit_expr: check_states_expr,
|
||||
visit_fn: |a,b,c,d,e,f,g| {
|
||||
do_nothing::<fn_ctxt>(a, b, c, d, e, f, g)
|
||||
}
|
||||
with *visit::default_visitor::<fn_ctxt>()});
|
||||
visit::visit_fn(fk, f_decl, f_body, sp, id, fcx, visitor);
|
||||
}
|
||||
|
||||
fn check_fn_states(fcx: fn_ctxt,
|
||||
fk: visit::fn_kind,
|
||||
f_decl: ast::fn_decl,
|
||||
f_body: ast::blk,
|
||||
sp: span,
|
||||
id: node_id) {
|
||||
/* Compute the pre- and post-states for this function */
|
||||
|
||||
// Fixpoint iteration
|
||||
while find_pre_post_state_fn(fcx, f_decl, f_body) { }
|
||||
|
||||
/* Now compare each expr's pre-state to its precondition
|
||||
and post-state to its postcondition */
|
||||
|
||||
check_states_against_conditions(fcx, fk, f_decl, f_body, sp, id);
|
||||
}
|
||||
|
||||
fn fn_states(fk: visit::fn_kind, f_decl: ast::fn_decl, f_body: ast::blk,
|
||||
sp: span, id: node_id,
|
||||
ccx: crate_ctxt, v: visit::vt<crate_ctxt>) {
|
||||
|
||||
visit::visit_fn(fk, f_decl, f_body, sp, id, ccx, v);
|
||||
|
||||
// We may not care about typestate for this function if it contains
|
||||
// no constrained calls
|
||||
if !ccx.fm.get(id).ignore {
|
||||
/* Look up the var-to-bit-num map for this function */
|
||||
|
||||
let f_info = ccx.fm.get(id);
|
||||
let name = visit::name_of_fn(fk);
|
||||
let fcx = {enclosing: f_info, id: id, name: name, ccx: ccx};
|
||||
check_fn_states(fcx, fk, f_decl, f_body, sp, id)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate(cx: ty::ctxt, crate: @crate) {
|
||||
let ccx: crate_ctxt = new_crate_ctxt(cx);
|
||||
/* Build the global map from function id to var-to-bit-num-map */
|
||||
|
||||
mk_f_to_fn_info(ccx, crate);
|
||||
/* Add a blank ts_ann for every statement (and expression) */
|
||||
|
||||
annotate_crate(ccx, *crate);
|
||||
/* Compute the pre and postcondition for every subexpression */
|
||||
|
||||
let vtor = visit::default_visitor::<crate_ctxt>();
|
||||
let vtor = @{visit_fn: fn_pre_post with *vtor};
|
||||
visit::visit_crate(*crate, ccx, visit::mk_vt(vtor));
|
||||
|
||||
/* Check the pre- and postcondition against the pre- and poststate
|
||||
for every expression */
|
||||
let vtor = visit::default_visitor::<crate_ctxt>();
|
||||
let vtor = @{visit_fn: fn_states with *vtor};
|
||||
visit::visit_crate(*crate, ccx, visit::mk_vt(vtor));
|
||||
}
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,165 +0,0 @@
|
||||
use option::*;
|
||||
use pat_util::*;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::*;
|
||||
use syntax::visit;
|
||||
use syntax::codemap::span;
|
||||
use syntax::ast_util::respan;
|
||||
use driver::session::session;
|
||||
use aux::*;
|
||||
use std::map::hashmap;
|
||||
use dvec::{dvec, extensions};
|
||||
|
||||
type ctxt = {cs: @mut ~[sp_constr], tcx: ty::ctxt};
|
||||
|
||||
fn collect_pred(e: @expr, cx: ctxt, v: visit::vt<ctxt>) {
|
||||
match e.node {
|
||||
expr_check(_, ch) { vec::push(*cx.cs, expr_to_constr(cx.tcx, ch)); }
|
||||
expr_if_check(ex, _, _) {
|
||||
vec::push(*cx.cs, expr_to_constr(cx.tcx, ex));
|
||||
}
|
||||
|
||||
// If it's a call, generate appropriate instances of the
|
||||
// call's constraints.
|
||||
expr_call(operator, operands, _) {
|
||||
for constraints_expr(cx.tcx, operator).each |c| {
|
||||
let ct: sp_constr =
|
||||
respan(c.span,
|
||||
aux::substitute_constr_args(cx.tcx, operands, c));
|
||||
vec::push(*cx.cs, ct);
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
// visit subexpressions
|
||||
visit::visit_expr(e, cx, v);
|
||||
}
|
||||
|
||||
fn find_locals(tcx: ty::ctxt,
|
||||
fk: visit::fn_kind,
|
||||
f_decl: fn_decl,
|
||||
f_body: blk,
|
||||
sp: span,
|
||||
id: node_id) -> ctxt {
|
||||
let cx: ctxt = {cs: @mut ~[], tcx: tcx};
|
||||
let visitor = visit::default_visitor::<ctxt>();
|
||||
let visitor =
|
||||
@{visit_expr: collect_pred,
|
||||
visit_fn: do_nothing
|
||||
with *visitor};
|
||||
visit::visit_fn(fk, f_decl, f_body, sp,
|
||||
id, cx, visit::mk_vt(visitor));
|
||||
return cx;
|
||||
}
|
||||
|
||||
fn add_constraint(tcx: ty::ctxt, c: sp_constr, next: uint, tbl: constr_map) ->
|
||||
uint {
|
||||
log(debug,
|
||||
constraint_to_str(tcx, c) + ~" |-> " + uint::str(next));
|
||||
|
||||
let {path: p, def_id: d_id, args: args} = c.node;
|
||||
match tbl.find(d_id) {
|
||||
Some(ct) {
|
||||
(*ct.descs).push(respan(c.span, {args: args, bit_num: next}));
|
||||
}
|
||||
None {
|
||||
let rslt = @dvec();
|
||||
(*rslt).push(respan(c.span, {args: args, bit_num: next}));
|
||||
tbl.insert(d_id, {path:p, descs:rslt});
|
||||
}
|
||||
}
|
||||
return next + 1u;
|
||||
}
|
||||
|
||||
fn contains_constrained_calls(tcx: ty::ctxt, body: blk) -> bool {
|
||||
type cx = @{
|
||||
tcx: ty::ctxt,
|
||||
mut has: bool
|
||||
};
|
||||
let cx = @{
|
||||
tcx: tcx,
|
||||
mut has: false
|
||||
};
|
||||
let vtor = visit::default_visitor::<cx>();
|
||||
let vtor = @{visit_expr: visit_expr with *vtor};
|
||||
visit::visit_block(body, cx, visit::mk_vt(vtor));
|
||||
return cx.has;
|
||||
|
||||
fn visit_expr(e: @expr, &&cx: cx, v: visit::vt<cx>) {
|
||||
import syntax::print::pprust;
|
||||
debug!("visiting %?", pprust::expr_to_str(e));
|
||||
|
||||
visit::visit_expr(e, cx, v);
|
||||
|
||||
if constraints_expr(cx.tcx, e).is_not_empty() {
|
||||
debug!("has constraints");
|
||||
cx.has = true;
|
||||
} else {
|
||||
debug!("has not constraints");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* builds a table mapping each local var defined in f
|
||||
to a bit number in the precondition/postcondition vectors */
|
||||
fn mk_fn_info(ccx: crate_ctxt,
|
||||
fk: visit::fn_kind,
|
||||
f_decl: fn_decl,
|
||||
f_body: blk,
|
||||
f_sp: span,
|
||||
id: node_id) {
|
||||
let name = visit::name_of_fn(fk);
|
||||
let res_map = new_def_hash::<constraint>();
|
||||
let mut next: uint = 0u;
|
||||
|
||||
let cx: ctxt = find_locals(ccx.tcx, fk, f_decl, f_body, f_sp, id);
|
||||
/* now we have to add bit nums for both the constraints
|
||||
and the variables... */
|
||||
|
||||
let ignore = !contains_constrained_calls(ccx.tcx, f_body);
|
||||
|
||||
if !ignore {
|
||||
let mut i = 0u, l = vec::len(*cx.cs);
|
||||
while i < l {
|
||||
next = add_constraint(cx.tcx, copy cx.cs[i], next, res_map);
|
||||
i += 1u;
|
||||
}
|
||||
/* if this function has any constraints, instantiate them to the
|
||||
argument names and add them */
|
||||
for f_decl.constraints.each |c| {
|
||||
let sc = ast_constr_to_sp_constr(cx.tcx, f_decl.inputs, c);
|
||||
next = add_constraint(cx.tcx, sc, next, res_map);
|
||||
}
|
||||
}
|
||||
|
||||
let v: @mut ~[node_id] = @mut ~[];
|
||||
let rslt =
|
||||
{constrs: res_map,
|
||||
num_constraints: next,
|
||||
cf: f_decl.cf,
|
||||
used_vars: v,
|
||||
ignore: ignore};
|
||||
ccx.fm.insert(id, rslt);
|
||||
debug!("%s has %u constraints", *name, num_constraints(rslt));
|
||||
}
|
||||
|
||||
|
||||
/* initializes the global fn_info_map (mapping each function ID, including
|
||||
nested locally defined functions, onto a mapping from local variable name
|
||||
to bit number) */
|
||||
fn mk_f_to_fn_info(ccx: crate_ctxt, c: @crate) {
|
||||
let visitor =
|
||||
visit::mk_simple_visitor(@{
|
||||
visit_fn: |a,b,c,d,e| mk_fn_info(ccx, a, b, c, d, e)
|
||||
with *visit::default_simple_visitor()});
|
||||
visit::visit_crate(*c, (), visitor);
|
||||
}
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,617 +0,0 @@
|
||||
use tstate::ann::*;
|
||||
use aux::*;
|
||||
use bitvectors::{bit_num, seq_preconds, seq_postconds,
|
||||
intersect_states,
|
||||
relax_precond_block, gen};
|
||||
use tritv::*;
|
||||
|
||||
use pat_util::*;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::*;
|
||||
use syntax::print::pprust::{expr_to_str, stmt_to_str};
|
||||
use syntax::visit;
|
||||
use util::common::{field_exprs, has_nonlocal_exits};
|
||||
use syntax::codemap::span;
|
||||
use driver::session::session;
|
||||
use std::map::hashmap;
|
||||
|
||||
fn find_pre_post_mod(_m: _mod) -> _mod {
|
||||
debug!("implement find_pre_post_mod!");
|
||||
fail;
|
||||
}
|
||||
|
||||
fn find_pre_post_foreign_mod(_m: foreign_mod) -> foreign_mod {
|
||||
debug!("implement find_pre_post_foreign_mod");
|
||||
fail;
|
||||
}
|
||||
|
||||
fn find_pre_post_method(ccx: crate_ctxt, m: @method) {
|
||||
assert (ccx.fm.contains_key(m.id));
|
||||
let fcx: fn_ctxt =
|
||||
{enclosing: ccx.fm.get(m.id),
|
||||
id: m.id,
|
||||
name: m.ident,
|
||||
ccx: ccx};
|
||||
find_pre_post_fn(fcx, m.body);
|
||||
}
|
||||
|
||||
fn find_pre_post_item(ccx: crate_ctxt, i: item) {
|
||||
match i.node {
|
||||
item_const(_, e) {
|
||||
// do nothing -- item_consts don't refer to local vars
|
||||
}
|
||||
item_fn(_, _, body) {
|
||||
assert (ccx.fm.contains_key(i.id));
|
||||
let fcx =
|
||||
{enclosing: ccx.fm.get(i.id), id: i.id, name: i.ident, ccx: ccx};
|
||||
find_pre_post_fn(fcx, body);
|
||||
}
|
||||
item_mod(m) { find_pre_post_mod(m); }
|
||||
item_foreign_mod(nm) { find_pre_post_foreign_mod(nm); }
|
||||
item_ty(*) | item_enum(*) | item_trait(*) { return; }
|
||||
item_class(*) {
|
||||
fail ~"find_pre_post_item: shouldn't be called on item_class";
|
||||
}
|
||||
item_impl(_, _, _, ms) {
|
||||
for ms.each |m| { find_pre_post_method(ccx, m); }
|
||||
}
|
||||
item_mac(*) { fail ~"item macros unimplemented" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Finds the pre and postcondition for each expr in <args>;
|
||||
sets the precondition in a to be the result of combining
|
||||
the preconditions for <args>, and the postcondition in a to
|
||||
be the union of all postconditions for <args> */
|
||||
fn find_pre_post_exprs(fcx: fn_ctxt, args: ~[@expr], id: node_id) {
|
||||
if vec::len::<@expr>(args) > 0u {
|
||||
debug!("find_pre_post_exprs: oper = %s", expr_to_str(args[0]));
|
||||
}
|
||||
fn do_one(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); }
|
||||
for args.each |e| { do_one(fcx, e); }
|
||||
|
||||
fn get_pp(ccx: crate_ctxt, &&e: @expr) -> pre_and_post {
|
||||
return expr_pp(ccx, e);
|
||||
}
|
||||
let pps = vec::map(args, |a| get_pp(fcx.ccx, a) );
|
||||
|
||||
set_pre_and_post(fcx.ccx, id, seq_preconds(fcx, pps),
|
||||
seq_postconds(fcx, vec::map(pps, get_post)));
|
||||
}
|
||||
|
||||
fn find_pre_post_loop(fcx: fn_ctxt, index: @expr, body: blk, id: node_id) {
|
||||
find_pre_post_expr(fcx, index);
|
||||
find_pre_post_block(fcx, body);
|
||||
|
||||
let loop_precond =
|
||||
seq_preconds(fcx, ~[expr_pp(fcx.ccx, index),
|
||||
block_pp(fcx.ccx, body)]);
|
||||
let loop_postcond =
|
||||
intersect_states(expr_postcond(fcx.ccx, index),
|
||||
block_postcond(fcx.ccx, body));
|
||||
copy_pre_post_(fcx.ccx, id, loop_precond, loop_postcond);
|
||||
}
|
||||
|
||||
// Generates a pre/post assuming that a is the
|
||||
// annotation for an if-expression with consequent conseq
|
||||
// and alternative maybe_alt
|
||||
fn join_then_else(fcx: fn_ctxt, antec: @expr, conseq: blk,
|
||||
maybe_alt: Option<@expr>, id: node_id, chck: if_ty) {
|
||||
find_pre_post_expr(fcx, antec);
|
||||
find_pre_post_block(fcx, conseq);
|
||||
match maybe_alt {
|
||||
none {
|
||||
match chck {
|
||||
if_check {
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
|
||||
gen(fcx, antec.id, c.node);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
||||
let precond_res =
|
||||
seq_preconds(fcx,
|
||||
~[expr_pp(fcx.ccx, antec),
|
||||
block_pp(fcx.ccx, conseq)]);
|
||||
set_pre_and_post(fcx.ccx, id, precond_res,
|
||||
expr_poststate(fcx.ccx, antec));
|
||||
}
|
||||
Some(altern) {
|
||||
/*
|
||||
if check = if_check, then
|
||||
be sure that the predicate implied by antec
|
||||
is *not* true in the alternative
|
||||
*/
|
||||
find_pre_post_expr(fcx, altern);
|
||||
let precond_false_case =
|
||||
seq_preconds(fcx,
|
||||
~[expr_pp(fcx.ccx, antec),
|
||||
expr_pp(fcx.ccx, altern)]);
|
||||
let postcond_false_case =
|
||||
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. */
|
||||
match chck {
|
||||
if_check {
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
|
||||
gen(fcx, antec.id, c.node);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
let precond_true_case =
|
||||
seq_preconds(fcx,
|
||||
~[expr_pp(fcx.ccx, antec),
|
||||
block_pp(fcx.ccx, conseq)]);
|
||||
let postcond_true_case =
|
||||
seq_postconds(fcx,
|
||||
~[expr_postcond(fcx.ccx, antec),
|
||||
block_postcond(fcx.ccx, conseq)]);
|
||||
|
||||
let precond_res =
|
||||
seq_postconds(fcx, ~[precond_true_case, precond_false_case]);
|
||||
let postcond_res =
|
||||
intersect_states(postcond_true_case, postcond_false_case);
|
||||
set_pre_and_post(fcx.ccx, id, precond_res, postcond_res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id,
|
||||
new_var: node_id) {
|
||||
match node_id_to_def(fcx.ccx, new_var) {
|
||||
Some(d) {
|
||||
match d {
|
||||
def_local(nid, _) {
|
||||
find_pre_post_expr(fcx, rhs);
|
||||
let p = expr_pp(fcx.ccx, rhs);
|
||||
set_pre_and_post(fcx.ccx, larger_id, p.precondition,
|
||||
p.postcondition);
|
||||
}
|
||||
_ { find_pre_post_exprs(fcx, ~[lhs, rhs], larger_id); }
|
||||
}
|
||||
}
|
||||
_ { find_pre_post_exprs(fcx, ~[lhs, rhs], larger_id); }
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr,
|
||||
ty: oper_type) {
|
||||
find_pre_post_expr(fcx, rhs);
|
||||
match lhs.node {
|
||||
expr_path(p) {
|
||||
let post = expr_postcond(fcx.ccx, parent);
|
||||
let tmp = post.clone();
|
||||
|
||||
match ty {
|
||||
oper_move {
|
||||
if is_path(rhs) { forget_in_postcond(fcx, parent.id, rhs.id); }
|
||||
}
|
||||
oper_swap {
|
||||
forget_in_postcond(fcx, parent.id, lhs.id);
|
||||
forget_in_postcond(fcx, parent.id, rhs.id);
|
||||
}
|
||||
oper_assign {
|
||||
forget_in_postcond(fcx, parent.id, lhs.id);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
||||
gen_if_local(fcx, lhs, rhs, parent.id, lhs.id);
|
||||
match rhs.node {
|
||||
expr_path(p1) {
|
||||
let d = local_node_id_to_local_def_id(fcx, lhs.id);
|
||||
let d1 = local_node_id_to_local_def_id(fcx, rhs.id);
|
||||
match d {
|
||||
Some(id) {
|
||||
match d1 {
|
||||
Some(id1) {
|
||||
let instlhs =
|
||||
{ident: path_to_ident(p), node: id};
|
||||
let instrhs =
|
||||
{ident: path_to_ident(p1), node: id1};
|
||||
copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs,
|
||||
ty);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
_ {/* do nothing */ }
|
||||
}
|
||||
}
|
||||
_ { find_pre_post_expr(fcx, lhs); }
|
||||
}
|
||||
}
|
||||
|
||||
fn forget_args_moved_in(fcx: fn_ctxt, parent: @expr, modes: ~[mode],
|
||||
operands: ~[@expr]) {
|
||||
do vec::iteri(modes) |i,mode| {
|
||||
match ty::resolved_mode(fcx.ccx.tcx, mode) {
|
||||
by_move { forget_in_postcond(fcx, parent.id, operands[i].id); }
|
||||
by_ref | by_val | by_mutbl_ref | by_copy { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pre_post_expr_fn_upvars(fcx: fn_ctxt, e: @expr) {
|
||||
let rslt = expr_pp(fcx.ccx, e);
|
||||
clear_pp(rslt);
|
||||
}
|
||||
|
||||
/* Fills in annotations as a side effect. Does not rebuild the expr */
|
||||
fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
let enclosing = fcx.enclosing;
|
||||
let num_local_vars = num_constraints(enclosing);
|
||||
fn do_rand_(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); }
|
||||
|
||||
|
||||
match e.node {
|
||||
expr_call(operator, operands, _) {
|
||||
/* copy */
|
||||
|
||||
let mut args = operands;
|
||||
vec::push(args, operator);
|
||||
|
||||
find_pre_post_exprs(fcx, args, e.id);
|
||||
/* see if the call has any constraints on its type */
|
||||
for constraints_expr(fcx.ccx.tcx, operator).each |c| {
|
||||
let i =
|
||||
bit_num(fcx, substitute_constr_args(fcx.ccx.tcx, args, c));
|
||||
require(i, expr_pp(fcx.ccx, e));
|
||||
}
|
||||
|
||||
forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id),
|
||||
operands);
|
||||
|
||||
/* if this is a failing call, its postcondition sets everything */
|
||||
match controlflow_expr(fcx.ccx, operator) {
|
||||
noreturn { set_postcond_false(fcx.ccx, e.id); }
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
expr_vstore(ee, _) {
|
||||
find_pre_post_expr(fcx, ee);
|
||||
let p = expr_pp(fcx.ccx, ee);
|
||||
set_pre_and_post(fcx.ccx, e.id, p.precondition, p.postcondition);
|
||||
}
|
||||
expr_vec(args, _) {
|
||||
find_pre_post_exprs(fcx, args, e.id);
|
||||
}
|
||||
expr_path(p) {
|
||||
let rslt = expr_pp(fcx.ccx, e);
|
||||
clear_pp(rslt);
|
||||
}
|
||||
expr_new(p, _, v) {
|
||||
find_pre_post_exprs(fcx, ~[p, v], e.id);
|
||||
}
|
||||
expr_log(_, lvl, arg) {
|
||||
find_pre_post_exprs(fcx, ~[lvl, arg], e.id);
|
||||
}
|
||||
expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) {
|
||||
find_pre_post_expr_fn_upvars(fcx, e);
|
||||
|
||||
for (*cap_clause).each |cap_item| {
|
||||
let d = local_node_id_to_local_def_id(fcx, cap_item.id);
|
||||
option::iter(d, |id| use_var(fcx, id) );
|
||||
}
|
||||
|
||||
for (*cap_clause).each |cap_item| {
|
||||
if cap_item.is_move {
|
||||
log(debug, (~"forget_in_postcond: ", cap_item));
|
||||
forget_in_postcond(fcx, e.id, cap_item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_block(b) {
|
||||
find_pre_post_block(fcx, b);
|
||||
let p = block_pp(fcx.ccx, b);
|
||||
set_pre_and_post(fcx.ccx, e.id, p.precondition, p.postcondition);
|
||||
}
|
||||
expr_rec(fields, maybe_base) {
|
||||
let mut es = field_exprs(fields);
|
||||
match maybe_base { none {/* no-op */ } Some(b) { vec::push(es, b); } }
|
||||
find_pre_post_exprs(fcx, es, e.id);
|
||||
}
|
||||
expr_tup(elts) { find_pre_post_exprs(fcx, elts, e.id); }
|
||||
expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); }
|
||||
expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); }
|
||||
expr_assign(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_assign); }
|
||||
expr_assign_op(_, lhs, rhs) {
|
||||
/* Different from expr_assign in that the lhs *must*
|
||||
already be initialized */
|
||||
|
||||
find_pre_post_exprs(fcx, ~[lhs, rhs], e.id);
|
||||
forget_in_postcond(fcx, e.id, lhs.id);
|
||||
}
|
||||
expr_lit(_) { clear_pp(expr_pp(fcx.ccx, e)); }
|
||||
expr_ret(maybe_val) {
|
||||
match maybe_val {
|
||||
none {
|
||||
clear_precond(fcx.ccx, e.id);
|
||||
set_postcond_false(fcx.ccx, e.id);
|
||||
}
|
||||
Some(ret_val) {
|
||||
find_pre_post_expr(fcx, ret_val);
|
||||
set_precondition(node_id_to_ts_ann(fcx.ccx, e.id),
|
||||
expr_precond(fcx.ccx, ret_val));
|
||||
set_postcond_false(fcx.ccx, e.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_if(antec, conseq, maybe_alt) {
|
||||
join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if);
|
||||
}
|
||||
expr_binary(bop, l, r) {
|
||||
if lazy_binop(bop) {
|
||||
find_pre_post_expr(fcx, l);
|
||||
find_pre_post_expr(fcx, r);
|
||||
let overall_pre =
|
||||
seq_preconds(fcx,
|
||||
~[expr_pp(fcx.ccx, l), expr_pp(fcx.ccx, r)]);
|
||||
set_precondition(node_id_to_ts_ann(fcx.ccx, e.id), overall_pre);
|
||||
set_postcondition(node_id_to_ts_ann(fcx.ccx, e.id),
|
||||
expr_postcond(fcx.ccx, l));
|
||||
} else { find_pre_post_exprs(fcx, ~[l, r], e.id); }
|
||||
}
|
||||
expr_addr_of(_, x) | expr_cast(x, _) | expr_unary(_, x) |
|
||||
expr_loop_body(x) | expr_do_body(x) | expr_assert(x) | expr_copy(x) {
|
||||
find_pre_post_expr(fcx, x);
|
||||
copy_pre_post(fcx.ccx, e.id, x);
|
||||
}
|
||||
expr_while(test, body) {
|
||||
find_pre_post_expr(fcx, test);
|
||||
find_pre_post_block(fcx, body);
|
||||
set_pre_and_post(fcx.ccx, e.id,
|
||||
seq_preconds(fcx,
|
||||
~[expr_pp(fcx.ccx, test),
|
||||
block_pp(fcx.ccx, body)]),
|
||||
intersect_states(expr_postcond(fcx.ccx, test),
|
||||
block_postcond(fcx.ccx, body)));
|
||||
}
|
||||
expr_loop(body) {
|
||||
find_pre_post_block(fcx, body);
|
||||
/* Infinite loop: if control passes it, everything is true. */
|
||||
let mut loop_postcond = false_postcond(num_local_vars);
|
||||
/* Conservative approximation: if the body has any nonlocal exits,
|
||||
the poststate is blank since we don't know what parts of it
|
||||
execute. */
|
||||
if has_nonlocal_exits(body) {
|
||||
loop_postcond = empty_poststate(num_local_vars);
|
||||
}
|
||||
set_pre_and_post(fcx.ccx, e.id, block_precond(fcx.ccx, body),
|
||||
loop_postcond);
|
||||
}
|
||||
expr_index(val, sub) { find_pre_post_exprs(fcx, ~[val, sub], e.id); }
|
||||
expr_match(ex, alts, _) {
|
||||
find_pre_post_expr(fcx, ex);
|
||||
fn do_an_alt(fcx: fn_ctxt, an_alt: arm) -> pre_and_post {
|
||||
match an_alt.guard {
|
||||
Some(e) { find_pre_post_expr(fcx, e); }
|
||||
_ {}
|
||||
}
|
||||
find_pre_post_block(fcx, an_alt.body);
|
||||
return block_pp(fcx.ccx, an_alt.body);
|
||||
}
|
||||
let mut alt_pps = ~[];
|
||||
for alts.each |a| { vec::push(alt_pps, do_an_alt(fcx, a)); }
|
||||
fn combine_pp(antec: pre_and_post, fcx: fn_ctxt, &&pp: pre_and_post,
|
||||
&&next: pre_and_post) -> pre_and_post {
|
||||
union(pp.precondition, seq_preconds(fcx, ~[antec, next]));
|
||||
intersect(pp.postcondition, next.postcondition);
|
||||
return pp;
|
||||
}
|
||||
let antec_pp = pp_clone(expr_pp(fcx.ccx, ex));
|
||||
let e_pp =
|
||||
{precondition: empty_prestate(num_local_vars),
|
||||
postcondition: false_postcond(num_local_vars)};
|
||||
let g = |a,b| combine_pp(antec_pp, fcx, a, b);
|
||||
let alts_overall_pp =
|
||||
vec::foldl(e_pp, alt_pps, g);
|
||||
set_pre_and_post(fcx.ccx, e.id, alts_overall_pp.precondition,
|
||||
alts_overall_pp.postcondition);
|
||||
}
|
||||
expr_field(operator, _, _) {
|
||||
find_pre_post_expr(fcx, operator);
|
||||
copy_pre_post(fcx.ccx, e.id, operator);
|
||||
}
|
||||
expr_fail(maybe_val) {
|
||||
let mut prestate;
|
||||
match maybe_val {
|
||||
none { prestate = empty_prestate(num_local_vars); }
|
||||
Some(fail_val) {
|
||||
find_pre_post_expr(fcx, fail_val);
|
||||
prestate = expr_precond(fcx.ccx, fail_val);
|
||||
}
|
||||
}
|
||||
set_pre_and_post(fcx.ccx, e.id,
|
||||
/* if execution continues after fail,
|
||||
then everything is true! */
|
||||
prestate, false_postcond(num_local_vars));
|
||||
}
|
||||
expr_check(_, p) {
|
||||
find_pre_post_expr(fcx, p);
|
||||
copy_pre_post(fcx.ccx, e.id, p);
|
||||
/* predicate p holds after this expression executes */
|
||||
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p);
|
||||
gen(fcx, e.id, c.node);
|
||||
}
|
||||
expr_if_check(p, conseq, maybe_alt) {
|
||||
join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check);
|
||||
}
|
||||
expr_break { clear_pp(expr_pp(fcx.ccx, e)); }
|
||||
expr_again { clear_pp(expr_pp(fcx.ccx, e)); }
|
||||
expr_mac(_) { fcx.ccx.tcx.sess.bug(~"unexpanded macro"); }
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) {
|
||||
debug!("stmt = %s", stmt_to_str(s));
|
||||
match s.node {
|
||||
stmt_decl(adecl, id) {
|
||||
match adecl.node {
|
||||
decl_local(alocals) {
|
||||
let prev_pp = empty_pre_post(num_constraints(fcx.enclosing));
|
||||
for alocals.each |alocal| {
|
||||
match alocal.node.init {
|
||||
Some(an_init) {
|
||||
/* LHS always becomes initialized,
|
||||
whether or not this is a move */
|
||||
find_pre_post_expr(fcx, an_init.expr);
|
||||
do pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
|
||||
|p_id, _s, _n| {
|
||||
copy_pre_post(fcx.ccx, p_id, an_init.expr);
|
||||
};
|
||||
/* Inherit ann from initializer, and add var being
|
||||
initialized to the postcondition */
|
||||
copy_pre_post(fcx.ccx, id, an_init.expr);
|
||||
|
||||
let mut p = none;
|
||||
match an_init.expr.node {
|
||||
expr_path(_p) { p = Some(_p); }
|
||||
_ { }
|
||||
}
|
||||
|
||||
do pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
|
||||
|p_id, _s, n| {
|
||||
let ident = path_to_ident(n);
|
||||
match p {
|
||||
Some(p) {
|
||||
copy_in_postcond(fcx, id,
|
||||
{ident: ident, node: p_id},
|
||||
{ident:
|
||||
path_to_ident(p),
|
||||
node: an_init.expr.id},
|
||||
op_to_oper_ty(an_init.op));
|
||||
}
|
||||
none { }
|
||||
}
|
||||
};
|
||||
|
||||
/* Clear out anything that the previous initializer
|
||||
guaranteed */
|
||||
let e_pp = expr_pp(fcx.ccx, an_init.expr);
|
||||
prev_pp.precondition.become(
|
||||
seq_preconds(fcx, ~[prev_pp, e_pp]));
|
||||
|
||||
/* Include the LHSs too, since those aren't in the
|
||||
postconds of the RHSs themselves */
|
||||
copy_pre_post_(fcx.ccx, id, prev_pp.precondition,
|
||||
prev_pp.postcondition);
|
||||
}
|
||||
none {
|
||||
do pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat)
|
||||
|p_id, _s, _n| {
|
||||
clear_pp(node_id_to_ts_ann(fcx.ccx, p_id).conditions);
|
||||
};
|
||||
clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
decl_item(anitem) {
|
||||
clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions);
|
||||
find_pre_post_item(fcx.ccx, *anitem);
|
||||
}
|
||||
}
|
||||
}
|
||||
stmt_expr(e, id) | stmt_semi(e, id) {
|
||||
find_pre_post_expr(fcx, e);
|
||||
copy_pre_post(fcx.ccx, id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pre_post_block(fcx: fn_ctxt, b: blk) {
|
||||
/* Want to say that if there is a break or cont in this
|
||||
block, then that invalidates the poststate upheld by
|
||||
any of the stmts after it.
|
||||
Given that the typechecker has run, we know any break will be in
|
||||
a block that forms a loop body. So that's ok. There'll never be an
|
||||
expr_break outside a loop body, therefore, no expr_break outside a block.
|
||||
*/
|
||||
|
||||
/* Conservative approximation for now: This says that if a block contains
|
||||
*any* breaks or conts, then its postcondition doesn't promise anything.
|
||||
This will mean that:
|
||||
x = 0;
|
||||
break;
|
||||
|
||||
won't have a postcondition that says x is initialized, but that's ok.
|
||||
*/
|
||||
|
||||
let nv = num_constraints(fcx.enclosing);
|
||||
fn do_one_(fcx: fn_ctxt, s: @stmt) {
|
||||
find_pre_post_stmt(fcx, *s);
|
||||
}
|
||||
for b.node.stmts.each |s| { do_one_(fcx, s); }
|
||||
fn do_inner_(fcx: fn_ctxt, &&e: @expr) { find_pre_post_expr(fcx, e); }
|
||||
let do_inner = |a| do_inner_(fcx, a);
|
||||
option::map::<@expr, ()>(b.node.expr, do_inner);
|
||||
|
||||
let mut pps: ~[pre_and_post] = ~[];
|
||||
for b.node.stmts.each |s| { vec::push(pps, stmt_pp(fcx.ccx, *s)); }
|
||||
match b.node.expr {
|
||||
none {/* no-op */ }
|
||||
Some(e) { vec::push(pps, expr_pp(fcx.ccx, e)); }
|
||||
}
|
||||
|
||||
let block_precond = seq_preconds(fcx, pps);
|
||||
|
||||
let mut postconds = ~[];
|
||||
for pps.each |pp| { vec::push(postconds, get_post(pp)); }
|
||||
|
||||
/* A block may be empty, so this next line ensures that the postconds
|
||||
vector is non-empty. */
|
||||
vec::push(postconds, block_precond);
|
||||
|
||||
let mut block_postcond = empty_poststate(nv);
|
||||
/* conservative approximation */
|
||||
|
||||
if !has_nonlocal_exits(b) {
|
||||
block_postcond = seq_postconds(fcx, postconds);
|
||||
}
|
||||
set_pre_and_post(fcx.ccx, b.node.id, block_precond, block_postcond);
|
||||
}
|
||||
|
||||
fn find_pre_post_fn(fcx: fn_ctxt, body: blk) {
|
||||
find_pre_post_block(fcx, body);
|
||||
|
||||
// Treat the tail expression as a return statement
|
||||
match body.node.expr {
|
||||
Some(tailexpr) { set_postcond_false(fcx.ccx, tailexpr.id); }
|
||||
none {/* fallthrough */ }
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_pre_post(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
||||
id: node_id,
|
||||
ccx: crate_ctxt, v: visit::vt<crate_ctxt>) {
|
||||
|
||||
visit::visit_fn(fk, decl, body, sp, id, ccx, v);
|
||||
assert (ccx.fm.contains_key(id));
|
||||
if !ccx.fm.get(id).ignore {
|
||||
let fcx =
|
||||
{enclosing: ccx.fm.get(id),
|
||||
id: id,
|
||||
name: visit::name_of_fn(fk),
|
||||
ccx: ccx};
|
||||
find_pre_post_fn(fcx, body);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
@ -1,623 +0,0 @@
|
||||
use ann::*;
|
||||
use aux::*;
|
||||
use tritv::*;
|
||||
|
||||
use syntax::print::pprust::block_to_str;
|
||||
use bitvectors::*;
|
||||
use pat_util::*;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::*;
|
||||
use syntax::print::pprust::{expr_to_str, stmt_to_str};
|
||||
use syntax::codemap::span;
|
||||
use middle::ty::{expr_ty, type_is_bot};
|
||||
use util::common::{field_exprs, has_nonlocal_exits, may_break};
|
||||
use driver::session::session;
|
||||
use std::map::hashmap;
|
||||
|
||||
fn forbid_upvar(fcx: fn_ctxt, rhs_id: node_id, sp: span, t: oper_type) {
|
||||
match t {
|
||||
oper_move {
|
||||
match local_node_id_to_def(fcx, rhs_id) {
|
||||
Some(def_upvar(_, _, _)) {
|
||||
fcx.ccx.tcx.sess.span_err(sp,
|
||||
~"tried to deinitialize a variable \
|
||||
declared in a different scope");
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
_ {/* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path,
|
||||
rhs_id: node_id, destlhs: dest, init_op: init_op) {
|
||||
forbid_upvar(fcx, rhs_id, rhs_path.span, op_to_oper_ty(init_op));
|
||||
|
||||
let rhs_d_id = local_node_id_to_def_id(fcx, rhs_id);
|
||||
match rhs_d_id {
|
||||
Some(rhsid) {
|
||||
// RHS is a local var
|
||||
let instrhs =
|
||||
{ident: path_to_ident(rhs_path), node: rhsid.node};
|
||||
match destlhs {
|
||||
local_dest(instlhs) {
|
||||
copy_in_poststate(fcx, post, instlhs, instrhs,
|
||||
op_to_oper_ty(init_op));
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
_ {
|
||||
// not a local -- do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn seq_states(fcx: fn_ctxt, pres: prestate, bindings: ~[binding]) ->
|
||||
{changed: bool, post: poststate} {
|
||||
let mut changed = false;
|
||||
let mut post = pres.clone();
|
||||
for bindings.each |b| {
|
||||
match b.rhs {
|
||||
Some(an_init) {
|
||||
// an expression, with or without a destination
|
||||
changed |=
|
||||
find_pre_post_state_expr(fcx, post, an_init.expr) || changed;
|
||||
post = expr_poststate(fcx.ccx, an_init.expr).clone();
|
||||
for b.lhs.each |d| {
|
||||
match an_init.expr.node {
|
||||
expr_path(p) {
|
||||
handle_move_or_copy(fcx, post, p, an_init.expr.id, d,
|
||||
an_init.op);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
|
||||
// Forget the RHS if we just moved it.
|
||||
if an_init.op == init_move {
|
||||
forget_in_poststate(fcx, post, an_init.expr.id);
|
||||
}
|
||||
}
|
||||
none {
|
||||
}
|
||||
}
|
||||
}
|
||||
return {changed: changed, post: post};
|
||||
}
|
||||
|
||||
fn find_pre_post_state_sub(fcx: fn_ctxt, pres: prestate, e: @expr,
|
||||
parent: node_id, c: Option<tsconstr>) -> bool {
|
||||
let mut changed = find_pre_post_state_expr(fcx, pres, e);
|
||||
|
||||
changed = set_prestate_ann(fcx.ccx, parent, pres) || changed;
|
||||
|
||||
let post = expr_poststate(fcx.ccx, e).clone();
|
||||
match c {
|
||||
none { }
|
||||
Some(c1) { set_in_poststate_(bit_num(fcx, c1), post); }
|
||||
}
|
||||
|
||||
changed = set_poststate_ann(fcx.ccx, parent, post) || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_two(fcx: fn_ctxt, pres: prestate, lhs: @expr,
|
||||
rhs: @expr, parent: node_id, ty: oper_type) ->
|
||||
bool {
|
||||
let mut changed = set_prestate_ann(fcx.ccx, parent, pres);
|
||||
changed = find_pre_post_state_expr(fcx, pres, lhs) || changed;
|
||||
changed =
|
||||
find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, lhs), rhs) ||
|
||||
changed;
|
||||
forbid_upvar(fcx, rhs.id, rhs.span, ty);
|
||||
|
||||
let post = expr_poststate(fcx.ccx, rhs).clone();
|
||||
|
||||
match lhs.node {
|
||||
expr_path(p) {
|
||||
// for termination, need to make sure intermediate changes don't set
|
||||
// changed flag
|
||||
// tmp remembers "old" constraints we'd otherwise forget,
|
||||
// for substitution purposes
|
||||
let tmp = post.clone();
|
||||
|
||||
match ty {
|
||||
oper_move {
|
||||
if is_path(rhs) { forget_in_poststate(fcx, post, rhs.id); }
|
||||
forget_in_poststate(fcx, post, lhs.id);
|
||||
}
|
||||
oper_swap {
|
||||
forget_in_poststate(fcx, post, lhs.id);
|
||||
forget_in_poststate(fcx, post, rhs.id);
|
||||
}
|
||||
_ { forget_in_poststate(fcx, post, lhs.id); }
|
||||
}
|
||||
|
||||
match rhs.node {
|
||||
expr_path(p1) {
|
||||
let d = local_node_id_to_local_def_id(fcx, lhs.id);
|
||||
let d1 = local_node_id_to_local_def_id(fcx, rhs.id);
|
||||
match d {
|
||||
Some(id) {
|
||||
match d1 {
|
||||
Some(id1) {
|
||||
let instlhs =
|
||||
{ident: path_to_ident(p), node: id};
|
||||
let instrhs =
|
||||
{ident: path_to_ident(p1), node: id1};
|
||||
copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs,
|
||||
ty);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
_ {/* do nothing */ }
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
changed = set_poststate_ann(fcx.ccx, parent, post) || changed;
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_call(fcx: fn_ctxt, pres: prestate, a: @expr,
|
||||
id: node_id, ops: ~[init_op], bs: ~[@expr],
|
||||
cf: ret_style) -> bool {
|
||||
let mut changed = find_pre_post_state_expr(fcx, pres, a);
|
||||
// FIXME (#2178): This could be a typestate constraint (except we're
|
||||
// not using them inside the compiler, I guess... see discussion in
|
||||
// bug)
|
||||
if vec::len(bs) != vec::len(ops) {
|
||||
fcx.ccx.tcx.sess.span_bug(a.span,
|
||||
fmt!("mismatched arg lengths: \
|
||||
%u exprs vs. %u ops",
|
||||
vec::len(bs), vec::len(ops)));
|
||||
}
|
||||
return find_pre_post_state_exprs(fcx, pres, id, ops,
|
||||
bs, cf) || changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_exprs(fcx: fn_ctxt, pres: prestate, id: node_id,
|
||||
ops: ~[init_op], es: ~[@expr],
|
||||
cf: ret_style) -> bool {
|
||||
let rs = seq_states(fcx, pres, arg_bindings(ops, es));
|
||||
let mut changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres);
|
||||
/* if this is a failing call, it sets everything as initialized */
|
||||
match cf {
|
||||
noreturn {
|
||||
let post = false_postcond(num_constraints(fcx.enclosing));
|
||||
changed |= set_poststate_ann(fcx.ccx, id, post);
|
||||
}
|
||||
_ { changed |= set_poststate_ann(fcx.ccx, id, rs.post); }
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn join_then_else(fcx: fn_ctxt, antec: @expr, conseq: blk,
|
||||
maybe_alt: Option<@expr>, id: node_id, chk: if_ty,
|
||||
pres: prestate) -> bool {
|
||||
let mut changed =
|
||||
set_prestate_ann(fcx.ccx, id, pres) |
|
||||
find_pre_post_state_expr(fcx, pres, antec);
|
||||
|
||||
match maybe_alt {
|
||||
none {
|
||||
match chk {
|
||||
if_check {
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
|
||||
let conseq_prestate = expr_poststate(fcx.ccx, antec).clone();
|
||||
conseq_prestate.set(bit_num(fcx, c.node), ttrue);
|
||||
changed |=
|
||||
find_pre_post_state_block(fcx, conseq_prestate, conseq) |
|
||||
set_poststate_ann(fcx.ccx, id,
|
||||
expr_poststate(fcx.ccx, antec));
|
||||
}
|
||||
_ {
|
||||
changed |=
|
||||
find_pre_post_state_block(fcx, expr_poststate(fcx.ccx, antec),
|
||||
conseq) |
|
||||
set_poststate_ann(fcx.ccx, id,
|
||||
expr_poststate(fcx.ccx, antec));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(altern) {
|
||||
changed |=
|
||||
find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, antec),
|
||||
altern);
|
||||
|
||||
let mut conseq_prestate = expr_poststate(fcx.ccx, antec);
|
||||
match chk {
|
||||
if_check {
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec);
|
||||
conseq_prestate = conseq_prestate.clone();
|
||||
conseq_prestate.set(bit_num(fcx, c.node), ttrue);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
||||
|
||||
changed |= find_pre_post_state_block(fcx, conseq_prestate, conseq);
|
||||
|
||||
let poststate_res =
|
||||
intersect_states(block_poststate(fcx.ccx, conseq),
|
||||
expr_poststate(fcx.ccx, altern));
|
||||
/*
|
||||
fcx.ccx.tcx.sess.span_note(antec.span,
|
||||
"poststate_res = " + aux::tritv_to_str(fcx, poststate_res));
|
||||
fcx.ccx.tcx.sess.span_note(antec.span,
|
||||
"altern poststate = " +
|
||||
aux::tritv_to_str(fcx, expr_poststate(fcx.ccx, altern)));
|
||||
fcx.ccx.tcx.sess.span_note(antec.span,
|
||||
"conseq poststate = " + aux::tritv_to_str(fcx,
|
||||
block_poststate(fcx.ccx, conseq)));
|
||||
*/
|
||||
|
||||
changed |= set_poststate_ann(fcx.ccx, id, poststate_res);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_cap_clause(fcx: fn_ctxt, e_id: node_id,
|
||||
pres: prestate, cap_clause: capture_clause)
|
||||
-> bool
|
||||
{
|
||||
let ccx = fcx.ccx;
|
||||
let pres_changed = set_prestate_ann(ccx, e_id, pres);
|
||||
let post = pres.clone();
|
||||
for (*cap_clause).each |cap_item| {
|
||||
if cap_item.is_move {
|
||||
forget_in_poststate(fcx, post, cap_item.id);
|
||||
}
|
||||
}
|
||||
return set_poststate_ann(ccx, e_id, post) || pres_changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
|
||||
let num_constrs = num_constraints(fcx.enclosing);
|
||||
|
||||
match e.node {
|
||||
expr_new(p, _, v) {
|
||||
return find_pre_post_state_two(fcx, pres, p, v, e.id, oper_pure);
|
||||
}
|
||||
expr_vstore(ee, _) {
|
||||
let mut changed = find_pre_post_state_expr(fcx, pres, ee);
|
||||
set_prestate_ann(fcx.ccx, e.id, expr_prestate(fcx.ccx, ee));
|
||||
set_poststate_ann(fcx.ccx, e.id, expr_poststate(fcx.ccx, ee));
|
||||
return changed;
|
||||
}
|
||||
expr_vec(elts, _) {
|
||||
return find_pre_post_state_exprs(fcx, pres, e.id,
|
||||
vec::from_elem(vec::len(elts),
|
||||
init_assign), elts,
|
||||
return_val);
|
||||
}
|
||||
expr_call(operator, operands, _) {
|
||||
debug!("hey it's a call: %s", expr_to_str(e));
|
||||
return find_pre_post_state_call(fcx, pres, operator, e.id,
|
||||
callee_arg_init_ops(fcx, operator.id),
|
||||
operands,
|
||||
controlflow_expr(fcx.ccx, operator));
|
||||
}
|
||||
expr_path(_) { return pure_exp(fcx.ccx, e.id, pres); }
|
||||
expr_log(_, lvl, ex) {
|
||||
return find_pre_post_state_two(fcx, pres, lvl, ex, e.id, oper_pure);
|
||||
}
|
||||
expr_mac(_) { fcx.ccx.tcx.sess.bug(~"unexpanded macro"); }
|
||||
expr_lit(l) { return pure_exp(fcx.ccx, e.id, pres); }
|
||||
expr_fn(_, _, _, cap_clause) {
|
||||
return find_pre_post_state_cap_clause(fcx, e.id, pres, cap_clause);
|
||||
}
|
||||
expr_fn_block(_, _, cap_clause) {
|
||||
return find_pre_post_state_cap_clause(fcx, e.id, pres, cap_clause);
|
||||
}
|
||||
expr_block(b) {
|
||||
return find_pre_post_state_block(fcx, pres, b) |
|
||||
set_prestate_ann(fcx.ccx, e.id, pres) |
|
||||
set_poststate_ann(fcx.ccx, e.id, block_poststate(fcx.ccx, b));
|
||||
}
|
||||
expr_rec(fields, maybe_base) {
|
||||
let exs = field_exprs(fields);
|
||||
let mut changed =
|
||||
find_pre_post_state_exprs(fcx, pres, e.id,
|
||||
vec::from_elem(vec::len(fields),
|
||||
init_assign),
|
||||
exs, return_val);
|
||||
|
||||
let base_pres = match vec::last_opt(exs) { none { pres }
|
||||
Some(f) { expr_poststate(fcx.ccx, f) }};
|
||||
option::iter(maybe_base, |base| {
|
||||
changed |= find_pre_post_state_expr(fcx, base_pres, base) |
|
||||
set_poststate_ann(fcx.ccx, e.id,
|
||||
expr_poststate(fcx.ccx, base))
|
||||
});
|
||||
return changed;
|
||||
}
|
||||
expr_tup(elts) {
|
||||
return find_pre_post_state_exprs(fcx, pres, e.id,
|
||||
vec::from_elem(vec::len(elts),
|
||||
init_assign), elts,
|
||||
return_val);
|
||||
}
|
||||
expr_move(lhs, rhs) {
|
||||
return find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_move);
|
||||
}
|
||||
expr_assign(lhs, rhs) {
|
||||
return find_pre_post_state_two(
|
||||
fcx, pres, lhs, rhs, e.id, oper_assign);
|
||||
}
|
||||
expr_swap(lhs, rhs) {
|
||||
return find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_swap);
|
||||
// Could be more precise and actually swap the role of
|
||||
// lhs and rhs in constraints
|
||||
}
|
||||
expr_ret(maybe_ret_val) {
|
||||
let mut changed = set_prestate_ann(fcx.ccx, e.id, pres);
|
||||
/* everything is true if execution continues after
|
||||
a return expression (since execution never continues locally
|
||||
after a return expression */
|
||||
let post = false_postcond(num_constrs);
|
||||
|
||||
set_poststate_ann(fcx.ccx, e.id, post);
|
||||
|
||||
match maybe_ret_val {
|
||||
none {/* do nothing */ }
|
||||
Some(ret_val) {
|
||||
changed |= find_pre_post_state_expr(fcx, pres, ret_val);
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
expr_if(antec, conseq, maybe_alt) {
|
||||
return join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if,
|
||||
pres);
|
||||
}
|
||||
expr_binary(bop, l, r) {
|
||||
if lazy_binop(bop) {
|
||||
let mut changed = find_pre_post_state_expr(fcx, pres, l);
|
||||
changed |=
|
||||
find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, l), r);
|
||||
return changed | set_prestate_ann(fcx.ccx, e.id, pres) |
|
||||
set_poststate_ann(fcx.ccx, e.id,
|
||||
expr_poststate(fcx.ccx, l));
|
||||
} else {
|
||||
return find_pre_post_state_two(fcx, pres, l, r, e.id, oper_pure);
|
||||
}
|
||||
}
|
||||
expr_assign_op(op, lhs, rhs) {
|
||||
return find_pre_post_state_two(fcx, pres, lhs, rhs, e.id,
|
||||
oper_assign_op);
|
||||
}
|
||||
expr_while(test, body) {
|
||||
let loop_pres =
|
||||
intersect_states(block_poststate(fcx.ccx, body), pres);
|
||||
|
||||
let mut changed =
|
||||
set_prestate_ann(fcx.ccx, e.id, loop_pres) |
|
||||
find_pre_post_state_expr(fcx, loop_pres, test) |
|
||||
find_pre_post_state_block(fcx, expr_poststate(fcx.ccx, test),
|
||||
body);
|
||||
|
||||
/* conservative approximation: if a loop contains a break
|
||||
or cont, we assume nothing about the poststate */
|
||||
/* which is still unsound -- see ~[Break-unsound] */
|
||||
if has_nonlocal_exits(body) {
|
||||
return changed | set_poststate_ann(fcx.ccx, e.id, pres);
|
||||
} else {
|
||||
let e_post = expr_poststate(fcx.ccx, test);
|
||||
let b_post = block_poststate(fcx.ccx, body);
|
||||
return changed |
|
||||
set_poststate_ann(fcx.ccx, e.id,
|
||||
intersect_states(e_post, b_post));
|
||||
}
|
||||
}
|
||||
expr_loop(body) {
|
||||
let loop_pres =
|
||||
intersect_states(block_poststate(fcx.ccx, body), pres);
|
||||
let mut changed = set_prestate_ann(fcx.ccx, e.id, loop_pres)
|
||||
| find_pre_post_state_block(fcx, loop_pres, body);
|
||||
/* conservative approximation: if a loop contains a break
|
||||
or cont, we assume nothing about the poststate (so, we
|
||||
set all predicates to "don't know" */
|
||||
/* which is still unsound -- see ~[Break-unsound] */
|
||||
if may_break(body) {
|
||||
/* Only do this if there are *breaks* not conts.
|
||||
An infinite loop with conts is still an infinite loop.
|
||||
We assume all preds are FALSE, not '?' -- because in the
|
||||
worst case, the body could invalidate all preds and
|
||||
deinitialize everything before breaking */
|
||||
let post = empty_poststate(num_constrs);
|
||||
post.kill();
|
||||
return changed | set_poststate_ann(fcx.ccx, e.id, post);
|
||||
} else {
|
||||
return changed | set_poststate_ann(fcx.ccx, e.id,
|
||||
false_postcond(num_constrs));
|
||||
}
|
||||
}
|
||||
expr_index(val, sub) {
|
||||
return find_pre_post_state_two(fcx, pres, val, sub, e.id, oper_pure);
|
||||
}
|
||||
expr_match(val, alts, _) {
|
||||
let mut changed =
|
||||
set_prestate_ann(fcx.ccx, e.id, pres) |
|
||||
find_pre_post_state_expr(fcx, pres, val);
|
||||
let e_post = expr_poststate(fcx.ccx, val);
|
||||
let mut a_post;
|
||||
if vec::len(alts) > 0u {
|
||||
a_post = false_postcond(num_constrs);
|
||||
for alts.each |an_alt| {
|
||||
match an_alt.guard {
|
||||
Some(e) {
|
||||
changed |= find_pre_post_state_expr(fcx, e_post, e);
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
changed |=
|
||||
find_pre_post_state_block(fcx, e_post, an_alt.body);
|
||||
intersect(a_post, block_poststate(fcx.ccx, an_alt.body));
|
||||
// We deliberately do *not* update changed here, because
|
||||
// we'd go into an infinite loop that way, and the change
|
||||
// gets made after the if expression.
|
||||
|
||||
}
|
||||
} else {
|
||||
// No alts; poststate is the poststate of the test
|
||||
|
||||
a_post = e_post;
|
||||
}
|
||||
return changed | set_poststate_ann(fcx.ccx, e.id, a_post);
|
||||
}
|
||||
expr_field(x, _, _) | expr_loop_body(x) | expr_do_body(x) |
|
||||
expr_unary(_, x) |
|
||||
expr_addr_of(_, x) | expr_assert(x) | expr_cast(x, _) |
|
||||
expr_copy(x) {
|
||||
return find_pre_post_state_sub(fcx, pres, x, e.id, None);
|
||||
}
|
||||
expr_fail(maybe_fail_val) {
|
||||
/* if execution continues after fail, then everything is true!
|
||||
woo! */
|
||||
let post = false_postcond(num_constrs);
|
||||
return set_prestate_ann(fcx.ccx, e.id, pres) |
|
||||
set_poststate_ann(fcx.ccx, e.id, post) |
|
||||
option::map_default(
|
||||
maybe_fail_val, false,
|
||||
|fail_val|
|
||||
find_pre_post_state_expr(fcx, pres, fail_val) );
|
||||
}
|
||||
expr_check(_, p) {
|
||||
/* predicate p holds after this expression executes */
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p);
|
||||
return find_pre_post_state_sub(fcx, pres, p, e.id, Some(c.node));
|
||||
}
|
||||
expr_if_check(p, conseq, maybe_alt) {
|
||||
return join_then_else(
|
||||
fcx, p, conseq, maybe_alt, e.id, if_check, pres);
|
||||
}
|
||||
expr_break { return pure_exp(fcx.ccx, e.id, pres); }
|
||||
expr_again { return pure_exp(fcx.ccx, e.id, pres); }
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pre_post_state_stmt(fcx: fn_ctxt, pres: prestate, s: @stmt) -> bool {
|
||||
let stmt_ann = stmt_to_ann(fcx.ccx, *s);
|
||||
|
||||
debug!("[ %s ]", *fcx.name);
|
||||
debug!("*At beginning: stmt = %s", stmt_to_str(*s));
|
||||
debug!("*prestate = %s", stmt_ann.states.prestate.to_str());
|
||||
debug!("*poststate = %s", stmt_ann.states.prestate.to_str());
|
||||
|
||||
match s.node {
|
||||
stmt_decl(adecl, id) {
|
||||
match adecl.node {
|
||||
decl_local(alocals) {
|
||||
set_prestate(stmt_ann, pres);
|
||||
let c_and_p = seq_states(fcx, pres,
|
||||
locals_to_bindings(fcx.ccx.tcx, alocals));
|
||||
/* important to do this in one step to ensure
|
||||
termination (don't want to set changed to true
|
||||
for intermediate changes) */
|
||||
|
||||
let mut changed =
|
||||
set_poststate(stmt_ann, c_and_p.post) | c_and_p.changed;
|
||||
|
||||
debug!("Summary: stmt = %s", stmt_to_str(*s));
|
||||
debug!("prestate = %s", stmt_ann.states.prestate.to_str());
|
||||
debug!("poststate = %s", stmt_ann.states.poststate.to_str());
|
||||
debug!("changed = %s", bool::to_str(changed));
|
||||
|
||||
return changed;
|
||||
}
|
||||
decl_item(an_item) {
|
||||
return set_prestate(stmt_ann, pres)
|
||||
| set_poststate(stmt_ann, pres);
|
||||
/* the outer visitor will recurse into the item */
|
||||
}
|
||||
}
|
||||
}
|
||||
stmt_expr(ex, _) | stmt_semi(ex, _) {
|
||||
let mut changed =
|
||||
find_pre_post_state_expr(fcx, pres, ex) |
|
||||
set_prestate(stmt_ann, expr_prestate(fcx.ccx, ex)) |
|
||||
set_poststate(stmt_ann, expr_poststate(fcx.ccx, ex));
|
||||
|
||||
|
||||
debug!("Finally: %s", stmt_to_str(*s));
|
||||
debug!("prestate = %s", stmt_ann.states.prestate.to_str());
|
||||
debug!("poststate = %s", stmt_ann.states.poststate.to_str());
|
||||
debug!("changed = %s", bool::to_str(changed));
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Updates the pre- and post-states of statements in the block,
|
||||
returns a boolean flag saying whether any pre- or poststates changed */
|
||||
fn find_pre_post_state_block(fcx: fn_ctxt, pres0: prestate, b: blk) -> bool {
|
||||
/* First, set the pre-states and post-states for every expression */
|
||||
|
||||
let mut pres = pres0;
|
||||
/* Iterate over each stmt. The new prestate is <pres>. The poststate
|
||||
consist of improving <pres> with whatever variables this stmt
|
||||
initializes. Then <pres> becomes the new poststate. */
|
||||
|
||||
let mut changed = false;
|
||||
for b.node.stmts.each |s| {
|
||||
changed |= find_pre_post_state_stmt(fcx, pres, s);
|
||||
pres = stmt_poststate(fcx.ccx, *s);
|
||||
}
|
||||
let mut post = pres;
|
||||
match b.node.expr {
|
||||
none { }
|
||||
Some(e) {
|
||||
changed |= find_pre_post_state_expr(fcx, pres, e);
|
||||
post = expr_poststate(fcx.ccx, e);
|
||||
}
|
||||
}
|
||||
|
||||
set_prestate_ann(fcx.ccx, b.node.id, pres0);
|
||||
set_poststate_ann(fcx.ccx, b.node.id, post);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
fn find_pre_post_state_fn(fcx: fn_ctxt,
|
||||
f_decl: fn_decl,
|
||||
f_body: blk) -> bool {
|
||||
// All constraints are considered false until proven otherwise.
|
||||
// This ensures that intersect works correctly.
|
||||
kill_all_prestate(fcx, f_body.node.id);
|
||||
|
||||
// Instantiate any constraints on the arguments so we can use them
|
||||
let block_pre = block_prestate(fcx.ccx, f_body);
|
||||
for f_decl.constraints.each |c| {
|
||||
let tsc = ast_constr_to_ts_constr(fcx.ccx.tcx, f_decl.inputs, c);
|
||||
set_in_prestate_constr(fcx, tsc, block_pre);
|
||||
}
|
||||
|
||||
let mut changed = find_pre_post_state_block(fcx, block_pre, f_body);
|
||||
|
||||
/*
|
||||
error!("find_pre_post_state_fn");
|
||||
log(error, changed);
|
||||
fcx.ccx.tcx.sess.span_note(f_body.span, fcx.name);
|
||||
*/
|
||||
|
||||
return changed;
|
||||
}
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// End:
|
||||
//
|
Loading…
x
Reference in New Issue
Block a user