use std::sync::Arc;
use std::{cmp, mem, ops};
use ghost_cell::{GhostCell, GhostToken};
pub trait PointerLike {
fn ptr_eq(&self, other: &Self) -> bool;
fn shallow_clone(&self) -> Self;
}
impl<T> PointerLike for Arc<T> {
fn ptr_eq(&self, other: &Self) -> bool {
Arc::ptr_eq(self, other)
}
fn shallow_clone(&self) -> Self {
Arc::clone(self)
}
}
pub struct WithGhostToken<'brand, T> {
pub token: GhostToken<'brand>,
pub inner: T,
}
impl<'brand, T> ops::Deref for WithGhostToken<'brand, T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<'brand, T> ops::DerefMut for WithGhostToken<'brand, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
pub struct UbElement<'brand, T> {
inner: Arc<GhostCell<'brand, UbInner<'brand, T>>>,
}
impl<T> Clone for UbElement<'_, T> {
fn clone(&self) -> Self {
UbElement {
inner: Arc::clone(&self.inner),
}
}
}
struct UbInner<'brand, T> {
data: UbData<'brand, T>,
rank: usize,
}
enum UbData<'brand, T> {
Root(T),
EqualTo(UbElement<'brand, T>),
}
impl<T> UbData<'_, T> {
fn unwrap_root(&self) -> &T {
match self {
UbData::Root(ref x) => x,
UbData::EqualTo(..) => unreachable!(),
}
}
}
impl<T> UbElement<'_, T> {
pub fn new(data: T) -> Self {
UbElement {
inner: Arc::new(GhostCell::new(UbInner {
data: UbData::Root(data),
rank: 0,
})),
}
}
pub fn shallow_clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
}
}
}
impl<'brand, T: PointerLike> UbElement<'brand, T> {
pub fn root(&self, token: &mut GhostToken<'brand>) -> T {
let root = self.root_element(token);
let inner_lock = root.inner.borrow(token);
inner_lock.data.unwrap_root().shallow_clone()
}
fn root_element(&self, token: &mut GhostToken<'brand>) -> Self {
let mut x: Self = self.shallow_clone();
loop {
let parent: &Self = match x.inner.borrow(token).data {
UbData::Root(..) => return x.shallow_clone(),
UbData::EqualTo(ref parent) => parent,
};
let grandparent: &Self = match parent.inner.borrow(token).data {
UbData::Root(..) => return parent.shallow_clone(),
UbData::EqualTo(ref grandparent) => grandparent,
};
let grandparent = grandparent.shallow_clone();
x.inner.borrow_mut(token).data = UbData::EqualTo(grandparent.shallow_clone());
x = grandparent;
}
}
pub fn unify<D, E, Bind: FnOnce(&mut WithGhostToken<'brand, D>, T, &T) -> Result<(), E>>(
&self,
with_token: &mut WithGhostToken<'brand, D>,
other: &Self,
bind_fn: Bind,
) -> Result<(), E> {
let token = &mut with_token.token;
let mut x_root = self.root_element(token);
let mut y_root = other.root_element(token);
let x_elem = x_root.inner.borrow(token);
let y_elem = y_root.inner.borrow(token);
if x_elem.data.unwrap_root().ptr_eq(y_elem.data.unwrap_root()) {
return Ok(());
}
let rank_ord = x_elem.rank.cmp(&y_elem.rank);
match rank_ord {
cmp::Ordering::Less => {
mem::swap(&mut x_root, &mut y_root);
}
cmp::Ordering::Equal => {
let x_elem = x_root.inner.borrow_mut(token);
assert_ne!(
x_elem.rank,
usize::MAX,
"Attempted to unify two frozen variables",
);
x_elem.rank += 1;
}
_ => {}
}
let x_elem = x_root.inner.borrow(token);
let x_data = match x_elem.data {
UbData::Root(ref data) => data.shallow_clone(),
UbData::EqualTo(..) => unreachable!(),
};
let y_elem = y_root.inner.borrow_mut(token);
let old_y_data = mem::replace(&mut y_elem.data, UbData::EqualTo(x_root.shallow_clone()));
match old_y_data {
UbData::Root(ref y_data) => {
match bind_fn(with_token, x_data, y_data) {
Ok(()) => Ok(()),
Err(e) => {
y_root.inner.borrow_mut(&mut with_token.token).data = old_y_data;
Err(e)
}
}
}
UbData::EqualTo(..) => unreachable!(),
}
}
}