rust/src/comp/middle/tstate/auxiliary.rs
Michael Sullivan 9ca0ce91bf Initial implementation of typestate for closures.
There are still things not handled properly: relying on other preconditions
of upvars is likely to cause bad things to happen. We probably want to
disallow it.
2011-07-26 12:30:14 -07:00

1100 lines
32 KiB
Rust

import std::ivec;
import std::int::str;
import std::str;
import std::option;
import std::option::*;
import std::int;
import std::uint;
import syntax::ast::*;
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(def_id d) -> str {
ret int::str(d.crate) + "," + int::str(d.node);
}
fn comma_str(&(@constr_arg_use)[] args) -> str {
auto rslt = "";
auto comma = false;
for (@constr_arg_use a in args) {
if (comma) { rslt += ", "; } else { comma = true; }
alt (a.node) {
case (carg_base) { rslt += "*"; }
case (carg_ident(?i)) { rslt += i.ident; }
case (carg_lit(?l)) { rslt += lit_to_str(l); }
}
}
ret rslt;
}
fn constraint_to_str(&ty::ctxt tcx, &sp_constr c) -> str {
alt (c.node) {
case (ninit(_,?i)) {
ret "init(" + i + " [" + tcx.sess.span_str(c.span) + "])";
}
case (npred(?p, _, ?args)) {
ret path_to_str(p) + "(" + comma_str(args) + ")" + "[" +
tcx.sess.span_str(c.span) + "]";
}
}
}
fn tritv_to_str(fn_ctxt fcx, &tritv::t v) -> str {
auto s = "";
auto comma = false;
for (norm_constraint p in constraints(fcx)) {
alt (tritv_get(v, p.bit_num)) {
case (dont_care) { }
case (?t) {
s +=
if (comma) { ", " } else { comma = true; "" } +
if (t == tfalse) { "!" } else { "" } +
constraint_to_str(fcx.ccx.tcx, p.c);
}
}
}
ret s;
}
fn log_tritv(&fn_ctxt fcx, &tritv::t v) { log tritv_to_str(fcx, v); }
fn first_difference_string(&fn_ctxt fcx, &tritv::t expected, &tritv::t actual)
-> str {
let str s = "";
for (norm_constraint c 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(fn_ctxt fcx, tritv::t v) { log_err tritv_to_str(fcx, v); }
fn tos(&uint[] v) -> str {
auto rslt = "";
for (uint i in v) { if (i == 0u) { rslt += "0"; }
else if (i == 1u) { rslt += "1"; }
else { rslt += "?"; } }
ret rslt;
}
fn log_cond(&uint[] v) { log tos(v); }
fn log_cond_err(&uint[] v) { log_err tos(v); }
fn log_pp(&pre_and_post pp) {
auto p1 = tritv::to_vec(pp.precondition);
auto p2 = tritv::to_vec(pp.postcondition);
log "pre:";
log_cond(p1);
log "post:";
log_cond(p2);
}
fn log_pp_err(&pre_and_post pp) {
auto p1 = tritv::to_vec(pp.precondition);
auto p2 = tritv::to_vec(pp.postcondition);
log_err "pre:";
log_cond_err(p1);
log_err "post:";
log_cond_err(p2);
}
fn log_states(&pre_and_post_state pp) {
auto p1 = tritv::to_vec(pp.prestate);
auto p2 = tritv::to_vec(pp.poststate);
log "prestate:";
log_cond(p1);
log "poststate:";
log_cond(p2);
}
fn log_states_err(&pre_and_post_state pp) {
auto p1 = tritv::to_vec(pp.prestate);
auto p2 = tritv::to_vec(pp.poststate);
log_err "prestate:";
log_cond_err(p1);
log_err "poststate:";
log_cond_err(p2);
}
fn print_ident(&ident i) { log " " + i + " "; }
fn print_idents(&mutable ident[] idents) {
if (ivec::len[ident](idents) == 0u) { ret; }
log "an ident: " + ivec::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_ = rec((@constr_arg_use)[] args, uint bit_num);
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 = rec(uint bit_num, sp_constr c);
type constr_map = @std::map::hashmap[def_id, constraint];
type fn_info = rec(constr_map constrs,
uint num_constraints,
controlflow cf,
/* list, accumulated during pre/postcondition
computation, of all local variables that may be
used*/
// Doesn't seem to work without the @ --
// bug?
@mutable node_id[] used_vars);
fn tsconstr_to_def_id(&tsconstr t) -> def_id {
alt (t) {
case (ninit(?id,_)) { local_def(id) }
case (npred(_,?id,_)) { id }
}
}
/* mapping from node ID to typestate annotation */
type node_ann_table = @mutable ts_ann[mutable];
/* mapping from function name to fn_info map */
type fn_info_map = @std::map::hashmap[node_id, fn_info];
type fn_ctxt = rec(fn_info enclosing, node_id id, ident name, crate_ctxt ccx);
type crate_ctxt = rec(ty::ctxt tcx, node_ann_table node_anns, fn_info_map fm);
fn get_fn_info(&crate_ctxt ccx, node_id id) -> fn_info {
assert (ccx.fm.contains_key(id));
ret ccx.fm.get(id);
}
fn add_node(&crate_ctxt ccx, node_id i, &ts_ann a) {
auto sz = ivec::len(*ccx.node_anns);
if (sz <= i as uint) {
ivec::grow_mut(*ccx.node_anns, (i as uint) - sz + 1u, empty_ann(0u));
}
ccx.node_anns.(i) = a;
}
fn get_ts_ann(&crate_ctxt ccx, node_id i) -> option::t[ts_ann] {
if (i as uint < ivec::len(*ccx.node_anns)) {
ret some[ts_ann](ccx.node_anns.(i));
} else { ret none[ts_ann]; }
}
/********* utils ********/
fn node_id_to_ts_ann(&crate_ctxt ccx, node_id id) -> ts_ann {
alt (get_ts_ann(ccx, id)) {
case (none) {
log_err "node_id_to_ts_ann: no ts_ann for node_id " +
int::str(id);
fail;
}
case (some(?t)) { ret t; }
}
}
fn node_id_to_poststate(&crate_ctxt ccx, node_id id) -> poststate {
log "node_id_to_poststate";
ret node_id_to_ts_ann(ccx, id).states.poststate;
}
fn stmt_to_ann(&crate_ctxt ccx, &stmt s) -> ts_ann {
log "stmt_to_ann";
alt (s.node) {
case (stmt_decl(_, ?id)) { ret node_id_to_ts_ann(ccx, id); }
case (stmt_expr(_, ?id)) { ret node_id_to_ts_ann(ccx, id); }
case (stmt_crate_directive(_)) {
log_err "expecting an annotated statement here";
fail;
}
}
}
/* fails if e has no annotation */
fn expr_states(&crate_ctxt ccx, @expr e) -> 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(&crate_ctxt ccx, @expr e) -> pre_and_post {
log "expr_pp";
ret node_id_to_ts_ann(ccx, e.id).conditions;
}
fn stmt_pp(&crate_ctxt ccx, &stmt s) -> pre_and_post {
ret stmt_to_ann(ccx, s).conditions;
}
/* fails if b has no annotation */
fn block_pp(&crate_ctxt ccx, &blk b) -> pre_and_post {
log "block_pp";
ret node_id_to_ts_ann(ccx, b.node.id).conditions;
}
fn clear_pp(pre_and_post pp) {
ann::clear(pp.precondition);
ann::clear(pp.postcondition);
}
fn clear_precond(&crate_ctxt ccx, node_id id) {
auto pp = node_id_to_ts_ann(ccx, id);
ann::clear(pp.conditions.precondition);
}
fn block_states(&crate_ctxt ccx, &blk b) -> pre_and_post_state {
log "block_states";
ret node_id_to_ts_ann(ccx, b.node.id).states;
}
fn stmt_states(&crate_ctxt ccx, &stmt s) -> pre_and_post_state {
ret stmt_to_ann(ccx, s).states;
}
fn expr_precond(&crate_ctxt ccx, @expr e) -> precond {
ret expr_pp(ccx, e).precondition;
}
fn expr_postcond(&crate_ctxt ccx, @expr e) -> postcond {
ret expr_pp(ccx, e).postcondition;
}
fn expr_prestate(&crate_ctxt ccx, @expr e) -> prestate {
ret expr_states(ccx, e).prestate;
}
fn expr_poststate(&crate_ctxt ccx, @expr e) -> poststate {
ret expr_states(ccx, e).poststate;
}
fn stmt_precond(&crate_ctxt ccx, &stmt s) -> precond {
ret stmt_pp(ccx, s).precondition;
}
fn stmt_postcond(&crate_ctxt ccx, &stmt s) -> postcond {
ret stmt_pp(ccx, s).postcondition;
}
fn states_to_poststate(&pre_and_post_state ss) -> poststate {
ret ss.poststate;
}
fn stmt_prestate(&crate_ctxt ccx, &stmt s) -> prestate {
ret stmt_states(ccx, s).prestate;
}
fn stmt_poststate(&crate_ctxt ccx, &stmt s) -> poststate {
ret stmt_states(ccx, s).poststate;
}
fn block_precond(&crate_ctxt ccx, &blk b) -> precond {
ret block_pp(ccx, b).precondition;
}
fn block_postcond(&crate_ctxt ccx, &blk b) -> postcond {
ret block_pp(ccx, b).postcondition;
}
fn block_prestate(&crate_ctxt ccx, &blk b) -> prestate {
ret block_states(ccx, b).prestate;
}
fn block_poststate(&crate_ctxt ccx, &blk b) -> poststate {
ret block_states(ccx, b).poststate;
}
fn set_prestate_ann(&crate_ctxt ccx, node_id id, &prestate pre) -> bool {
log "set_prestate_ann";
ret set_prestate(node_id_to_ts_ann(ccx, id), pre);
}
fn extend_prestate_ann(&crate_ctxt ccx, node_id id, &prestate pre) -> bool {
log "extend_prestate_ann";
ret extend_prestate(node_id_to_ts_ann(ccx, id).states.prestate, pre);
}
fn set_poststate_ann(&crate_ctxt ccx, node_id id, &poststate post) -> bool {
log "set_poststate_ann";
ret set_poststate(node_id_to_ts_ann(ccx, id), post);
}
fn extend_poststate_ann(&crate_ctxt ccx, node_id id, &poststate post)
-> bool {
log "extend_poststate_ann";
ret extend_poststate(node_id_to_ts_ann(ccx, id).states.poststate, post);
}
fn set_pre_and_post(&crate_ctxt ccx, node_id id, &precond pre,
&postcond post) {
log "set_pre_and_post";
auto t = node_id_to_ts_ann(ccx, id);
set_precondition(t, pre);
set_postcondition(t, post);
}
fn copy_pre_post(&crate_ctxt ccx, node_id id, &@expr sub) {
log "set_pre_and_post";
auto p = expr_pp(ccx, sub);
copy_pre_post_(ccx, id, p.precondition, p.postcondition);
}
fn copy_pre_post_(&crate_ctxt ccx, node_id id, &prestate pre,
&poststate post) {
log "set_pre_and_post";
auto 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(&crate_ctxt ccx, node_id id) {
auto p = node_id_to_ts_ann(ccx, id);
ann::set(p.conditions.postcondition);
}
fn pure_exp(&crate_ctxt ccx, node_id id, &prestate p) -> bool {
ret set_prestate_ann(ccx, id, p) |
set_poststate_ann(ccx, id, p);
}
fn num_constraints(fn_info m) -> uint { ret m.num_constraints; }
fn new_crate_ctxt(ty::ctxt cx) -> crate_ctxt {
let ts_ann[mutable] na = ~[mutable];
ret rec(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(&crate_ctxt ccx, @expr e) -> controlflow {
alt (ty::struct(ccx.tcx, ty::node_id_to_type(ccx.tcx, e.id))) {
case (ty::ty_fn(_, _, _, ?cf, _)) { ret cf; }
case (_) { ret return; }
}
}
fn constraints_expr(&ty::ctxt cx, @expr e) -> (@ty::constr)[] {
alt (ty::struct(cx, ty::node_id_to_type(cx, e.id))) {
case (ty::ty_fn(_, _, _, _, ?cs)) { ret cs; }
case (_) { ret ~[]; }
}
}
fn node_id_to_def_upvar_strict(&fn_ctxt cx, node_id id) -> def {
alt (freevars::def_lookup(cx.ccx.tcx, cx.id, id)) {
case (none) {
log_err "node_id_to_def: node_id " + int::str(id) + " has no def";
fail;
}
case (some(?d)) { ret d; }
}
}
fn node_id_to_def_strict(&ty::ctxt cx, node_id id) -> def {
alt (cx.def_map.find(id)) {
case (none) {
log_err "node_id_to_def: node_id " + int::str(id) + " has no def";
fail;
}
case (some(?d)) { ret d; }
}
}
fn node_id_to_def(&crate_ctxt ccx, node_id id) -> option::t[def] {
ret ccx.tcx.def_map.find(id);
}
fn norm_a_constraint(def_id id, &constraint c) -> norm_constraint[] {
alt (c) {
case (cinit(?n, ?sp, ?i)) {
ret ~[rec(bit_num=n, c=respan(sp, ninit(id.node, i)))];
}
case (cpred(?p, ?descs)) {
let norm_constraint[] rslt = ~[];
for (pred_args pd in *descs) {
rslt += ~[rec(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(&fn_ctxt fcx) -> norm_constraint[] {
let norm_constraint[] rslt = ~[];
for each (@rec(def_id key, constraint val) p
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(&fn_ctxt fcx, &(@mutable pred_args[]) occs,
&(@constr_arg_use)[] occ) ->
uint {
log "match_args: looking at " +
constr_args_to_str(fn(&inst i) -> str { ret i.ident; }, occ);
for (pred_args pd in *occs) {
log "match_args: candidate " + pred_args_to_str(pd);
fn eq(&inst p, &inst q) -> 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(ty::ctxt tcx, node_id t) -> def_id {
alt (tcx.def_map.find(t)) {
case (none) {
tcx.sess.bug("node_id_for_constr: bad node_id " + int::str(t));
}
case (some(def_fn(?i,_))) { ret i; }
case (_) {
tcx.sess.bug("node_id_for_constr: pred is not a function");
}
}
}
fn expr_to_constr_arg(ty::ctxt tcx, &@expr e) -> @constr_arg_use {
alt (e.node) {
case (expr_path(?p)) {
alt (tcx.def_map.find(e.id)) {
case (some(def_local(?l_id))) {
ret @respan(p.span,
carg_ident(rec(ident=p.node.idents.(0),
node=l_id.node)));
}
case (some(def_arg(?a_id))) {
ret @respan(p.span,
carg_ident(rec(ident=p.node.idents.(0),
node=a_id.node)));
}
case (_) {
tcx.sess.bug("exprs_to_constr_args: non-local variable " +
"as pred arg");
}
}
}
case (expr_lit(?l)) { ret @respan(e.span, carg_lit(l)); }
case (_) {
tcx.sess.span_fatal(e.span,
"Arguments to constrained functions must be "
+ "literals or local variables");
}
}
}
fn exprs_to_constr_args(ty::ctxt tcx, &(@expr)[] args)
-> (@constr_arg_use)[] {
auto f = bind expr_to_constr_arg(tcx, _);
let (@constr_arg_use)[] rslt = ~[];
for (@expr e in args) {
rslt += ~[f(e)];
}
rslt
}
fn expr_to_constr(ty::ctxt tcx, &@expr e) -> sp_constr {
alt (e.node) {
case (
// FIXME change the first pattern to expr_path to test a
// typechecker bug
expr_call(?operator, ?args)) {
alt (operator.node) {
case (expr_path(?p)) {
ret respan(e.span,
npred(p, def_id_for_constr(tcx, operator.id),
exprs_to_constr_args(tcx, args)));
}
case (_) {
tcx.sess.span_fatal(operator.span,
"Internal error: " +
" ill-formed operator \
in predicate");
}
}
}
case (_) {
tcx.sess.span_fatal(e.span,
"Internal error: " + " ill-formed predicate");
}
}
}
fn pred_args_to_str(&pred_args p) -> str {
"<" + uint::str(p.node.bit_num) + ", " +
constr_args_to_str(fn(&inst i) -> str { ret i.ident; },
p.node.args) + ">"
}
fn substitute_constr_args(&ty::ctxt cx, &(@expr)[] actuals,
&@ty::constr c) -> tsconstr {
let (@constr_arg_use)[] rslt = ~[];
for (@constr_arg a in c.node.args) {
rslt += ~[substitute_arg(cx, actuals, a)];
}
ret npred(c.node.path, c.node.id, rslt);
}
fn substitute_arg(&ty::ctxt cx, &(@expr)[] actuals, @constr_arg a) ->
@constr_arg_use {
auto num_actuals = ivec::len(actuals);
alt (a.node) {
case (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");
}
}
case (carg_base) { ret @respan(a.span, carg_base); }
case (carg_lit(?l)) { ret @respan(a.span, carg_lit(l)); }
}
}
fn pred_args_matches(&(constr_arg_general_[inst])[] pattern,
&pred_args desc) -> bool {
auto i = 0u;
for (@constr_arg_use c in desc.node.args) {
auto n = pattern.(i);
alt (c.node) {
case (carg_ident(?p)) {
alt (n) {
case (carg_ident(?q)) {
if (p.node != q.node) {
ret false;
}
}
case (_) { ret false; }
}
}
case (carg_base) {
if (n != carg_base) {
ret false;
}
}
case (carg_lit(?l)) {
alt (n) {
case (carg_lit(?m)) {
if (!lit_eq(l, m)) {
ret false;
}
}
case (_) { ret false; }
}
}
}
i += 1u;
}
ret true;
}
fn find_instance_(&(constr_arg_general_[inst])[] pattern,
&pred_args[] descs) -> option::t[uint] {
for (pred_args d in descs) {
if (pred_args_matches(pattern, d)) {
ret some(d.node.bit_num);
}
}
ret none;
}
type inst = rec(ident ident, node_id node);
type subst = rec(inst from, inst to)[];
fn find_instances(&fn_ctxt fcx, &subst subst, &constraint c)
-> (rec(uint from, uint to))[] {
auto rslt = ~[];
if (ivec::len(subst) == 0u) {
ret rslt;
}
alt (c) {
case (cinit(_,_,_)) { /* this is dealt with separately */ }
case (cpred(?p, ?descs)) {
for (pred_args d in *descs) {
if (args_mention(d.node.args, find_in_subst_bool, subst)) {
auto old_bit_num = d.node.bit_num;
auto new = replace(subst, d);
alt (find_instance_(new, *descs)) {
case (some(?d1)) {
rslt += ~[rec(from=old_bit_num, to=d1)];
}
case (_) { }
}
}
}
}
}
rslt
}
fn find_in_subst(node_id id, &subst s) -> option::t[inst] {
for (rec(inst from, inst to) p in s) {
if (id == p.from.node) {
ret some(p.to);
}
}
ret none;
}
fn find_in_subst_bool(&subst s, node_id id) -> bool {
is_some(find_in_subst(id, s))
}
fn insts_to_str(&(constr_arg_general_[inst])[] stuff) -> str {
auto rslt = "<";
for (constr_arg_general_[inst] i in stuff) {
rslt += " " + alt(i) {
case (carg_ident(?p)) { p.ident }
case (carg_base) { "*" }
case (carg_lit(_)) { "[lit]" } } + " ";
}
rslt += ">";
rslt
}
fn replace(subst subst, pred_args d) -> (constr_arg_general_[inst])[] {
let (constr_arg_general_[inst])[] rslt = ~[];
for (@constr_arg_use c in d.node.args) {
alt (c.node) {
case (carg_ident(?p)) {
alt (find_in_subst(p.node, subst)) {
case (some(?new)) {
rslt += ~[carg_ident(new)];
}
case (_) {
rslt += ~[c.node];
}
}
}
case (_) {
// 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(&ty::ctxt cx, &path p) -> ident {
alt (ivec::last(p.node.idents)) {
case (none) { cx.sess.span_fatal(p.span, "Malformed path"); }
case (some(?i)) { ret i; }
}
}
tag if_ty {
if_check;
plain_if;
}
fn local_node_id_to_def_id_strict(&fn_ctxt fcx, &span sp, &node_id i)
-> def_id {
alt (local_node_id_to_def(fcx, i)) {
case (some(def_local(?d_id))) {
ret d_id;
}
case (some (def_arg(?a_id))) {
ret a_id;
}
case (some(_)) {
fcx.ccx.tcx.sess.span_fatal(sp, "local_node_id_to_def_id: id \
isn't a local");
}
case (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(&fn_ctxt fcx, &node_id i) -> option::t[def]
{ fcx.ccx.tcx.def_map.find(i) }
fn local_node_id_to_def_id(&fn_ctxt fcx, &node_id i) -> option::t[def_id] {
alt (local_node_id_to_def(fcx, i)) {
case (some(def_local(?d_id))) { some(d_id) }
case (some (def_arg(?a_id))) { some(a_id) }
case (_) { none }
}
}
fn local_node_id_to_local_def_id(&fn_ctxt fcx, &node_id i)
-> option::t[node_id] {
alt (local_node_id_to_def(fcx, i)) {
case (some (def_local(?d_id))) { some(d_id.node) }
case (some (def_arg(?a_id))) { some(a_id.node) }
case (_) { none }
}
}
fn copy_in_postcond(&fn_ctxt fcx, node_id parent_exp, inst dest, inst src,
oper_type ty) {
auto 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(&fn_ctxt fcx, &poststate post, inst dest, inst src,
oper_type ty) {
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(&fn_ctxt fcx, &poststate src_post,
&poststate target_post, inst dest, inst src,
oper_type ty) {
auto subst;
alt (ty) {
case (oper_swap) {
subst = ~[rec(from=dest, to=src),
rec(from=src, to=dest)];
}
case (oper_assign_op) {
ret; // Don't do any propagation
}
case (_) {
subst = ~[rec(from=src, to=dest)];
}
}
for each (@rec(def_id key, constraint val) p in
fcx.enclosing.constrs.items()) {
// replace any occurrences of the src def_id with the
// dest def_id
auto insts = find_instances(fcx, subst, p.val);
for (rec(uint from, uint to) p in insts) {
if (promises_(p.from, src_post)) {
set_in_poststate_(p.to, target_post);
}
}
}
}
/* FIXME should refactor this better */
fn forget_in_postcond(&fn_ctxt fcx, node_id parent_exp, node_id dead_v) {
// In the postcondition given by parent_exp, clear the bits
// for any constraints mentioning dead_v
auto d = local_node_id_to_local_def_id(fcx, dead_v);
alt (d) {
case (some(?d_id)) {
for (norm_constraint c 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);
}
}
}
case (_) {}
}
}
fn forget_in_postcond_still_init(&fn_ctxt fcx, node_id parent_exp,
node_id dead_v) {
// In the postcondition given by parent_exp, clear the bits
// for any constraints mentioning dead_v
auto d = local_node_id_to_local_def_id(fcx, dead_v);
alt (d) {
case (some(?d_id)) {
for (norm_constraint c 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);
}
}
}
case (_) { }
}
}
fn forget_in_poststate(&fn_ctxt fcx, &poststate p, node_id dead_v) -> bool {
// In the poststate given by parent_exp, clear the bits
// for any constraints mentioning dead_v
auto d = local_node_id_to_local_def_id(fcx, dead_v);
auto changed = false;
alt (d) {
case (some(?d_id)) {
for (norm_constraint c in constraints(fcx)) {
if (constraint_mentions(fcx, c, d_id)) {
changed |= clear_in_poststate_(c.bit_num, p);
}
}
}
case (_) {}
}
ret changed;
}
fn forget_in_poststate_still_init(&fn_ctxt fcx, &poststate p, node_id dead_v)
-> bool {
// In the poststate given by parent_exp, clear the bits
// for any constraints mentioning dead_v
auto d = local_node_id_to_local_def_id(fcx, dead_v);
auto changed = false;
alt (d) {
case (some(?d_id)) {
for (norm_constraint c in constraints(fcx)) {
if (non_init_constraint_mentions(fcx, c, d_id)) {
changed |= clear_in_poststate_(c.bit_num, p);
}
}
}
case (_) {}
}
ret changed;
}
fn any_eq(&(node_id)[] v, node_id d) -> bool {
for (node_id i in v) {
if (i == d) { ret true; }
}
false
}
fn constraint_mentions(&fn_ctxt fcx, &norm_constraint c, node_id v) -> bool {
ret (alt (c.c.node) {
case (ninit(?id,_)) { v == id }
case (npred(_, _, ?args)) {
args_mention(args, any_eq, ~[v])
}
});
}
fn non_init_constraint_mentions(&fn_ctxt fcx, &norm_constraint c,
&node_id v) -> bool {
ret (alt (c.c.node) {
case (ninit(_,_)) {
false
}
case (npred(_, _, ?args)) {
args_mention(args, any_eq, ~[v])
}
});
}
fn args_mention[T](&(@constr_arg_use)[] args, fn(&(T)[], node_id) -> bool q,
&(T)[] s) -> 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 ivec::any(bind mentions(s,q,_), args);
*/
for (@constr_arg_use a in args) {
alt (a.node) {
case (carg_ident(?p1)) {
if (q(s, p1.node)) {
ret true;
}
}
case (_) {}
}
}
ret false;
}
fn use_var(&fn_ctxt fcx, &node_id v) {
*fcx.enclosing.used_vars += ~[v];
}
// FIXME: This should be a function in std::ivec::.
fn vec_contains(&@mutable (node_id[]) v, &node_id i) -> bool {
for (node_id d in *v) {
if (d == i) { ret true; }
}
ret false;
}
fn op_to_oper_ty(init_op io) -> oper_type {
alt (io) {
case (init_move) { oper_move }
case (_) { oper_assign }
}
}
// default function visitor
fn do_nothing[T](&_fn f, &ty_param[] tp, &span sp, &fn_ident i,
node_id iid, &T cx, &visit::vt[T] v) {
}
fn args_to_constr_args(&span sp, &arg[] args) -> (@constr_arg_use)[] {
let (@constr_arg_use)[] actuals = ~[];
for (arg a in args) {
actuals += ~[@respan(sp, carg_ident(rec(ident=a.ident, node=a.id)))];
}
ret actuals;
}
fn ast_constr_to_ts_constr(&ty::ctxt tcx, &arg[] args, &@constr c)
-> tsconstr {
auto 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(&ty::ctxt tcx, &arg[] args, &@constr c)
-> sp_constr {
auto tconstr = ast_constr_to_ts_constr(tcx, args, c);
ret respan(c.span, tconstr);
}
//
// 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:
//