diff --git a/codegen/src/attr.rs b/codegen/src/attr.rs index 14df77e47e..c79c1a25fe 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, + 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/vm_type.rs b/codegen/src/vm_type.rs index a4a5cf0cc1..729468266f 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/codegen/tests/derive_userdata.rs b/codegen/tests/derive_userdata.rs index a5678887c4..a0965ddd65 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, metadata: Arc, diff --git a/codegen/tests/derive_vm_type.rs b/codegen/tests/derive_vm_type.rs index 5d09888e60..64c9fdfc9a 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/vm/src/thread.rs b/vm/src/thread.rs index c99e50cc78..4673830c54 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -701,6 +701,10 @@ impl Thread { self.global_env().register_type_as(name, alias, id) } + pub fn cache_alias(&self, alias: Alias) -> 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() @@ -1484,7 +1488,9 @@ impl<'b> OwnedContext<'b> { maybe_context = match state { State::Unknown => return Ok(Async::Ready(Some(context))), - State::Extern(ref ext) if ext.is_locked() => return Ok(Async::Ready(Some(context))), + State::Extern(ref ext) if ext.is_locked() => { + return Ok(Async::Ready(Some(context))) + } State::Extern(mut ext) => { // We are currently in the poll call of this extern function. diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 6bb77ace06..8102359326 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -592,6 +592,16 @@ impl GlobalVmState { } } + pub fn cache_alias(&self, alias: Alias) -> 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 }