Skip to content

Instantly share code, notes, and snippets.

@lalaithion
Last active May 24, 2021 03:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lalaithion/125704f8f69408ec6d825b9075804787 to your computer and use it in GitHub Desktop.
Save lalaithion/125704f8f69408ec6d825b9075804787 to your computer and use it in GitHub Desktop.
Reproducing code for Double Unroot issue
// [dependencies]
// gc = { version = "0.4", features = ["derive"] }
// rpds = "0.9.0"
// fails with `thread 'scope::tests::test' panicked at 'Can't double-unroot a Gc<T>'`
use core::fmt::{Debug, Formatter, Result};
use gc::{Finalize, Gc, Trace};
use rpds::HashTrieMap;
use std::io::Write;
use std::iter::Iterator;
#[derive(PartialEq)]
pub struct Scope<V: Trace + 'static> {
inner: HashTrieMap<String, Gc<V>>,
}
impl<V: Trace + Debug> Debug for Scope<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_map().entries(self.inner.iter()).finish()
}
}
// We need a custom Clone because HashTrieMap is actually Cloneable even if
// the values in it are not, and the auto-derived implementation doesn't know
// that.
impl<V: Trace> Clone for Scope<V> {
fn clone(&self) -> Self {
Scope {
inner: self.inner.clone(),
}
}
}
impl<V: Trace> Scope<V> {
pub fn of(inner: HashTrieMap<String, Gc<V>>) -> Self {
Scope { inner }
}
pub fn new() -> Self {
Scope {
inner: HashTrieMap::new(),
}
}
pub fn insert(&self, key: String, value: Gc<V>) -> Self {
Scope {
inner: self.inner.insert(key, value),
}
}
pub fn get(&self, key: &String) -> Option<Gc<V>> {
self.inner.get(key).map(Clone::clone)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Gc<V>)> {
self.inner.iter()
}
}
pub fn merge<V: Trace>(a: &Scope<V>, b: &Scope<V>) -> Scope<V> {
let (mut new, other) = if a.inner.size() > b.inner.size() {
(a.clone(), b)
} else {
(b.clone(), a)
};
for (k, v) in other.inner.iter() {
new = new.insert(k.clone(), v.clone());
}
return new;
}
impl<V: Trace> Finalize for Scope<V> {}
unsafe impl<V: Trace + Debug> Trace for Scope<V> {
custom_trace!(this, {
for (_, v) in this.inner.iter() {
mark(v);
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use gc::GcCell;
use std::fmt;
use std::ops::Deref;
#[derive(Clone, PartialEq, Finalize, Trace)]
enum NamedTree {
Node(u32),
Branch(GcCell<Scope<NamedTree>>),
}
use NamedTree::*;
impl fmt::Debug for NamedTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Node(i) => f.debug_tuple("Node").field(i).finish(),
Branch(branches) => f
.debug_set()
.entries(
branches
.borrow()
.iter()
.map(|x| x.0)
.collect::<Vec<&String>>(),
)
.finish(),
}
}
}
#[test]
fn test() {
let mut scope = Scope::new()
.insert("foo".to_owned(), Gc::new(Node(10)))
.insert("bar".to_owned(), Gc::new(Node(11)));
let a = Branch(GcCell::new(Scope::new()));
scope = scope.insert("a".to_owned(), Gc::new(a));
let b = Branch(GcCell::new(Scope::new()));
scope = scope.insert("b".to_owned(), Gc::new(b));
for (_, tree) in scope.clone().iter() {
match tree.deref() {
Branch(branches) => {
*branches.borrow_mut() = scope.clone();
}
_ => (),
}
}
assert_eq!(
scope.get(&"a".to_owned()),
Some(Gc::new(Branch(GcCell::new(scope))))
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment