import option::*; import pat_util::*; import syntax::ast::*; import syntax::ast_util::*; import syntax::{visit, codemap}; import codemap::span; import std::map::{hashmap, int_hash}; import syntax::print::pprust::path_to_str; import 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_}; import driver::session::session; import dvec::{dvec, extensions}; import tritv::{dont_care, tfalse, tritv_get, ttrue}; import 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 { ret 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; } 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 { ret #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| { alt tritv_get(v, p.bit_num) { dont_care { } tt { s += if comma { ", " } else { comma = true; "" } + if tt == tfalse { "!" } else { "" } + constraint_to_str(fcx.ccx.tcx, p.c); } } } ret 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 tritv_get(expected, c.bit_num) == ttrue && tritv_get(actual, c.bit_num) != ttrue { s = constraint_to_str(fcx.ccx.tcx, c.c); break; } } ret 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 += "?"; } } ret 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 = tritv::to_vec(pp.precondition); let p2 = tritv::to_vec(pp.postcondition); #debug("pre:"); log_cond(p1); #debug("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); #error("pre:"); log_cond_err(p1); #error("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); #debug("prestate:"); log_cond(p1); #debug("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); #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::(idents) == 0u { ret; } log(debug, "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. 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; // The attached node ID is the *defining* node ID // for this local. type constr_arg_use = spanned>; /* Predicate constraints refer to the truth value of a predicate on variables (definitely false, maybe true, or definitely true). The field (and the 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 }; type tsconstr = { path: @path, def_id: def_id, args: ~[@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. 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; 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(*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 { 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 { #error("node_id_to_ts_ann: no ts_ann for node_id %d", id); fail; } some(tt) { ret tt; } } } fn node_id_to_poststate(ccx: crate_ctxt, id: node_id) -> poststate { #debug("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 { #debug("stmt_to_ann"); alt s.node { stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) { ret 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"); 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 { #debug("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 { #debug("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 { #debug("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 { #debug("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 { #debug("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 { #debug("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 { #debug("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) { #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 { 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: ~[mut ts_ann] = ~[mut]; ret {tcx: cx, node_anns: @mut na, fm: 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) -> ret_style { alt ty::get(ty::node_id_to_type(ccx.tcx, e.id)).struct { ty::ty_fn(f) { ret f.ret_style; } _ { ret return_val; } } } fn constraints_expr(cx: ty::ctxt, e: @expr) -> ~[@ty::constr] { alt ty::get(ty::node_id_to_type(cx, e.id)).struct { ty::ty_fn(f) { ret f.constraints; } _ { ret ~[]; } } } fn node_id_to_def_strict(cx: ty::ctxt, id: node_id) -> def { alt cx.def_map.find(id) { none { #error("node_id_to_def: node_id %d has no def", id); fail; } some(d) { ret d; } } } fn node_id_to_def(ccx: crate_ctxt, id: node_id) -> option { ret 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})}); } 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 mut rslt: ~[norm_constraint] = ~[]; for fcx.enclosing.constrs.each |key, val| { vec::push_all(rslt, norm_a_constraint(key, val)); }; ret 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, occ: ~[@constr_arg_use]) -> uint { #debug("match_args: looking at %s", constr_args_to_str(fn@(i: inst) -> str { ret *i.ident; }, occ)); for (*occs).each |pd| { log(debug, "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(nid, _)) | some(def_arg(nid, _)) | some(def_binding(nid)) | some(def_upvar(nid, _, _)) { ret @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) { 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 = |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 { alt e.node { expr_call(operator, args, _) { alt operator.node { expr_path(p) { ret 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 { ret *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)); } ret {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); 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 mut i = 0u; for desc.node.args.each |c| { 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 !const_eval::lit_eq(l, m) { ret false; } } _ { ret false; } } } } i += 1u; } ret true; } fn find_instance_(pattern: ~[constr_arg_general_], descs: ~[pred_args]) -> option { for descs.each |d| { if pred_args_matches(pattern, d) { ret some(d.node.bit_num); } } ret 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 { ret ~[]; } 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); alt find_instance_(newv, v) { some(d1) {vec::push(res, {from: old_bit_num, to: d1})} _ {} } } else {} } vec::to_mut(v) } ret res; } fn find_in_subst(id: node_id, s: subst) -> option { for s.each |p| { 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 mut rslt = "<"; for stuff.each |i| { 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 mut rslt: ~[constr_arg_general_] = ~[]; for d.node.args.each |c| { alt c.node { carg_ident(p) { alt find_in_subst(p.node, subst) { some(newv) { vec::push(rslt, carg_ident(newv)); } _ { vec::push(rslt, c.node); } } } _ { vec::push(rslt, c.node); } } } ret 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 { alt local_node_id_to_def(fcx, i) { some(def_local(nid, _)) | some(def_arg(nid, _)) | some(def_upvar(nid, _, _)) { ret 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 { fcx.ccx.tcx.def_map.find(i) } fn local_node_id_to_def_id(fcx: fn_ctxt, i: node_id) -> option { alt 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 { alt 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; 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 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); } } ret changed; } fn any_eq(v: ~[node_id], d: node_id) -> bool { for v.each |i| { if i == d { ret true; } } false } fn constraint_mentions(_fcx: fn_ctxt, c: norm_constraint, v: node_id) -> bool { ret args_mention(c.c.node.args, any_eq, ~[v]); } fn args_mention(args: ~[@constr_arg_use], q: fn(~[T], node_id) -> bool, s: ~[T]) -> bool { for args.each |a| { alt a.node { carg_ident(p1) { if q(s, p1.node) { ret true; } } _ { } } } ret 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 { alt io { init_move { oper_move } _ { oper_assign } } } // default function visitor fn do_nothing(_fk: visit::fn_kind, _decl: fn_decl, _body: blk, _sp: span, _id: node_id, _t: T, _v: visit::vt) { } fn args_to_constr_args(tcx: ty::ctxt, args: ~[arg], indices: ~[@sp_constr_arg]) -> ~[@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, alt 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) } })); } 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 {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); ret respan(c.span, tconstr); } type binding = {lhs: ~[dest], rhs: option}; fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding { let mut lhs = ~[]; do pat_bindings(tcx.def_map, loc.node.pat) |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)); } ret 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)); alt ty::get(ty).struct { ty::ty_fn({inputs: args, _}) { let mut modes = ~[]; for args.each |arg| { vec::push(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] { do vec::map(callee_modes(fcx, callee)) |m| { alt 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; } ret bindings; } // // Local Variables: // mode: rust // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // End: //