Repository: gluon-lang/gluon
Pull request #756 (2019-07-07 17:51:07)

feat: Use line/column numbers in lambda names
bors r+

bors[bot]: # Build failed
  * [continuous-integration/travis-ci/push](https://travis-ci.org/gluon-lang/gluon/builds/555406713?utm_source=github_status&utm_medium=notification)

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index a7612229..e36354ac 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -445,28 +445,6 @@ dependencies = [
  "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "crossbeam"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "crossbeam-deque"
 version = "0.2.0"
@@ -1049,7 +1027,7 @@ dependencies = [
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "codespan 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "collect-mac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "frunk_core 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1073,7 +1051,6 @@ dependencies = [
  "serde_derive_state 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_state 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "typed-arena 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -3120,8 +3097,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
 "checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394"
 "checksum criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e"
-"checksum crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b14492071ca110999a20bf90e3833406d5d66bfd93b4e52ec9539025ff43fe0d"
-"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
diff --git a/base/src/ast.rs b/base/src/ast.rs
index 8d873444..9909e8f7 100644
--- a/base/src/ast.rs
+++ b/base/src/ast.rs
@@ -857,7 +857,10 @@ pub trait Typed {
         self.try_type_of(env).unwrap()
     }
 
-    fn try_type_of(&self, env: &dyn TypeEnv<Type = ArcType>) -> Result<ArcType<Self::Ident>, String>;
+    fn try_type_of(
+        &self,
+        env: &dyn TypeEnv<Type = ArcType>,
+    ) -> Result<ArcType<Self::Ident>, String>;
 }
 
 impl<Id: Clone> Typed for TypedIdent<Id> {
diff --git a/benches/check.rs b/benches/check.rs
index df56393d..c0a3ddd9 100644
--- a/benches/check.rs
+++ b/benches/check.rs
@@ -16,13 +16,13 @@ use gluon::{compiler_pipeline::*, new_vm, Compiler};
 fn typecheck_prelude(b: &mut Bencher) {
     let vm = new_vm();
     let mut compiler = Compiler::new();
-    let MacroValue { expr } = {
-        let text = fs::read_to_string("std/prelude.glu").unwrap();
-        text.expand_macro(&mut compiler, &vm, "std.prelude", &text)
-            .unwrap_or_else(|(_, err)| panic!("{}", err))
-    };
+    let text = fs::read_to_string("std/prelude.glu").unwrap();
+    let MacroValue { expr } = text
+        .expand_macro(&mut compiler, &vm, "std.prelude", &text)
+        .unwrap_or_else(|(_, err)| panic!("{}", err));
     b.iter(|| {
-        let result = MacroValue { expr: expr.clone() }.typecheck(&mut compiler, &vm, "<top>", "");
+        let result =
+            MacroValue { expr: expr.clone() }.typecheck(&mut compiler, &vm, "std.prelude", &text);
         if let Err(ref err) = result {
             println!("{}", err);
             assert!(false);
@@ -45,13 +45,13 @@ fn clone_prelude(b: &mut Bencher) {
 fn typecheck_24(b: &mut Bencher) {
     let vm = new_vm();
     let mut compiler = Compiler::new();
-    let MacroValue { expr } = {
-        let text = fs::read_to_string("examples/24.glu").unwrap();
-        text.expand_macro(&mut compiler, &vm, "examples.24", &text)
-            .unwrap_or_else(|(_, err)| panic!("{}", err))
-    };
+    let text = fs::read_to_string("examples/24.glu").unwrap();
+    let MacroValue { expr } = text
+        .expand_macro(&mut compiler, &vm, "examples.24", &text)
+        .unwrap_or_else(|(_, err)| panic!("{}", err));
     b.iter(|| {
-        let result = MacroValue { expr: expr.clone() }.typecheck(&mut compiler, &vm, "<top>", "");
+        let result =
+            MacroValue { expr: expr.clone() }.typecheck(&mut compiler, &vm, "examples.24", &text);
         if let Err(ref err) = result {
             println!("{}", err);
             assert!(false);
@@ -63,11 +63,11 @@ fn typecheck_24(b: &mut Bencher) {
 fn typecheck_file(b: &mut Bencher, file: &str) {
     let vm = new_vm();
     let mut compiler = Compiler::new();
-    let reparsed = {
-        let text = fs::read_to_string(file).unwrap();
-        text.reparse_infix(&mut compiler, &vm, &base::filename_to_module(file), &text)
-            .unwrap_or_else(|(_, err)| panic!("{}", err))
-    };
+    let text = fs::read_to_string(file).unwrap();
+    let module_name = base::filename_to_module(file);
+    let reparsed = text
+        .reparse_infix(&mut compiler, &vm, &module_name, &text)
+        .unwrap_or_else(|(_, err)| panic!("{}", err));
     let InfixReparsed {
         metadata,
         metadata_map,
@@ -80,7 +80,7 @@ fn typecheck_file(b: &mut Bencher, file: &str) {
             expr: expr.clone(),
         },
         |input| {
-            let result = input.typecheck(&mut compiler, &vm, "<top>", "");
+            let result = input.typecheck(&mut compiler, &vm, &module_name, &text);
             if let Err(ref err) = result {
                 println!("{}", err);
                 assert!(false);
diff --git a/check/src/rename.rs b/check/src/rename.rs
index 97b78b51..66f3eb36 100644
--- a/check/src/rename.rs
+++ b/check/src/rename.rs
@@ -6,6 +6,7 @@ use crate::base::{
     fnv::FnvMap,
     pos::{self, ByteOffset, BytePos, Span},
     scoped_map::ScopedMap,
+    source::Source,
     symbol::{Symbol, SymbolModule},
     types::Type,
 };
@@ -14,19 +15,24 @@ struct Environment {
     stack: ScopedMap<Symbol, (Symbol, Span<BytePos>)>,
 }
 
-pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
+pub fn rename<'s>(
+    source: &'s (dyn Source + 's),
+    symbols: &mut SymbolModule,
+    expr: &mut SpannedExpr<Symbol>,
+) {
     enum TailCall {
         TailCall,
         Return,
     }
 
-    struct RenameVisitor<'a: 'b, 'b> {
+    struct RenameVisitor<'a: 'b, 'b, 's> {
+        source: &'s (dyn Source + 's),
         symbols: &'b mut SymbolModule<'a>,
         seen_symbols: FnvMap<Symbol, u32>,
         env: Environment,
     }
 
-    impl<'a, 'b> RenameVisitor<'a, 'b> {
+    impl<'a, 'b, 's> RenameVisitor<'a, 'b, 's> {
         fn new_pattern(&mut self, pattern: &mut ast::SpannedPattern<Symbol>) {
             match pattern.value {
                 Pattern::Record {
@@ -205,6 +211,10 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
                     return TailCall::TailCall;
                 }
                 Expr::Lambda(ref mut lambda) => {
+                    let location = self.source.location(expr.span.start()).unwrap_or_else(|| ice!("Lambda without source location"));
+                    let name = format!("{}.lambda:{}_{}", self.symbols.module(), location.line.number(), location.column.number());
+                    lambda.id.name = self.symbols.symbol(name);
+
                     self.env.stack.enter_scope();
 
                     for arg in &mut lambda.args {
@@ -264,7 +274,7 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
         }
     }
 
-    impl<'a, 'b, 'c> MutVisitor<'c> for RenameVisitor<'a, 'b> {
+    impl<'a, 'b, 'c, 's> MutVisitor<'c> for RenameVisitor<'a, 'b, 's> {
         type Ident = Symbol;
 
         fn visit_pattern(&mut self, pattern: &mut ast::SpannedPattern<Symbol>) {
@@ -325,6 +335,7 @@ pub fn rename(symbols: &mut SymbolModule, expr: &mut SpannedExpr<Symbol>) {
     }
 
     let mut visitor = RenameVisitor {
+        source,
         symbols: symbols,
         seen_symbols: Default::default(),
         env: Environment {
diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs
index 1f07d6c1..f1ee4cd9 100644
--- a/check/src/typecheck.rs
+++ b/check/src/typecheck.rs
@@ -1008,8 +1008,6 @@ impl<'a> Typecheck<'a> {
                 Ok(TailCall::Type(ModType::wobbly(array_type)))
             }
             Expr::Lambda(ref mut lambda) => {
-                let loc = format!("{}.lambda:{}", self.symbols.module(), expr.span.start());
-                lambda.id.name = self.symbols.symbol(loc);
                 let level = self.subs.var_id();
                 let function_type = expected_type
                     .take()
@@ -1912,11 +1910,6 @@ impl<'a> Typecheck<'a> {
                     resolved_types[i].concrete.clone()
                 };
                 self.typecheck_let_pattern(&mut bind.name, typ);
-                if let Expr::Lambda(ref mut lambda) = bind.expr.value {
-                    if let Pattern::Ident(ref name) = bind.name.value {
-                        lambda.id.name = name.name.clone();
-                    }
-                }
             }
         }
 
diff --git a/src/compiler_pipeline.rs b/src/compiler_pipeline.rs
index e7289417..c3863c95 100644
--- a/src/compiler_pipeline.rs
+++ b/src/compiler_pipeline.rs
@@ -256,10 +256,11 @@ where
         compiler: &mut Compiler,
         _thread: &Thread,
         file: &str,
-        _expr_str: &str,
+        expr_str: &str,
     ) -> SalvageResult<Renamed<Self::Expr>> {
+        let source = compiler.get_or_insert_filemap(file, expr_str);
         let mut symbols = SymbolModule::new(String::from(file), &mut compiler.symbols);
-        rename::rename(&mut symbols, &mut self.expr.borrow_mut());
+        rename::rename(&*source, &mut symbols, &mut self.expr.borrow_mut());
         Ok(Renamed { expr: self.expr })
     }
 }
diff --git a/src/import.rs b/src/import.rs
index b0902978..d6feadfb 100644
--- a/src/import.rs
+++ b/src/import.rs
@@ -495,9 +495,10 @@ where
                     format!("@{}", filename_to_module(filename))
                 }
                 _ => {
-                    return Err(
-                        Error::String("Expected a string literal or path to import".into()).into(),
-                    );
+                    return Err(Error::String(
+                        "Expected a string literal or path to import".into(),
+                    )
+                    .into());
                 }
             };
             Ok(modulename)
diff --git a/src/lib.rs b/src/lib.rs
index 616134b4..17962894 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -218,6 +218,16 @@ impl State {
             })
     }
 
+    fn get_or_insert_filemap<S>(&mut self, file: &str, source: S) -> Arc<codespan::FileMap>
+    where
+        S: AsRef<str> + Into<String>,
+    {
+        if let Some(i) = self.index_map.get(file) {
+            return self.code_map.find_file(*i).unwrap().clone();
+        }
+        self.add_filemap(file, source)
+    }
+
     pub fn get_filemap(&self, file: &str) -> Option<&Arc<codespan::FileMap>> {
         self.index_map
             .get(file)
@@ -352,6 +362,13 @@ impl Compiler {
         self.state().update_filemap(file, source)
     }
 
+    pub(crate) fn get_or_insert_filemap<S>(&self, file: &str, source: S) -> Arc<codespan::FileMap>
+    where
+        S: AsRef<str> + Into<String>,
+    {
+        self.state().get_or_insert_filemap(file, source)
+    }
+
     pub fn get_filemap(&self, file: &str) -> Option<Arc<codespan::FileMap>> {
         self.state().get_filemap(file).cloned()
     }
diff --git a/vm/Cargo.toml b/vm/Cargo.toml
index 50ad44d9..eed59687 100644
--- a/vm/Cargo.toml
+++ b/vm/Cargo.toml
@@ -20,7 +20,7 @@ travis-ci = { repository = "gluon-lang/gluon" }
 bitflags = "1.0.0"
 codespan = "0.3"
 collect-mac = "0.1.0"
-crossbeam = "0.7"
+crossbeam-utils = "0.6"
 frunk_core = "0.2"
 futures = "0.1.0"
 itertools = "0.8"
@@ -30,7 +30,6 @@ ordered-float = "1"
 pretty = "0.5"
 quick-error = "1.1.0"
 smallvec = "0.6"
-slab = "0.4"
 typed-arena = "1.2.0"
 
 serde = { version = "1.0.0", optional = true }
diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs
index a7f9f023..4938bb3f 100644
--- a/vm/src/api/mod.rs
+++ b/vm/src/api/mod.rs
@@ -279,15 +279,6 @@ pub enum IO<T> {
     Exception(String),
 }
 
-impl<T> Into<StdResult<T, String>> for IO<T> {
-    fn into(self) -> StdResult<T, String> {
-        match self {
-            IO::Value(x) => Ok(x),
-            IO::Exception(x) => Err(x),
-        }
-    }
-}
-
 impl<T, E> From<StdResult<T, E>> for IO<T>
 where
     E: fmt::Display,
diff --git a/vm/src/gc.rs b/vm/src/gc.rs
index 78813572..4e138184 100644
--- a/vm/src/gc.rs
+++ b/vm/src/gc.rs
@@ -186,19 +186,6 @@ pub struct Gc {
     generation: Generation,
 }
 
-impl Drop for Gc {
-    fn drop(&mut self) {
-        if let Some(values) = self.values.take() {
-            mem::forget(values);
-            if std::thread::panicking() {
-                eprintln!("Gc values were not dropped explicitly. Leaking the allocatons!");
-            } else {
-                panic!("Gc values were not dropped explicitly. Leaking the allocatons!");
-            }
-        }
-    }
-}
-
 /// Trait which creates a typed pointer from a *mut () pointer.
 /// For `Sized` types this is just a cast but for unsized types some more metadata must be taken
 /// from the provided `D` value to make it initialize correctly.
@@ -361,35 +348,12 @@ impl GcHeader {
     }
 }
 
-pub struct OwnedPtr<T: ?Sized>(NonNull<T>);
-
-impl<T: ?Sized> Deref for OwnedPtr<T> {
-    type Target = T;
-    fn deref(&self) -> &T {
-        unsafe { self.0.as_ref() }
-    }
-}
-
-impl<T: ?Sized> DerefMut for OwnedPtr<T> {
-    fn deref_mut(&mut self) -> &mut T {
-        unsafe { self.0.as_mut() }
-    }
-}
-
-impl<T: ?Sized> From<OwnedPtr<T>> for GcPtr<T> {
-    /// Freezes `self` into a shared pointer
-    fn from(ptr: OwnedPtr<T>) -> GcPtr<T> {
-        GcPtr(ptr.0)
-    }
-}
-
 /// A pointer to a garbage collected value.
 ///
 /// It is only safe to access data through a `GcPtr` if the value is rooted (stored in a place
 /// where the garbage collector will find it during the mark phase).
 pub struct GcPtr<T: ?Sized>(NonNull<T>);
 
-// SAFETY Copied from `Arc`
 unsafe impl<T: ?Sized + Send + Sync> Send for GcPtr<T> {}
 unsafe impl<T: ?Sized + Send + Sync> Sync for GcPtr<T> {}
 
@@ -733,7 +697,7 @@ impl Gc {
     /// will occur.
     ///
     /// Unsafe since `roots` must be able to trace all accesible `GcPtr` values.
-    pub unsafe fn alloc_and_collect<R, D>(&mut self, roots: R, def: D) -> Result<OwnedPtr<D::Value>>
+    pub unsafe fn alloc_and_collect<R, D>(&mut self, roots: R, def: D) -> Result<GcPtr<D::Value>>
     where
         R: Trace + CollectScope,
         D: DataDef + Trace,
@@ -756,19 +720,11 @@ impl Gc {
         }
 
         self.check_collect(Scope1(roots, &def));
-        self.alloc_owned(def)
+        self.alloc(def)
     }
 
     /// Allocates a new object.
     pub fn alloc<D>(&mut self, def: D) -> Result<GcPtr<D::Value>>
-    where
-        D: DataDef,
-        D::Value: Sized + Any,
-    {
-        self.alloc_owned(def).map(GcPtr::from)
-    }
-
-    pub fn alloc_owned<D>(&mut self, def: D) -> Result<OwnedPtr<D::Value>>
     where
         D: DataDef,
         D::Value: Sized + Any,
@@ -789,7 +745,7 @@ impl Gc {
         D: DataDef,
         D::Value: Sized + Any,
     {
-        GcPtr::from(self.alloc_ignore_limit_(def.size(), def))
+        self.alloc_ignore_limit_(def.size(), def)
     }
 
     fn get_type_info(
@@ -846,7 +802,7 @@ impl Gc {
         }
     }
 
-    fn alloc_ignore_limit_<D>(&mut self, size: usize, def: D) -> OwnedPtr<D::Value>
+    fn alloc_ignore_limit_<D>(&mut self, size: usize, def: D) -> GcPtr<D::Value>
     where
         D: DataDef,
         D::Value: Sized + Any,
@@ -872,7 +828,7 @@ impl Gc {
             // that the pointer was initialized
             assert!(ret == p);
             self.values = Some(ptr);
-            let ptr = OwnedPtr(NonNull::new_unchecked(p));
+            let ptr = GcPtr(NonNull::new_unchecked(p));
             D::Value::unroot(&ptr);
             ptr
         }
@@ -965,13 +921,6 @@ impl Gc {
         self.values = first;
     }
 
-    // Drop all values.
-    //
-    // SAFETY: No `GcPtr` allocated from this Gc must be reachable after calling this
-    pub unsafe fn clear(&mut self) {
-        self.values = None;
-    }
-
     fn free(&mut self, header: Option<AllocPtr>) {
         if let Some(ref ptr) = header {
             self.allocated_memory -= ptr.size();
@@ -1077,8 +1026,6 @@ mod tests {
         let other: &mut GcHeader = gc.values.as_mut().unwrap();
         assert_eq!(&*ptr as *const _ as *const (), other.value());
         assert_eq!(header, other as *const _);
-
-        unsafe { gc.clear() }
     }
 
     #[test]
@@ -1107,8 +1054,6 @@ mod tests {
             gc.collect(&mut *stack);
         }
         assert_eq!(object_count(&gc), 0);
-
-        unsafe { gc.clear() }
     }
 
     #[derive(Trace)]
@@ -1140,7 +1085,5 @@ mod tests {
             gc.collect(());
         }
         assert_eq!(true, dropped.get());
-
-        unsafe { gc.clear() }
     }
 }
diff --git a/vm/src/serialization.rs b/vm/src/serialization.rs
index 9199221f..838fd5cf 100644
--- a/vm/src/serialization.rs
+++ b/vm/src/serialization.rs
@@ -15,12 +15,12 @@ use crate::base::symbol::{Symbol, Symbols};
 use crate::base::types::ArcType;
 
 use crate::array::Array;
-use crate::gc::{DataDef, GcPtr, OwnedPtr, WriteOnly};
-use crate::thread::{RootedThread, RootedValue, Thread, ThreadInternal};
+use crate::gc::{DataDef, GcPtr, WriteOnly};
+use crate::thread::{RootedThread, Thread, ThreadInternal};
 use crate::types::VmIndex;
 use crate::value::{
     BytecodeFunction, Callable, ClosureData, ExternFunction, PartialApplicationData,
-    PartialApplicationDataDef, Value, ValueArray, ValueRepr,
+    PartialApplicationDataDef, Value, ValueRepr,
 };
 use crate::Variants;
 
@@ -106,17 +106,13 @@ impl<T> From<DeSeed> for Seed<T> {
 
 pub mod gc {
     use super::*;
-    use crate::serde::{
-        de::{Deserialize, DeserializeState, Deserializer},
-        ser::{Serialize, SerializeState, Serializer},
-    };
-
-    use crate::{
-        interner::InternedStr,
-        thread::ThreadInternal,
-        types::VmTag,
-        value::{DataStruct, GcStr, ValueArray},
-    };
+    use crate::serde::de::{Deserialize, DeserializeState, Deserializer};
+    use crate::serde::ser::{Serialize, SerializeState, Serializer};
+
+    use crate::interner::InternedStr;
+    use crate::thread::ThreadInternal;
+    use crate::types::VmTag;
+    use crate::value::{DataStruct, GcStr, ValueArray};
 
     impl Serialize for GcStr {
         fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
@@ -129,7 +125,7 @@ pub mod gc {
 
     impl<'de, T> DeserializeState<'de, DeSeed> for crate::gc::Move<T>
     where
-        T: crate::gc::Trace + PostDeserialize,
+        T: crate::gc::Trace,
         T: DeserializeState<'de, DeSeed>,
     {
         fn deserialize_state<D>(seed: &mut DeSeed, deserializer: D) -> Result<Self, D::Error>
@@ -142,7 +138,7 @@ pub mod gc {
 
     impl<'de, T> DeserializeState<'de, DeSeed> for GcPtr<T>
     where
-        T: crate::gc::Trace + PostDeserialize + 'static,
+        T: crate::gc::Trace + 'static,
         T: DeserializeState<'de, DeSeed>,
     {
         fn deserialize_state<D>(seed: &mut DeSeed, deserializer: D) -> Result<Self, D::Error>
@@ -436,7 +432,7 @@ pub mod typ {
 pub mod atomic_cell {
     use super::*;
 
-    use crossbeam::atomic::AtomicCell;
+    use crossbeam_utils::atomic::AtomicCell;
 
     pub fn deserialize<'de, D, T>(deserializer: D) -> Result<AtomicCell<T>, D::Error>
     where
@@ -652,51 +648,12 @@ where
     )
 }
 
-pub trait PostDeserialize {
-    fn init(parent: &Thread, ptr: OwnedPtr<Self>) -> GcPtr<Self>;
-}
-
-impl PostDeserialize for PartialApplicationData {
-    fn init(_parent: &Thread, ptr: OwnedPtr<Self>) -> GcPtr<Self> {
-        ptr.into()
-    }
-}
-
-impl PostDeserialize for ValueArray {
-    fn init(_parent: &Thread, ptr: OwnedPtr<Self>) -> GcPtr<Self> {
-        ptr.into()
-    }
-}
-
-impl PostDeserialize for ExternFunction {
-    fn init(_parent: &Thread, ptr: OwnedPtr<Self>) -> GcPtr<Self> {
-        ptr.into()
-    }
-}
-
-impl PostDeserialize for BytecodeFunction {
-    fn init(_parent: &Thread, ptr: OwnedPtr<Self>) -> GcPtr<Self> {
-        ptr.into()
-    }
-}
-
-impl PostDeserialize for Thread {
-    fn init(parent: &Thread, mut ptr: OwnedPtr<Self>) -> GcPtr<Self> {
-        let mut parent_threads = parent.child_threads.write().unwrap();
-        let entry = parent_threads.vacant_entry();
-        ptr.thread_index = entry.key();
-        let ptr = ptr.into();
-        entry.insert((ptr, 0));
-        ptr
-    }
-}
-
 struct DataDefSeed<T>(PhantomData<T>);
 
 impl<'de, T> DeserializeSeed<'de> for crate::serialization::Seed<DataDefSeed<T>>
 where
     T: DataDef + 'static,
-    <T as DataDef>::Value: Sized + PostDeserialize,
+    <T as DataDef>::Value: Sized,
     T: DeserializeState<'de, DeSeed>,
 {
     type Value = GcPtr<<T as DataDef>::Value>;
@@ -720,7 +677,7 @@ where
         impl<'de, T> DeserializeState<'de, GcSeed<T>> for GcPtr<T::Value>
         where
             T: DataDef + 'static,
-            <T as DataDef>::Value: PostDeserialize + Sized,
+            <T as DataDef>::Value: Sized,
             T: DeserializeState<'de, DeSeed>,
         {
             fn deserialize_state<D>(seed: &mut GcSeed<T>, deserializer: D) -> Result<Self, D::Error>
@@ -728,14 +685,12 @@ where
                 D: Deserializer<'de>,
             {
                 let def = T::deserialize_state(&mut seed.state, deserializer)?;
-                let ptr = seed
-                    .state
+                seed.state
                     .thread
                     .context()
                     .gc
-                    .alloc_owned(def)
-                    .map_err(D::Error::custom)?;
-                Ok(T::Value::init(&seed.state.thread, ptr))
+                    .alloc(def)
+                    .map_err(D::Error::custom)
             }
         }
 
@@ -833,24 +788,6 @@ impl<'a> crate::serde::ser::SerializeState<crate::serialization::SeSeed> for Var
     }
 }
 
-impl<'de> DeserializeState<'de, DeSeed> for RootedValue<RootedThread> {
-    fn deserialize_state<D>(seed: &mut DeSeed, deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        eprintln!("before value ");
-        let value = Value::deserialize_state(seed, deserializer)?;
-        // TODO Prevent Value from being deserialized directly so we don't need to have this unsafe
-        eprintln!("after value ");
-        unsafe {
-            let x = Ok(seed.thread.root_value(Variants::new(&value)));
-
-            eprintln!("after value 2");
-            x
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     extern crate serde_json;
diff --git a/vm/src/stack.rs b/vm/src/stack.rs
index 716b859a..2e870f53 100644
--- a/vm/src/stack.rs
+++ b/vm/src/stack.rs
@@ -965,8 +965,6 @@ mod tests {
         }
         // Panic as it attempts to access past the lock
         StackFrame::frame(&mut stack, 1, State::Unknown);
-
-        unsafe { gc.clear() }
     }
 
     #[test]
@@ -986,8 +984,6 @@ mod tests {
         }
         // Panic as it attempts to pop a locked value
         stack.pop();
-
-        unsafe { gc.clear() }
     }
 
     #[test]
@@ -1014,8 +1010,6 @@ mod tests {
         stack.release_lock(lock);
         let mut frame = StackFrame::<State>::current(&mut stack);
         assert_eq!(frame.pop(), Value::from(Int(2)));
-
-        unsafe { gc.clear() }
     }
 
     #[test]
diff --git a/vm/src/thread.rs b/vm/src/thread.rs
index ec039f67..a0def9cb 100644
--- a/vm/src/thread.rs
+++ b/vm/src/thread.rs
@@ -4,19 +4,17 @@ use std::{
     cmp::Ordering,
     fmt, mem,
     ops::{Add, Deref, DerefMut, Div, Mul, Sub},
-    ptr,
     result::Result as StdResult,
     string::String as StdString,
     sync::{
-        self,
         atomic::{self, AtomicBool},
-        Arc, Mutex, MutexGuard, RwLock,
+        Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
     },
     usize,
 };
 
 use {
-    crossbeam::atomic::AtomicCell,
+    crossbeam_utils::atomic::AtomicCell,
     futures::{
         future::{self, Either, FutureResult},
         try_ready, Async, Future, Poll,
@@ -30,28 +28,25 @@ use crate::base::{
     types::{self, Alias, ArcType},
 };
 
-use crate::{
-    api::{Getable, Pushable, ValueRef, VmType},
-    compiler::UpvarInfo,
-    gc,
-    gc::{DataDef, Gc, GcPtr, Generation, Move},
-    interner::InternedStr,
-    macros::MacroEnv,
-    source_map::LocalIter,
-    stack::{
-        ClosureState, ExternCallState, ExternState, Frame, Stack, StackFrame, StackState, State,
-    },
-    types::*,
-    value::{
-        BytecodeFunction, Callable, ClosureData, ClosureDataDef, ClosureInitDef, Def,
-        ExternFunction, PartialApplicationDataDef, RecordDef, UninitializedRecord,
-        UninitializedVariantDef, Userdata, Value, ValueRepr,
-        ValueRepr::{Closure, Data, Float, Function, Int, PartialApplication, String},
-        VariantDef,
-    },
-    vm::{GlobalVmState, GlobalVmStateBuilder, ThreadSlab, VmEnv},
-    BoxFuture, Error, Result, Variants,
+use crate::api::{Getable, Pushable, ValueRef, VmType};
+use crate::compiler::UpvarInfo;
+use crate::gc::{DataDef, Gc, GcPtr, Generation, Move};
+use crate::interner::InternedStr;
+use crate::macros::MacroEnv;
+use crate::source_map::LocalIter;
+use crate::stack::{
+    ClosureState, ExternCallState, ExternState, Frame, Stack, StackFrame, StackState, State,
+};
+use crate::types::*;
+use crate::value::{
+    BytecodeFunction, Callable, ClosureData, ClosureDataDef, ClosureInitDef, Def, ExternFunction,
+    PartialApplicationDataDef, RecordDef, UninitializedRecord, UninitializedVariantDef, Userdata,
+    Value, ValueRepr, VariantDef,
 };
+use crate::vm::{GlobalVmState, GlobalVmStateBuilder, VmEnv};
+use crate::{BoxFuture, Error, Result, Variants};
+
+use crate::value::ValueRepr::{Closure, Data, Float, Function, Int, PartialApplication, String};
 
 pub use crate::gc::Trace;
 
@@ -369,7 +364,7 @@ impl<'b> Roots<'b> {
         &self,
         gc: &mut Gc,
     ) -> Vec<(
-        sync::RwLockReadGuard<ThreadSlab>,
+        RwLockReadGuard<Vec<GcPtr<Thread>>>,
         MutexGuard<Context>,
         GcPtr<Thread>,
     )> {
@@ -377,7 +372,7 @@ impl<'b> Roots<'b> {
         let mut locks: Vec<(_, _, GcPtr<Thread>)> = Vec::new();
 
         let child_threads = self.vm.child_threads.read().unwrap();
-        stack.extend(child_threads.iter().map(|(_, (t, _))| t.clone()));
+        stack.extend(child_threads.iter().cloned());
 
         while let Some(thread_ptr) = stack.pop() {
             if locks.iter().any(|&(_, _, lock_thread)| {
@@ -388,7 +383,7 @@ impl<'b> Roots<'b> {
 
             let thread = mem::transmute::<&Thread, &'static Thread>(&*thread_ptr);
             let child_threads = thread.child_threads.read().unwrap();
-            stack.extend(child_threads.iter().map(|(_, (t, _))| t.clone()));
+            stack.extend(child_threads.iter().cloned());
 
             let context = thread.context.lock().unwrap();
 
@@ -429,23 +424,16 @@ pub struct Thread {
     // The parent of this thread, if it exists must live at least as long as this thread as this
     // thread can refer to any value in the parent thread
     #[cfg_attr(feature = "serde_derive", serde(state))]
-    parent: Option<GcPtr<Thread>>,
-
+    parent: Option<RootedThread>,
     #[cfg_attr(feature = "serde_derive", serde(state))]
     rooted_values: RwLock<Vec<Value>>,
-
     /// All threads which this thread have spawned in turn. Necessary as this thread needs to scan
     /// the roots of all its children as well since those may contain references to this threads
     /// garbage collected values
-    #[cfg_attr(feature = "serde_derive", serde(skip))]
-    pub(crate) child_threads: RwLock<ThreadSlab>,
-    // Default to an invalid index so we panic reliably if it is not filled in when deserializing
-    #[cfg_attr(feature = "serde_derive", serde(skip, default = "usize::max_value"))]
-    pub(crate) thread_index: usize,
-
+    #[cfg_attr(feature = "serde_derive", serde(state))]
+    child_threads: RwLock<Vec<GcPtr<Thread>>>,
     #[cfg_attr(feature = "serde_derive", serde(state))]
     context: Mutex<Context>,
-
     #[cfg_attr(feature = "serde_derive", serde(skip))]
     interrupt: AtomicBool,
 }
@@ -514,7 +502,10 @@ impl<'vm, 'value> Getable<'vm, 'value> for RootedThread {
 pub struct RootedThread {
     #[cfg_attr(feature = "serde_derive", serde(state))]
     thread: GcPtr<Thread>,
-    #[cfg_attr(feature = "serde_derive", serde(skip))]
+    #[cfg_attr(
+        feature = "serde_derive",
+        serde(serialize_with = "crate::serialization::atomic_cell::serialize")
+    )]
     rooted: AtomicCell<bool>,
 }
 
@@ -536,39 +527,18 @@ impl<'de> serde::de::DeserializeState<'de, crate::serialization::DeSeed> for Roo
         pub struct RootedThreadProxy {
             #[serde(state)]
             thread: GcPtr<Thread>,
+            #[serde(deserialize_with = "crate::serialization::atomic_cell::deserialize")]
+            rooted: AtomicCell<bool>,
         }
 
-        let RootedThreadProxy { thread } =
+        let RootedThreadProxy { thread, rooted } =
             RootedThreadProxy::deserialize_state(seed, deserializer)?;
-
-        Ok(thread.root_thread())
-    }
-}
-
-impl Drop for Thread {
-    fn drop(&mut self) {
-        // The child threads need to refer to `self` so drop the gc (and thus the child threads)
-        // first so that `self` is valid while dropping them
-        let context = self.context.get_mut().unwrap_or_else(|err| {
-            // Ignore poisoning since we don't need to interact with the Gc values, only
-            // drop them
-            err.into_inner()
-        });
-        let mut gc_to_drop =
-            ::std::mem::replace(&mut context.gc, Gc::new(Generation::default(), 0));
-        // Make sure that the RefMut is dropped before the Gc itself as the RwLock is dropped
-        // when the Gc is dropped
-        drop(context);
-
-        // SAFETY GcPtr's may not leak outside of the `Thread` so we can safely clear it when
-        // droppting the thread
-        unsafe {
-            gc_to_drop.clear();
+        let mut rooted_thread = RootedThread { thread, rooted };
+        if *rooted_thread.rooted.get_mut() {
+            rooted_thread.parent_threads().push(thread);
         }
 
-        let mut parent_threads = self.parent_threads();
-        debug_assert!(parent_threads[self.thread_index].1 == 0);
-        parent_threads.remove(self.thread_index);
+        Ok(rooted_thread)
     }
 }
 
@@ -576,25 +546,23 @@ impl Drop for RootedThread {
     fn drop(&mut self) {
         if *self.rooted.get_mut() {
             let is_empty = self.unroot_();
-            if is_empty {
+            if self.parent.is_none() && is_empty {
                 // The last RootedThread was dropped, there is no way to refer to the global state any
                 // longer so drop everything
-                let mut gc_ref = self.thread.global_state.gc.lock().unwrap_or_else(|err| {
+                let mut gc_ref = self
+                    .thread
+                    .global_state
+                    .gc
+                    .lock()
                     // Ignore poisoning since we don't need to interact with the Gc values, only
                     // drop them
-                    err.into_inner()
-                });
-                let mut gc_to_drop =
+                    .unwrap_or_else(|err| err.into_inner());
+                let gc_to_drop =
                     ::std::mem::replace(&mut *gc_ref, Gc::new(Generation::default(), 0));
                 // Make sure that the RefMut is dropped before the Gc itself as the RwLock is dropped
                 // when the Gc is dropped
                 drop(gc_ref);
-
-                // SAFETY GcPtr's may not leak outside of the `Thread` so we can safely clear it when
-                // droppting the thread
-                unsafe {
-                    gc_to_drop.clear();
-                }
+                drop(gc_to_drop);
             }
         }
     }
@@ -632,36 +600,22 @@ impl RootedThread {
     }
 
     pub fn with_global_state(mut global_state: GlobalVmState) -> RootedThread {
-        let context = Mutex::new(Context::new(
-            global_state.gc.get_mut().unwrap().new_child_gc(),
-        ));
-        let global_state = Arc::new(global_state);
         let thread = Thread {
             parent: None,
-            context,
-            global_state: global_state.clone(),
+            context: Mutex::new(Context::new(
+                global_state.gc.get_mut().unwrap().new_child_gc(),
+            )),
+            global_state: Arc::new(global_state),
             rooted_values: RwLock::new(Vec::new()),
-            child_threads: Default::default(),
+            child_threads: RwLock::new(Vec::new()),
             interrupt: AtomicBool::new(false),
-            thread_index: usize::max_value(),
         };
         let mut gc = Gc::new(Generation::default(), usize::MAX);
-        let mut ptr = gc
-            .alloc_owned(Move(thread))
-            .expect("Not enough memory to allocate thread");
-        *ptr.global_state.gc.lock().unwrap() = gc;
-
-        let ptr = {
-            let mut parent_threads = global_state.generation_0_threads.write().unwrap();
-            let entry = parent_threads.vacant_entry();
-            ptr.thread_index = entry.key();
-            let ptr = ptr.into();
-            entry.insert((ptr, 0));
-            ptr
-        };
-
-        let vm = ptr.root_thread();
-
+        let vm = gc
+            .alloc(Move(thread))
+            .expect("Not enough memory to allocate thread")
+            .root_thread();
+        *vm.global_state.gc.lock().unwrap() = gc;
         // Enter the top level scope
         {
             let mut context = vm.context.lock().unwrap();
@@ -693,40 +647,19 @@ impl RootedThread {
         let mut parent_threads_lock = self.parent_threads();
         assert!(!self.rooted.load());
         self.rooted.store(true);
-        parent_threads_lock[self.thread_index].1 += 1;
+        parent_threads_lock.push(self.thread);
     }
 
     fn unroot_(&self) -> bool {
         assert!(self.rooted.load());
-        let root_count = {
-            let mut roots = self.parent_threads();
-            self.rooted.store(false);
-            let (_, root_count) = &mut roots[self.thread_index];
-            assert!(*root_count > 0);
-            *root_count -= 1;
-            *root_count
-        };
-
-        if root_count == 0 {
-            fn is_unrooted(thread: &Thread) -> bool {
-                let child_threads = thread.child_threads.read().unwrap();
-                child_threads
-                    .iter()
-                    .all(|(_, (t, count))| *count == 0 && is_unrooted(t))
-            }
-
-            let mut top = &**self;
-            while let Some(thread) = &top.parent {
-                top = thread;
-            }
-
-            let child_threads = top.parent_threads_read();
-            child_threads
-                .iter()
-                .all(|(_, (t, count))| *count == 0 && is_unrooted(t))
-        } else {
-            false
-        }
+        let mut roots = self.parent_threads();
+        self.rooted.store(false);
+        let index = roots
+            .iter()
+            .position(|p| &**p as *const Thread == &*self.thread as *const Thread)
+            .expect("VM ptr");
+        roots.swap_remove(index);
+        roots.is_empty()
     }
 }
 
@@ -736,28 +669,18 @@ impl Thread {
     pub fn new_thread(&self) -> Result<RootedThread> {
         let vm = Thread {
             global_state: self.global_state.clone(),
-            parent: Some(unsafe { GcPtr::from_raw(self) }),
+            parent: Some(self.root_thread()),
             context: Mutex::new(Context::new(self.owned_context().gc.new_child_gc())),
             rooted_values: RwLock::new(Vec::new()),
-            child_threads: Default::default(),
+            child_threads: RwLock::new(Vec::new()),
             interrupt: AtomicBool::new(false),
-            thread_index: usize::max_value(),
         };
         // Enter the top level scope
         {
             let mut context = vm.owned_context();
             StackFrame::<State>::frame(&mut context.stack, 0, State::Unknown);
         }
-        let mut ptr = self.context().alloc_owned(Move(vm))?;
-
-        let ptr = {
-            let mut parent_threads = self.child_threads.write().unwrap();
-            let entry = parent_threads.vacant_entry();
-            ptr.thread_index = entry.key();
-            let ptr = ptr.into();
-            entry.insert((ptr, 0));
-            ptr
-        };
+        let ptr = self.context().alloc(Move(vm))?;
 
         Ok(ptr.root_thread())
     }
@@ -767,7 +690,7 @@ impl Thread {
     pub fn root_thread(&self) -> RootedThread {
         unsafe {
             let thread = GcPtr::from_raw(self);
-            self.parent_threads()[self.thread_index].1 += 1;
+            self.parent_threads().push(thread);
             RootedThread {
                 thread,
                 rooted: AtomicCell::new(true),
@@ -918,7 +841,7 @@ impl Thread {
     }
 
     /// Locks and retrieves the global environment of the vm
-    pub fn get_env<'b>(&'b self) -> sync::RwLockReadGuard<'b, VmEnv> {
+    pub fn get_env<'b>(&'b self) -> RwLockReadGuard<'b, VmEnv> {
         self.global_env().get_env()
     }
 
@@ -930,12 +853,7 @@ impl Thread {
     /// Runs a garbage collection.
     pub fn collect(&self) {
         let mut context = self.owned_context();
-        self.collect_with_context(&mut context);
-    }
-
-    fn collect_with_context(&self, context: &mut OwnedContext) {
-        debug_assert!(ptr::eq::<Thread>(self, context.thread));
-        self.with_roots(context, |gc, roots| unsafe {
+        self.with_roots(&mut context, |gc, roots| unsafe {
             gc.collect(roots);
         })
     }
@@ -992,20 +910,13 @@ impl Thread {
         self.child_threads.read().unwrap().trace(gc);
     }
 
-    pub(crate) fn parent_threads(&self) -> sync::RwLockWriteGuard<ThreadSlab> {
+    fn parent_threads(&self) -> RwLockWriteGuard<Vec<GcPtr<Thread>>> {
         match self.parent {
             Some(ref parent) => parent.child_threads.write().unwrap(),
             None => self.global_state.generation_0_threads.write().unwrap(),
         }
     }
 
-    fn parent_threads_read(&self) -> sync::RwLockReadGuard<ThreadSlab> {
-        match self.parent {
-            Some(ref parent) => parent.child_threads.read().unwrap(),
-            None => self.global_state.generation_0_threads.read().unwrap(),
-        }
-    }
-
     fn with_roots<F, R>(&self, context: &mut Context, f: F) -> R
     where
         F: for<'b> FnOnce(&mut Gc, Roots<'b>) -> R,
@@ -1618,14 +1529,6 @@ impl Context {
 
 impl<'b> OwnedContext<'b> {
     pub fn alloc<D>(&mut self, data: D) -> Result<GcPtr<D::Value>>
-    where
-        D: DataDef + Trace,
-        D::Value: Sized + Any,
-    {
-        self.alloc_owned(data).map(GcPtr::from)
-    }
-
-    pub fn alloc_owned<D>(&mut self, data: D) -> Result<gc::OwnedPtr<D::Value>>
     where
         D: DataDef + Trace,
         D::Value: Sized + Any,
@@ -1636,7 +1539,7 @@ impl<'b> OwnedContext<'b> {
             ref stack,
             ..
         } = **self;
-        alloc_owned(gc, thread, &stack, data)
+        alloc(gc, thread, &stack, data)
     }
 
     pub fn debug_info(&self) -> DebugInfo {
@@ -1664,19 +1567,6 @@ pub(crate) fn alloc<D>(
     stack: &Stack,
     def: D,
 ) -> Result<GcPtr<D::Value>>
-where
-    D: DataDef + Trace,
-    D::Value: Sized + Any,
-{
-    alloc_owned(gc, thread, stack, def).map(GcPtr::from)
-}
-
-pub(crate) fn alloc_owned<D>(
-    gc: &mut Gc,
-    thread: &Thread,
-    stack: &Stack,
-    def: D,
-) -> Result<gc::OwnedPtr<D::Value>>
 where
     D: DataDef + Trace,
     D::Value: Sized + Any,
@@ -2127,16 +2017,20 @@ impl<'b> ExecuteContext<'b> {
                             ValueRepr::Tag(0)
                         } else {
                             let fields = &self.stack[self.stack.len() - args..];
-                            let field_names = &function.records[record as usize];
-                            Data(alloc(
-                                self.gc,
-                                self.thread,
-                                &self.stack.stack,
-                                RecordDef {
-                                    elems: fields,
-                                    fields: field_names,
-                                },
-                            )?)
+                            unsafe {
+                                let roots = Roots {
+                                    vm: GcPtr::from_raw(self.thread),
+                                    stack: &self.stack.stack,
+                                };
+                                let field_names = &function.records[record as usize];
+                                Data(self.gc.alloc_and_collect(
+                                    roots,
+                                    RecordDef {
+                                        elems: fields,
+                                        fields: field_names,
+                                    },
+                                )?)
+                            }
                         }
                     };
                     self.stack.pop_many(args);
@@ -2165,16 +2059,20 @@ impl<'b> ExecuteContext<'b> {
                         if args == 0 {
                             ValueRepr::Tag(0)
                         } else {
-                            let field_names = &function.records[record as usize];
-                            Data(alloc(
-                                &mut self.gc,
-                                self.thread,
-                                &self.stack.stack,
-                                UninitializedRecord {
-                                    elems: args as usize,
-                                    fields: field_names,
-                                },
-                            )?)
+                            unsafe {
+                                let roots = Roots {
+                                    vm: GcPtr::from_raw(self.thread),
+                                    stack: &self.stack.stack,
+                                };
+                                let field_names = &function.records[record as usize];
+                                Data(self.gc.alloc_and_collect(
+                                    roots,
+                                    UninitializedRecord {
+                                        elems: args as usize,
+                                        fields: field_names,
+                                    },
+                                )?)
+                            }
                         }
                     };
                     self.stack.push(d);
diff --git a/vm/src/value.rs b/vm/src/value.rs
index dd16230d..ef254c24 100644
--- a/vm/src/value.rs
+++ b/vm/src/value.rs
@@ -1637,8 +1637,6 @@ mod tests {
             ),
             "Cons 0 (Cons 123 Nil)"
         );
-
-        unsafe { gc.clear() }
     }
 
     #[test]
@@ -1662,8 +1660,6 @@ mod tests {
             ),
             "[1, 2, 3]"
         );
-
-        unsafe { gc.clear() }
     }
 
     #[test]
diff --git a/vm/src/vm.rs b/vm/src/vm.rs
index c705b21d..5de96614 100644
--- a/vm/src/vm.rs
+++ b/vm/src/vm.rs
@@ -37,16 +37,6 @@ pub use crate::{
     value::Userdata,
 };
 
-pub(crate) type ThreadSlab = slab::Slab<(GcPtr<Thread>, usize)>;
-
-unsafe impl Trace for ThreadSlab {
-    impl_trace! { self, gc,
-        for x in self {
-            mark(&x, gc);
-        }
-    }
-}
-
 fn new_bytecode(
     env: &VmEnv,
     interner: &mut Interner,
@@ -185,8 +175,8 @@ pub struct GlobalVmState {
     // List of all generation 0 threads (ie, threads allocated by the global gc). when doing a
     // generation 0 sweep these threads are scanned as generation 0 values may be refered to by any
     // thread
-    #[cfg_attr(feature = "serde_derive", serde(skip))]
-    pub generation_0_threads: RwLock<ThreadSlab>,
+    #[cfg_attr(feature = "serde_derive", serde(state))]
+    pub generation_0_threads: RwLock<Vec<GcPtr<Thread>>>,
 
     #[cfg_attr(feature = "serde_derive", serde(skip))]
     debug_level: RwLock<DebugLevel>,
@@ -437,7 +427,7 @@ impl GlobalVmStateBuilder {
             gc: Mutex::new(Gc::new(Generation::default(), usize::MAX)),
             macros: MacroEnv::new(),
             type_cache: TypeCache::default(),
-            generation_0_threads: Default::default(),
+            generation_0_threads: RwLock::new(Vec::new()),
             debug_level: RwLock::new(DebugLevel::default()),
         };
         vm.add_types().unwrap();


Test patch:
diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs
index 295aeca3..9f51b8fe 100644
--- a/check/tests/support/mod.rs
+++ b/check/tests/support/mod.rs
@@ -201,7 +201,9 @@ pub fn typecheck_expr_expected(
     let interner = get_local_interner();
     let mut interner = interner.borrow_mut();
 
+    let source = codespan::FileMap::new("test".into(), text.to_string());
     rename::rename(
+        &source,
         &mut SymbolModule::new("test".into(), &mut interner),
         &mut expr,
     );
@@ -242,7 +244,9 @@ pub fn typecheck_partial_expr(
     let interner = get_local_interner();
     let mut interner = interner.borrow_mut();
 
+    let source = codespan::FileMap::new("test".into(), text.to_string());
     rename::rename(
+        &source,
         &mut SymbolModule::new("test".into(), &mut interner),
         &mut expr,
     );
diff --git a/completion/tests/support/mod.rs b/completion/tests/support/mod.rs
index 8602ffce..add75552 100644
--- a/completion/tests/support/mod.rs
+++ b/completion/tests/support/mod.rs
@@ -129,7 +129,11 @@ pub fn typecheck_expr_expected(
     let interner = get_local_interner();
     let mut interner = interner.borrow_mut();
 
+    let mut code_map = codespan::CodeMap::new();
+    let source = code_map.add_filemap("test".into(), text.into());
+
     rename::rename(
+        &*source,
         &mut SymbolModule::new("test".into(), &mut interner),
         &mut expr,
     );
@@ -145,14 +149,7 @@ pub fn typecheck_expr_expected(
 
     let result = tc.typecheck_expr_expected(&mut expr, expected);
 
-    (
-        expr,
-        result.map_err(|err| {
-            let mut source = codespan::CodeMap::new();
-            source.add_filemap(codespan::FileName::virtual_("test"), text.into());
-            InFile::new(source, err)
-        }),
-    )
+    (expr, result.map_err(|err| InFile::new(code_map, err)))
 }
 
 pub fn typecheck_expr(
@@ -179,7 +176,11 @@ pub fn typecheck_partial_expr(
     let interner = get_local_interner();
     let mut interner = interner.borrow_mut();
 
+    let mut code_map = codespan::CodeMap::new();
+    let source = code_map.add_filemap("test".into(), text.into());
+
     rename::rename(
+        &*source,
         &mut SymbolModule::new("test".into(), &mut interner),
         &mut expr,
     );
@@ -196,14 +197,7 @@ pub fn typecheck_partial_expr(
 
     let result = tc.typecheck_expr(&mut expr);
 
-    (
-        expr,
-        result.map_err(|err| {
-            let mut source = codespan::CodeMap::new();
-            source.add_filemap("test".into(), text.into());
-            InFile::new(source, err)
-        }),
-    )
+    (expr, result.map_err(|err| InFile::new(code_map, err)))
 }
 
 pub fn typ(s: &str) -> ArcType {
diff --git a/tests/api.rs b/tests/api.rs
index d90c2544..c9be6d4e 100644
--- a/tests/api.rs
+++ b/tests/api.rs
@@ -541,7 +541,7 @@ impl VmType for NoisyDrop {
 }
 
 #[test]
-fn cyclic_userdata_simple() {
+fn cyclic_userdata() {
     let _ = ::env_logger::try_init();
 
     #[derive(Debug, Userdata, Trace)]
@@ -639,69 +639,3 @@ fn cyclic_userdata_mutable() {
         "The virtual machine and its values were not dropped"
     );
 }
-
-#[test]
-fn child_vm_do_not_cause_undroppable_cycle_normal_drop_order() {
-    let _ = ::env_logger::try_init();
-
-    let mut noisy_drop = NoisyDrop::default();
-    {
-        let vm = make_vm();
-        let child_vm = vm.new_thread().unwrap();
-
-        vm.register_type::<NoisyDrop>("NoisyDrop", &[])
-            .unwrap_or_else(|_| panic!("Could not add type"));
-
-        Compiler::new()
-            .load_script(&vm, "function", "\\x -> ()")
-            .unwrap();
-
-        {
-            let mut f: FunctionRef<fn(NoisyDrop)> = vm
-                .get_global("function")
-                .unwrap_or_else(|err| panic!("{}", err));
-            f.call(noisy_drop.clone()).unwrap();
-        }
-
-        drop(child_vm);
-        drop(vm);
-    }
-
-    assert!(
-        Arc::get_mut(&mut noisy_drop.0).is_some(),
-        "The virtual machine and its values were not dropped"
-    );
-}
-
-#[test]
-fn child_vm_do_not_cause_undroppable_cycle_reverse_drop_order() {
-    let _ = ::env_logger::try_init();
-
-    let mut noisy_drop = NoisyDrop::default();
-    {
-        let vm = make_vm();
-        let child_vm = vm.new_thread().unwrap();
-
-        vm.register_type::<NoisyDrop>("NoisyDrop", &[])
-            .unwrap_or_else(|_| panic!("Could not add type"));
-
-        Compiler::new()
-            .load_script(&vm, "function", "\\x -> ()")
-            .unwrap();
-
-        {
-            let mut f: FunctionRef<fn(NoisyDrop)> = vm
-                .get_global("function")
-                .unwrap_or_else(|err| panic!("{}", err));
-            f.call(noisy_drop.clone()).unwrap();
-        }
-
-        drop(vm);
-        drop(child_vm);
-    }
-
-    assert!(
-        Arc::get_mut(&mut noisy_drop.0).is_some(),
-        "The virtual machine and its values were not dropped"
-    );
-}
diff --git a/tests/io.rs b/tests/io.rs
index 3540b4f3..f25f2140 100644
--- a/tests/io.rs
+++ b/tests/io.rs
@@ -1,15 +1,13 @@
-use std::fs;
+extern crate env_logger;
+extern crate gluon;
+extern crate tempfile;
+extern crate tokio;
 
+use gluon::vm::api::{Hole, OpaqueValue, OwnedFunction, ValueRef, IO};
+use gluon::{new_vm, Compiler, Thread};
+use std::fs;
 use tempfile::NamedTempFile;
 
-use gluon::{
-    new_vm,
-    vm::api::{Hole, OpaqueValue, OwnedFunction, ValueRef, IO},
-    Compiler, Thread,
-};
-
-use tokio::runtime::current_thread::Runtime;
-
 #[macro_use]
 mod support;
 
@@ -166,7 +164,7 @@ fn spawn_on_twice() {
         action
     "#;
 
-    let mut runtime = Runtime::new().unwrap();
+    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
     let vm = make_vm();
     let (result, _) = runtime
         .block_on(
@@ -212,7 +210,7 @@ fn spawn_on_runexpr() {
         wrap x.value
     "#;
 
-    let mut runtime = Runtime::new().unwrap();
+    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
     let vm = make_vm();
     let (result, _) = runtime
         .block_on(
@@ -251,7 +249,7 @@ fn spawn_on_do_action_twice() {
         wrap (load counter)
     "#;
 
-    let mut runtime = Runtime::new().unwrap();
+    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
     let vm = make_vm();
     let (result, _) = runtime
         .block_on(
@@ -284,7 +282,7 @@ fn spawn_on_force_action_twice() {
         wrap (load counter)
     "#;
 
-    let mut runtime = Runtime::new().unwrap();
+    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
     let vm = make_vm();
     let (result, _) = runtime
         .block_on(
@@ -316,7 +314,7 @@ fn spawn_on_runexpr_in_catch() {
         (io.catch action wrap >>= io.println) *> wrap "123"
     "#;
 
-    let mut runtime = Runtime::new().unwrap();
+    let mut runtime = self::tokio::runtime::Runtime::new().unwrap();
     let vm = make_vm();
     let (result, _) = runtime
         .block_on(
diff --git a/tests/serialization.rs b/tests/serialization.rs
index 2dc296df..bf119e0e 100644
--- a/tests/serialization.rs
+++ b/tests/serialization.rs
@@ -7,22 +7,18 @@ extern crate walkdir;
 
 extern crate gluon;
 
-use std::{fs::File, io::Read};
+use std::fs::File;
+use std::io::Read;
 
 use futures::Future;
 
 use crate::serde::ser::SerializeState;
 
-use gluon::{
-    new_vm,
-    vm::{
-        api::{Hole, OpaqueValue, IO},
-        serialization::{DeSeed, SeSeed},
-        thread::{RootedThread, RootedValue, Thread},
-        Variants,
-    },
-    Compiler,
-};
+use gluon::vm::api::{Hole, OpaqueValue};
+use gluon::vm::serialization::{DeSeed, SeSeed};
+use gluon::vm::thread::{RootedThread, RootedValue, Thread, ThreadInternal};
+use gluon::vm::Variants;
+use gluon::{new_vm, Compiler};
 
 fn serialize_value(value: Variants) {
     let mut buffer = Vec::new();
@@ -34,10 +30,10 @@ fn serialize_value(value: Variants) {
     String::from_utf8(buffer).unwrap();
 }
 
-fn roundtrip(
-    thread: &RootedThread,
+fn roundtrip<'t>(
+    thread: &'t RootedThread,
     value: &OpaqueValue<&Thread, Hole>,
-) -> RootedValue<RootedThread> {
+) -> RootedValue<&'t Thread> {
     use std::str::from_utf8;
 
     let value = value.get_variant();
@@ -55,6 +51,7 @@ fn roundtrip(
     let deserialize_value = DeSeed::new(thread)
         .deserialize(&mut de)
         .unwrap_or_else(|err| panic!("{}\n{}", err, buffer));
+    let deserialize_value = thread.root_value(unsafe { Variants::new(&deserialize_value) });
 
     // We can't compare functions for equality so serialize again and check that for equality with
     // the first serialization
@@ -64,7 +61,6 @@ fn roundtrip(
         let ser_state = SeSeed::new();
         value.serialize_state(&mut ser, &ser_state).unwrap();
     }
-    eprintln!("{}", buffer);
     assert_eq!(buffer, from_utf8(&buffer2).unwrap());
 
     deserialize_value
@@ -203,7 +199,7 @@ fn roundtrip_lazy() {
 }
 
 #[test]
-fn roundtrip_std_thread() {
+fn roundtrip_thread() {
     let thread = new_vm();
     let expr = r#" import! std.thread "#;
     let (value, _) = Compiler::new()
@@ -211,15 +207,3 @@ fn roundtrip_std_thread() {
         .unwrap_or_else(|err| panic!("{}", err));
     roundtrip(&thread, &value);
 }
-
-#[test]
-#[ignore] // Unimplemented so far
-fn roundtrip_thread() {
-    let thread = new_vm();
-    let expr = r#" let t = import! std.thread in t.new_thread ()"#;
-    let (value, _) = Compiler::new()
-        .run_io(true)
-        .run_expr::<IO<OpaqueValue<&Thread, Hole>>>(&thread, "test", &expr)
-        .unwrap_or_else(|err| panic!("{}", err));
-    roundtrip(&thread, &Into::<Result<_, _>>::into(value).unwrap());
-}