Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Make VmType generate correct variant constructors #902

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions base/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,7 @@ impl<SpId, T> Field<SpId, T> {

pub fn ctor_with<J, Id>(
context: &mut (impl TypeContext<Id, T> + ?Sized),
enum_type: KindedIdent<Id>,
ctor_name: SpId,
elems: J,
) -> Self
Expand All @@ -868,22 +869,22 @@ impl<SpId, T> Field<SpId, T> {
J::IntoIter: DoubleEndedIterator,
T: TypePtr<Id = Id>,
{
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<J, Id>(ctor_name: SpId, elems: J) -> Self
pub fn ctor<J, Id>(enum_type: KindedIdent<Id>, ctor_name: SpId, elems: J) -> Self
where
J: IntoIterator<Item = T>,
J::IntoIter: DoubleEndedIterator,
T: TypeExt<Id = Id> + From<Type<Id, T>>,
T::Types: Default + Extend<T>,
{
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,
Expand Down
8 changes: 6 additions & 2 deletions check/tests/pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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),
Expand Down
15 changes: 11 additions & 4 deletions check/tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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<Symbol, ArcType> {
pub fn variant(enum_type: &str, arg: &str, types: &[ArcType]) -> Field<Symbol, ArcType> {
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 {
Expand All @@ -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)
Expand Down
19 changes: 12 additions & 7 deletions codegen/src/arena_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),*],
Expand All @@ -105,17 +105,22 @@ 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),*],
)
}}
}
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![],
)
}},
}
Expand Down
42 changes: 32 additions & 10 deletions codegen/src/vm_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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],
)
Expand All @@ -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![],
)
}},
}
Expand Down Expand Up @@ -173,21 +191,25 @@ 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!(),
}
}
});

quote! {
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,
);
Expand Down
14 changes: 11 additions & 3 deletions parser/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::support::*;

use crate::base::{
ast::*,
kind::Kind,
metadata::*,
mk_ast_arena,
pos::{self, BytePos, Span, Spanned},
Expand Down Expand Up @@ -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![
Expand Down Expand Up @@ -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)]),
)
Expand Down
7 changes: 6 additions & 1 deletion parser/tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -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,
)
Expand All @@ -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::<Vec<_>>(),
),
);
Expand Down
42 changes: 41 additions & 1 deletion tests/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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::<OpaqueValue<&Thread, Hole>>(&vm, text);
}
Loading