1144 lines
33 KiB
Rust
1144 lines
33 KiB
Rust
import std::vec;
|
|
import std::int::str;
|
|
import std::str;
|
|
import std::option;
|
|
import std::option::*;
|
|
import std::int;
|
|
import std::uint;
|
|
import syntax::ast::*;
|
|
import syntax::ast_util::*;
|
|
import syntax::codemap::span;
|
|
import syntax::visit;
|
|
import util::common;
|
|
import util::common::log_block;
|
|
import std::map::new_int_hash;
|
|
import std::map::new_uint_hash;
|
|
import util::common::log_expr_err;
|
|
import util::common::lit_eq;
|
|
import syntax::print::pprust::path_to_str;
|
|
import tstate::ann::pre_and_post;
|
|
import tstate::ann::pre_and_post_state;
|
|
import tstate::ann::empty_ann;
|
|
import tstate::ann::prestate;
|
|
import tstate::ann::poststate;
|
|
import tstate::ann::precond;
|
|
import tstate::ann::postcond;
|
|
import tstate::ann::empty_states;
|
|
import tstate::ann::pps_len;
|
|
import tstate::ann::set_prestate;
|
|
import tstate::ann::set_poststate;
|
|
import tstate::ann::set_in_poststate_;
|
|
import tstate::ann::extend_prestate;
|
|
import tstate::ann::extend_poststate;
|
|
import tstate::ann::set_precondition;
|
|
import tstate::ann::set_postcondition;
|
|
import tstate::ann::set_in_postcond_;
|
|
import tstate::ann::ts_ann;
|
|
import tstate::ann::clear_in_postcond;
|
|
import tstate::ann::clear_in_poststate;
|
|
import tstate::ann::clear_in_poststate_;
|
|
import tritv::*;
|
|
import bitvectors::promises_;
|
|
|
|
import syntax::print::pprust::constr_args_to_str;
|
|
import syntax::print::pprust::constr_arg_to_str;
|
|
import syntax::print::pprust::lit_to_str;
|
|
|
|
// Used to communicate which operands should be invalidated
|
|
// to helper functions
|
|
tag oper_type {
|
|
oper_move;
|
|
oper_swap;
|
|
oper_assign;
|
|
oper_assign_op;
|
|
oper_pure;
|
|
}
|
|
|
|
/* logging funs */
|
|
fn def_id_to_str(d: def_id) -> str {
|
|
ret int::str(d.crate) + "," + int::str(d.node);
|
|
}
|
|
|
|
fn comma_str(args: &[@constr_arg_use]) -> str {
|
|
let rslt = "";
|
|
let comma = false;
|
|
for a: @constr_arg_use in args {
|
|
if comma { rslt += ", "; } else { comma = true; }
|
|
alt a.node {
|
|
carg_base. { rslt += "*"; }
|
|
carg_ident(i) { rslt += i.ident; }
|
|
carg_lit(l) { rslt += lit_to_str(l); }
|
|
}
|
|
}
|
|
ret rslt;
|
|
}
|
|
|
|
fn constraint_to_str(tcx: &ty::ctxt, c: &sp_constr) -> str {
|
|
alt c.node {
|
|
ninit(_, i) {
|
|
ret "init(" + i + " [" + tcx.sess.span_str(c.span) + "])";
|
|
}
|
|
npred(p, _, args) {
|
|
ret path_to_str(p) + "(" + comma_str(args) + ")" + "[" +
|
|
tcx.sess.span_str(c.span) + "]";
|
|
}
|
|
}
|
|
}
|
|
|
|
fn tritv_to_str(fcx: fn_ctxt, v: &tritv::t) -> str {
|
|
let s = "";
|
|
let comma = false;
|
|
for p: norm_constraint in constraints(fcx) {
|
|
alt tritv_get(v, p.bit_num) {
|
|
dont_care. { }
|
|
t {
|
|
s +=
|
|
if comma { ", " } else { comma = true; "" } +
|
|
if t == tfalse { "!" } else { "" } +
|
|
constraint_to_str(fcx.ccx.tcx, p.c);
|
|
}
|
|
}
|
|
}
|
|
ret s;
|
|
}
|
|
|
|
fn log_tritv(fcx: &fn_ctxt, v: &tritv::t) { log tritv_to_str(fcx, v); }
|
|
|
|
fn first_difference_string(fcx: &fn_ctxt, expected: &tritv::t,
|
|
actual: &tritv::t) -> str {
|
|
let s: str = "";
|
|
for c: norm_constraint in constraints(fcx) {
|
|
if tritv_get(expected, c.bit_num) == ttrue &&
|
|
tritv_get(actual, c.bit_num) != ttrue {
|
|
ret constraint_to_str(fcx.ccx.tcx, c.c);
|
|
}
|
|
}
|
|
ret s;
|
|
}
|
|
|
|
fn log_tritv_err(fcx: fn_ctxt, v: tritv::t) { log_err tritv_to_str(fcx, v); }
|
|
|
|
fn tos(v: &[uint]) -> str {
|
|
let rslt = "";
|
|
for i: uint in v {
|
|
if i == 0u {
|
|
rslt += "0";
|
|
} else if i == 1u { rslt += "1"; } else { rslt += "?"; }
|
|
}
|
|
ret rslt;
|
|
}
|
|
|
|
fn log_cond(v: &[uint]) { log tos(v); }
|
|
|
|
fn log_cond_err(v: &[uint]) { log_err tos(v); }
|
|
|
|
fn log_pp(pp: &pre_and_post) {
|
|
let p1 = tritv::to_vec(pp.precondition);
|
|
let p2 = tritv::to_vec(pp.postcondition);
|
|
log "pre:";
|
|
log_cond(p1);
|
|
log "post:";
|
|
log_cond(p2);
|
|
}
|
|
|
|
fn log_pp_err(pp: &pre_and_post) {
|
|
let p1 = tritv::to_vec(pp.precondition);
|
|
let p2 = tritv::to_vec(pp.postcondition);
|
|
log_err "pre:";
|
|
log_cond_err(p1);
|
|
log_err "post:";
|
|
log_cond_err(p2);
|
|
}
|
|
|
|
fn log_states(pp: &pre_and_post_state) {
|
|
let p1 = tritv::to_vec(pp.prestate);
|
|
let p2 = tritv::to_vec(pp.poststate);
|
|
log "prestate:";
|
|
log_cond(p1);
|
|
log "poststate:";
|
|
log_cond(p2);
|
|
}
|
|
|
|
fn log_states_err(pp: &pre_and_post_state) {
|
|
let p1 = tritv::to_vec(pp.prestate);
|
|
let p2 = tritv::to_vec(pp.poststate);
|
|
log_err "prestate:";
|
|
log_cond_err(p1);
|
|
log_err "poststate:";
|
|
log_cond_err(p2);
|
|
}
|
|
|
|
fn print_ident(i: &ident) { log " " + i + " "; }
|
|
|
|
fn print_idents(idents: &mutable [ident]) {
|
|
if vec::len::<ident>(idents) == 0u { ret; }
|
|
log "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.
|
|
cinit constraints represent a single constraint, for the initialization
|
|
state of a variable; a cpred constraint, with a single operator and a
|
|
list of possible argument lists, could represent several constraints at
|
|
once.
|
|
|
|
norm_constraint, in contrast, gets used when handling an instance
|
|
of a constraint rather than a definition of a constraint. It can
|
|
also be init or pred (ninit or npred), but the npred case just has
|
|
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>>;
|
|
|
|
tag constraint {
|
|
cinit(uint, span, ident);
|
|
|
|
// FIXME: really only want it to be mutable during collect_locals.
|
|
// freeze it after that.
|
|
cpred(path, @mutable [pred_args]);
|
|
}
|
|
|
|
// An ninit variant has a node_id because it refers to a local var.
|
|
// An npred has a def_id since the definition of the typestate
|
|
// predicate need not be local.
|
|
// FIXME: would be nice to give both a def_id field,
|
|
// and give ninit a constraint saying it's local.
|
|
tag tsconstr {
|
|
ninit(node_id, ident);
|
|
npred(path, def_id, [@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. i_return holds if all control paths in this function terminate
|
|
in either a return expression, or an appropriate tail expression.
|
|
i_diverge holds if all control paths in this function terminate in a fail
|
|
or diverging call.
|
|
|
|
It might be tempting to use a single constraint C for both properties,
|
|
where C represents i_return and !C represents i_diverge. This is
|
|
inadvisable, because then the sense of the bit depends on context. If we're
|
|
inside a ! function, that reverses the sense of the bit: C would be
|
|
i_diverge and !C would be i_return. That's awkward, because we have to
|
|
pass extra context around to functions that shouldn't care.
|
|
|
|
Okay, suppose C represents i_return and !C represents i_diverge, regardless
|
|
of context. Consider this code:
|
|
|
|
if (foo) { ret; } else { fail; }
|
|
|
|
C is true in the consequent and false in the alternative. What's T `join`
|
|
F, then? ? doesn't work, because this code should definitely-return if the
|
|
context is a returning function (and be definitely-rejected if the context
|
|
is a ! function). F doesn't work, because then the code gets incorrectly
|
|
rejected if the context is a returning function. T would work, but it
|
|
doesn't make sense for T `join` F to be T (consider init constraints, for
|
|
example).;
|
|
|
|
So we need context. And so it seems clearer to just have separate
|
|
constraints.
|
|
*/
|
|
type fn_info =
|
|
{constrs: constr_map,
|
|
num_constraints: uint,
|
|
cf: controlflow,
|
|
i_return: tsconstr,
|
|
i_diverge: tsconstr,
|
|
/* list, accumulated during pre/postcondition
|
|
computation, of all local variables that may be
|
|
used */
|
|
// Doesn't seem to work without the @ -- bug
|
|
used_vars: @mutable [node_id]};
|
|
|
|
fn tsconstr_to_def_id(t: &tsconstr) -> def_id {
|
|
alt t { ninit(id, _) { local_def(id) } npred(_, id, _) { id } }
|
|
}
|
|
|
|
fn tsconstr_to_node_id(t: &tsconstr) -> node_id {
|
|
alt t {
|
|
ninit(id, _) { id }
|
|
npred(_, id, _) { fail "tsconstr_to_node_id called on pred constraint" }
|
|
}
|
|
}
|
|
|
|
/* mapping from node ID to typestate annotation */
|
|
type node_ann_table = @mutable [mutable 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));
|
|
ret 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_mut(*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::t<ts_ann> {
|
|
if i as uint < vec::len(*ccx.node_anns) {
|
|
ret some::<ts_ann>(ccx.node_anns[i]);
|
|
} else { ret none::<ts_ann>; }
|
|
}
|
|
|
|
|
|
/********* utils ********/
|
|
fn node_id_to_ts_ann(ccx: &crate_ctxt, id: node_id) -> ts_ann {
|
|
alt get_ts_ann(ccx, id) {
|
|
none. {
|
|
log_err "node_id_to_ts_ann: no ts_ann for node_id " + int::str(id);
|
|
fail;
|
|
}
|
|
some(t) { ret t; }
|
|
}
|
|
}
|
|
|
|
fn node_id_to_poststate(ccx: &crate_ctxt, id: node_id) -> poststate {
|
|
log "node_id_to_poststate";
|
|
ret node_id_to_ts_ann(ccx, id).states.poststate;
|
|
}
|
|
|
|
fn stmt_to_ann(ccx: &crate_ctxt, s: &stmt) -> ts_ann {
|
|
log "stmt_to_ann";
|
|
alt s.node {
|
|
stmt_decl(_, id) { ret node_id_to_ts_ann(ccx, id); }
|
|
stmt_expr(_, id) { ret node_id_to_ts_ann(ccx, id); }
|
|
stmt_crate_directive(_) {
|
|
log_err "expecting an annotated statement here";
|
|
fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* fails if e has no annotation */
|
|
fn expr_states(ccx: &crate_ctxt, e: @expr) -> pre_and_post_state {
|
|
log "expr_states";
|
|
ret 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 {
|
|
log "expr_pp";
|
|
ret node_id_to_ts_ann(ccx, e.id).conditions;
|
|
}
|
|
|
|
fn stmt_pp(ccx: &crate_ctxt, s: &stmt) -> pre_and_post {
|
|
ret stmt_to_ann(ccx, s).conditions;
|
|
}
|
|
|
|
|
|
/* fails if b has no annotation */
|
|
fn block_pp(ccx: &crate_ctxt, b: &blk) -> pre_and_post {
|
|
log "block_pp";
|
|
ret 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 {
|
|
log "block_states";
|
|
ret node_id_to_ts_ann(ccx, b.node.id).states;
|
|
}
|
|
|
|
fn stmt_states(ccx: &crate_ctxt, s: &stmt) -> pre_and_post_state {
|
|
ret stmt_to_ann(ccx, s).states;
|
|
}
|
|
|
|
fn expr_precond(ccx: &crate_ctxt, e: @expr) -> precond {
|
|
ret expr_pp(ccx, e).precondition;
|
|
}
|
|
|
|
fn expr_postcond(ccx: &crate_ctxt, e: @expr) -> postcond {
|
|
ret expr_pp(ccx, e).postcondition;
|
|
}
|
|
|
|
fn expr_prestate(ccx: &crate_ctxt, e: @expr) -> prestate {
|
|
ret expr_states(ccx, e).prestate;
|
|
}
|
|
|
|
fn expr_poststate(ccx: &crate_ctxt, e: @expr) -> poststate {
|
|
ret expr_states(ccx, e).poststate;
|
|
}
|
|
|
|
fn stmt_precond(ccx: &crate_ctxt, s: &stmt) -> precond {
|
|
ret stmt_pp(ccx, s).precondition;
|
|
}
|
|
|
|
fn stmt_postcond(ccx: &crate_ctxt, s: &stmt) -> postcond {
|
|
ret stmt_pp(ccx, s).postcondition;
|
|
}
|
|
|
|
fn states_to_poststate(ss: &pre_and_post_state) -> poststate {
|
|
ret ss.poststate;
|
|
}
|
|
|
|
fn stmt_prestate(ccx: &crate_ctxt, s: &stmt) -> prestate {
|
|
ret stmt_states(ccx, s).prestate;
|
|
}
|
|
|
|
fn stmt_poststate(ccx: &crate_ctxt, s: &stmt) -> poststate {
|
|
ret stmt_states(ccx, s).poststate;
|
|
}
|
|
|
|
fn block_precond(ccx: &crate_ctxt, b: &blk) -> precond {
|
|
ret block_pp(ccx, b).precondition;
|
|
}
|
|
|
|
fn block_postcond(ccx: &crate_ctxt, b: &blk) -> postcond {
|
|
ret block_pp(ccx, b).postcondition;
|
|
}
|
|
|
|
fn block_prestate(ccx: &crate_ctxt, b: &blk) -> prestate {
|
|
ret block_states(ccx, b).prestate;
|
|
}
|
|
|
|
fn block_poststate(ccx: &crate_ctxt, b: &blk) -> poststate {
|
|
ret block_states(ccx, b).poststate;
|
|
}
|
|
|
|
fn set_prestate_ann(ccx: &crate_ctxt, id: node_id, pre: &prestate) -> bool {
|
|
log "set_prestate_ann";
|
|
ret set_prestate(node_id_to_ts_ann(ccx, id), pre);
|
|
}
|
|
|
|
fn extend_prestate_ann(ccx: &crate_ctxt, id: node_id, pre: &prestate) ->
|
|
bool {
|
|
log "extend_prestate_ann";
|
|
ret 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 {
|
|
log "set_poststate_ann";
|
|
ret set_poststate(node_id_to_ts_ann(ccx, id), post);
|
|
}
|
|
|
|
fn extend_poststate_ann(ccx: &crate_ctxt, id: node_id, post: &poststate) ->
|
|
bool {
|
|
log "extend_poststate_ann";
|
|
ret 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) {
|
|
log "set_pre_and_post";
|
|
let t = node_id_to_ts_ann(ccx, id);
|
|
set_precondition(t, pre);
|
|
set_postcondition(t, post);
|
|
}
|
|
|
|
fn copy_pre_post(ccx: &crate_ctxt, id: node_id, sub: &@expr) {
|
|
log "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) {
|
|
log "set_pre_and_post";
|
|
let t = node_id_to_ts_ann(ccx, id);
|
|
set_precondition(t, pre);
|
|
set_postcondition(t, 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 {
|
|
ret set_prestate_ann(ccx, id, p) | set_poststate_ann(ccx, id, p);
|
|
}
|
|
|
|
fn num_constraints(m: fn_info) -> uint { ret m.num_constraints; }
|
|
|
|
fn new_crate_ctxt(cx: ty::ctxt) -> crate_ctxt {
|
|
let na: [mutable ts_ann] = [mutable];
|
|
ret {tcx: cx, node_anns: @mutable na, fm: @new_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) -> controlflow {
|
|
alt ty::struct(ccx.tcx, ty::node_id_to_type(ccx.tcx, e.id)) {
|
|
ty::ty_fn(_, _, _, cf, _) { ret cf; }
|
|
_ { ret return; }
|
|
}
|
|
}
|
|
|
|
fn constraints_expr(cx: &ty::ctxt, e: @expr) -> [@ty::constr] {
|
|
alt ty::struct(cx, ty::node_id_to_type(cx, e.id)) {
|
|
ty::ty_fn(_, _, _, _, cs) { ret cs; }
|
|
_ { ret []; }
|
|
}
|
|
}
|
|
|
|
fn node_id_to_def_upvar_strict(cx: &fn_ctxt, id: node_id) -> def {
|
|
alt freevars::def_lookup(cx.ccx.tcx, cx.id, id) {
|
|
none. {
|
|
log_err "node_id_to_def: node_id " + int::str(id) + " has no def";
|
|
fail;
|
|
}
|
|
some(d) { ret d; }
|
|
}
|
|
}
|
|
fn node_id_to_def_strict(cx: &ty::ctxt, id: node_id) -> def {
|
|
alt cx.def_map.find(id) {
|
|
none. {
|
|
log_err "node_id_to_def: node_id " + int::str(id) + " has no def";
|
|
fail;
|
|
}
|
|
some(d) { ret d; }
|
|
}
|
|
}
|
|
|
|
fn node_id_to_def(ccx: &crate_ctxt, id: node_id) -> option::t<def> {
|
|
ret ccx.tcx.def_map.find(id);
|
|
}
|
|
fn node_id_to_def_upvar(cx: &fn_ctxt, id: node_id) -> option::t<def> {
|
|
ret freevars::def_lookup(cx.ccx.tcx, cx.id, id);
|
|
}
|
|
|
|
fn norm_a_constraint(id: def_id, c: &constraint) -> [norm_constraint] {
|
|
alt c {
|
|
cinit(n, sp, i) {
|
|
ret [{bit_num: n, c: respan(sp, ninit(id.node, i))}];
|
|
}
|
|
cpred(p, descs) {
|
|
let rslt: [norm_constraint] = [];
|
|
for pd: pred_args in *descs {
|
|
rslt +=
|
|
[{bit_num: pd.node.bit_num,
|
|
c: respan(pd.span, npred(p, id, pd.node.args))}];
|
|
}
|
|
ret 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 rslt: [norm_constraint] = [];
|
|
for each p: @{key: def_id, val: constraint} in
|
|
fcx.enclosing.constrs.items() {
|
|
rslt += norm_a_constraint(p.key, p.val);
|
|
}
|
|
ret rslt;
|
|
}
|
|
|
|
// FIXME
|
|
// Would rather take an immutable vec as an argument,
|
|
// should freeze it at some earlier point.
|
|
fn match_args(fcx: &fn_ctxt, occs: &@mutable [pred_args],
|
|
occ: &[@constr_arg_use]) -> uint {
|
|
log "match_args: looking at " +
|
|
constr_args_to_str(fn (i: &inst) -> str { ret i.ident; }, occ);
|
|
for pd: pred_args in *occs {
|
|
log "match_args: candidate " + pred_args_to_str(pd);
|
|
fn eq(p: &inst, q: &inst) -> bool { ret p.node == q.node; }
|
|
if ty::args_eq(eq, pd.node.args, occ) { ret 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 {
|
|
alt tcx.def_map.find(t) {
|
|
none. {
|
|
tcx.sess.bug("node_id_for_constr: bad node_id " + int::str(t));
|
|
}
|
|
some(def_fn(i, _)) { ret 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 {
|
|
alt e.node {
|
|
expr_path(p) {
|
|
alt tcx.def_map.find(e.id) {
|
|
some(def_local(l_id)) {
|
|
ret @respan(p.span,
|
|
carg_ident({ident: p.node.idents[0],
|
|
node: l_id.node}));
|
|
}
|
|
some(def_arg(a_id)) {
|
|
ret @respan(p.span,
|
|
carg_ident({ident: p.node.idents[0],
|
|
node: a_id.node}));
|
|
}
|
|
some (def_binding(b_id)) {
|
|
ret @respan(p.span,
|
|
carg_ident({ident: p.node.idents[0],
|
|
node: b_id.node}));
|
|
}
|
|
some(_) {
|
|
tcx.sess.bug("exprs_to_constr_args: non-local variable " +
|
|
"as pred arg");
|
|
}
|
|
none {
|
|
tcx.sess.bug("exprs_to_constr_args: NONE " +
|
|
"as pred arg");
|
|
|
|
}
|
|
}
|
|
}
|
|
expr_lit(l) { ret @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 = bind expr_to_constr_arg(tcx, _);
|
|
let rslt: [@constr_arg_use] = [];
|
|
for e: @expr in args { rslt += [f(e)]; }
|
|
rslt
|
|
}
|
|
|
|
fn expr_to_constr(tcx: ty::ctxt, e: &@expr) -> sp_constr {
|
|
alt e.node {
|
|
expr_call(operator, args) {
|
|
alt operator.node {
|
|
expr_path(p) {
|
|
ret respan(e.span,
|
|
npred(p, def_id_for_constr(tcx, operator.id),
|
|
exprs_to_constr_args(tcx, args)));
|
|
}
|
|
_ {
|
|
tcx.sess.span_fatal(operator.span,
|
|
"Internal error: " +
|
|
" ill-formed operator \
|
|
in predicate");
|
|
}
|
|
}
|
|
}
|
|
_ {
|
|
tcx.sess.span_fatal(e.span,
|
|
"Internal error: " + " 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 { ret i.ident; }, p.node.args)
|
|
+ ">"
|
|
}
|
|
|
|
fn substitute_constr_args(cx: &ty::ctxt, actuals: &[@expr], c: &@ty::constr)
|
|
-> tsconstr {
|
|
let rslt: [@constr_arg_use] = [];
|
|
for a: @constr_arg in c.node.args {
|
|
rslt += [substitute_arg(cx, actuals, a)];
|
|
}
|
|
ret npred(c.node.path, c.node.id, rslt);
|
|
}
|
|
|
|
fn substitute_arg(cx: &ty::ctxt, actuals: &[@expr], a: @constr_arg) ->
|
|
@constr_arg_use {
|
|
let num_actuals = vec::len(actuals);
|
|
alt a.node {
|
|
carg_ident(i) {
|
|
if i < num_actuals {
|
|
ret expr_to_constr_arg(cx, actuals[i]);
|
|
} else {
|
|
cx.sess.span_fatal(a.span, "Constraint argument out of bounds");
|
|
}
|
|
}
|
|
carg_base. { ret @respan(a.span, carg_base); }
|
|
carg_lit(l) { ret @respan(a.span, carg_lit(l)); }
|
|
}
|
|
}
|
|
|
|
fn pred_args_matches(pattern: &[constr_arg_general_<inst>], desc: &pred_args)
|
|
-> bool {
|
|
let i = 0u;
|
|
for c: @constr_arg_use in desc.node.args {
|
|
let n = pattern[i];
|
|
alt c.node {
|
|
carg_ident(p) {
|
|
alt n {
|
|
carg_ident(q) { if p.node != q.node { ret false; } }
|
|
_ { ret false; }
|
|
}
|
|
}
|
|
carg_base. { if n != carg_base { ret false; } }
|
|
carg_lit(l) {
|
|
alt n {
|
|
carg_lit(m) { if !lit_eq(l, m) { ret false; } }
|
|
_ { ret false; }
|
|
}
|
|
}
|
|
}
|
|
i += 1u;
|
|
}
|
|
ret true;
|
|
}
|
|
|
|
fn find_instance_(pattern: &[constr_arg_general_<inst>], descs: &[pred_args])
|
|
-> option::t<uint> {
|
|
for d: pred_args in descs {
|
|
if pred_args_matches(pattern, d) { ret some(d.node.bit_num); }
|
|
}
|
|
ret none;
|
|
}
|
|
|
|
type inst = {ident: ident, node: node_id};
|
|
type subst = [{from: inst, to: inst}];
|
|
|
|
fn find_instances(_fcx: &fn_ctxt, subst: &subst, c: &constraint) ->
|
|
[{from: uint, to: uint}] {
|
|
|
|
let rslt = [];
|
|
if vec::len(subst) == 0u { ret rslt; }
|
|
|
|
alt c {
|
|
cinit(_, _, _) {/* this is dealt with separately */ }
|
|
cpred(p, descs) {
|
|
for d: pred_args in *descs {
|
|
if args_mention(d.node.args, find_in_subst_bool, subst) {
|
|
let old_bit_num = d.node.bit_num;
|
|
let new = replace(subst, d);
|
|
alt find_instance_(new, *descs) {
|
|
some(d1) { rslt += [{from: old_bit_num, to: d1}]; }
|
|
_ { }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rslt
|
|
}
|
|
|
|
fn find_in_subst(id: node_id, s: &subst) -> option::t<inst> {
|
|
for p: {from: inst, to: inst} in s {
|
|
if id == p.from.node { ret some(p.to); }
|
|
}
|
|
ret 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 rslt = "<";
|
|
for i: constr_arg_general_<inst> in stuff {
|
|
rslt +=
|
|
" " +
|
|
alt 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 rslt: [constr_arg_general_<inst>] = [];
|
|
for c: @constr_arg_use in d.node.args {
|
|
alt c.node {
|
|
carg_ident(p) {
|
|
alt find_in_subst(p.node, subst) {
|
|
some(new) { rslt += [carg_ident(new)]; }
|
|
_ { rslt += [c.node]; }
|
|
}
|
|
}
|
|
_ {
|
|
// log_err "##";
|
|
rslt += [c.node];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
for (constr_arg_general_<tup(ident, def_id)> p in rslt) {
|
|
alt (p) {
|
|
case (carg_ident(?p)) {
|
|
log_err p._0;
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
*/
|
|
|
|
ret rslt;
|
|
}
|
|
|
|
fn path_to_ident(cx: &ty::ctxt, p: &path) -> ident {
|
|
alt vec::last(p.node.idents) {
|
|
none. { cx.sess.span_fatal(p.span, "Malformed path"); }
|
|
some(i) { ret i; }
|
|
}
|
|
}
|
|
|
|
tag if_ty { if_check; plain_if; }
|
|
|
|
fn local_node_id_to_def_id_strict(fcx: &fn_ctxt, sp: &span, i: &node_id) ->
|
|
def_id {
|
|
alt local_node_id_to_def(fcx, i) {
|
|
some(def_local(d_id)) { ret d_id; }
|
|
some(def_arg(a_id)) { ret a_id; }
|
|
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::t<def> {
|
|
fcx.ccx.tcx.def_map.find(i)
|
|
}
|
|
|
|
fn local_node_id_to_def_id(fcx: &fn_ctxt, i: &node_id) -> option::t<def_id> {
|
|
alt local_node_id_to_def(fcx, i) {
|
|
some(def_local(d_id)) { some(d_id) }
|
|
some(def_arg(a_id)) { some(a_id) }
|
|
_ { none }
|
|
}
|
|
}
|
|
|
|
fn local_node_id_to_local_def_id(fcx: &fn_ctxt, i: &node_id) ->
|
|
option::t<node_id> {
|
|
alt local_node_id_to_def(fcx, i) {
|
|
some(def_local(d_id)) { some(d_id.node) }
|
|
some(def_arg(a_id)) { some(a_id.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);
|
|
}
|
|
|
|
// FIXME refactor
|
|
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 subst;
|
|
alt ty {
|
|
oper_swap. { subst = [{from: dest, to: src}, {from: src, to: dest}]; }
|
|
oper_assign_op. {
|
|
ret; // Don't do any propagation
|
|
}
|
|
_ { subst = [{from: src, to: dest}]; }
|
|
}
|
|
|
|
|
|
for each p: @{key: def_id, val: constraint} in
|
|
fcx.enclosing.constrs.items() {
|
|
// replace any occurrences of the src def_id with the
|
|
// dest def_id
|
|
let insts = find_instances(fcx, subst, p.val);
|
|
for p: {from: uint, to: uint} in insts {
|
|
if promises_(p.from, src_post) {
|
|
set_in_poststate_(p.to, target_post);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* FIXME should refactor this better */
|
|
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);
|
|
alt d {
|
|
some(d_id) {
|
|
for c: norm_constraint in constraints(fcx) {
|
|
if constraint_mentions(fcx, c, d_id) {
|
|
clear_in_postcond(c.bit_num,
|
|
node_id_to_ts_ann(fcx.ccx,
|
|
parent_exp).conditions);
|
|
}
|
|
}
|
|
}
|
|
_ { }
|
|
}
|
|
}
|
|
|
|
fn forget_in_postcond_still_init(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);
|
|
alt d {
|
|
some(d_id) {
|
|
for c: norm_constraint in constraints(fcx) {
|
|
if non_init_constraint_mentions(fcx, c, d_id) {
|
|
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 changed = false;
|
|
alt d {
|
|
some(d_id) {
|
|
for c: norm_constraint in constraints(fcx) {
|
|
if constraint_mentions(fcx, c, d_id) {
|
|
changed |= clear_in_poststate_(c.bit_num, p);
|
|
}
|
|
}
|
|
}
|
|
_ { }
|
|
}
|
|
ret changed;
|
|
}
|
|
|
|
fn forget_in_poststate_still_init(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 changed = false;
|
|
alt d {
|
|
some(d_id) {
|
|
for c: norm_constraint in constraints(fcx) {
|
|
if non_init_constraint_mentions(fcx, c, d_id) {
|
|
changed |= clear_in_poststate_(c.bit_num, p);
|
|
}
|
|
}
|
|
}
|
|
_ { }
|
|
}
|
|
ret changed;
|
|
}
|
|
|
|
fn any_eq(v: &[node_id], d: node_id) -> bool {
|
|
for i: node_id in v { if i == d { ret true; } }
|
|
false
|
|
}
|
|
|
|
fn constraint_mentions(_fcx: &fn_ctxt, c: &norm_constraint, v: node_id) ->
|
|
bool {
|
|
ret alt c.c.node {
|
|
ninit(id, _) { v == id }
|
|
npred(_, _, args) { args_mention(args, any_eq, [v]) }
|
|
};
|
|
}
|
|
|
|
fn non_init_constraint_mentions(_fcx: &fn_ctxt, c: &norm_constraint,
|
|
v: &node_id) -> bool {
|
|
ret alt c.c.node {
|
|
ninit(_, _) { false }
|
|
npred(_, _, args) { args_mention(args, any_eq, [v]) }
|
|
};
|
|
}
|
|
|
|
fn args_mention<T>(args: &[@constr_arg_use], q: fn(&[T], node_id) -> bool,
|
|
s: &[T]) -> bool {
|
|
/*
|
|
FIXME
|
|
The following version causes an assertion in trans to fail
|
|
(something about type_is_tup_like)
|
|
fn mentions<T>(&[T] s, &fn(&[T], def_id) -> bool q,
|
|
&@constr_arg_use a) -> bool {
|
|
alt (a.node) {
|
|
case (carg_ident(?p1)) {
|
|
auto res = q(s, p1._1);
|
|
log_err (res);
|
|
res
|
|
}
|
|
case (_) { false }
|
|
}
|
|
}
|
|
ret vec::any(bind mentions(s,q,_), args);
|
|
*/
|
|
|
|
for a: @constr_arg_use in args {
|
|
alt a.node { carg_ident(p1) { if q(s, p1.node) { ret true; } } _ { } }
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn use_var(fcx: &fn_ctxt, v: &node_id) { *fcx.enclosing.used_vars += [v]; }
|
|
|
|
// FIXME: This should be a function in std::vec::.
|
|
fn vec_contains(v: &@mutable [node_id], i: &node_id) -> bool {
|
|
for d: node_id in *v { if d == i { ret true; } }
|
|
ret false;
|
|
}
|
|
|
|
fn op_to_oper_ty(io: init_op) -> oper_type {
|
|
alt io { init_move. { oper_move } _ { oper_assign } }
|
|
}
|
|
|
|
// default function visitor
|
|
fn do_nothing<T>(_f: &_fn, _tp: &[ty_param], _sp: &span, _i: &fn_ident,
|
|
_iid: node_id, _cx: &T, _v: &visit::vt<T>) {
|
|
}
|
|
|
|
|
|
fn args_to_constr_args(sp: &span, args: &[arg]) -> [@constr_arg_use] {
|
|
let actuals: [@constr_arg_use] = [];
|
|
for a: arg in args {
|
|
actuals += [@respan(sp, carg_ident({ident: a.ident, node: a.id}))];
|
|
}
|
|
ret actuals;
|
|
}
|
|
|
|
fn ast_constr_to_ts_constr(tcx: &ty::ctxt, args: &[arg], c: &@constr) ->
|
|
tsconstr {
|
|
let tconstr = ty::ast_constr_to_constr(tcx, c);
|
|
ret npred(tconstr.node.path, tconstr.node.id,
|
|
args_to_constr_args(tconstr.span, 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);
|
|
ret respan(c.span, tconstr);
|
|
}
|
|
|
|
type binding = {lhs: [inst], rhs: option::t<initializer>};
|
|
|
|
fn local_to_bindings(loc: &@local) -> binding {
|
|
let lhs = [];
|
|
for each p: @pat in pat_bindings(loc.node.pat) {
|
|
let ident = alt p.node { pat_bind(name) { name } };
|
|
lhs += [{ident: ident, node: p.id}];
|
|
}
|
|
{lhs: lhs, rhs: loc.node.init}
|
|
}
|
|
|
|
fn locals_to_bindings(locals: &[@local]) -> [binding] {
|
|
vec::map(local_to_bindings, locals)
|
|
}
|
|
|
|
fn callee_modes(fcx: &fn_ctxt, callee: node_id) -> [ty::mode] {
|
|
let ty =
|
|
ty::type_autoderef(fcx.ccx.tcx,
|
|
ty::node_id_to_type(fcx.ccx.tcx, callee));
|
|
alt ty::struct(fcx.ccx.tcx, ty) {
|
|
ty::ty_fn(_, args, _, _, _) | ty::ty_native_fn(_, args, _) {
|
|
let modes = [];
|
|
for arg: ty::arg in args { modes += [arg.mode]; }
|
|
ret modes;
|
|
}
|
|
_ {
|
|
// 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] {
|
|
fn mode_to_op(m: &ty::mode) -> init_op {
|
|
alt m { ty::mo_move. { init_move } _ { init_assign } }
|
|
}
|
|
vec::map(mode_to_op, callee_modes(fcx, callee))
|
|
}
|
|
|
|
fn anon_bindings(ops: &[init_op], es: &[@expr]) -> [binding] {
|
|
let bindings: [binding] = [];
|
|
let i = 0;
|
|
for op: init_op in ops {
|
|
bindings += [{lhs: [], rhs: some({op: op, expr: es[i]})}];
|
|
i += 1;
|
|
}
|
|
ret bindings;
|
|
}
|
|
|
|
//
|
|
// 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:
|
|
//
|