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::(idents) == 0u { ret; } log "an ident: " + vec::pop::(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; // The attached node ID is the *defining* node ID // for this local. type constr_arg_use = spanned>; 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; type norm_constraint = {bit_num: uint, c: sp_constr}; type constr_map = @std::map::hashmap; /* 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; 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 { if i as uint < vec::len(*ccx.node_anns) { ret some::(ccx.node_anns[i]); } else { ret none::; } } /********* 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::()}; } /* 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 { ret ccx.tcx.def_map.find(id); } fn node_id_to_def_upvar(cx: &fn_ctxt, id: node_id) -> option::t { 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_], 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_], descs: &[pred_args]) -> option::t { 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 { 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_]) -> str { let rslt = "<"; for i: constr_arg_general_ 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_] { let rslt: [constr_arg_general_] = []; 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_ 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 { fcx.ccx.tcx.def_map.find(i) } fn local_node_id_to_def_id(fcx: &fn_ctxt, i: &node_id) -> option::t { 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 { 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(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] 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(_f: &_fn, _tp: &[ty_param], _sp: &span, _i: &fn_ident, _iid: node_id, _cx: &T, _v: &visit::vt) { } 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}; 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: //