Repository: gluon-lang/gluon
Pull request #716 (2019-04-20 23:58:34)

Add marshalling features to implement gluon_specs
https://github.com/Marwes/gluon_specs

Allows references to passed to gluon and adds support for generating aliases when deriving `VmType`


Patch:
diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs
index 955ae5b5..a34f6652 100644
--- a/check/src/typecheck.rs
+++ b/check/src/typecheck.rs
@@ -2846,7 +2846,7 @@ impl<'a> Typecheck<'a> {
             Some((found_arg_type, arg, ret))
                 if function_arg_type == Some(found_arg_type) || function_arg_type == None =>
             {
-                return (found_arg_type, arg.clone(), ret.clone())
+                return (found_arg_type, arg.clone(), ret.clone());
             }
             _ => (),
         }
diff --git a/codegen/src/attr.rs b/codegen/src/attr.rs
index 14df77e4..c79c1a25 100644
--- a/codegen/src/attr.rs
+++ b/codegen/src/attr.rs
@@ -27,6 +27,7 @@ pub enum CrateName {
 pub struct Container {
     pub crate_name: CrateName,
     pub vm_type: Option<String>,
+    pub newtype: bool,
 }
 
 impl Container {
@@ -35,6 +36,7 @@ impl Container {
 
         let mut crate_name = CrateName::None;
         let mut vm_type = None;
+        let mut newtype = false;
 
         for meta_items in item.attrs.iter().filter_map(get_gluon_meta_items) {
             for meta_item in meta_items {
@@ -51,6 +53,10 @@ impl Container {
                         crate_name = CrateName::GluonVm;
                     }
 
+                    Meta(Word(ref w)) if w == "newtype" => {
+                        newtype = true;
+                    }
+
                     Meta(NameValue(ref m)) if m.ident == "vm_type" => {
                         vm_type = Some(get_lit_str(&m.ident, &m.ident, &m.lit).unwrap().value())
                     }
@@ -63,6 +69,7 @@ impl Container {
         Container {
             crate_name,
             vm_type,
+            newtype,
         }
     }
 }
diff --git a/codegen/src/getable.rs b/codegen/src/getable.rs
index 4044a48c..ecb0d3a4 100644
--- a/codegen/src/getable.rs
+++ b/codegen/src/getable.rs
@@ -179,16 +179,19 @@ fn gen_impl(
             use #ident::api as _gluon_api;
             use #ident::thread as _gluon_thread;
             use #ident::Variants as _GluonVariants;
+            use #ident::Result as _GluonResult;
         },
         attr::CrateName::GluonVm => quote! {
-            use api as _gluon_api;
-            use thread as _gluon_thread;
-            use Variants as _GluonVariants;
+            use crate::api as _gluon_api;
+            use crate::thread as _gluon_thread;
+            use crate::Variants as _GluonVariants;
+            use crate::Result as _GluonResult;
         },
         attr::CrateName::None => quote! {
             use gluon::vm::api as _gluon_api;
             use gluon::vm::thread as _gluon_thread;
             use gluon::vm::Variants as _GluonVariants;
+            use gluon::vm::Result as _GluonResult;
         },
     };
 
@@ -202,6 +205,16 @@ fn gen_impl(
             impl #impl_generics _gluon_api::Getable<'__vm, '__value> for #ident #ty_generics
             #where_clause #(#getable_bounds,)* #(#lifetime_bounds),*
             {
+                type Proxy = _GluonVariants<'__value>;
+
+                fn to_proxy(vm: &'__vm _gluon_thread::Thread, value: _GluonVariants<'__value>) -> _GluonResult<Self::Proxy> {
+                    Ok(value)
+                }
+
+                fn from_proxy(vm: &'__vm _gluon_thread::Thread, proxy: &'__value mut Self::Proxy) -> Self {
+                    Self::from_value(vm, *proxy)
+                }
+
                 fn from_value(vm: &'__vm _gluon_thread::Thread, variants: _GluonVariants<'__value>) -> Self {
                     #push_impl
                 }
diff --git a/codegen/src/userdata.rs b/codegen/src/userdata.rs
index a18fea47..81ba35ea 100644
--- a/codegen/src/userdata.rs
+++ b/codegen/src/userdata.rs
@@ -1,6 +1,6 @@
 use proc_macro2::{Ident, Span, TokenStream};
 use shared::{map_lifetimes, map_type_params, split_for_impl};
-use syn::{self, Data, DeriveInput, GenericParam, Generics};
+use syn::{self, Data, DeriveInput, Generics};
 
 use attr::{Container, CrateName};
 
@@ -48,12 +48,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr
         },
     };
 
-    let associated_type_generics = generics.params.iter().map(|param| match param {
-        GenericParam::Type(ty) => quote!( #ty :: Type ),
-        GenericParam::Lifetime(_) => quote!( 'static ),
-        GenericParam::Const(c) => quote!( #c ),
-    });
-
     let dummy_const = Ident::new(&format!("_IMPL_USERDATA_FOR_{}", ident), Span::call_site());
 
     quote! {
@@ -71,16 +65,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr
             #[automatically_derived]
             #[allow(unused_attributes, unused_variables)]
             impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics {}
-
-            #[automatically_derived]
-            #[allow(unused_attributes, unused_variables)]
-            impl #impl_generics _gluon_api::VmType for #ident #ty_generics
-            #where_clause #(#trait_bounds,)* #(#lifetime_bounds),*
-            {
-                type Type = #ident<
-                        #(#associated_type_generics),*
-                    >;
-            }
         };
     }
 }
diff --git a/codegen/src/vm_type.rs b/codegen/src/vm_type.rs
index a4a5cf0c..72946826 100644
--- a/codegen/src/vm_type.rs
+++ b/codegen/src/vm_type.rs
@@ -135,6 +135,32 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data
 
     let dummy_const = Ident::new(&format!("_IMPL_VM_TYPE_FOR_{}", ident), Span::call_site());
 
+    let make_type_impl = if container.newtype {
+        let type_application = gen_type_application(&generics);
+        let generic_params = map_type_params(&generics, |param| {
+            let lower_param = param.to_string().to_ascii_lowercase();
+            quote! {
+                vm.global_env().get_generic(#lower_param)
+            }
+        });
+
+        quote! {
+            let ty = if let Ok(ty) = vm.find_type_info(stringify!(#ident)) {
+                ty.into_type()
+            } else {
+                let ty = _gluon_base::types::Alias::new(
+                    _gluon_base::symbol::Symbol::from(stringify!(#ident)),
+                    vec![#(#generic_params),*],
+                    #make_type_impl,
+                );
+                vm.cache_alias(ty)
+            };
+            #type_application
+        }
+    } else {
+        make_type_impl
+    };
+
     quote! {
         #[allow(non_upper_case_globals)]
         const #dummy_const: () = {
diff --git a/examples/marshalling.rs b/examples/marshalling.rs
index 692fe8e9..8488a425 100644
--- a/examples/marshalling.rs
+++ b/examples/marshalling.rs
@@ -47,6 +47,7 @@ impl<'vm, 'value> api::Pushable<'vm> for Enum {
 }
 
 impl<'vm, 'value> api::Getable<'vm, 'value> for Enum {
+    impl_getable_simple!();
     fn from_value(thread: &'vm Thread, value: vm::Variants<'value>) -> Self {
         api::de::De::from_value(thread, value).0
     }
@@ -271,6 +272,8 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for GluonUser<T>
 where
     T: Getable<'vm, 'value>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, data: Variants<'value>) -> GluonUser<T> {
         // get the data, it must be a complex type
         let data = match data.as_ref() {
@@ -331,7 +334,8 @@ fn marshal_wrapper() -> Result<()> {
     Ok(())
 }
 
-#[derive(Userdata, Debug, Clone)]
+#[derive(Userdata, Debug, Clone, VmType)]
+#[gluon(vm_type = "WindowHandle")]
 struct WindowHandle {
     id: Arc<u64>,
     metadata: Arc<str>,
diff --git a/repl/src/repl.rs b/repl/src/repl.rs
index dca1efff..bd2b9d49 100644
--- a/repl/src/repl.rs
+++ b/repl/src/repl.rs
@@ -190,14 +190,16 @@ macro_rules! impl_userdata {
     };
 }
 
-#[derive(Userdata)]
+#[derive(Userdata, VmType)]
+#[gluon(vm_type = "Editor")]
 struct Editor {
     editor: Mutex<rustyline::Editor<Completer>>,
 }
 
 impl_userdata! { Editor }
 
-#[derive(Userdata)]
+#[derive(Userdata, VmType)]
+#[gluon(vm_type = "CpuPool")]
 struct CpuPool(self::futures_cpupool::CpuPool);
 
 impl_userdata! { CpuPool }
diff --git a/src/lib.rs b/src/lib.rs
index 7f06f030..6daee109 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -716,7 +716,7 @@ impl Compiler {
 
 pub const PRELUDE: &'static str = r#"
 let __implicit_prelude = import! std.prelude
-let { Num, Eq, Ord, Show, Functor, Applicative, Monad, Option, Bool, ? } = __implicit_prelude
+let { IO, Num, Eq, Ord, Show, Functor, Applicative, Monad, Option, Bool, ? } = __implicit_prelude
 
 let { (+), (-), (*), (/), (==), (/=), (<), (<=), (>=), (>), (++), show, not, flat_map } = __implicit_prelude
 
diff --git a/src/std_lib/http.rs b/src/std_lib/http.rs
index c2759d26..27172971 100644
--- a/src/std_lib/http.rs
+++ b/src/std_lib/http.rs
@@ -81,6 +81,8 @@ impl<'vm> Pushable<'vm> for Headers {
 }
 
 impl<'vm, 'value> Getable<'vm, 'value> for Headers {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         Headers(
             Collect::from_value(vm, value)
@@ -98,7 +100,8 @@ impl<'vm, 'value> Getable<'vm, 'value> for Headers {
 
 // By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
 // threads
-#[derive(Userdata)]
+#[derive(Userdata, VmType)]
+#[gluon(vm_type = "std.http.types.Body")]
 #[gluon(crate_name = "::vm")]
 // Representation of a http body that is in the prograss of being read
 pub struct Body(Arc<Mutex<Box<Stream<Item = PushAsRef<Chunk, [u8]>, Error = vm::Error> + Send>>>);
@@ -125,13 +128,14 @@ fn read_chunk(
 }
 
 // A http body that is being written
-#[derive(Userdata)]
+#[derive(Userdata, VmType)]
+#[gluon(vm_type = "std.http.types.ResponseBody")]
 #[gluon(crate_name = "::vm")]
 pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);
 
 impl fmt::Debug for ResponseBody {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "hyper::Response")
+        write!(f, "ResponseBody")
     }
 }
 
@@ -174,7 +178,8 @@ fn write_response(
     })
 }
 
-#[derive(Debug, Userdata)]
+#[derive(Debug, Userdata, VmType)]
+#[gluon(vm_type = "std.http.types.Uri")]
 #[gluon(crate_name = "::vm")]
 struct Uri(http::Uri);
 
@@ -381,9 +386,9 @@ pub fn load_types(vm: &Thread) -> vm::Result<ExternModule> {
         vm,
         record! {
             // Define the types so that they can be used from gluon
-            type Body => Body,
-            type ResponseBody => ResponseBody,
-            type Uri => Uri,
+            type std::http::types::Body => Body,
+            type std::http::types::ResponseBody => ResponseBody,
+            type std::http::types::Uri => Uri,
             type std::http::Method => String,
             type std::http::StatusCode => u16,
             type std::http::Request => Request,
diff --git a/src/std_lib/io.rs b/src/std_lib/io.rs
index 16351512..79aeac18 100644
--- a/src/std_lib/io.rs
+++ b/src/std_lib/io.rs
@@ -49,7 +49,8 @@ fn eprintln(s: &str) -> IO<()> {
     IO::Value(())
 }
 
-#[derive(Userdata)]
+#[derive(Userdata, VmType)]
+#[gluon(vm_type = "std.io.File")]
 #[gluon(crate_name = "::vm")]
 struct GluonFile(Mutex<Option<File>>);
 
@@ -359,9 +360,9 @@ pub fn load(vm: &Thread) -> Result<ExternModule> {
     ExternModule::new(
         vm,
         record! {
-            type File => GluonFile,
+            type std::io::File => GluonFile,
             type OpenOptions => OpenOptions,
-            type IO a => IO<A>,
+            type std::io::IO a => IO<A>,
             flat_map => TypedBytecode::<FlatMap>::new("std.io.prim.flat_map", 3, flat_map),
             wrap => TypedBytecode::<Wrap>::new("std.io.prim.wrap", 2, wrap),
             open_file_with => primitive!(2, std::io::prim::open_file_with),
diff --git a/src/std_lib/random.rs b/src/std_lib/random.rs
index ffcdb819..57bd8787 100644
--- a/src/std_lib/random.rs
+++ b/src/std_lib/random.rs
@@ -5,12 +5,16 @@ extern crate rand_xorshift;
 
 use self::rand::{Rng, SeedableRng};
 
-use crate::vm::api::{RuntimeResult, IO};
-use crate::vm::thread::Thread;
-use crate::vm::types::VmInt;
-use crate::vm::{self, ExternModule};
+use crate::vm::{
+    self,
+    api::{RuntimeResult, IO},
+    thread::Thread,
+    types::VmInt,
+    ExternModule,
+};
 
-#[derive(Clone, Debug, Userdata)]
+#[derive(Clone, Debug, Userdata, VmType)]
+#[gluon(vm_type = "std.random.XorShiftRng")]
 #[gluon(crate_name = "::vm")]
 struct XorShiftRng(self::rand_xorshift::XorShiftRng);
 
@@ -66,7 +70,7 @@ pub fn load(vm: &Thread) -> vm::Result<ExternModule> {
     ExternModule::new(
         vm,
         record! {
-            type XorShiftRng => XorShiftRng,
+            type std::random::XorShiftRng => XorShiftRng,
             next_int => primitive!(1, std::random::prim::next_int),
             next_float => primitive!(1, std::random::prim::next_float),
             gen_int_range => primitive!(2, std::random::prim::gen_int_range),
diff --git a/src/std_lib/regex.rs b/src/std_lib/regex.rs
index f444b936..3cc91a7d 100644
--- a/src/std_lib/regex.rs
+++ b/src/std_lib/regex.rs
@@ -6,11 +6,13 @@ use crate::real_std::error::Error as StdError;
 
 use crate::vm::{self, api::Collect, thread::Thread, ExternModule};
 
-#[derive(Debug, Userdata)]
+#[derive(Debug, Userdata, VmType)]
+#[gluon(vm_type = "std.regex.Regex")]
 #[gluon(crate_name = "vm")]
 struct Regex(regex::Regex);
 
-#[derive(Debug, Userdata)]
+#[derive(Debug, Userdata, VmType)]
+#[gluon(vm_type = "std.regex.Error")]
 #[gluon(crate_name = "vm")]
 struct Error(regex::Error);
 
diff --git a/std/disposable.glu b/std/disposable.glu
index b1b56286..b98d2e15 100644
--- a/std/disposable.glu
+++ b/std/disposable.glu
@@ -1,7 +1,7 @@
 //@NO-IMPLICIT-PRELUDE
 //! A `Disposable` abstracts over different kinds of resources.
 
-let { wrap, flat_map } = import! std.io.prim
+let { IO, wrap, flat_map } = import! std.io.prim
 let { Bool } = import! std.types
 
 
diff --git a/std/effect/st.glu b/std/effect/st.glu
index 951df030..1a7762f9 100644
--- a/std/effect/st.glu
+++ b/std/effect/st.glu
@@ -2,9 +2,9 @@ let { Eff, inject_rest, ? } = import! std.effect
 let { map } = import! std.functor
 let { wrap } = import! std.applicative
 let { (<<) } = import! std.function
-let { ref, (<-), load } = import! std.reference
+let { Reference, ref, (<-), load } = import! std.reference
 
-type STRef s a = { __ref : Ref a }
+type STRef s a = { __ref : Reference a }
 type State s r a =
     | New : forall b . b -> State s r (STRef s b)
     | Read : STRef s a -> State s r a
diff --git a/std/http.glu b/std/http.glu
index ec043b1b..fe292918 100644
--- a/std/http.glu
+++ b/std/http.glu
@@ -22,8 +22,10 @@ let {
     Request,
     StatusCode,
     Response,
+    ResponseBody,
     HttpEffect,
-    HttpState, } = import! std.http.types
+    HttpState,
+    Uri, } = import! std.http.types
 let http_prim = import! std.http.prim
 
 let status =
diff --git a/std/http/types.glu b/std/http/types.glu
index c23f6ef7..a443a7cc 100644
--- a/std/http/types.glu
+++ b/std/http/types.glu
@@ -1,4 +1,4 @@
-let { Body, ResponseBody, StatusCode, Method, Request, Response, Headers, HttpState } = import! std.http.prim_types
+let { Body, ResponseBody, StatusCode, Method, Request, Response, Headers, HttpState, Uri } = import! std.http.prim_types
 
 let { Eff } = import! std.effect
 let { Error } = import! std.effect.error
@@ -19,6 +19,8 @@ type HttpEffect r a = [| alt : Alt, state : State HttpState, lift : Lift IO | r
     StatusCode,
     Headers,
     Response,
+    ResponseBody,
     HttpEffect,
     HttpState,
+    Uri,
 }
diff --git a/std/io.glu b/std/io.glu
index 1d9e151c..0a34708d 100644
--- a/std/io.glu
+++ b/std/io.glu
@@ -1,7 +1,7 @@
 //@NO-IMPLICIT-PRELUDE
 //! Functions for working with I/O
 
-let io_prim @ { IO } = import! std.io.prim
+let io_prim @ { IO, File } = import! std.io.prim
 let { Read } = import! std.io.read
 let { Write } = import! std.io.write
 let { Disposable } = import! std.disposable
diff --git a/std/io/read.glu b/std/io/read.glu
index f3853d0a..15f9a663 100644
--- a/std/io/read.glu
+++ b/std/io/read.glu
@@ -1,7 +1,7 @@
 //@NO-IMPLICIT-PRELUDE
 //! Functions and types for working with `Read`ers.
 
-let { wrap, flat_map, default_buf_len } = import! std.io.prim
+let { IO, wrap, flat_map, default_buf_len } = import! std.io.prim
 let { Option, Bool } = import! std.types
 let { Disposable, dispose, is_disposed } = import! std.disposable
 let { (>), (<=), (>=), min } = import! std.cmp
@@ -9,7 +9,7 @@ let { assert } = import! std.assert
 let { not } = import! std.bool
 let { ? } = import! std.int
 let { Result } = import! std.result
-let { ref, load, (<-) } = import! std.reference
+let { Reference, ref, load, (<-) } = import! std.reference
 let array = import! std.array
 let string = import! std.string
 
@@ -65,7 +65,7 @@ let default_read_to_end read : forall a . (a -> Int -> IO (Option (Array Byte)))
 /// If you are reading all data at once, buffering is not necessary. 
 type Buffered r = {
     reader : r,
-    buf : Ref (Array Byte),
+    buf : Reference (Array Byte),
     capacity : Int,
 }
 
diff --git a/std/io/write.glu b/std/io/write.glu
index 4fe28b75..34494a12 100644
--- a/std/io/write.glu
+++ b/std/io/write.glu
@@ -4,9 +4,9 @@
 let string = import! std.string
 let array = import! std.array
 let { wrap } = import! std.applicative
-let { flat_map, wrap, default_buf_len, throw } = import! std.io.prim
+let { IO, flat_map, wrap, default_buf_len, throw } = import! std.io.prim
 let { Disposable, dispose, is_disposed } = import! std.disposable
-let { (<-), load, ref } = import! std.reference
+let { Reference, (<-), load, ref } = import! std.reference
 let { ? } = import! std.int
 let { assert } = import! std.assert
 let { (>=), (>), (==) } = import! std.cmp
@@ -68,7 +68,7 @@ let flush ?write : [Write a] -> a -> IO () = write.flush
 /// If you are writing all data at once, buffering is not necessary.
 type Buffered w = {
     writer : w,
-    buf : Ref (Array Byte),
+    buf : Reference (Array Byte),
     capacity : Int,
 }
 
diff --git a/std/prelude.glu b/std/prelude.glu
index 09964169..9ad423a4 100644
--- a/std/prelude.glu
+++ b/std/prelude.glu
@@ -1,6 +1,7 @@
 //@NO-IMPLICIT-PRELUDE
 //! Definitions which gets implicit re-export in every file.
 
+let { IO } = import! std.io.prim
 let { Option } = import! std.types
 let { Functor } = import! std.functor
 let { Applicative, (*>), wrap } = import! std.applicative
@@ -20,6 +21,8 @@ let { error } = import! std.prim
 let { flat_map } = import! std.monad
 
 {
+    IO,
+
     Ordering,
 
     Semigroup,
diff --git a/std/random.glu b/std/random.glu
index 449d4da3..7dfb94bd 100644
--- a/std/random.glu
+++ b/std/random.glu
@@ -2,7 +2,7 @@
 //!
 //! _This module is only available if gluon is compiled with the `rand` feature._
 
-let prim = import! std.random.prim
+let prim @ { XorShiftRng } = import! std.random.prim
 
 type RandomGen g = { next : g -> { value : Int, gen : g } }
 
@@ -18,6 +18,7 @@ let xor_shift_rng =
 
 {
     RandomGen,
+    XorShiftRng,
 
     xor_shift_rng,
 
diff --git a/std/reference.glu b/std/reference.glu
index a1c9b07c..4c5a4d91 100644
--- a/std/reference.glu
+++ b/std/reference.glu
@@ -1,9 +1,10 @@
 //! A mutable reference type
 
-let reference = import! std.reference.prim
+let reference @ { Reference } = import! std.reference.prim
 #[infix(right, 9)]
 let (<-) = reference.(<-)
 {
+    Reference,
     (<-),
     ..
     reference
diff --git a/std/regex.glu b/std/regex.glu
index 0722f3f9..a391b30e 100644
--- a/std/regex.glu
+++ b/std/regex.glu
@@ -2,7 +2,7 @@
 //! Bindings for rust-lang/regex
 
 let { Match, eq_Match, show_Match } = import! std.regex.types
-let regex_prim = import! std.regex.prim
+let regex_prim @ { Regex, Error  } = import! std.regex.prim
 
 {
     Match,
diff --git a/std/stream.glu b/std/stream.glu
index b153d3b1..357c8c2d 100644
--- a/std/stream.glu
+++ b/std/stream.glu
@@ -9,7 +9,7 @@ let { Option } = import! std.option
 let { Monoid } = import! std.monoid
 let { Semigroup, (<>) } = import! std.semigroup
 let { Foldable } = import! std.foldable
-let { lazy, force } = import! std.lazy
+let { Lazy, lazy, force } = import! std.lazy
 
 rec
 type Stream_ a =
diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs
index b0f16a58..a838d738 100644
--- a/vm/src/api/de.rs
+++ b/vm/src/api/de.rs
@@ -175,6 +175,8 @@ where
     T: VmType,
     T: DeserializeOwned,
 {
+    impl_getable_simple!();
+
     fn from_value(thread: &'vm Thread, value: Variants<'value>) -> Self {
         let typ = T::make_type(thread);
         match from_value(thread, value, &typ).map(De) {
diff --git a/vm/src/api/function.rs b/vm/src/api/function.rs
index b6827764..82e6060e 100644
--- a/vm/src/api/function.rs
+++ b/vm/src/api/function.rs
@@ -245,6 +245,8 @@ where
 }
 
 impl<'vm, 'value, F> Getable<'vm, 'value> for Function<&'vm Thread, F> {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Function<&'vm Thread, F> {
         Function {
             value: vm.root_value(value),
@@ -254,6 +256,8 @@ impl<'vm, 'value, F> Getable<'vm, 'value> for Function<&'vm Thread, F> {
 }
 
 impl<'vm, 'value, F> Getable<'vm, 'value> for Function<RootedThread, F> {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         Function {
             value: vm.root_value(value),
@@ -322,17 +326,25 @@ where $($args: Getable<'vm, 'vm> + 'vm,)*
         let mut i = 0;
         let lock;
         let r = unsafe {
-            let ($($args,)*) = {
-                let stack = StackFrame::<ExternState>::current(context.stack());
-                $(let $args = {
-                    let x = $args::from_value(vm, Variants::with_root(stack[i].clone(), vm));
-                    i += 1;
-                    x
-                });*;
-// Lock the frame to ensure that any references to the stack stay rooted
-                lock = stack.into_lock();
-                ($($args,)*)
-            };
+
+            let stack = StackFrame::<ExternState>::current(context.stack());
+            $(
+                let variants = Variants::with_root(stack[i].clone(), vm);
+                let mut proxy = match $args::to_proxy(vm, variants) {
+                    Ok(x) => x,
+                    Err(err) => {
+                        drop(stack);
+                        err.to_string().push(&mut context).unwrap();
+                        return Status::Error;
+                    }
+                };
+                // The proxy will live as along as the 'value lifetime we just created
+                let $args = $args::from_proxy(vm, &mut *(&mut proxy as *mut _));
+                i += 1;
+            )*
+            // Lock the frame to ensure that any references to the stack stay rooted
+            lock = stack.into_lock();
+
             drop(context);
             let r = (*self)($($args),*);
             context = vm.current_context();
@@ -499,6 +511,27 @@ make_vm_function!(A, B, C, D, E);
 make_vm_function!(A, B, C, D, E, F);
 make_vm_function!(A, B, C, D, E, F, G);
 
+impl<T, F> Function<T, F>
+where
+    T: Deref<Target = Thread>,
+    F: VmType,
+{
+    pub fn cast<F2>(self) -> Result<Function<T, F2>>
+    where
+        F2: VmType,
+    {
+        let vm = self.value.vm();
+        if F::make_type(vm) == F2::make_type(vm) {
+            Ok(Function {
+                value: self.value,
+                _marker: PhantomData,
+            })
+        } else {
+            Err(Error::Message("Function cast is not compatible".into()))
+        }
+    }
+}
+
 pub struct TypedBytecode<T> {
     id: Symbol,
     args: VmIndex,
diff --git a/vm/src/api/json.rs b/vm/src/api/json.rs
index 52691038..e0c2cf9c 100644
--- a/vm/src/api/json.rs
+++ b/vm/src/api/json.rs
@@ -153,6 +153,8 @@ impl<'vm> crate::api::Pushable<'vm> for JsonValue {
 }
 
 impl<'vm, 'value> crate::api::Getable<'vm, 'value> for JsonValue {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         JsonValue(crate::api::Getable::from_value(vm, value))
     }
@@ -223,7 +225,8 @@ impl<'de> de::DeserializeState<'de, ActiveThread<'de>> for JsonValue {
             where
                 E: de::Error,
             {
-                let value = crate::api::convert_with_active_thread(self.0, value).map_err(E::custom)?;
+                let value =
+                    crate::api::convert_with_active_thread(self.0, value).map_err(E::custom)?;
                 Ok(self.marshal(Value::String(value)))
             }
 
@@ -232,7 +235,8 @@ impl<'de> de::DeserializeState<'de, ActiveThread<'de>> for JsonValue {
             where
                 E: de::Error,
             {
-                let value = crate::api::convert_with_active_thread(self.0, value).map_err(E::custom)?;
+                let value =
+                    crate::api::convert_with_active_thread(self.0, value).map_err(E::custom)?;
                 Ok(self.marshal(Value::String(value)))
             }
 
diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs
index cafaedc8..1d78271c 100644
--- a/vm/src/api/mod.rs
+++ b/vm/src/api/mod.rs
@@ -33,11 +33,12 @@ use std::{
 
 use futures::{Async, Future};
 
-pub use self::function::*;
-pub use self::opaque::{Opaque, OpaqueRef, OpaqueValue};
-pub use self::record::Record;
-pub use crate::thread::ActiveThread;
-pub use crate::value::Userdata;
+pub use self::{
+    function::*,
+    opaque::{Opaque, OpaqueRef, OpaqueValue},
+    record::Record,
+};
+pub use crate::{thread::ActiveThread, value::Userdata};
 
 macro_rules! count {
     () => { 0 };
@@ -45,11 +46,31 @@ macro_rules! count {
     ($_e: ident, $($rest: ident),*) => { 1 + count!($($rest),*) }
 }
 
+/// Implements the proxy methods, letting only `from_value` be specified
+#[macro_export]
+macro_rules! impl_getable_simple {
+    () => {
+        type Proxy = $crate::Variants<'value>;
+        #[inline(always)]
+        fn to_proxy(
+            _vm: &'vm Thread,
+            value: $crate::Variants<'value>,
+        ) -> $crate::Result<Self::Proxy> {
+            Ok(value)
+        }
+        #[inline(always)]
+        fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
+            <Self as Getable<'vm, 'value>>::from_value(vm, *proxy)
+        }
+    };
+}
+
 #[macro_use]
 pub mod mac;
 pub mod function;
 mod opaque;
 pub mod record;
+pub mod scoped;
 
 #[cfg(feature = "serde")]
 pub mod de;
@@ -515,9 +536,24 @@ pub trait Pushable<'vm>: AsyncPushable<'vm> {
     }
 }
 
+impl<T> Userdata for std::sync::RwLock<T> where T: Userdata {}
+
+impl<T> Traverseable for std::sync::RwLock<T>
+where
+    T: Traverseable,
+{
+    fn traverse(&self, gc: &mut Gc) {
+        self.read().unwrap().traverse(gc);
+    }
+}
+
 /// Trait which allows rust values to be retrieved from the virtual machine
 pub trait Getable<'vm, 'value>: Sized {
+    type Proxy: 'value;
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self;
+
+    fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy>;
+    fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self;
 }
 
 pub fn convert<'vm, T, U>(thread: &'vm Thread, t: T) -> Result<U>
@@ -552,6 +588,8 @@ impl<'vm, T: vm::Userdata> Pushable<'vm> for T {
 }
 
 impl<'vm, 'value> Getable<'vm, 'value> for ValueRef<'value> {
+    impl_getable_simple!();
+
     fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self {
         value.as_ref()
     }
@@ -597,6 +635,8 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for UserdataValue<T>
 where
     T: vm::Userdata + Clone,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         UserdataValue(<&'value T as Getable<'vm, 'value>>::from_value(vm, value).clone())
     }
@@ -618,10 +658,42 @@ impl<'vm, T: ?Sized + VmType> VmType for &'vm T {
     }
 }
 
+pub enum RefProxy<'value, T> {
+    Lock(scoped::ReadGuard<'value, T>),
+    Ref(&'value T),
+}
+
 impl<'vm, 'value, T> Getable<'vm, 'value> for &'value T
 where
     T: vm::Userdata,
 {
+    type Proxy = RefProxy<'value, T>;
+
+    fn to_proxy(_vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy> {
+        match value.as_ref() {
+            ValueRef::Userdata(data) => data.downcast_ref::<T>().map_or_else(
+                || {
+                    Ok(RefProxy::Lock(
+                        data.downcast_ref::<scoped::Scoped<T, &'static ()>>()
+                            .map(|s| s.read())
+                            .or_else(|| {
+                                data.downcast_ref::<scoped::Scoped<T, &'static mut ()>>()
+                                    .map(|s| s.read())
+                            })
+                            .unwrap()?,
+                    ))
+                },
+                |x| Ok(RefProxy::Ref(x)),
+            ),
+            _ => ice!("ValueRef is not an Userdata"),
+        }
+    }
+    fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
+        match proxy {
+            RefProxy::Lock(v) => &*v,
+            RefProxy::Ref(v) => v,
+        }
+    }
     // Only allow the unsafe version to be used
     fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
@@ -631,7 +703,40 @@ where
     }
 }
 
+impl<'vm, T: ?Sized + VmType> VmType for &'vm mut T {
+    type Type = T::Type;
+    fn make_type(vm: &Thread) -> ArcType {
+        T::make_type(vm)
+    }
+}
+
+impl<'vm, 'value, T> Getable<'vm, 'value> for &'value mut T
+where
+    T: vm::Userdata,
+{
+    type Proxy = scoped::WriteGuard<'value, T>;
+
+    fn to_proxy(_vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy> {
+        match value.as_ref() {
+            ValueRef::Userdata(data) => Ok(data
+                .downcast_ref::<scoped::Scoped<T, &'static mut ()>>()
+                .unwrap()
+                .write()?),
+            _ => ice!("ValueRef is not an Userdata"),
+        }
+    }
+    fn from_proxy(_vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
+        proxy
+    }
+    // Only allow the unsafe version to be used
+    fn from_value(_vm: &'vm Thread, _value: Variants<'value>) -> Self {
+        panic!("Mutable references can only be created via proxies")
+    }
+}
+
 impl<'vm, 'value> Getable<'vm, 'value> for &'value str {
+    impl_getable_simple!();
+
     fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::String(ref s) => s,
@@ -641,6 +746,8 @@ impl<'vm, 'value> Getable<'vm, 'value> for &'value str {
 }
 
 impl<'vm, 'value> Getable<'vm, 'value> for &'value Path {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         Path::new(<&'value str>::from_value(vm, value))
     }
@@ -680,6 +787,18 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for WithVM<'vm, T>
 where
     T: Getable<'vm, 'value>,
 {
+    type Proxy = T::Proxy;
+
+    fn to_proxy(vm: &'vm Thread, value: Variants<'value>) -> Result<Self::Proxy> {
+        T::to_proxy(vm, value)
+    }
+    fn from_proxy(vm: &'vm Thread, proxy: &'value mut Self::Proxy) -> Self {
+        WithVM {
+            vm,
+            value: T::from_proxy(vm, proxy),
+        }
+    }
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> WithVM<'vm, T> {
         let t = T::from_value(vm, value);
         WithVM { vm, value: t }
@@ -696,6 +815,8 @@ impl<'vm> Pushable<'vm> for () {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for () {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, _: Variants) -> () {
         ()
     }
@@ -712,6 +833,8 @@ impl<'vm> Pushable<'vm> for u8 {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for u8 {
+    impl_getable_simple!();
+
     #[inline]
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> u8 {
         match value.as_ref() {
@@ -735,6 +858,8 @@ macro_rules! int_impls {
             }
         }
         impl<'vm, 'value> Getable<'vm, 'value> for $id {
+            impl_getable_simple!();
+
             #[inline]
             fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self {
                 match value.as_ref() {
@@ -760,6 +885,8 @@ impl<'vm> Pushable<'vm> for f64 {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for f64 {
+    impl_getable_simple!();
+
     #[inline]
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> f64 {
         match value.as_ref() {
@@ -768,6 +895,29 @@ impl<'vm, 'value> Getable<'vm, 'value> for f64 {
         }
     }
 }
+
+impl VmType for f32 {
+    type Type = Self;
+}
+impl<'vm> Pushable<'vm> for f32 {
+    #[inline]
+    fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
+        context.push(ValueRepr::Float(self as f64));
+        Ok(())
+    }
+}
+impl<'vm, 'value> Getable<'vm, 'value> for f32 {
+    impl_getable_simple!();
+
+    #[inline]
+    fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self {
+        match value.as_ref() {
+            ValueRef::Float(f) => f as f32,
+            _ => ice!("ValueRef is not a Float"),
+        }
+    }
+}
+
 impl VmType for bool {
     type Type = Self;
     fn make_type(vm: &Thread) -> ArcType {
@@ -787,6 +937,8 @@ impl<'vm> Pushable<'vm> for bool {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for bool {
+    impl_getable_simple!();
+
     #[inline]
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> bool {
         match value.as_ref() {
@@ -819,6 +971,8 @@ impl<'vm> Pushable<'vm> for Ordering {
 }
 impl<'vm, 'value> Getable<'vm, 'value> for Ordering {
     #[inline]
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> Ordering {
         let tag = match value.as_ref() {
             ValueRef::Data(data) => data.tag(),
@@ -856,6 +1010,8 @@ impl<'vm, 's> Pushable<'vm> for &'s str {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for String {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> String {
         match value.as_ref() {
             ValueRef::String(i) => String::from(&i[..]),
@@ -880,6 +1036,8 @@ impl<'vm> Pushable<'vm> for char {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for char {
+    impl_getable_simple!();
+
     #[inline]
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> char {
         match value.as_ref() {
@@ -917,6 +1075,8 @@ impl<'vm, 's> Pushable<'vm> for &'s Path {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for PathBuf {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::String(i) => PathBuf::from(&i[..]),
@@ -952,6 +1112,8 @@ impl<'vm, 's> Pushable<'vm> for &'s OsStr {
     }
 }
 impl<'vm, 'value> Getable<'vm, 'value> for OsString {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::String(i) => OsString::from(&i[..]),
@@ -995,6 +1157,8 @@ where
     }
 }
 impl<'vm, 'value, T: Copy + ArrayRepr> Getable<'vm, 'value> for &'value [T] {
+    impl_getable_simple!();
+
     fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::Array(ptr) => ptr.as_slice().unwrap(),
@@ -1040,6 +1204,8 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for Vec<T>
 where
     T: Getable<'vm, 'value>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Vec<T> {
         Collect::<GetableIter<T>>::from_value(vm, value).collect()
     }
@@ -1053,6 +1219,8 @@ impl<'s, T: VmType> VmType for *const T {
 }
 
 impl<'vm, 'value, T: vm::Userdata> Getable<'vm, 'value> for *const T {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> *const T {
         match value.as_ref() {
             ValueRef::Userdata(data) => {
@@ -1072,6 +1240,8 @@ impl<'s, T: VmType> VmType for Box<T> {
 }
 
 impl<'vm, 'value, T: Getable<'vm, 'value>> Getable<'vm, 'value> for Box<T> {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Box<T> {
         Box::new(T::from_value(vm, value))
     }
@@ -1125,6 +1295,8 @@ where
     K: Getable<'vm, 'value> + Ord,
     V: Getable<'vm, 'value>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         fn build_map<'vm2, 'value2, K2, V2>(
             map: &mut BTreeMap<K2, V2>,
@@ -1191,6 +1363,8 @@ impl<'vm, T: Pushable<'vm>> Pushable<'vm> for Option<T> {
     }
 }
 impl<'vm, 'value, T: Getable<'vm, 'value>> Getable<'vm, 'value> for Option<T> {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Option<T> {
         match value.as_ref() {
             ValueRef::Data(data) => {
@@ -1252,6 +1426,8 @@ impl<'vm, T: Pushable<'vm>, E: Pushable<'vm>> Pushable<'vm> for StdResult<T, E>
 impl<'vm, 'value, T: Getable<'vm, 'value>, E: Getable<'vm, 'value>> Getable<'vm, 'value>
     for StdResult<T, E>
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> StdResult<T, E> {
         match value.as_ref() {
             ValueRef::Data(data) => match data.tag() {
@@ -1356,7 +1532,7 @@ where
     type Type = IO<T::Type>;
     fn make_type(vm: &Thread) -> ArcType {
         let env = vm.global_env().get_env();
-        let alias = env.find_type_info("IO").unwrap().into_owned();
+        let alias = env.find_type_info("std.io.IO").unwrap().into_owned();
         Type::app(alias.into_type(), collect![T::make_type(vm)])
     }
     fn extra_args() -> VmIndex {
@@ -1365,6 +1541,8 @@ where
 }
 
 impl<'vm, 'value, T: Getable<'vm, 'value>> Getable<'vm, 'value> for IO<T> {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> IO<T> {
         IO::Value(T::from_value(vm, value))
     }
@@ -1409,6 +1587,8 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for RootedValue<T>
 where
     T: Deref<Target = Thread> + VmRoot<'vm>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         vm.root_value(value)
     }
@@ -1495,6 +1675,8 @@ macro_rules! define_tuple {
 
         #[allow(non_snake_case)]
         impl<'vm, 'value, $($id: Getable<'vm, 'value>),+> Getable<'vm, 'value> for ($($id),+) {
+            impl_getable_simple!();
+
             #[allow(unused_assignments)]
             fn from_value(vm: &'vm Thread, value: Variants<'value>) -> ($($id),+) {
                 match value.as_ref() {
@@ -1684,6 +1866,8 @@ impl<'vm, 'value, T> Getable<'vm, 'value> for Collect<GetableIter<'vm, 'value, T
 where
     T: Getable<'vm, 'value>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::Array(data) => Collect::new(GetableIter {
diff --git a/vm/src/api/opaque.rs b/vm/src/api/opaque.rs
index c3d3c7cc..8f3bc173 100644
--- a/vm/src/api/opaque.rs
+++ b/vm/src/api/opaque.rs
@@ -13,10 +13,10 @@ use crate::value::{ArrayRepr, Value, ValueArray};
 use crate::vm;
 use crate::{Result, Variants};
 
-#[cfg(feature = "serde")]
-use std::result::Result as StdResult;
 #[cfg(feature = "serde")]
 use crate::thread::RootedThread;
+#[cfg(feature = "serde")]
+use std::result::Result as StdResult;
 
 #[cfg(feature = "serde")]
 use crate::serde::de::{Deserialize, Deserializer};
@@ -356,6 +356,8 @@ impl<'vm, 'value, V> Getable<'vm, 'value> for Opaque<Variants<'value>, V>
 where
     V: ?Sized,
 {
+    impl_getable_simple!();
+
     fn from_value(_vm: &'vm Thread, value: Variants<'value>) -> Self {
         Opaque::from_value(value)
     }
@@ -366,6 +368,8 @@ where
     V: ?Sized,
     T: VmRoot<'vm>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         OpaqueValue::from_value(vm.root_value(value))
     }
diff --git a/vm/src/api/record.rs b/vm/src/api/record.rs
index 5beb5de6..fa56006e 100644
--- a/vm/src/api/record.rs
+++ b/vm/src/api/record.rs
@@ -232,6 +232,8 @@ where
     T: Default,
     U: GetableFieldList<'vm, 'value>,
 {
+    impl_getable_simple!();
+
     fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::Data(ref data) => {
diff --git a/vm/src/api/scoped.rs b/vm/src/api/scoped.rs
new file mode 100644
index 00000000..1f77f56b
--- /dev/null
+++ b/vm/src/api/scoped.rs
@@ -0,0 +1,248 @@
+use std::{
+    fmt,
+    marker::PhantomData,
+    ops::{Deref, DerefMut},
+    ptr::NonNull,
+};
+
+use std::sync::RwLock;
+
+use base::types::ArcType;
+
+use crate::{
+    api::{Opaque, OpaqueValue, Pushable, VmType},
+    gc::{Gc, Traverseable},
+    thread::{ActiveThread, RootedThread, Thread, ThreadInternal},
+    value::Userdata,
+    Result,
+};
+
+pub struct Ref<'a, T>
+where
+    T: Userdata,
+{
+    reference: &'a T,
+    gluon_reference: Option<RefGuard<T, &'static ()>>,
+}
+
+impl<'a, 'b, T> VmType for &'b mut Ref<'a, T>
+where
+    T: Userdata + VmType,
+{
+    type Type = T::Type;
+    fn make_type(vm: &Thread) -> ArcType {
+        T::make_type(vm)
+    }
+}
+
+impl<'vm, 'a, 'b, T> Pushable<'vm> for &'b mut Ref<'a, T>
+where
+    T: VmType + Userdata,
+{
+    fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
+        Scoped::<T, _>::new(self.reference).push(context)?;
+        let value = context.last().unwrap();
+        self.gluon_reference = Some(RefGuard {
+            gluon_reference: Opaque::from_value(context.thread().root_value(value)),
+        });
+        Ok(())
+    }
+}
+
+impl<'a, T> Ref<'a, T>
+where
+    T: Userdata,
+{
+    pub fn new(reference: &'a T) -> Self {
+        Ref {
+            reference,
+            gluon_reference: None,
+        }
+    }
+}
+
+pub struct RefMut<'a, T>
+where
+    T: Userdata,
+{
+    reference: &'a mut T,
+    gluon_reference: Option<RefGuard<T, &'static mut ()>>,
+}
+
+impl<'a, 'b, T> VmType for &'b mut RefMut<'a, T>
+where
+    T: Userdata + VmType,
+{
+    type Type = T::Type;
+    fn make_type(vm: &Thread) -> ArcType {
+        T::make_type(vm)
+    }
+}
+
+impl<'vm, 'a, 'b, T> Pushable<'vm> for &'b mut RefMut<'a, T>
+where
+    T: VmType + Userdata,
+{
+    fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
+        Scoped::<T, _>::new_mut(self.reference).push(context)?;
+        let value = context.last().unwrap();
+        self.gluon_reference = Some(RefGuard {
+            gluon_reference: Opaque::from_value(context.thread().root_value(value)),
+        });
+        Ok(())
+    }
+}
+
+impl<'a, T> RefMut<'a, T>
+where
+    T: Userdata,
+{
+    pub fn new(reference: &'a mut T) -> Self {
+        RefMut {
+            reference,
+            gluon_reference: None,
+        }
+    }
+}
+
+struct RefGuard<T, M>
+where
+    T: Userdata,
+    M: 'static,
+{
+    gluon_reference: OpaqueValue<RootedThread, Scoped<T, M>>,
+}
+
+impl<T, M> Drop for RefGuard<T, M>
+where
+    T: Userdata,
+    M: 'static,
+{
+    fn drop(&mut self) {
+        Scoped::invalidate(&*self.gluon_reference);
+    }
+}
+
+pub(crate) struct Scoped<T: ?Sized, M> {
+    ptr: RwLock<Option<NonNull<T>>>,
+    _marker: PhantomData<M>,
+}
+
+unsafe impl<T, M> Send for Scoped<T, M> where T: Send + Sync + ?Sized {}
+unsafe impl<T, M> Sync for Scoped<T, M> where T: Send + Sync + ?Sized {}
+
+impl<T, M> fmt::Debug for Scoped<T, M> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Scoped")
+    }
+}
+
+impl<T> Scoped<T, &'static ()> {
+    pub fn new(ptr: &T) -> Self {
+        Scoped {
+            ptr: RwLock::new(NonNull::new(ptr as *const T as *mut T)),
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<T> Scoped<T, &'static mut ()> {
+    pub fn new_mut(ptr: &mut T) -> Self {
+        Scoped {
+            ptr: RwLock::new(NonNull::new(ptr as *mut T)),
+            _marker: PhantomData,
+        }
+    }
+
+    pub fn write(&self) -> Result<WriteGuard<T>> {
+        let ptr = self.ptr.write().unwrap();
+        if let None = *ptr {
+            return Err("Scoped pointer is invalidated".to_string().into());
+        }
+        Ok(WriteGuard(ptr))
+    }
+}
+
+impl<T, M> Scoped<T, M> {
+    pub fn read(&self) -> Result<ReadGuard<T>> {
+        let ptr = self.ptr.read().unwrap();
+        if let None = *ptr {
+            return Err("Scoped pointer is invalidated".to_string().into());
+        }
+        Ok(ReadGuard(ptr))
+    }
+
+    pub fn invalidate(&self) {
+        *self.ptr.write().unwrap() = None;
+    }
+}
+
+impl<'vm, T: VmType, M> VmType for Scoped<T, M> {
+    type Type = T::Type;
+    fn make_type(vm: &Thread) -> ArcType {
+        T::make_type(vm)
+    }
+}
+
+impl<T, M> Traverseable for Scoped<T, M>
+where
+    T: Traverseable,
+{
+    fn traverse(&self, gc: &mut Gc) {
+        unsafe {
+            if let Some(v) = *self.ptr.read().unwrap() {
+                v.as_ref().traverse(gc);
+            }
+        }
+    }
+}
+
+impl<T, M> Userdata for Scoped<T, M>
+where
+    T: Userdata,
+    M: 'static,
+{
+}
+
+#[doc(hidden)]
+pub struct ReadGuard<'a, T>(std::sync::RwLockReadGuard<'a, Option<NonNull<T>>>);
+
+impl<'a, T> Deref for ReadGuard<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            match *self.0 {
+                Some(v) => &*v.as_ptr(),
+                None => panic!("Scoped pointer is invalidated"),
+            }
+        }
+    }
+}
+
+#[doc(hidden)]
+pub struct WriteGuard<'a, T>(std::sync::RwLockWriteGuard<'a, Option<NonNull<T>>>);
+
+impl<'a, T> Deref for WriteGuard<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            match *self.0 {
+                Some(v) => &*v.as_ptr(),
+                None => panic!("Scoped pointer is invalidated"),
+            }
+        }
+    }
+}
+
+impl<'a, T> DerefMut for WriteGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe {
+            match *self.0 {
+                Some(v) => &mut *v.as_ptr(),
+                None => panic!("Scoped pointer is invalidated"),
+            }
+        }
+    }
+}
diff --git a/vm/src/channel.rs b/vm/src/channel.rs
index 09ba78b5..15069991 100644
--- a/vm/src/channel.rs
+++ b/vm/src/channel.rs
@@ -95,7 +95,7 @@ where
         let symbol = vm
             .global_env()
             .get_env()
-            .find_type_info("Sender")
+            .find_type_info("std.channel.Sender")
             .unwrap()
             .name
             .clone();
@@ -112,7 +112,7 @@ where
         let symbol = vm
             .global_env()
             .get_env()
-            .find_type_info("Receiver")
+            .find_type_info("std.channel.Receiver")
             .unwrap()
             .name
             .clone();
diff --git a/vm/src/lazy.rs b/vm/src/lazy.rs
index dcea3ad9..7ed3e6d9 100644
--- a/vm/src/lazy.rs
+++ b/vm/src/lazy.rs
@@ -6,15 +6,17 @@ use futures::{
     Future,
 };
 
-use crate::api::generic::A;
-use crate::api::{FunctionRef, Getable, OpaqueValue, Pushable, Pushed, Userdata, VmType, WithVM};
-use crate::base::types;
-use crate::base::types::{ArcType, Type};
-use crate::gc::{Gc, GcPtr, Move, Traverseable};
-use crate::thread::{RootedThread, ThreadInternal};
-use crate::value::{Cloner, Value};
-use crate::vm::Thread;
-use crate::{Error, ExternModule, Result, Variants};
+use crate::{
+    api::{
+        generic::A, FunctionRef, Getable, OpaqueValue, Pushable, Pushed, Userdata, VmType, WithVM,
+    },
+    base::types::{self, ArcType},
+    gc::{Gc, GcPtr, Move, Traverseable},
+    thread::{RootedThread, ThreadInternal},
+    value::{Cloner, Value},
+    vm::Thread,
+    Error, ExternModule, Result, Variants,
+};
 
 pub struct Lazy<T> {
     value: Mutex<Lazy_>,
@@ -79,9 +81,12 @@ where
 
     fn make_type(vm: &Thread) -> ArcType {
         let env = vm.global_env().get_env();
-        let symbol = env.find_type_info("Lazy").unwrap().name.clone();
-        let ctor = Type::ident(symbol);
-        types::Type::app(ctor, collect![T::make_type(vm)])
+        let alias = env
+            .find_type_info("std.lazy.Lazy")
+            .unwrap()
+            .into_owned()
+            .into_type();
+        types::Type::app(alias, collect![T::make_type(vm)])
     }
 }
 
@@ -194,6 +199,7 @@ pub fn load(vm: &Thread) -> Result<ExternModule> {
     ExternModule::new(
         vm,
         record! {
+            type std::lazy::Lazy a => Lazy<A>,
             lazy => primitive!(1, std::lazy::lazy),
             force => primitive!(1, async fn std::lazy::force)
         },
diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs
index 6aff0c8f..6acde751 100644
--- a/vm/src/primitives.rs
+++ b/vm/src/primitives.rs
@@ -593,11 +593,13 @@ pub enum Component<'a> {
     Normal(&'a OsStr),
 }
 
-#[derive(Userdata, Debug)]
+#[derive(Userdata, Debug, VmType)]
+#[gluon(vm_type = "std.fs.Metadata")]
 #[gluon(gluon_vm)]
 pub struct Metadata(fs::Metadata);
 
-#[derive(Userdata, Debug)]
+#[derive(Userdata, Debug, VmType)]
+#[gluon(vm_type = "std.fs.DirEntry")]
 #[gluon(gluon_vm)]
 pub struct DirEntry(fs::DirEntry);
 
diff --git a/vm/src/reference.rs b/vm/src/reference.rs
index 7dae6185..12290ca0 100644
--- a/vm/src/reference.rs
+++ b/vm/src/reference.rs
@@ -1,14 +1,17 @@
 use crate::real_std::{any::Any, fmt, marker::PhantomData, sync::Mutex};
 
-use crate::api::generic::A;
-use crate::api::{Generic, RuntimeResult, Unrooted, Userdata, VmType, WithVM};
-use crate::base::types::{ArcType, Type};
-use crate::gc::{Gc, GcPtr, Move, Traverseable};
-use crate::thread::ThreadInternal;
-use crate::value::{Cloner, Value};
-use crate::vm::Thread;
-use crate::{ExternModule, Result};
+use crate::{
+    api::{generic::A, Generic, RuntimeResult, Unrooted, Userdata, WithVM},
+    gc::{Gc, GcPtr, Move, Traverseable},
+    thread::ThreadInternal,
+    value::{Cloner, Value},
+    vm::Thread,
+    ExternModule, Result,
+};
 
+#[derive(VmType)]
+#[gluon(gluon_vm)]
+#[gluon(vm_type = "std.reference.Reference")]
 pub struct Reference<T> {
     value: Mutex<Value>,
     thread: GcPtr<Thread>,
@@ -43,21 +46,6 @@ impl<T> Traverseable for Reference<T> {
     }
 }
 
-impl<T> VmType for Reference<T>
-where
-    T: VmType,
-    T::Type: Sized,
-{
-    type Type = Reference<T::Type>;
-
-    fn make_type(vm: &Thread) -> ArcType {
-        let env = vm.global_env().get_env();
-        let symbol = env.find_type_info("Ref").unwrap().name.clone();
-        let ctor = Type::ident(symbol);
-        Type::app(ctor, collect![T::make_type(vm)])
-    }
-}
-
 fn set(r: &Reference<A>, a: Generic<A>) -> RuntimeResult<(), String> {
     match r.thread.deep_clone_value(&r.thread, a.get_variant()) {
         Ok(a) => {
@@ -91,7 +79,7 @@ mod std {
 pub fn load(vm: &Thread) -> Result<ExternModule> {
     use self::std;
 
-    let _ = vm.register_type::<Reference<A>>("std.reference.Ref", &["a"]);
+    let _ = vm.register_type::<Reference<A>>("std.reference.Reference", &["a"]);
     ExternModule::new(
         vm,
         record! {
diff --git a/vm/src/stack.rs b/vm/src/stack.rs
index d28aaef8..7f27d426 100644
--- a/vm/src/stack.rs
+++ b/vm/src/stack.rs
@@ -486,7 +486,7 @@ where
 {
     fn drop(&mut self) {
         // Move the cached frame back to storage
-        self.store_frame()
+        self.store_frame();
     }
 }
 
diff --git a/vm/src/thread.rs b/vm/src/thread.rs
index c52772ad..4673830c 100644
--- a/vm/src/thread.rs
+++ b/vm/src/thread.rs
@@ -367,8 +367,6 @@ pub struct Thread {
     // thread can refer to any value in the parent thread
     #[cfg_attr(feature = "serde_derive", serde(state))]
     parent: Option<RootedThread>,
-    #[cfg_attr(feature = "serde_derive", serde(skip))]
-    roots: RwLock<Vec<GcPtr<Traverseable + Send + Sync>>>,
     #[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
@@ -419,6 +417,8 @@ impl<'vm> Pushable<'vm> for RootedThread {
 }
 
 impl<'vm, 'value> Getable<'vm, 'value> for RootedThread {
+    impl_getable_simple!();
+
     fn from_value(_: &'vm Thread, value: Variants<'value>) -> Self {
         match value.as_ref() {
             ValueRef::Thread(thread) => thread.root_thread(),
@@ -497,7 +497,6 @@ impl RootedThread {
                 global_state.gc.get_mut().unwrap().new_child_gc(),
             )),
             global_state: Arc::new(global_state),
-            roots: RwLock::new(Vec::new()),
             rooted_values: RwLock::new(Vec::new()),
             child_threads: RwLock::new(Vec::new()),
             interrupt: AtomicBool::new(false),
@@ -540,7 +539,6 @@ impl Thread {
             global_state: self.global_state.clone(),
             parent: Some(self.root_thread()),
             context: Mutex::new(Context::new(self.owned_context().gc.new_child_gc())),
-            roots: RwLock::new(Vec::new()),
             rooted_values: RwLock::new(Vec::new()),
             child_threads: RwLock::new(Vec::new()),
             interrupt: AtomicBool::new(false),
@@ -703,6 +701,10 @@ impl Thread {
         self.global_env().register_type_as(name, alias, id)
     }
 
+    pub fn cache_alias(&self, alias: Alias<Symbol, ArcType>) -> ArcType {
+        self.global_env().cache_alias(alias)
+    }
+
     /// Locks and retrieves the global environment of the vm
     pub fn get_env<'b>(&'b self) -> RwLockReadGuard<'b, VmEnv> {
         self.global_env().get_env()
@@ -769,7 +771,6 @@ impl Thread {
 
     fn traverse_fields_except_stack(&self, gc: &mut Gc) {
         self.global_state.traverse(gc);
-        self.roots.read().unwrap().traverse(gc);
         self.rooted_values.read().unwrap().traverse(gc);
         self.child_threads.read().unwrap().traverse(gc);
     }
@@ -1763,7 +1764,7 @@ impl<'b> ExecuteContext<'b> {
                             return Err(Error::Panic(
                                 format!("ICE: Stack push out of bounds in {}", function.name),
                                 Some(self.stack.stack.stacktrace(0)),
-                            ))
+                            ));
                         }
                     };
                     self.stack.push(v);
@@ -2477,6 +2478,12 @@ impl<'vm> ActiveThread<'vm> {
         PopValue(self, Variants(value.get_repr(), ::std::marker::PhantomData))
     }
 
+    pub(crate) fn last<'a>(&'a self) -> Option<Variants<'a>> {
+        let stack = &self.context.as_ref().unwrap().stack;
+        let last = stack.len() - 1;
+        stack.get_variant(last)
+    }
+
     // For gluon_codegen
     #[doc(hidden)]
     pub fn context(&mut self) -> &mut Context {
diff --git a/vm/src/vm.rs b/vm/src/vm.rs
index 29b8fdc8..81023593 100644
--- a/vm/src/vm.rs
+++ b/vm/src/vm.rs
@@ -286,14 +286,20 @@ where
 impl VmEnv {
     pub fn find_type_info(&self, name: &str) -> Result<Cow<Alias<Symbol, ArcType>>> {
         let name = Name::new(name);
-        let module_str = name.module().as_str();
-        if module_str == "" {
-            return match self.type_infos.id_to_type.get(name.as_str()) {
-                Some(alias) => Ok(Cow::Borrowed(alias)),
-                None => Err(Error::UndefinedBinding(name.as_str().into())),
-            };
+
+        if let Some(alias) = self.type_infos.id_to_type.get(name.as_str()) {
+            return Ok(Cow::Borrowed(alias));
         }
-        let (_, typ) = self.get_binding(name.module().as_str())?;
+
+        let (_, typ) = self
+            .get_binding(name.module().as_str())
+            .map_err(|mut err| {
+                if let Error::UndefinedBinding(module) = &mut err {
+                    module.clear();
+                    module.push_str(name.as_str());
+                }
+                err
+            })?;
         let maybe_type_info = map_cow_option(typ.clone(), |typ| {
             let field_name = name.name();
             typ.type_field_iter()
@@ -571,8 +577,8 @@ impl GlobalVmState {
     ) -> Result<ArcType> {
         let mut env = self.env.write().unwrap();
         let type_infos = &mut env.type_infos;
-        if type_infos.id_to_type.contains_key(name.declared_name()) {
-            Err(Error::TypeAlreadyExists(name.declared_name().into()))
+        if type_infos.id_to_type.contains_key(name.definition_name()) {
+            Err(Error::TypeAlreadyExists(name.definition_name().into()))
         } else {
             self.typeids
                 .write()
@@ -581,11 +587,21 @@ impl GlobalVmState {
             let t = alias.clone().into_type();
             type_infos
                 .id_to_type
-                .insert(name.declared_name().into(), alias);
+                .insert(name.definition_name().into(), alias);
             Ok(t)
         }
     }
 
+    pub fn cache_alias(&self, alias: Alias<Symbol, ArcType>) -> ArcType {
+        let mut env = self.env.write().unwrap();
+        let type_infos = &mut env.type_infos;
+        let t = alias.clone().into_type();
+        type_infos
+            .id_to_type
+            .insert(alias.name.definition_name().into(), alias);
+        t
+    }
+
     pub fn get_macros(&self) -> &MacroEnv {
         &self.macros
     }


Test patch:
diff --git a/codegen/tests/derive_userdata.rs b/codegen/tests/derive_userdata.rs
index a5678887..a0965ddd 100644
--- a/codegen/tests/derive_userdata.rs
+++ b/codegen/tests/derive_userdata.rs
@@ -11,7 +11,8 @@ use gluon::{import, Compiler, Thread};
 use init::new_vm;
 use std::sync::Arc;
 
-#[derive(Userdata, Debug)]
+#[derive(Userdata, Debug, VmType)]
+#[gluon(vm_type = "WindowHandle")]
 struct WindowHandle {
     id: Arc<u64>,
     metadata: Arc<str>,
diff --git a/codegen/tests/derive_vm_type.rs b/codegen/tests/derive_vm_type.rs
index 5d09888e..64c9fdfc 100644
--- a/codegen/tests/derive_vm_type.rs
+++ b/codegen/tests/derive_vm_type.rs
@@ -4,7 +4,7 @@ extern crate gluon;
 
 mod init;
 
-use gluon::vm::api::VmType;
+use gluon::{base::types::Type, vm::api::VmType};
 use init::new_vm;
 
 #[derive(VmType)]
@@ -45,14 +45,35 @@ fn enum_() {
 
 #[derive(VmType)]
 #[allow(unused)]
-struct Newtype(Struct);
+struct NewtypeInner(Struct);
 
 #[test]
-fn newtype() {
+fn newtype_inner() {
     let vm = new_vm();
 
     assert_eq!(
-        Newtype::make_type(&vm).to_string(),
+        NewtypeInner::make_type(&vm).to_string(),
         Struct::make_type(&vm).to_string(),
     );
 }
+
+#[derive(VmType)]
+#[gluon(newtype)]
+#[allow(unused)]
+struct Newtype(Struct);
+
+#[test]
+fn newtype() {
+    let vm = new_vm();
+
+    match &*Newtype::make_type(&vm) {
+        Type::Alias(alias) => {
+            assert_eq!(alias.name.declared_name(), "Newtype");
+            assert_eq!(
+                alias.unresolved_type().to_string(),
+                Struct::make_type(&vm).to_string()
+            );
+        }
+        _ => panic!(),
+    }
+}
diff --git a/tests/api.rs b/tests/api.rs
index d0b44773..362a5a67 100644
--- a/tests/api.rs
+++ b/tests/api.rs
@@ -7,17 +7,22 @@ extern crate gluon;
 #[macro_use]
 extern crate gluon_vm;
 
-use futures::future::lazy;
-use futures::{Future, IntoFuture};
-
-use gluon::base::types::{Alias, ArcType, Type};
-use gluon::import::{add_extern_module, Import};
-use gluon::vm::api::de::De;
-use gluon::vm::api::{FunctionRef, FutureResult, OpaqueValue, RuntimeResult, Userdata, VmType, IO};
-use gluon::vm::thread::{RootedThread, Thread, Traverseable};
-use gluon::vm::types::VmInt;
-use gluon::vm::{Error, ExternModule};
-use gluon::Compiler;
+use futures::{future::lazy, Future, IntoFuture};
+
+use gluon::{
+    base::types::{Alias, ArcType, Type},
+    import::{add_extern_module, Import},
+    vm::{
+        api::{
+            de::De, scoped::{Ref, RefMut}, FunctionRef, FutureResult, OpaqueValue, OwnedFunction,
+            RuntimeResult, Userdata, VmType, IO,
+        },
+        thread::{RootedThread, Thread, Traverseable},
+        types::VmInt,
+        Error, ExternModule,
+    },
+    Compiler,
+};
 
 fn load_script(vm: &Thread, filename: &str, input: &str) -> ::gluon::Result<()> {
     Compiler::new().load_script(vm, filename, input)
@@ -34,6 +39,14 @@ fn make_vm() -> RootedThread {
     vm
 }
 
+#[derive(Debug)]
+struct Test(VmInt);
+impl Userdata for Test {}
+impl Traverseable for Test {}
+impl VmType for Test {
+    type Type = Test;
+}
+
 #[test]
 fn call_function() {
     let _ = ::env_logger::try_init();
@@ -60,14 +73,6 @@ fn call_function() {
 fn root_data() {
     let _ = ::env_logger::try_init();
 
-    #[derive(Debug)]
-    struct Test(VmInt);
-    impl Userdata for Test {}
-    impl Traverseable for Test {}
-    impl VmType for Test {
-        type Type = Test;
-    }
-
     let expr = r#"
         let test = import! test
         \x -> test x 1
@@ -405,3 +410,120 @@ fn runtime_result_vm_type_forwarding() {
         .run_expr::<IO<()>>(&vm, "test", text)
         .unwrap_or_else(|err| panic!("{}", err));
 }
+
+#[test]
+fn scoped_reference_basic() {
+    let _ = ::env_logger::try_init();
+
+    fn function(r: &Test, i: VmInt) -> VmInt {
+        r.0 + i
+    }
+
+    let expr = r#"
+        let function = import! function
+        \x -> function x 1
+    "#;
+
+    let vm = make_vm();
+    vm.register_type::<Test>("Test", &[])
+        .unwrap_or_else(|_| panic!("Could not add type"));
+    add_extern_module(&vm, "function", |thread| {
+        ExternModule::new(thread, primitive!(2, function))
+    });
+
+    let (mut result, _) = Compiler::new()
+        .run_expr::<OwnedFunction<fn(_) -> VmInt>>(&vm, "<top>", expr)
+        .unwrap_or_else(|err| panic!("{}", err));
+
+    assert_eq!(
+        result
+            .call(&mut Ref::new(&Test(2)))
+            .unwrap_or_else(|err| panic!("{}", err)),
+        3
+    );
+}
+
+#[test]
+fn scoped_reference_out_of_scope() {
+    let _ = ::env_logger::try_init();
+
+    fn recursive<'a, 'b>(
+        f: OwnedFunction<fn(OpaqueValue<RootedThread, Test>) -> OpaqueValue<RootedThread, Test>>,
+    ) -> OpaqueValue<RootedThread, Test> {
+        let mut f: OwnedFunction<fn(_) -> OpaqueValue<RootedThread, Test>> = f.cast().unwrap();
+        f.call(&mut Ref::new(&Test(1))).unwrap()
+    }
+
+    fn function(r: &Test, i: VmInt) -> VmInt {
+        r.0 + i
+    }
+
+    let expr = r#"
+        let { recursive, function } = import! module
+        function (recursive (\t -> t)) 1
+    "#;
+
+    let vm = make_vm();
+    vm.register_type::<Test>("Test", &[])
+        .unwrap_or_else(|_| panic!("Could not add type"));
+    add_extern_module(&vm, "module", |thread| {
+        ExternModule::new(
+            thread,
+            record! {
+                recursive => primitive!(1, recursive),
+                function => primitive!(2, function)
+            },
+        )
+    });
+
+    let result = Compiler::new().run_expr::<VmInt>(&vm, "<top>", expr);
+    match result {
+        Err(gluon::Error::VM(Error::Panic(ref m, _))) if m == "Scoped pointer is invalidated" => (),
+        Err(err) => panic!("Wrong error: {:#?}", err),
+        Ok(_) => panic!("Unexpected success"),
+    }
+}
+
+#[test]
+fn scoped_mutable_reference() {
+    let _ = ::env_logger::try_init();
+
+    fn write(r: &mut Test, i: VmInt) -> IO<()> {
+        r.0 = i;
+        IO::Value(())
+    }
+
+    fn read(r: &mut Test) -> IO<VmInt> {
+        IO::Value(r.0)
+    }
+
+    let expr = r#"
+        let { read, write } = import! function
+        let { ? } = import! std.io
+        \t i ->
+            seq write t (i + 10)
+            read t
+    "#;
+
+    let vm = make_vm();
+    vm.register_type::<Test>("Test", &[])
+        .unwrap_or_else(|_| panic!("Could not add type"));
+    add_extern_module(&vm, "function", |thread| {
+        ExternModule::new(thread, record! {
+            write => primitive!(2, write),
+            read => primitive!(1, read)
+        })
+    });
+
+    let (mut result, _) = Compiler::new()
+        .run_expr::<OwnedFunction<fn(_, _) -> IO<VmInt>>>(&vm, "<top>", expr)
+        .unwrap_or_else(|err| panic!("{}", err));
+
+    assert_eq!(
+        result
+            .call(&mut RefMut::new(&mut Test(2)), 3)
+            .unwrap_or_else(|err| panic!("{}", err)),
+        IO::Value(13)
+    );
+}
+
diff --git a/tests/pass/buffered_io.glu b/tests/pass/buffered_io.glu
index 2b8140f0..2e53be22 100644
--- a/tests/pass/buffered_io.glu
+++ b/tests/pass/buffered_io.glu
@@ -8,7 +8,7 @@ let { (<|), (|>) } = import! std.function
 let { ? } = import! std.io
 let io_read @ { Read, default_read_to_end, read, read_to_end, ? } = import! std.io.read
 let io_write @ { Write, write_slice, write, write_all, flush, ? } = import! std.io.write
-let { ref, (<-), load } = import! std.reference
+let { Reference, ref, (<-), load } = import! std.reference
 let { min } = import! std.cmp
 let { ? } = import! std.byte
 let array @ { ? } = import! std.array
@@ -17,7 +17,7 @@ let { lift } = import! std.effect.lift
 let { test, group } = import! std.test
 
 type Cursor = {
-    pos : Ref Int,
+    pos : Reference Int,
     buf : Array Byte
 }
 
@@ -43,7 +43,7 @@ let read_cursor : Read Cursor =
         read_to_end = default_read_to_end read
     }
 
-let write_array_ref : Write (Ref (Array Byte)) = {
+let write_array_ref : Write (Reference (Array Byte)) = {
     write_slice = \array_ref buf start end ->
         let written = array.append (load array_ref) (array.slice buf start end)
         array_ref <- written
@@ -101,7 +101,7 @@ let test_read_to_end = [
 
 let test_write_and_flush = [
     test "buffer all data if it fits" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 5
 
         do bytes_written = lift <| write writer [1b, 2b]
@@ -111,7 +111,7 @@ let test_write_and_flush = [
         assert_eq (load written) [1b, 2b],
     
     test "flush immediately if buffer is empty and new data wouldn't fit" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 2
 
         do bytes_written = lift <| write writer [1b, 2b, 3b]
@@ -121,7 +121,7 @@ let test_write_and_flush = [
         assert_eq (load written) [1b, 2b, 3b],
 
     test "flush buffer and then write new data if it wouldn't fit the buffer" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 2
 
         do bytes_written = lift <| write writer [1b]
@@ -134,7 +134,7 @@ let test_write_and_flush = [
         assert_eq (load written) [1b, 2b, 3b],
 
     test "flush buffer if new data doesn't fit, then buffer the data" <| \_ -> 
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 5
 
         do bytes_written = lift <| write writer [1b, 2b, 3b]
@@ -149,7 +149,7 @@ let test_write_and_flush = [
 
 let test_write_slice = [
     test "empty slice" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 5
         do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 0 0
         do _ = assert_eq (bytes_written) 0
@@ -157,7 +157,7 @@ let test_write_slice = [
         assert_eq (load written) [],
 
     test "slice" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 5
         do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 1 3
         do _ = assert_eq (bytes_written) 2
@@ -165,7 +165,7 @@ let test_write_slice = [
         assert_eq (load written) [2b, 3b],
 
     test "slice everything" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 5
         do bytes_written = lift <| write_slice writer [1b, 2b, 3b, 4b] 0 4
         do _ = assert_eq (bytes_written) 4
@@ -175,14 +175,14 @@ let test_write_slice = [
 
 let test_write_all = [
     test "basic" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 1
         do _ = lift <| write_all writer [1b, 2b, 3b, 4b]
         do _ = lift <| flush writer
         assert_eq (load written) [1b, 2b, 3b, 4b],
 
     test "write the data that's already buffered too" <| \_ ->
-        let written : Ref (Array Byte) = ref []
+        let written : Reference (Array Byte) = ref []
         let writer = written |> io_write.buffered_with_capacity 2
         do _ = lift <| write writer [1b]
         do _ = lift <| write_all writer [2b, 3b, 4b]
diff --git a/tests/pattern_match.rs b/tests/pattern_match.rs
index 6fbd1ac8..3d5091e0 100644
--- a/tests/pattern_match.rs
+++ b/tests/pattern_match.rs
@@ -5,8 +5,8 @@ extern crate gluon;
 #[macro_use]
 mod support;
 
-use gluon::Compiler;
 use crate::support::*;
+use gluon::Compiler;
 
 test_expr! { prelude match_on_bool,
 r#"
diff --git a/tests/vm.rs b/tests/vm.rs
index f7dfd14d..cdb0e04e 100644
--- a/tests/vm.rs
+++ b/tests/vm.rs
@@ -810,7 +810,7 @@ fn completion_with_prelude() {
 let prelude  = import! std.prelude
 let { Option } = import! std.option
 let { Num } = prelude
-let { lazy } = import! std.lazy
+let { Lazy, lazy } = import! std.lazy
 
 rec
 type Stream_ a =
@@ -1059,7 +1059,7 @@ let category : Category (->) = {
 
 test_expr! { load_io_skolem_bug,
 r"
-let io_prim = import! std.io.prim
+let io_prim @ { IO } = import! std.io.prim
 
 type Monad (m : Type -> Type) = {
     flat_map : forall a b . (a -> m b) -> m a -> m b