diff --git a/Cargo.lock b/Cargo.lock index 6f32136cad..b1b557d7c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2524,6 +2524,7 @@ dependencies = [ "roc_solve", "roc_types", "roc_unify", + "tempfile", ] [[package]] diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 6d7de92be4..0896942814 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -227,19 +227,7 @@ fn can_annotation_help( // instantiate variables actual.substitute(&substitutions); - // Type::Alias(symbol, vars, Box::new(actual)) - if vars.is_empty() { - let actual_var = var_store.fresh(); - introduced_variables.insert_host_exposed_alias(symbol, actual_var); - Type::HostExposedAlias { - name: symbol, - arguments: vars, - actual: Box::new(actual), - actual_var, - } - } else { - Type::Alias(symbol, vars, Box::new(actual)) - } + Type::Alias(symbol, vars, Box::new(actual)) } None => { let mut args = Vec::new(); @@ -373,7 +361,7 @@ fn can_annotation_help( // Type::Alias(symbol, vars, Box::new(alias.typ.clone())) - if vars.is_empty() { + if vars.is_empty() && env.home == symbol.module_id() { let actual_var = var_store.fresh(); introduced_variables.insert_host_exposed_alias(symbol, actual_var); Type::HostExposedAlias { diff --git a/compiler/load/Cargo.toml b/compiler/load/Cargo.toml index 2ddd58a367..6bc982191c 100644 --- a/compiler/load/Cargo.toml +++ b/compiler/load/Cargo.toml @@ -25,6 +25,7 @@ crossbeam = "0.7" num_cpus = "1" [dev-dependencies] +tempfile = "3.1.0" pretty_assertions = "0.5.1" maplit = "1.0.1" indoc = "0.3.3" diff --git a/compiler/load/tests/test_load.rs b/compiler/load/tests/test_load.rs index aa8a92f2ab..97c6b7bbee 100644 --- a/compiler/load/tests/test_load.rs +++ b/compiler/load/tests/test_load.rs @@ -1,4 +1,6 @@ #[macro_use] +extern crate indoc; +#[macro_use] extern crate pretty_assertions; #[macro_use] extern crate maplit; @@ -28,6 +30,87 @@ mod test_load { // HELPERS + fn multiple_modules(files: Vec<(&str, &str)>) -> LoadedModule { + multiple_modules_help(files).unwrap() + } + + fn multiple_modules_help(mut files: Vec<(&str, &str)>) -> Result { + use std::fs::File; + use std::io::Write; + use std::path::PathBuf; + use tempfile::tempdir; + + let arena = Bump::new(); + let arena = &arena; + + let stdlib = roc_builtins::std::standard_stdlib(); + + let mut file_handles: Vec<_> = Vec::new(); + let exposed_types = MutMap::default(); + let loaded = { + // create a temporary directory + let dir = tempdir()?; + + let app_module = files.pop().unwrap(); + let interfaces = files; + + debug_assert!( + app_module.1.starts_with("app"), + "The final module should be the application module" + ); + + for (name, source) in interfaces { + let mut filename = PathBuf::from(name); + filename.set_extension("roc"); + let file_path = dir.path().join(filename.clone()); + let mut file = File::create(file_path)?; + writeln!(file, "{}", source)?; + file_handles.push(file); + } + + let result = { + let (name, source) = app_module; + + let filename = PathBuf::from(name); + let file_path = dir.path().join(filename.clone()); + let full_file_path = PathBuf::from(file_path.clone()); + let mut file = File::create(file_path)?; + writeln!(file, "{}", source)?; + file_handles.push(file); + + roc_load::file::load_and_typecheck( + arena, + full_file_path, + stdlib, + dir.path(), + exposed_types, + ) + }; + + dir.close()?; + + result + }; + + let mut loaded_module = loaded.expect("failed to load module"); + + let home = loaded_module.module_id; + + assert_eq!( + loaded_module.can_problems.remove(&home).unwrap_or_default(), + Vec::new() + ); + assert_eq!( + loaded_module + .type_problems + .remove(&home) + .unwrap_or_default(), + Vec::new() + ); + + Ok(loaded_module) + } + fn load_fixture( dir_name: &str, module_name: &str, @@ -146,6 +229,46 @@ mod test_load { // TESTS + #[test] + fn import_transitive_alias() { + // this had a bug where NodeColor was HostExposed, and it's `actual_var` conflicted + // with variables in the importee + let modules = vec![ + ( + "RBTree", + indoc!( + r#" + interface RBTree exposes [ Dict, empty ] imports [] + + # The color of a node. Leaves are considered Black. + NodeColor : [ Red, Black ] + + Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ] + + # Create an empty dictionary. + empty : Dict k v + empty = + Empty + "# + ), + ), + ( + "Main", + indoc!( + r#" + app Test provides [ main ] imports [ RBTree ] + + empty : RBTree.Dict Int Int + empty = RBTree.empty + + main = empty + "# + ), + ), + ]; + multiple_modules(modules); + } + #[test] fn interface_with_deps() { let subs_by_module = MutMap::default(); diff --git a/compiler/reporting/src/error/type.rs b/compiler/reporting/src/error/type.rs index 19ac11d6b2..4153b0c8f6 100644 --- a/compiler/reporting/src/error/type.rs +++ b/compiler/reporting/src/error/type.rs @@ -972,7 +972,7 @@ fn add_category<'b>( this_is, alloc.text(" an uniqueness attribute of type:"), ]), - Storage => alloc.concat(vec![this_is, alloc.text(" a value of type:")]), + Storage(_file, _line) => alloc.concat(vec![this_is, alloc.text(" a value of type:")]), DefaultValue(_) => alloc.concat(vec![this_is, alloc.text(" a default field of type:")]), } diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 7838257d86..98cc649f02 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -154,9 +154,6 @@ pub fn run( constraint, ); - //dbg!(&subs, &state.env.vars_by_symbol); - //panic!(); - (Solved(subs), state.env) } diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index 4029815fa3..0488e4580e 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -1007,7 +1007,7 @@ pub enum Category { StrInterpolation, // storing variables in the ast - Storage, + Storage(&'static str, u32), // control flow If, diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index 301dd70c08..0969b878ce 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -1,12 +1,12 @@ -app Main provides [ main ] imports [ Effect, ConsList ] +app Main provides [ main ] imports [ Effect, RBTree ] -empty : ConsList.ConsList Int -empty = ConsList.empty +foo : RBTree.Dict Int Int +foo = Empty # RBTree.empty main : Effect.Effect {} as Fx main = - # if ConsList.isEmpty empty then - if ConsList.len empty == 0 then + # if RBTree.isEmpty empty then + if RBTree.size foo == 0 then Effect.putLine "Yay" |> Effect.after (\{} -> Effect.getLine) |> Effect.after (\line -> Effect.putLine line) diff --git a/examples/effect/RBTree.roc b/examples/effect/RBTree.roc index 5523e09aa3..3d2b376923 100644 --- a/examples/effect/RBTree.roc +++ b/examples/effect/RBTree.roc @@ -1,6 +1,4 @@ -interface RBTree exposes [ Dict, empty, singleton, size, isEmpty, insert, remove, balance ] imports [] -# TODO remove `balance` from the exposed list -# todo change `foobar` to `balance` +interface RBTree exposes [ Dict, empty, size, singleton ] imports [] # The color of a node. Leaves are considered Black. NodeColor : [ Red, Black ]