use std::collections::hash_map::{Entry, HashMap};
use std::collections::HashSet;
use std::collections::VecDeque;
use log::Level;
use rustc::hir::def_id::DefId;
use super::constraint::{ConstraintSet, Perm};
use super::context::Ctxt;
use super::Var;
struct WorkList {
queue: VecDeque<DefId>,
in_queue: HashSet<DefId>,
}
impl WorkList {
fn new() -> WorkList {
WorkList {
queue: VecDeque::new(),
in_queue: HashSet::new(),
}
}
fn push(&mut self, id: DefId) {
if !self.in_queue.contains(&id) {
self.queue.push_back(id);
self.in_queue.insert(id);
}
}
fn pop(&mut self) -> Option<DefId> {
let r = self.queue.pop_front();
if let Some(id) = r {
self.in_queue.remove(&id);
}
r
}
}
pub struct InterCtxt<'c, 'lty, 'tcx> {
cx: &'c mut Ctxt<'lty, 'tcx>,
complete_cset: HashMap<DefId, ConstraintSet<'lty>>,
work_list: WorkList,
rev_deps: HashMap<DefId, HashSet<DefId>>,
static_rev_deps: HashMap<Var, HashSet<DefId>>,
}
impl<'c, 'lty, 'tcx> InterCtxt<'c, 'lty, 'tcx> {
pub fn new(cx: &'c mut Ctxt<'lty, 'tcx>) -> InterCtxt<'c, 'lty, 'tcx> {
InterCtxt {
cx,
complete_cset: HashMap::new(),
work_list: WorkList::new(),
rev_deps: HashMap::new(),
static_rev_deps: HashMap::new(),
}
}
fn compute_one_cset(&mut self, def_id: DefId) -> ConstraintSet<'lty> {
let dummy_cset = ConstraintSet::new();
let mut cset = {
let (func, var) = self.cx.first_variant_summ(def_id);
if func.cset_provided {
return func.sig_cset.clone();
}
var.inst_cset.clone()
};
let mut used_statics = HashSet::new();
cset.for_each_perm(|p| if let Perm::StaticVar(v) = p {
used_statics.insert(v);
});
for &v in &used_statics {
debug!(" import static: {:?} = {:?}", v, self.cx.static_assign[v]);
cset.add(Perm::Concrete(self.cx.static_assign[v]), Perm::StaticVar(v));
self.static_rev_deps
.entry(v)
.or_insert_with(HashSet::new)
.insert(def_id);
}
let arena = self.cx.arena;
for inst in &self.cx.first_variant_summ(def_id).1.insts {
let complete = self.complete_cset.get(&inst.callee).unwrap_or(&dummy_cset);
debug!(
" instantiate {:?} for vars {}..",
inst.callee, inst.first_inst_var
);
cset.import_substituted(complete, arena, |p| match p {
Perm::SigVar(v) => Perm::InstVar(Var(v.0 + inst.first_inst_var)),
p => p,
});
self.rev_deps
.entry(inst.callee)
.or_insert_with(HashSet::new)
.insert(def_id);
}
debug!(" original constraints:");
if log_enabled!(Level::Debug) {
for &(a, b) in cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
cset.remove_useless();
cset.simplify_min_lhs(self.cx.arena);
cset.retain_perms(self.cx.arena, |p| match p {
Perm::LocalVar(_) | Perm::InstVar(_) => false,
_ => true,
});
debug!(" simplified constraints:");
if log_enabled!(Level::Debug) {
for &(a, b) in cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
for &v in &used_statics {
let old = self.cx.static_assign[v];
let new = cset.lower_bound(Perm::StaticVar(v));
debug!(" static {:?}: {:?} -> {:?}", v, old, new);
if new > old {
self.cx.static_assign[v] = new;
if let Some(rev_deps) = self.static_rev_deps.get(&v) {
for &id in rev_deps {
self.work_list.push(id);
}
}
}
}
cset.retain_perms(self.cx.arena, |p| match p {
Perm::LocalVar(_) | Perm::InstVar(_) | Perm::StaticVar(_) => false,
_ => true,
});
cset.simplify(self.cx.arena);
cset
}
fn process_one(&mut self, def_id: DefId) {
let cset = self.compute_one_cset(def_id);
debug!("save cset for {:?}", def_id);
if log_enabled!(Level::Debug) {
for &(a, b) in cset.iter() {
debug!(" {:?} <= {:?}", a, b);
}
}
let did_update = match self.complete_cset.entry(def_id) {
Entry::Vacant(e) => {
e.insert(cset);
true
}
Entry::Occupied(mut e) => {
if e.get() != &cset {
*e.get_mut() = cset;
true
} else {
false
}
}
};
if did_update {
if let Some(rev_deps) = self.rev_deps.get(&def_id) {
for &id in rev_deps {
self.work_list.push(id);
}
}
}
}
pub fn process(&mut self) {
let mut idx = 0;
let ids = self.cx.func_ids().collect::<Vec<_>>();
debug!("\ninterprocedural analysis: process {} fns", ids.len());
for id in ids {
debug!("process {} (init): {:?}", idx, id);
idx += 1;
self.process_one(id);
}
while let Some(id) = self.work_list.pop() {
debug!("process {}: {:?}", idx, id);
idx += 1;
self.process_one(id);
}
}
pub fn finish(self) {
for (id, cset) in self.complete_cset {
let func = self.cx.func_summ(id);
func.sig_cset = cset;
}
}
}