2021-04-10 19:58:44 +03:00
|
|
|
#[macro_use]
|
|
|
|
extern crate pretty_assertions;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate indoc;
|
|
|
|
|
|
|
|
use bumpalo::Bump;
|
|
|
|
use roc_can::expected::Expected;
|
2021-04-12 22:26:20 +03:00
|
|
|
use roc_collections::all::MutMap;
|
|
|
|
use roc_editor::lang::solve;
|
2021-04-10 19:58:44 +03:00
|
|
|
use roc_editor::lang::{
|
|
|
|
constrain::constrain_expr,
|
2021-04-12 22:26:20 +03:00
|
|
|
constrain::Constraint,
|
2021-04-10 19:58:44 +03:00
|
|
|
expr::{str_to_expr2, Env},
|
|
|
|
pool::Pool,
|
|
|
|
scope::Scope,
|
|
|
|
types::Type2,
|
|
|
|
};
|
2021-04-13 00:29:07 +03:00
|
|
|
use roc_module::ident::Lowercase;
|
2021-04-27 18:05:47 +03:00
|
|
|
use roc_module::symbol::Interns;
|
2021-04-12 22:26:20 +03:00
|
|
|
use roc_module::symbol::Symbol;
|
2021-04-10 19:58:44 +03:00
|
|
|
use roc_module::symbol::{IdentIds, ModuleIds};
|
|
|
|
use roc_region::all::Region;
|
2021-04-12 22:26:20 +03:00
|
|
|
use roc_types::solved_types::Solved;
|
|
|
|
use roc_types::subs::{Subs, Variable};
|
2021-04-13 00:29:07 +03:00
|
|
|
use roc_types::{pretty_print::content_to_string, subs::VarStore};
|
2021-04-10 19:58:44 +03:00
|
|
|
|
2021-04-30 06:05:48 +03:00
|
|
|
fn run_solve<'a>(
|
|
|
|
arena: &'a Bump,
|
2021-04-13 00:29:07 +03:00
|
|
|
mempool: &mut Pool,
|
2021-04-12 22:26:20 +03:00
|
|
|
aliases: MutMap<Symbol, roc_types::types::Alias>,
|
|
|
|
rigid_variables: MutMap<Variable, Lowercase>,
|
|
|
|
constraint: Constraint,
|
|
|
|
var_store: VarStore,
|
|
|
|
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
|
|
|
let env = solve::Env {
|
|
|
|
vars_by_symbol: MutMap::default(),
|
|
|
|
aliases,
|
|
|
|
};
|
|
|
|
|
2021-05-08 19:43:15 +03:00
|
|
|
let mut subs = Subs::new(var_store);
|
2021-04-12 22:26:20 +03:00
|
|
|
|
|
|
|
for (var, name) in rigid_variables {
|
|
|
|
subs.rigid_var(var, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the module is parsed, canonicalized, and constrained,
|
|
|
|
// we need to type check it.
|
|
|
|
let mut problems = Vec::new();
|
|
|
|
|
|
|
|
// Run the solver to populate Subs.
|
2021-04-30 06:05:48 +03:00
|
|
|
let (solved_subs, solved_env) =
|
|
|
|
solve::run(arena, mempool, &env, &mut problems, subs, &constraint);
|
2021-04-12 22:26:20 +03:00
|
|
|
|
|
|
|
(solved_subs, solved_env, problems)
|
|
|
|
}
|
|
|
|
|
2021-04-10 19:58:44 +03:00
|
|
|
fn infer_eq(actual: &str, expected_str: &str) {
|
|
|
|
let mut env_pool = Pool::with_capacity(1024);
|
|
|
|
let env_arena = Bump::new();
|
|
|
|
let code_arena = Bump::new();
|
|
|
|
|
|
|
|
let mut var_store = VarStore::default();
|
|
|
|
let var = var_store.fresh();
|
|
|
|
let dep_idents = IdentIds::exposed_builtins(8);
|
|
|
|
let exposed_ident_ids = IdentIds::default();
|
|
|
|
let mut module_ids = ModuleIds::default();
|
|
|
|
let mod_id = module_ids.get_or_insert(&"ModId123".into());
|
|
|
|
|
|
|
|
let mut env = Env::new(
|
|
|
|
mod_id,
|
|
|
|
&env_arena,
|
|
|
|
&mut env_pool,
|
|
|
|
&mut var_store,
|
|
|
|
dep_idents,
|
|
|
|
&module_ids,
|
|
|
|
exposed_ident_ids,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut scope = Scope::new(env.home, env.pool, env.var_store);
|
|
|
|
|
|
|
|
let region = Region::zero();
|
|
|
|
|
|
|
|
let expr2_result = str_to_expr2(&code_arena, actual, &mut env, &mut scope, region);
|
|
|
|
|
|
|
|
match expr2_result {
|
|
|
|
Ok((expr, _)) => {
|
|
|
|
let constraint = constrain_expr(
|
2021-04-23 04:31:02 +03:00
|
|
|
&code_arena,
|
2021-04-10 19:58:44 +03:00
|
|
|
&mut env,
|
|
|
|
&expr,
|
|
|
|
Expected::NoExpectation(Type2::Variable(var)),
|
2021-04-16 04:41:15 +03:00
|
|
|
Region::zero(),
|
2021-04-10 19:58:44 +03:00
|
|
|
);
|
|
|
|
|
2021-04-13 00:29:07 +03:00
|
|
|
let Env {
|
|
|
|
pool,
|
|
|
|
var_store: ref_var_store,
|
2021-08-04 23:10:55 +03:00
|
|
|
mut dep_idents,
|
2021-04-13 00:29:07 +03:00
|
|
|
..
|
|
|
|
} = env;
|
|
|
|
|
|
|
|
// extract the var_store out of the env again
|
|
|
|
let mut var_store = VarStore::default();
|
|
|
|
std::mem::swap(ref_var_store, &mut var_store);
|
|
|
|
|
2021-04-10 19:58:44 +03:00
|
|
|
let (mut solved, _, _) = run_solve(
|
2021-04-30 06:05:48 +03:00
|
|
|
&code_arena,
|
2021-04-13 00:29:07 +03:00
|
|
|
pool,
|
2021-04-10 19:58:44 +03:00
|
|
|
Default::default(),
|
|
|
|
Default::default(),
|
|
|
|
constraint,
|
|
|
|
var_store,
|
|
|
|
);
|
|
|
|
|
2021-04-13 00:29:07 +03:00
|
|
|
let subs = solved.inner_mut();
|
2021-04-10 19:58:44 +03:00
|
|
|
|
2021-07-30 23:15:17 +03:00
|
|
|
let content = subs.get_content_without_compacting(var);
|
2021-04-10 19:58:44 +03:00
|
|
|
|
2021-08-04 23:10:55 +03:00
|
|
|
// Connect the ModuleId to it's IdentIds
|
|
|
|
dep_idents.insert(mod_id, env.ident_ids);
|
|
|
|
|
2021-04-27 18:05:47 +03:00
|
|
|
let interns = Interns {
|
2021-06-28 18:53:47 +03:00
|
|
|
module_ids: env.module_ids.clone(),
|
2021-04-27 18:05:47 +03:00
|
|
|
all_ident_ids: dep_idents,
|
|
|
|
};
|
2021-06-29 16:25:16 +03:00
|
|
|
|
2021-07-29 18:32:08 +03:00
|
|
|
let actual_str = content_to_string(content, subs, mod_id, &interns);
|
2021-04-10 19:58:44 +03:00
|
|
|
|
|
|
|
assert_eq!(actual_str, expected_str);
|
|
|
|
}
|
|
|
|
Err(e) => panic!("syntax error {:?}", e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_str() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
"type inference!"
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"Str",
|
|
|
|
)
|
|
|
|
}
|
2021-04-16 04:41:15 +03:00
|
|
|
|
2021-05-08 19:44:01 +03:00
|
|
|
// This will be more useful once we actually map
|
|
|
|
// strings less than 15 chars to SmallStr
|
|
|
|
#[test]
|
|
|
|
fn constrain_small_str() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
"a"
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"Str",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-04-16 04:41:15 +03:00
|
|
|
#[test]
|
|
|
|
fn constrain_empty_record() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
{}
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"{}",
|
|
|
|
)
|
|
|
|
}
|
2021-04-23 19:29:20 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_small_int() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
12
|
|
|
|
"#
|
|
|
|
),
|
2021-05-06 05:58:50 +03:00
|
|
|
"Num *",
|
2021-04-23 19:29:20 +03:00
|
|
|
)
|
|
|
|
}
|
2021-05-01 04:50:21 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_float() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
3.14
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"Float *",
|
|
|
|
)
|
|
|
|
}
|
2021-05-05 17:45:11 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_record() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
{ x : 1, y : "hi" }
|
|
|
|
"#
|
|
|
|
),
|
2021-05-06 05:59:23 +03:00
|
|
|
"{ x : Num *, y : Str }",
|
2021-05-05 17:45:11 +03:00
|
|
|
)
|
|
|
|
}
|
2021-05-08 19:44:29 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_empty_list() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
[]
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"List *",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_list() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
[ 1, 2 ]
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"List (Num *)",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_list_of_records() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
[ { x: 1 }, { x: 3 } ]
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"List { x : Num * }",
|
|
|
|
)
|
|
|
|
}
|
2021-05-10 04:00:09 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_global_tag() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
Foo
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"[ Foo ]*",
|
|
|
|
)
|
|
|
|
}
|
2021-05-17 04:58:16 +03:00
|
|
|
|
2021-08-04 23:10:55 +03:00
|
|
|
#[test]
|
|
|
|
fn constrain_private_tag() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
@Foo
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"[ @Foo ]*",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-17 04:58:16 +03:00
|
|
|
#[test]
|
|
|
|
fn constrain_call_and_accessor() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
.foo { foo: "bar" }
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"Str",
|
|
|
|
)
|
|
|
|
}
|
2021-05-18 02:48:01 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_access() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
2021-05-19 04:21:57 +03:00
|
|
|
{ foo: "bar" }.foo
|
2021-05-18 02:48:01 +03:00
|
|
|
"#
|
|
|
|
),
|
|
|
|
"Str",
|
|
|
|
)
|
|
|
|
}
|
2021-05-28 19:57:45 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_if() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
if True then Green else Red
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"[ Green, Red ]*",
|
|
|
|
)
|
|
|
|
}
|
2021-06-16 02:21:20 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_when() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
when if True then Green else Red is
|
|
|
|
Green -> Blue
|
|
|
|
Red -> Purple
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"[ Blue, Purple ]*",
|
|
|
|
)
|
|
|
|
}
|
2021-06-21 07:19:00 +03:00
|
|
|
|
2021-06-22 00:26:55 +03:00
|
|
|
#[test]
|
|
|
|
fn constrain_let_value() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
person = { name: "roc" }
|
|
|
|
|
|
|
|
person
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"{ name : Str }",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-21 07:19:00 +03:00
|
|
|
#[test]
|
|
|
|
fn constrain_update() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
2021-06-22 00:26:55 +03:00
|
|
|
person = { name: "roc" }
|
2021-06-21 07:19:00 +03:00
|
|
|
|
2021-06-22 00:26:55 +03:00
|
|
|
{ person & name: "bird" }
|
2021-06-21 07:19:00 +03:00
|
|
|
"#
|
|
|
|
),
|
2021-06-22 00:26:55 +03:00
|
|
|
"{ name : Str }",
|
2021-06-21 07:19:00 +03:00
|
|
|
)
|
|
|
|
}
|
2021-07-30 21:21:01 +03:00
|
|
|
|
|
|
|
#[ignore = "TODO: implement builtins in the editor"]
|
|
|
|
#[test]
|
|
|
|
fn constrain_run_low_level() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
List.map [ { name: "roc" }, { name: "bird" } ] .name
|
|
|
|
"#
|
|
|
|
),
|
|
|
|
"List Str",
|
|
|
|
)
|
|
|
|
}
|
2021-08-06 19:44:19 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn constrain_closure() {
|
|
|
|
infer_eq(
|
|
|
|
indoc!(
|
|
|
|
r#"
|
|
|
|
x = 1
|
|
|
|
|
2021-08-06 23:27:16 +03:00
|
|
|
\{} -> x
|
2021-08-06 19:44:19 +03:00
|
|
|
"#
|
|
|
|
),
|
2021-08-06 23:27:16 +03:00
|
|
|
"{}* -> Num *",
|
2021-08-06 19:44:19 +03:00
|
|
|
)
|
|
|
|
}
|