diff --git a/src/glimpse/internal/typecheck.gleam b/src/glimpse/internal/typecheck.gleam index 08685d3..8731e32 100644 --- a/src/glimpse/internal/typecheck.gleam +++ b/src/glimpse/internal/typecheck.gleam @@ -117,8 +117,8 @@ pub fn expression( container, )) case container_expression_type { - types.NamespaceType(nested_env) -> - nested_env + types.NamespaceType(nested_defs, _nested_types) -> + nested_defs |> dict.get(label) |> result.replace_error(error.InvalidName(label)) type_ -> diff --git a/src/glimpse/internal/typecheck/imports.gleam b/src/glimpse/internal/typecheck/imports.gleam index 7fd86ab..c22d541 100644 --- a/src/glimpse/internal/typecheck/imports.gleam +++ b/src/glimpse/internal/typecheck/imports.gleam @@ -38,9 +38,13 @@ pub fn fold_import_from_env( namespace, types.NamespaceType( module_env.definitions - |> dict.filter(fn(key, _) { - set.contains(module_env.public_definitions, key) - }), + |> dict.filter(fn(key, _) { + set.contains(module_env.public_definitions, key) + }), + module_env.custom_types + |> dict.filter(fn(key, _) { + set.contains(module_env.public_custom_types, key) + }), ), ) |> types.add_import_mapping_to_env(module, namespace) diff --git a/src/glimpse/internal/typecheck/types.gleam b/src/glimpse/internal/typecheck/types.gleam index 5377e80..d395992 100644 --- a/src/glimpse/internal/typecheck/types.gleam +++ b/src/glimpse/internal/typecheck/types.gleam @@ -24,7 +24,10 @@ pub type Type { return: Type, ) /// Used for field access on imports; no direct glance analog - NamespaceType(Dict(String, Type)) + NamespaceType( + definitions: Dict(String, Type), + custom_types: Dict(String, Type), + ) } pub type TypeResult = @@ -37,6 +40,7 @@ pub type Environment { definitions: dict.Dict(String, Type), public_definitions: set.Set(String), custom_types: dict.Dict(String, Type), + public_custom_types: set.Set(String), // absolute path to whatever the relative name is in this env import_names: dict.Dict(String, String), // other environments that could be imported from this one @@ -76,6 +80,7 @@ pub fn new_env(current_module: String) -> Environment { definitions: dict.new(), public_definitions: set.new(), custom_types: dict.new(), + public_custom_types: set.new(), import_names: dict.new(), module_environments: dict.new(), ) @@ -101,6 +106,18 @@ pub fn publish_def_in_env(environment: Environment, name: String) -> Environment ) } +/// publish a custom_type that is already in the custom_types so that it is +/// visible to importing modules +pub fn publish_custom_type_in_env( + environment: Environment, + name: String, +) -> Environment { + Environment( + ..environment, + public_custom_types: set.insert(environment.public_custom_types, name), + ) +} + /// Adds the custom_type to the environment, assuming that the current module // @@ -161,9 +178,17 @@ pub fn type_(environment: Environment, glance_type: glance.Type) -> TypeResult { // TODO: custom types with parameters need to be supported // TODO: not 100% certain all named types that are not covered // above are actually custom types - // TODO: support named types in other modules glance.NamedType(name, option.None, []) -> lookup_custom_type(environment, name) + glance.NamedType(name, option.Some(module), []) -> { + let namespace = environment.definitions |> dict.get(module) + case namespace { + Ok(NamespaceType(_, custom_types)) -> + dict.get(custom_types, name) + |> result.replace_error(error.InvalidFieldAccess(module, name)) + _ -> Error(error.InvalidFieldAccess(module, name)) + } + } glance.VariableType(name) -> lookup_variable_type(environment, name) _ -> { @@ -180,14 +205,14 @@ pub fn to_string(environment: Environment, type_: Type) -> String { FloatType -> "Float" StringType -> "String" BoolType -> "Bool" - CustomType(_module, name) -> name + CustomType(module, name) -> module <> "." <> name CallableType(parameters, _labels, return) -> string_builder.from_string("fn (") |> string_builder.append(list_to_string(parameters, environment)) |> string_builder.append(") -> ") |> string_builder.append(to_string(environment, return)) |> string_builder.to_string - NamespaceType(_) -> "" + NamespaceType(..) -> "" } } @@ -208,13 +233,20 @@ pub fn to_glance(environment: Environment, type_: Type) -> glance.Type { FloatType -> glance.NamedType("Float", option.None, []) StringType -> glance.NamedType("String", option.None, []) BoolType -> glance.NamedType("Bool", option.None, []) - CustomType(_module, name) -> glance.NamedType(name, option.None, []) + CustomType(module, name) -> { + case dict.get(environment.import_names, module) { + Ok(relative) if module == environment.current_module -> + glance.NamedType(name, option.None, []) + Ok(relative) -> glance.NamedType(name, option.Some(relative), []) + Error(_) -> panic as "Custom type should always have a valid module" + } + } CallableType(parameters, _labels, return) -> glance.FunctionType( list.map(parameters, to_glance(environment, _)), to_glance(environment, return), ) - NamespaceType(_) -> panic as "Cannot convert namespace to glance" + NamespaceType(..) -> panic as "Cannot convert namespace to glance" } } diff --git a/src/glimpse/typecheck.gleam b/src/glimpse/typecheck.gleam index d718f46..138479b 100644 --- a/src/glimpse/typecheck.gleam +++ b/src/glimpse/typecheck.gleam @@ -143,7 +143,14 @@ pub fn custom_type( Error(_) -> { // add to env first so variants can parse recursive types let environment = - environment |> types.add_custom_type_to_env(custom_type.name) + environment + |> types.add_custom_type_to_env(custom_type.name) + + let environment = case custom_type.publicity { + glance.Public -> + types.publish_custom_type_in_env(environment, custom_type.name) + glance.Private -> environment + } list.fold_until( custom_type.variants, diff --git a/test/typecheck/imports_test.gleam b/test/typecheck/imports_test.gleam index fe3a6c0..d97500b 100644 --- a/test/typecheck/imports_test.gleam +++ b/test/typecheck/imports_test.gleam @@ -23,13 +23,10 @@ pub fn import_adds_function_to_env_test() { |> assertions.should_have_dict_size(1) |> dict.get("foo") |> should.be_ok - |> should.equal( - types.NamespaceType( - dict.from_list([ - #("bar", types.CallableType([], dict.new(), types.NilType)), - ]), - ), - ) + |> should.equal(types.NamespaceType( + dict.from_list([#("bar", types.CallableType([], dict.new(), types.NilType))]), + dict.new(), + )) main_env.import_names |> assertions.should_have_dict_size(1) @@ -53,7 +50,7 @@ pub fn import_no_add_private_function_to_env_test() { |> assertions.should_have_dict_size(1) |> dict.get("foo") |> should.be_ok - |> should.equal(types.NamespaceType(dict.new())) + |> should.equal(types.NamespaceType(dict.new(), dict.new())) main_env.import_names |> assertions.should_have_dict_size(1) @@ -77,20 +74,19 @@ pub fn import_adds_variant_to_env_test() { |> assertions.should_have_dict_size(1) |> dict.get("foo") |> should.be_ok - |> should.equal( - types.NamespaceType( - dict.from_list([ - #( - "Foo", - types.CallableType( - [], - dict.new(), - types.CustomType("main_module", "Foo"), - ), + |> should.equal(types.NamespaceType( + dict.from_list([ + #( + "Foo", + types.CallableType( + [], + dict.new(), + types.CustomType("main_module", "Foo"), ), - ]), - ), - ) + ), + ]), + dict.from_list([#("Foo", types.CustomType("main_module", "Foo"))]), + )) main_env.import_names |> assertions.should_have_dict_size(1) @@ -114,7 +110,7 @@ pub fn import_no_add_private_variant_to_env_test() { |> assertions.should_have_dict_size(1) |> dict.get("foo") |> should.be_ok - |> should.equal(types.NamespaceType(dict.from_list([]))) + |> should.equal(types.NamespaceType(dict.from_list([]), dict.new())) main_env.import_names |> assertions.should_have_dict_size(1) @@ -143,13 +139,10 @@ pub fn import_call_function_field_access_test() { |> assertions.should_have_dict_size(2) |> dict.get("foo") |> should.be_ok - |> should.equal( - types.NamespaceType( - dict.from_list([ - #("bar", types.CallableType([], dict.new(), types.NilType)), - ]), - ), - ) + |> should.equal(types.NamespaceType( + dict.from_list([#("bar", types.CallableType([], dict.new(), types.NilType))]), + dict.new(), + )) main_env.import_names |> assertions.should_have_dict_size(1) @@ -165,9 +158,8 @@ pub fn variant_call_function_field_access_test() { let #(_, main_env) = glance.module( "import foo - pub fn main() -> Nil { + pub fn main() -> foo.Foo { foo.Foo() - Nil }", ) |> should.be_ok @@ -179,20 +171,19 @@ pub fn variant_call_function_field_access_test() { |> assertions.should_have_dict_size(2) |> dict.get("foo") |> should.be_ok - |> should.equal( - types.NamespaceType( - dict.from_list([ - #( - "Foo", - types.CallableType( - [], - dict.new(), - types.CustomType("main_module", "Foo"), - ), + |> should.equal(types.NamespaceType( + dict.from_list([ + #( + "Foo", + types.CallableType( + [], + dict.new(), + types.CustomType("main_module", "Foo"), ), - ]), - ), - ) + ), + ]), + dict.from_list([#("Foo", types.CustomType("main_module", "Foo"))]), + )) main_env.import_names |> assertions.should_have_dict_size(1)