use std::cmp;
use std::collections::btree_set::{self, BTreeSet};
use std::collections::Bound;
use std::collections::HashSet;
use std::collections::VecDeque;
use arena::SyncDroplessArena;
use super::{ConcretePerm, PermVar, Var};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum Perm<'lty> {
Concrete(ConcretePerm),
Min(&'lty [Perm<'lty>]),
StaticVar(Var),
SigVar(Var),
InstVar(Var),
LocalVar(Var),
}
impl<'lty, 'tcx> Perm<'lty> {
pub fn read() -> Perm<'lty> {
Perm::Concrete(ConcretePerm::Read)
}
pub fn write() -> Perm<'lty> {
Perm::Concrete(ConcretePerm::Write)
}
pub fn move_() -> Perm<'lty> {
Perm::Concrete(ConcretePerm::Move)
}
pub fn var(pv: PermVar) -> Perm<'lty> {
match pv {
PermVar::Static(v) => Perm::StaticVar(v),
PermVar::Sig(v) => Perm::SigVar(v),
PermVar::Inst(v) => Perm::InstVar(v),
PermVar::Local(v) => Perm::LocalVar(v),
}
}
pub fn as_var(&self) -> Option<PermVar> {
match *self {
Perm::Concrete(_) => None,
Perm::Min(_) => None,
Perm::StaticVar(v) => Some(PermVar::Static(v)),
Perm::SigVar(v) => Some(PermVar::Sig(v)),
Perm::InstVar(v) => Some(PermVar::Inst(v)),
Perm::LocalVar(v) => Some(PermVar::Local(v)),
}
}
pub fn min(a: Perm<'lty>, b: Perm<'lty>, arena: &'lty SyncDroplessArena) -> Perm<'lty> {
eprintln!("finding min of {:?} and {:?}", a, b);
match (a, b) {
(Perm::Concrete(ConcretePerm::Read), _) | (_, Perm::Concrete(ConcretePerm::Read)) => {
Perm::read()
}
(Perm::Concrete(ConcretePerm::Move), p) => p,
(p, Perm::Concrete(ConcretePerm::Move)) => p,
(Perm::Min(ps1), Perm::Min(ps2)) => {
let mut all = Vec::with_capacity(ps1.len() + ps2.len());
all.extend(ps1.iter().cloned());
for &p in ps2 {
if !all.contains(&p) {
all.push(p);
}
}
let all = if all.len() == 0 {
&[] as &[_]
} else {
arena.alloc_slice(&all)
};
eprintln!("nontrivial min: {:?}", all);
Perm::Min(all)
}
(Perm::Min(ps), p) | (p, Perm::Min(ps)) => {
if ps.contains(&p) {
Perm::Min(ps)
} else {
let mut all = Vec::with_capacity(ps.len() + 1);
all.extend(ps.iter().cloned());
all.push(p);
let all = if all.len() == 0 {
&[] as &[_]
} else {
arena.alloc_slice(&all)
};
eprintln!("nontrivial min: {:?}", all);
Perm::Min(all)
}
}
(a, b) => {
if a == b {
a
} else {
let all = arena.alloc_slice(&[a, b]);
eprintln!("nontrivial min: {:?}", all);
Perm::Min(all)
}
}
}
}
pub fn contains(&self, other: Perm<'lty>) -> bool {
if *self == other {
return true;
}
match *self {
Perm::Min(ps) => ps.iter().cloned().any(|p| p.contains(other)),
_ => false,
}
}
pub fn for_each_replacement<F>(
&self,
arena: &'lty SyncDroplessArena,
old: Perm<'lty>,
news: &[Perm<'lty>],
mut callback: F,
) where
F: FnMut(Perm<'lty>),
{
if *self == old {
for &new in news {
callback(new);
}
return;
}
let self_ps = match *self {
Perm::Min(ps) => ps,
_ => {
callback(*self);
return;
}
};
let mut buf = self_ps.to_owned();
buf.retain(|&p| p != old);
let base_len = buf.len();
for &new in news {
match new {
Perm::Min(ps) => {
for &p in ps {
if !buf.contains(&p) {
buf.push(p);
}
}
}
_ => {
if !buf.contains(&new) {
buf.push(new);
}
}
}
if buf.len() == 1 {
callback(buf[0]);
} else {
callback(Perm::Min(arena.alloc_slice(&buf)));
}
buf.truncate(base_len);
}
}
pub fn for_each_atom<F: FnMut(Perm<'lty>)>(&self, callback: &mut F) {
match *self {
Perm::Min(ps) => {
for &p in ps {
p.for_each_atom(callback);
}
}
_ => callback(*self),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ConstraintSet<'lty> {
less: BTreeSet<(Perm<'lty>, Perm<'lty>)>,
greater: BTreeSet<(Perm<'lty>, Perm<'lty>)>,
}
fn perm_range<'lty, 'tcx>(
p: Perm<'lty>,
) -> (
Bound<(Perm<'lty>, Perm<'lty>)>,
Bound<(Perm<'lty>, Perm<'lty>)>,
) {
(
Bound::Included((p, Perm::read())),
Bound::Included((p, Perm::LocalVar(Var(!0)))),
)
}
impl<'lty, 'tcx> ConstraintSet<'lty> {
pub fn new() -> ConstraintSet<'lty> {
ConstraintSet {
less: BTreeSet::new(),
greater: BTreeSet::new(),
}
}
pub fn iter(&self) -> btree_set::Iter<(Perm<'lty>, Perm<'lty>)> {
self.less.iter()
}
pub fn add(&mut self, a: Perm<'lty>, b: Perm<'lty>) {
self.less.insert((a, b));
self.greater.insert((b, a));
}
pub fn import(&mut self, other: &ConstraintSet<'lty>) {
debug!("IMPORT {} constraints", other.less.len());
self.less
.extend(other.less.iter().cloned().filter(|&(ref a, ref b)| {
debug!("IMPORT CONSTRAINT: {:?} <= {:?}", a, b);
true
}));
self.greater.extend(other.greater.iter().cloned());
}
pub fn import_substituted<F>(
&mut self,
other: &ConstraintSet<'lty>,
arena: &'lty SyncDroplessArena,
f: F,
) where
F: Fn(Perm<'lty>) -> Perm<'lty>,
{
debug!("IMPORT {} constraints (substituted)", other.less.len());
let subst_one = |p| match p {
Perm::Min(ps) => {
let mut buf = Vec::with_capacity(ps.len());
for &p in ps {
let q = f(p);
if !buf.contains(&q) {
buf.push(q);
}
}
Perm::Min(arena.alloc_slice(&buf))
}
p => f(p),
};
for &(a, b) in other.less.iter() {
let (a2, b2) = (subst_one(a), subst_one(b));
debug!(
"IMPORT CONSTRANT: {:?} <= {:?} (substituted from {:?} <= {:?})",
a2, b2, a, b
);
self.add(a2, b2);
}
}
pub fn clone_substituted<F>(&self, arena: &'lty SyncDroplessArena, f: F) -> ConstraintSet<'lty>
where
F: Fn(Perm<'lty>) -> Perm<'lty>,
{
let mut new_cset = ConstraintSet::new();
new_cset.import_substituted(self, arena, f);
new_cset
}
fn traverse_constraints<F>(map: &BTreeSet<(Perm<'lty>, Perm<'lty>)>, p: Perm<'lty>, mut f: F)
where
F: FnMut(Perm<'lty>) -> bool,
{
let mut seen = HashSet::new();
let mut queue = VecDeque::new();
if f(p) {
seen.insert(p);
queue.push_back(p);
}
while let Some(cur) = queue.pop_front() {
for &(_, next) in map.range(perm_range(cur)) {
if !seen.contains(&next) {
if f(next) {
seen.insert(next);
queue.push_back(next);
}
}
}
}
}
pub fn for_each_less_than<F>(&self, p: Perm<'lty>, f: F)
where
F: FnMut(Perm<'lty>) -> bool,
{
Self::traverse_constraints(&self.greater, p, f);
}
pub fn for_each_greater_than<F>(&self, p: Perm<'lty>, f: F)
where
F: FnMut(Perm<'lty>) -> bool,
{
Self::traverse_constraints(&self.less, p, f);
}
pub fn lower_bound(&self, p: Perm<'lty>) -> ConcretePerm {
match p {
Perm::Concrete(p) => return p,
_ => {}
}
let mut bound = ConcretePerm::Read;
self.for_each_less_than(p, |p| match p {
Perm::Concrete(p) => {
bound = cmp::max(bound, p);
false
}
_ => true,
});
bound
}
pub fn upper_bound(&self, p: Perm<'lty>) -> ConcretePerm {
match p {
Perm::Concrete(p) => return p,
_ => {}
}
let mut bound = ConcretePerm::Move;
self.for_each_greater_than(p, |p| match p {
Perm::Concrete(p) => {
bound = cmp::min(bound, p);
false
}
_ => true,
});
bound
}
pub fn check_partial_assignment<F>(&self, eval: F) -> bool
where
F: Fn(Perm<'lty>) -> Option<ConcretePerm>,
{
fn eval_rec<'lty, 'tcx, F>(p: Perm<'lty>, eval: &F) -> (ConcretePerm, bool, bool)
where
F: Fn(Perm<'lty>) -> Option<ConcretePerm>,
{
match p {
Perm::Concrete(c) => (c, false, false),
Perm::Min(ps) => {
let mut min = ConcretePerm::Move;
let mut any_missing = false;
let mut all_missing = true;
for &p in ps {
let (c, any, all) = eval_rec(p, eval);
min = cmp::min(min, c);
any_missing |= any;
all_missing &= all;
}
(min, any_missing, all_missing)
}
p => {
if let Some(c) = eval(p) {
(c, false, false)
} else {
(ConcretePerm::Move, true, true)
}
}
}
}
for &(a, b) in &self.less {
let (a, a_any, a_all) = eval_rec(a, &eval);
let (b, _b_any, b_all) = eval_rec(b, &eval);
if a <= b {
continue;
}
if a_all || b_all {
continue;
}
if a_any {
continue;
}
return false;
}
true
}
pub fn edit<'a>(&'a mut self) -> EditConstraintSet<'a, 'lty> {
let to_visit = self.less.iter().cloned().collect();
EditConstraintSet {
cset: self,
to_visit: to_visit,
}
}
pub fn for_each_perm<F: FnMut(Perm<'lty>)>(&self, mut f: F) {
for &(a, b) in &self.less {
a.for_each_atom(&mut f);
b.for_each_atom(&mut f);
}
}
}
pub struct EditConstraintSet<'a, 'lty> {
cset: &'a mut ConstraintSet<'lty>,
to_visit: VecDeque<(Perm<'lty>, Perm<'lty>)>,
}
impl<'a, 'lty> EditConstraintSet<'a, 'lty> {
pub fn next(&mut self) -> Option<(Perm<'lty>, Perm<'lty>)> {
while let Some((a, b)) = self.to_visit.pop_front() {
if self.cset.less.contains(&(a, b)) {
return Some((a, b));
}
}
None
}
pub fn add(&mut self, a: Perm<'lty>, b: Perm<'lty>) {
if self.cset.less.contains(&(a, b)) {
return;
}
self.cset.less.insert((a, b));
self.cset.greater.insert((b, a));
self.to_visit.push_back((a, b));
}
pub fn add_no_visit(&mut self, a: Perm<'lty>, b: Perm<'lty>) {
if self.cset.less.contains(&(a, b)) {
return;
}
self.cset.less.insert((a, b));
self.cset.greater.insert((b, a));
}
pub fn remove(&mut self, a: Perm<'lty>, b: Perm<'lty>) {
self.cset.less.remove(&(a, b));
self.cset.greater.remove(&(b, a));
}
}
impl<'lty, 'tcx> ConstraintSet<'lty> {
pub fn remove_useless(&mut self) {
let mut edit = self.edit();
while let Some((a, b)) = edit.next() {
let remove = match (a, b) {
(Perm::Concrete(_), Perm::Concrete(_)) => true,
(Perm::Concrete(ConcretePerm::Read), _) => true,
(_, Perm::Concrete(ConcretePerm::Move)) => true,
_ => a == b,
};
if remove {
debug!("remove: {:?} <= {:?}", a, b);
edit.remove(a, b);
}
}
}
pub fn expand_min_rhs(&mut self) {
let mut edit = self.edit();
while let Some((a, b)) = edit.next() {
match b {
Perm::Min(ps) => {
debug!("expand: {:?} <= {:?}", a, b);
edit.remove(a, b);
for &p in ps {
edit.add(a, p);
}
}
_ => {}
}
}
}
pub fn simplify_min_lhs(&mut self, arena: &'lty SyncDroplessArena) {
let mut edit = self.edit();
'next: while let Some((a, b)) = edit.next() {
let ps = match a {
Perm::Min(ps) => ps,
_ => continue,
};
if ps.len() == 0 {
edit.remove(a, b);
continue;
}
let mut greater_sets = Vec::with_capacity(ps.len());
for &p in ps {
let mut seen = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(p);
while let Some(cur) = queue.pop_front() {
for &(_, next) in edit.cset.less.range(perm_range(cur)) {
if !seen.contains(&next) {
seen.insert(next);
queue.push_back(next);
}
}
}
greater_sets.push(seen);
}
let mut to_remove = HashSet::new();
for (i, &pi) in ps.iter().enumerate() {
if to_remove.contains(&i) {
continue;
}
for (j, &pj) in ps.iter().enumerate() {
if i != j && greater_sets[i].contains(&pj) {
to_remove.insert(j);
}
}
if greater_sets[i].contains(&b) {
debug!("remove {:?} <= {:?} ({:?} <= {:?})", a, b, pi, b);
edit.remove(a, b);
continue 'next;
}
}
assert!(
to_remove.len() < ps.len(),
"tried to remove all arguments of `min`"
);
if to_remove.len() == ps.len() - 1 {
edit.remove(a, b);
let (_, p) = ps
.iter()
.cloned()
.enumerate()
.filter(|&(i, _)| !to_remove.contains(&i))
.next()
.unwrap();
debug!("replace {:?} <= {:?} with {:?} <= {:?}", a, b, p, b);
edit.add(p, b);
} else if to_remove.len() > 0 {
edit.remove(a, b);
let ps = ps
.iter()
.cloned()
.enumerate()
.filter(|&(i, _)| !to_remove.contains(&i))
.map(|(_, p)| p)
.collect::<Vec<_>>();
let new_min = Perm::Min(arena.alloc_slice(&ps));
debug!("replace {:?} <= {:?} with {:?} <= {:?}", a, b, new_min, b);
edit.add(new_min, b);
}
}
}
pub fn simplify(&mut self, arena: &'lty SyncDroplessArena) {
self.remove_useless();
self.expand_min_rhs();
self.simplify_min_lhs(arena);
}
pub fn retain_perms<F>(&mut self, arena: &'lty SyncDroplessArena, filter: F)
where
F: Fn(Perm<'lty>) -> bool,
{
let mut atomic_perms = HashSet::new();
fn collect_atomic<'lty, 'tcx>(p: Perm<'lty>, dest: &mut HashSet<Perm<'lty>>) {
match p {
Perm::Min(ps) => {
for &p in ps {
collect_atomic(p, dest);
}
}
_ => {
dest.insert(p);
}
}
}
for &(p1, p2) in &self.less {
collect_atomic(p1, &mut atomic_perms);
collect_atomic(p2, &mut atomic_perms);
}
for &p in &atomic_perms {
if filter(p) {
continue;
}
debug!("removing perm {:?}", p);
let less = self
.greater
.range(perm_range(p))
.map(|&(_, b)| b)
.filter(|&b| b != p)
.collect::<Vec<_>>();
let greater = self
.less
.range(perm_range(p))
.map(|&(_, b)| b)
.filter(|&b| b != p)
.collect::<Vec<_>>();
debug!(" less: {:?}", less);
debug!(" greater: {:?}", greater);
let mut edit = self.edit();
while let Some((a, b)) = edit.next() {
if !a.contains(p) && !b.contains(p) {
continue;
}
debug!(" remove {:?} <= {:?}", a, b);
edit.remove(a, b);
a.for_each_replacement(arena, p, &less, |a| {
b.for_each_replacement(arena, p, &greater, |b| {
debug!(" replacement: {:?} <= {:?}", a, b);
edit.add_no_visit(a, b);
});
});
}
}
}
}