Skip to content

Commit

Permalink
fix: Make VmType generate correct variant constructors
Browse files Browse the repository at this point in the history
The opaque return type was likely a quick hack that went unnoticed. I
also had to force caching on the enum since the `Symbol` values created
for the variants and the returned enum will otherwise be different
between multiple `VmType::make_type` invocations (unless

Fixes gluon-lang#901
  • Loading branch information
Marwes committed Jan 9, 2021
1 parent bbd2ee5 commit af05abf
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 36 deletions.
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

0 comments on commit af05abf

Please sign in to comment.