diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 568e454a8b..ead9d30c80 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -860,6 +860,7 @@ impl Field { pub fn ctor_with( context: &mut (impl TypeContext + ?Sized), + enum_type: KindedIdent, ctor_name: SpId, elems: J, ) -> Self @@ -868,22 +869,22 @@ impl Field { J::IntoIter: DoubleEndedIterator, T: TypePtr, { - let opaque = context.opaque(); - let typ = context.function_type(ArgType::Constructor, elems, opaque); + let enum_type = context.ident(enum_type); + let typ = context.function_type(ArgType::Constructor, elems, enum_type); Field { name: ctor_name, typ, } } - pub fn ctor(ctor_name: SpId, elems: J) -> Self + pub fn ctor(enum_type: KindedIdent, ctor_name: SpId, elems: J) -> Self where J: IntoIterator, J::IntoIter: DoubleEndedIterator, T: TypeExt + From>, T::Types: Default + Extend, { - let typ = Type::function_type(ArgType::Constructor, elems, Type::opaque()); + let typ = Type::function_type(ArgType::Constructor, elems, Type::ident(enum_type)); Field { name: ctor_name, typ, diff --git a/check/tests/pass.rs b/check/tests/pass.rs index a9213e001a..63e30b9189 100644 --- a/check/tests/pass.rs +++ b/check/tests/pass.rs @@ -127,7 +127,7 @@ in Test2 (\x -> x #Int+ 2) let test2 = AliasData::new( intern("Test2"), vec![], - Type::variant(vec![support::variant("Test2", &[typ("Test")])]), + Type::variant(vec![support::variant("Test2", "Test2", &[typ("Test")])]), ); let expected = Ok(Alias::group(vec![test, test2])[1].as_type().clone()); @@ -536,7 +536,11 @@ fn type_pattern() { type Test = | Test String Int in { Test, x = 1 } "#; let result = support::typecheck(text); - let test = Type::variant(vec![support::variant("Test", &[typ("String"), typ("Int")])]); + let test = Type::variant(vec![support::variant( + "Test", + "Test", + &[typ("String"), typ("Int")], + )]); let types = vec![Field { name: support::intern_unscoped("Test"), typ: Alias::new(intern("Test"), Vec::new(), test), diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs index ad5f09289b..69045f7a55 100644 --- a/check/tests/support/mod.rs +++ b/check/tests/support/mod.rs @@ -7,7 +7,7 @@ extern crate gluon_parser as parser; use self::{ base::{ - ast::{DisplayEnv, Expr, IdentEnv, KindedIdent, RootExpr, SpannedExpr}, + ast::{DisplayEnv, Expr, IdentEnv, KindedIdent, RootExpr, SpannedExpr, TypedIdent}, error::{Errors, InFile}, kind::{ArcKind, Kind, KindEnv}, metadata::{Metadata, MetadataEnv}, @@ -342,9 +342,16 @@ pub fn alias_implicit(s: &str, args: &[&str], typ: ArcType, is_implicit: bool) - ) } -pub fn variant(arg: &str, types: &[ArcType]) -> Field { +pub fn variant(enum_type: &str, arg: &str, types: &[ArcType]) -> Field { let arg = intern_unscoped(arg); - Field::ctor(arg, types.iter().cloned()) + Field::ctor( + TypedIdent { + name: intern_unscoped(enum_type), + typ: Kind::typ(), + }, + arg, + types.iter().cloned(), + ) } pub fn alias_variant(s: &str, params: &[&str], args: &[(&str, &[ArcType])]) -> ArcType { @@ -359,7 +366,7 @@ pub fn alias_variant_implicit( ) -> ArcType { let variants = Type::variant( args.iter() - .map(|(arg, types)| variant(arg, types)) + .map(|(arg, types)| variant(s, arg, types)) .collect(), ); alias_implicit(s, params, variants, is_implicit) diff --git a/codegen/src/arena_clone.rs b/codegen/src/arena_clone.rs index 26125ee16b..4319e7f97d 100644 --- a/codegen/src/arena_clone.rs +++ b/codegen/src/arena_clone.rs @@ -72,21 +72,21 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data }, Data::Enum(ref enum_) => { let variants = enum_.variants.iter().map(|variant| { - let ident = variant.ident.to_string(); + let variant_ident = variant.ident.to_string(); match variant.fields { Fields::Named(ref fields) => { let fields = fields.named.iter().map(|field| { - let ident = field.ident.as_ref().unwrap().to_string(); + let variant_ident = field.variant_ident.as_ref().unwrap().to_string(); let typ = &field.ty; quote! { _gluon_base::types::Field { - name: _gluon_base::symbol::Symbol::from(#ident), + name: _gluon_base::symbol::Symbol::from(#variant_ident), typ: <#typ as _gluon_api::VmType>::make_type(vm), } } }); quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); let typ = _gluon_base::types::Type::record( vec![], vec![#(#fields),*], @@ -105,7 +105,7 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data } }); quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); _gluon_base::types::Field::ctor( ctor_name, vec![#(#args),*], @@ -113,9 +113,14 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data }} } Fields::Unit => quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); _gluon_base::types::Field::ctor( - ctor_name, vec![], + _gluon_base::ast::TypedIdent { + name: _gluon_base::symbol::Symbol::from(#ident), + typ: vm.global_env().type_cache().kind_cache.typ(), + }, + ctor_name, + vec![], ) }}, } diff --git a/codegen/src/vm_type.rs b/codegen/src/vm_type.rs index c54e70a067..ff2ed9ce0f 100644 --- a/codegen/src/vm_type.rs +++ b/codegen/src/vm_type.rs @@ -51,6 +51,10 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data }, }; + // Enums create symbols so we need to cache the created typ or multiple invocations will return + // different types + let mut is_enum = false; + let make_type_impl = match container.vm_type { Some(ref gluon_type) => { let type_application = gen_type_application(&generics); @@ -101,27 +105,32 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data Fields::Unit => quote!(_gluon_base::types::Type::unit()), }, Data::Enum(ref enum_) => { + is_enum = true; let variants = enum_.variants.iter().map(|variant| { - let ident = variant.ident.to_string(); + let variant_ident = variant.ident.to_string(); match variant.fields { Fields::Named(ref fields) => { let fields = fields.named.iter().map(|field| { - let ident = field.ident.as_ref().unwrap().to_string(); + let variant_ident = field.ident.as_ref().unwrap().to_string(); let typ = &field.ty; quote! { _gluon_base::types::Field { - name: _gluon_base::symbol::Symbol::from(#ident), + name: _gluon_base::symbol::Symbol::from(#variant_ident), typ: <#typ as _gluon_api::VmType>::make_type(vm), } } }); quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); let typ = _gluon_base::types::Type::record( vec![], vec![#(#fields),*], ); _gluon_base::types::Field::ctor( + _gluon_base::ast::TypedIdent { + name: type_name.clone(), + typ: vm.global_env().type_cache().kind_cache.typ() + }, ctor_name, vec![typ], ) @@ -135,17 +144,26 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data } }); quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); _gluon_base::types::Field::ctor( + _gluon_base::ast::TypedIdent { + name: type_name.clone(), + typ: vm.global_env().type_cache().kind_cache.typ() + }, ctor_name, vec![#(#args),*], ) }} } Fields::Unit => quote! {{ - let ctor_name = _gluon_base::symbol::Symbol::from(#ident); + let ctor_name = _gluon_base::symbol::Symbol::from(#variant_ident); _gluon_base::types::Field::ctor( - ctor_name, vec![], + _gluon_base::ast::TypedIdent { + name: type_name.clone(), + typ: vm.global_env().type_cache().kind_cache.typ() + }, + ctor_name, + vec![], ) }}, } @@ -173,12 +191,15 @@ 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 make_type_impl = if container.newtype || is_enum { 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) + match *vm.global_env().get_generic(#lower_param) { + _gluon_base::types::Type::Generic(ref gen) => gen.clone(), + _ => unreachable!(), + } } }); @@ -186,8 +207,9 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data let ty = if let Some(ty) = vm.get_cache_alias(stringify!(#ident)) { ty } else { + let type_name = _gluon_base::symbol::Symbol::from(stringify!(#ident)); let ty = _gluon_base::types::Alias::new( - _gluon_base::symbol::Symbol::from(stringify!(#ident)), + type_name.clone(), vec![#(#generic_params),*], #make_type_impl, ); diff --git a/parser/tests/basic.rs b/parser/tests/basic.rs index 8016ac2bfa..5289dbdb0e 100644 --- a/parser/tests/basic.rs +++ b/parser/tests/basic.rs @@ -10,6 +10,7 @@ use crate::support::*; use crate::base::{ ast::*, + kind::Kind, metadata::*, mk_ast_arena, pos::{self, BytePos, Span, Spanned}, @@ -129,7 +130,14 @@ test_parse! { type Test2 = { x: Int, y: {} } in 1"#, |mut arena| { - let test = arena.clone().variant(arena.alloc_extend(vec![Field::ctor_with(&mut arena.clone(), intern("Test").into(), vec![typ(arena, "Int")])])); + let test = arena.clone().variant(arena.alloc_extend(vec![ + Field::ctor_with( + &mut arena.clone(), + TypedIdent { name: intern("Test"), typ: Kind::typ() }, + intern("Test").into(), + vec![typ(arena, "Int")], + ), + ])); let test2 = arena.record( Default::default(), arena.alloc_extend(vec![ @@ -216,8 +224,8 @@ test_parse! { intern("Option"), vec![generic("a")], arena.clone().variant(arena.alloc_extend(vec![ - Field::ctor_with(&mut arena.clone(), intern("None").into(), vec![]), - Field::ctor_with(&mut arena.clone(), intern("Some").into(), vec![typ(arena, "a")]), + Field::ctor_with(&mut arena.clone(), TypedIdent { name: intern("Option"), typ: Kind::typ() }, intern("None").into(), vec![]), + Field::ctor_with(&mut arena.clone(), TypedIdent { name: intern("Option"), typ: Kind::typ() }, intern("Some").into(), vec![typ(arena, "a")]), ])), app(arena, id("Some"), vec![int(1)]), ) diff --git a/parser/tests/support/mod.rs b/parser/tests/support/mod.rs index aba52e890c..eecb28b9c4 100644 --- a/parser/tests/support/mod.rs +++ b/parser/tests/support/mod.rs @@ -472,6 +472,7 @@ pub fn line_comment(s: &str) -> Metadata { pub fn variant<'ast, Id>( mut arena: ast::ArenaRef<'_, 'ast, Id>, + enum_type: &str, arg: &str, types: impl IntoIterator< Item = AstType<'ast, Id>, @@ -483,6 +484,10 @@ where { Field::ctor_with( &mut arena, + TypedIdent { + name: enum_type.into(), + typ: Kind::typ(), + }, pos::spanned(Default::default(), arg.into()), types, ) @@ -508,7 +513,7 @@ where let variants = arena.clone().variant( arena.alloc_extend( args.into_iter() - .map(|(arg, types)| variant(arena, arg, types)) + .map(|(arg, types)| variant(arena, s, arg, types)) .collect::>(), ), ); diff --git a/tests/vm.rs b/tests/vm.rs index 8e6fb1734a..33ac900a43 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -10,14 +10,17 @@ use crate::support::*; use gluon::{ base::{pos::BytePos, source::Source, types::Type}, - vm, + import::add_extern_module, + record, vm, vm::{ api::{FunctionRef, Hole, OpaqueValue, ValueRef, IO}, channel::Sender, thread::Thread, + ExternModule, }, Error, ThreadExt, }; +use gluon_codegen::{Getable, Pushable, VmType}; test_expr! { pass_function_value, r" @@ -1103,3 +1106,40 @@ let f a = { f } " } + +#[derive(Clone, Debug, VmType, Pushable, Getable)] +enum E1 { + S1(S), +} + +#[derive(Clone, Debug, VmType, Pushable, Getable)] +struct S { + e: E2, +} + +#[derive(Clone, Debug, VmType, Pushable, Getable)] +enum E2 { + Num(i32), +} + +#[test] +fn issue_901() { + let _ = ::env_logger::try_init(); + let text = r" +let { E1, S, E2 } = import! test +let e1 = S1 { e = Num 3 } +() +"; + let vm = make_vm(); + add_extern_module(&vm, "test", |vm| { + ExternModule::new( + vm, + record! { + type E1 => E1, + type S => S, + type E2 => E2, + }, + ) + }); + run_expr::>(&vm, text); +} diff --git a/vm/src/api/typ.rs b/vm/src/api/typ.rs index daa06185d2..26259ed41c 100644 --- a/vm/src/api/typ.rs +++ b/vm/src/api/typ.rs @@ -2,8 +2,11 @@ //! //! _This module requires Gluon to be built with the `serde` feature._ -use crate::base::symbol::{Symbol, Symbols}; -use crate::base::types::{ArcType, Field, Type, TypeCache, TypeExt}; +use crate::base::{ + ast::TypedIdent, + symbol::{Symbol, Symbols}, + types::{ArcType, Field, Type, TypeCache, TypeExt}, +}; use crate::api::VmType; use crate::thread::Thread; @@ -521,6 +524,10 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { fn unit_variant(self) -> Result<()> { self.de.variant = Some(Field::ctor( + TypedIdent { + name: self.de.state.symbols.simple_symbol(self.de.name), + typ: self.de.state.cache.kind_cache.typ(), + }, self.de.state.symbols.simple_symbol(self.variant), vec![], )); @@ -533,6 +540,10 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { { let value = seed.deserialize(&mut *self.de)?; self.de.variant = Some(Field::ctor( + TypedIdent { + name: self.de.state.symbols.simple_symbol(self.de.name), + typ: self.de.state.cache.kind_cache.typ(), + }, self.de.state.symbols.simple_symbol(self.variant), vec![self.de.typ.take().expect("typ")], )); @@ -551,6 +562,10 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { ) }; self.de.variant = Some(Field::ctor( + TypedIdent { + name: self.de.state.symbols.simple_symbol(self.de.name), + typ: self.de.state.cache.kind_cache.typ(), + }, self.de.state.symbols.simple_symbol(self.variant), types, )); @@ -569,6 +584,10 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { ) }; self.de.variant = Some(Field::ctor( + TypedIdent { + name: self.de.state.symbols.simple_symbol(self.de.name), + typ: self.de.state.cache.kind_cache.typ(), + }, self.de.state.symbols.simple_symbol(self.variant), vec![self.de.state.cache.record(vec![], types)], )); @@ -582,6 +601,8 @@ mod tests { use super::*; use crate::thread::RootedThread; + use base::kind::Kind; + #[allow(dead_code)] #[derive(Deserialize)] struct Test { @@ -622,16 +643,26 @@ mod tests { let (name, typ) = from_rust_with_symbols::(&mut symbols, &RootedThread::new()).unwrap(); assert_eq!(name.declared_name(), "Enum"); + let enum_ident = TypedIdent { + name: symbols.simple_symbol("Enum"), + typ: Kind::typ(), + }; assert_eq!( typ, Type::variant(vec![ - Field::ctor(symbols.simple_symbol("A"), vec![]), - Field::ctor(symbols.simple_symbol("B"), vec![Type::int()]), + Field::ctor(enum_ident.clone(), symbols.simple_symbol("A"), vec![]), + Field::ctor( + enum_ident.clone(), + symbols.simple_symbol("B"), + vec![Type::int()] + ), Field::ctor( + enum_ident.clone(), symbols.simple_symbol("C"), vec![Type::string(), Type::float()], ), Field::ctor( + enum_ident, symbols.simple_symbol("D"), vec![Type::record( vec![],