Reproducing code for Double Unroot issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [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