Merge branch 'docs-exposed-values' of github.com:rtfeldman/roc into docs-exposed-values

This commit is contained in:
Chadtech 2021-07-05 13:03:13 -04:00
commit c23d77a931
14 changed files with 824 additions and 303 deletions

View file

@ -26,6 +26,16 @@ pub enum BuildOutcome {
Errors,
}
impl BuildOutcome {
pub fn status_code(&self) -> i32 {
match self {
Self::NoProblems => 0,
Self::OnlyWarnings => 1,
Self::Errors => 2,
}
}
}
pub struct BuiltFile {
pub binary_path: PathBuf,
pub outcome: BuildOutcome,
@ -205,10 +215,14 @@ pub fn build_file<'a>(
let total_time = compilation_start.elapsed().unwrap();
// If the cmd errored out, return the Err.
cmd_result?;
let exit_status = cmd_result?;
// TODO change this to report whether there were errors or warnings!
let outcome = BuildOutcome::NoProblems;
let outcome = if exit_status.success() {
BuildOutcome::NoProblems
} else {
BuildOutcome::Errors
};
Ok(BuiltFile {
binary_path,

View file

@ -196,13 +196,6 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::
.strip_prefix(env::current_dir().unwrap())
.unwrap_or(&binary_path);
// Return a nonzero exit code if there were problems
let status_code = match outcome {
BuildOutcome::NoProblems => 0,
BuildOutcome::OnlyWarnings => 1,
BuildOutcome::Errors => 2,
};
// No need to waste time freeing this memory,
// since the process is about to exit anyway.
std::mem::forget(arena);
@ -213,7 +206,8 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::
total_time.as_millis()
);
Ok(status_code)
// Return a nonzero exit code if there were problems
Ok(outcome.status_code())
}
BuildAndRun { roc_file_arg_index } => {
let mut cmd = Command::new(binary_path);
@ -231,7 +225,10 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::
}
}
roc_run(cmd.current_dir(original_cwd))
match outcome {
BuildOutcome::Errors => Ok(outcome.status_code()),
_ => roc_run(cmd.current_dir(original_cwd)),
}
}
}
}

View file

@ -3150,118 +3150,123 @@ pub fn with_hole<'a>(
branches,
final_else,
} => {
let ret_layout = layout_cache
.from_var(env.arena, branch_var, env.subs)
.expect("invalid ret_layout");
let cond_layout = layout_cache
.from_var(env.arena, cond_var, env.subs)
.expect("invalid cond_layout");
match (
layout_cache.from_var(env.arena, branch_var, env.subs),
layout_cache.from_var(env.arena, cond_var, env.subs),
) {
(Ok(ret_layout), Ok(cond_layout)) => {
// if the hole is a return, then we don't need to merge the two
// branches together again, we can just immediately return
let is_terminated = matches!(hole, Stmt::Ret(_));
// if the hole is a return, then we don't need to merge the two
// branches together again, we can just immediately return
let is_terminated = matches!(hole, Stmt::Ret(_));
if is_terminated {
let terminator = hole;
if is_terminated {
let terminator = hole;
let mut stmt = with_hole(
env,
final_else.value,
branch_var,
procs,
layout_cache,
assigned,
terminator,
);
let mut stmt = with_hole(
env,
final_else.value,
branch_var,
procs,
layout_cache,
assigned,
terminator,
);
for (loc_cond, loc_then) in branches.into_iter().rev() {
let branching_symbol = env.unique_symbol();
for (loc_cond, loc_then) in branches.into_iter().rev() {
let branching_symbol = env.unique_symbol();
let then = with_hole(
env,
loc_then.value,
branch_var,
procs,
layout_cache,
assigned,
terminator,
);
let then = with_hole(
env,
loc_then.value,
branch_var,
procs,
layout_cache,
assigned,
terminator,
);
stmt = cond(env, branching_symbol, cond_layout, then, stmt, ret_layout);
stmt = cond(env, branching_symbol, cond_layout, then, stmt, ret_layout);
// add condition
stmt = with_hole(
env,
loc_cond.value,
cond_var,
procs,
layout_cache,
branching_symbol,
env.arena.alloc(stmt),
);
}
stmt
} else {
let assigned_in_jump = env.unique_symbol();
let id = JoinPointId(env.unique_symbol());
// add condition
stmt = with_hole(
env,
loc_cond.value,
cond_var,
procs,
layout_cache,
branching_symbol,
env.arena.alloc(stmt),
);
}
stmt
} else {
let assigned_in_jump = env.unique_symbol();
let id = JoinPointId(env.unique_symbol());
let terminator = env
.arena
.alloc(Stmt::Jump(id, env.arena.alloc([assigned_in_jump])));
let mut stmt = with_hole(
env,
final_else.value,
branch_var,
procs,
layout_cache,
assigned_in_jump,
terminator,
);
for (loc_cond, loc_then) in branches.into_iter().rev() {
let branching_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
let then = with_hole(
env,
loc_then.value,
branch_var,
procs,
layout_cache,
assigned_in_jump,
terminator,
);
stmt = cond(env, branching_symbol, cond_layout, then, stmt, ret_layout);
// add condition
stmt = assign_to_symbol(
env,
procs,
layout_cache,
cond_var,
loc_cond,
branching_symbol,
stmt,
);
}
let layout = layout_cache
.from_var(env.arena, branch_var, env.subs)
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
let param = Param {
symbol: assigned,
layout,
borrow: false,
};
Stmt::Join {
id,
parameters: env.arena.alloc([param]),
remainder: env.arena.alloc(stmt),
body: hole,
let terminator = env
.arena
.alloc(Stmt::Jump(id, env.arena.alloc([assigned_in_jump])));
let mut stmt = with_hole(
env,
final_else.value,
branch_var,
procs,
layout_cache,
assigned_in_jump,
terminator,
);
for (loc_cond, loc_then) in branches.into_iter().rev() {
let branching_symbol =
possible_reuse_symbol(env, procs, &loc_cond.value);
let then = with_hole(
env,
loc_then.value,
branch_var,
procs,
layout_cache,
assigned_in_jump,
terminator,
);
stmt = cond(env, branching_symbol, cond_layout, then, stmt, ret_layout);
// add condition
stmt = assign_to_symbol(
env,
procs,
layout_cache,
cond_var,
loc_cond,
branching_symbol,
stmt,
);
}
let layout = layout_cache
.from_var(env.arena, branch_var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
let param = Param {
symbol: assigned,
layout,
borrow: false,
};
Stmt::Join {
id,
parameters: env.arena.alloc([param]),
remainder: env.arena.alloc(stmt),
body: hole,
}
}
}
(Err(_), _) => Stmt::RuntimeError("invalid ret_layout"),
(_, Err(_)) => Stmt::RuntimeError("invalid cond_layout"),
}
}
@ -4126,17 +4131,17 @@ fn convert_tag_union<'a>(
hole,
),
ByteUnion(tag_names) => {
let tag_id = tag_names
.iter()
.position(|key| key == &tag_name)
.expect("tag must be in its own type");
let opt_tag_id = tag_names.iter().position(|key| key == &tag_name);
Stmt::Let(
assigned,
Expr::Literal(Literal::Byte(tag_id as u8)),
Layout::Builtin(Builtin::Int8),
hole,
)
match opt_tag_id {
Some(tag_id) => Stmt::Let(
assigned,
Expr::Literal(Literal::Byte(tag_id as u8)),
Layout::Builtin(Builtin::Int8),
hole,
),
None => Stmt::RuntimeError("tag must be in its own type"),
}
}
Newtype {

View file

@ -1939,3 +1939,23 @@ fn list_sort_with() {
RocList<i64>
);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "invalid ret_layout""#)]
fn lists_with_incompatible_type_param_in_if() {
assert_evals_to!(
indoc!(
r#"
list1 = [ {} ]
list2 = [ "" ]
x = if True then list1 else list2
""
"#
),
RocStr::empty(),
RocStr
);
}

View file

@ -1033,3 +1033,20 @@ fn applied_tag_function_linked_list() {
i64
);
}
#[test]
#[should_panic(expected = "")]
fn tag_must_be_its_own_type() {
assert_evals_to!(
indoc!(
r#"
z : [ A, B, C ]
z = Z
z
"#
),
1,
i64
);
}

View file

@ -88,7 +88,27 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
* Show edit history for this function.
* Adjusting settings: switch to light theme, increase font size...
* Use (context specific) voice command state machine to assist Machine Learning voice recognition model.
* Nice special use case: using voice to code while on treadmill desk.
* Nice special use case: using voice to code while on treadmill desk.
* Use word embeddings to find most similar voice command to recorded input in vector space.
#### Useful voice commands
* clear all breakpoints
* increase/decrease font size
* switch to dark/light/high-contrast mode
* open/go to file "Main"(fuzzy matching)
* go to function "foo"
* go to definition
* show all references(uses) of this function/type/...
* show history timeline of this function/file
* show recent projects
* generate unit test for this function
* generate unit test for this function based on debug trace (input and output is recorded and used in test)
* who wrote this line (git blame integration)
* search documentation of library X for Foo
* show example of how to use library function Foo
* open google/github/duckduckgo search for error...
* show editor plugins for library X
#### Inspiration

View file

@ -349,6 +349,12 @@ pub fn expr2_to_markup<'a, 'b>(
syn_high_style: HighlightStyle::Blank,
parent_id_opt: None,
}),
Expr2::RuntimeError() => new_markup_node(
"RunTimeError".to_string(),
expr2_node_id,
HighlightStyle::Blank,
markup_node_pool,
),
rest => todo!("implement expr2_to_markup for {:?}", rest),
}
}

View file

@ -1048,27 +1048,27 @@ pub mod test_ed_update {
fn test_record() -> Result<(), String> {
assert_insert(&[""], &["{ ┃ }"], '{')?;
assert_insert(&["{ ┃ }"], &["{ a┃ }"], 'a')?;
assert_insert(&["{ a┃ }"], &["{ ab┃ }"], 'b')?;
assert_insert(&["{ a┃ }"], &["{ a1┃ }"], '1')?;
assert_insert(&["{ a1┃ }"], &["{ a1z┃ }"], 'z')?;
assert_insert(&["{ a1┃ }"], &["{ a15┃ }"], '5')?;
assert_insert(&["{ ab┃ }"], &["{ abc┃ }"], 'c')?;
assert_insert(&["{ ┃abc }"], &["{ z┃abc }"], 'z')?;
assert_insert(&["{ a┃b }"], &["{ az┃b }"], 'z')?;
assert_insert(&["{ a┃b }"], &["{ a9┃b }"], '9')?;
assert_insert(&["{ a┃ }"], &["{ ab┃: RunTimeError }"], 'b')?;
assert_insert(&["{ a┃ }"], &["{ a1┃: RunTimeError }"], '1')?;
assert_insert(&["{ a1┃ }"], &["{ a1z┃: RunTimeError }"], 'z')?;
assert_insert(&["{ a1┃ }"], &["{ a15┃: RunTimeError }"], '5')?;
assert_insert(&["{ ab┃ }"], &["{ abc┃: RunTimeError }"], 'c')?;
assert_insert(&["{ ┃abc }"], &["{ z┃abc: RunTimeError }"], 'z')?;
assert_insert(&["{ a┃b }"], &["{ az┃b: RunTimeError }"], 'z')?;
assert_insert(&["{ a┃b }"], &["{ a9┃b: RunTimeError }"], '9')?;
// extra space for Blank node
assert_insert(&["{ a┃ }"], &["{ a: ┃ }"], ':')?;
assert_insert(&["{ abc┃ }"], &["{ abc: ┃ }"], ':')?;
assert_insert(&["{ aBc┃ }"], &["{ aBc: ┃ }"], ':')?;
assert_insert(&["{ a┃ }"], &["{ a┃: RunTimeError }"], ':')?;
assert_insert(&["{ abc┃ }"], &["{ abc┃: RunTimeError }"], ':')?;
assert_insert(&["{ aBc┃ }"], &["{ aBc┃: RunTimeError }"], ':')?;
assert_insert_seq(&["{ a┃ }"], &["{ a: \"\" }"], ":\"")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: \"\" }"], ":\"")?;
assert_insert_seq(&["{ a┃ }"], &["{ a┃: RunTimeError }"], ":\"")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc┃: RunTimeError }"], ":\"")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 0┃ }"], ":0")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 9┃ }"], ":9")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 1000┃ }"], ":1000")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 98761┃ }"], ":98761")?;
assert_insert_seq(&["{ a┃ }"], &["{ a0┃: RunTimeError }"], ":0")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc9┃: RunTimeError }"], ":9")?;
assert_insert_seq(&["{ a┃ }"], &["{ a1000┃: RunTimeError }"], ":1000")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc98761┃: RunTimeError }"], ":98761")?;
assert_insert(&["{ a: \"\" }"], &["{ a: \"a┃\" }"], 'a')?;
assert_insert(&["{ a: \"a┃\" }"], &["{ a: \"ab┃\" }"], 'b')?;
@ -1124,9 +1124,9 @@ pub mod test_ed_update {
#[test]
fn test_nested_record() -> Result<(), String> {
assert_insert_seq(&["{ a┃ }"], &["{ a: { ┃ } }"], ":{")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: { ┃ } }"], ":{")?;
assert_insert_seq(&["{ camelCase┃ }"], &["{ camelCase: { ┃ } }"], ":{")?;
assert_insert_seq(&["{ a┃ }"], &["{ a┃: RunTimeError }"], ":{")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc┃: RunTimeError }"], ":{")?;
assert_insert_seq(&["{ camelCase┃ }"], &["{ camelCase┃: RunTimeError }"], ":{")?;
assert_insert_seq(&["{ a: { ┃ } }"], &["{ a: { zulu┃ } }"], "zulu")?;
assert_insert_seq(
@ -1136,35 +1136,51 @@ pub mod test_ed_update {
)?;
assert_insert_seq(&["{ camelCase: { ┃ } }"], &["{ camelCase: { z┃ } }"], "z")?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: ┃ } }"], ":")?;
assert_insert_seq(
&["{ a: { zulu┃ } }"],
&["{ a: { zulu┃: RunTimeError } }"],
":",
)?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: ┃ } }"],
&["{ abc: { camelCase┃: RunTimeError } }"],
":",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: ┃ } }"],
&["{ camelCase: { z┃: RunTimeError } }"],
":",
)?;
assert_insert_seq(&["{ a┃: { zulu } }"], &["{ a0┃: { zulu } }"], "0")?;
assert_insert_seq(
&["{ a┃: { zulu } }"],
&["{ a0┃: { zulu: RunTimeError } }"],
"0",
)?;
assert_insert_seq(
&["{ ab┃c: { camelCase } }"],
&["{ abz┃c: { camelCase } }"],
&["{ abz┃c: { camelCase: RunTimeError } }"],
"z",
)?;
assert_insert_seq(&["{ ┃camelCase: { z } }"], &["{ x┃camelCase: { z } }"], "x")?;
assert_insert_seq(
&["{ ┃camelCase: { z } }"],
&["{ x┃camelCase: { z: RunTimeError } }"],
"x",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: \"\" } }"], ":\"")?;
assert_insert_seq(
&["{ a: { zulu┃ } }"],
&["{ a: { zulu┃: RunTimeError } }"],
":\"",
)?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: \"\" } }"],
&["{ abc: { camelCase┃: RunTimeError } }"],
":\"",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: \"\" } }"],
&["{ camelCase: { z┃: RunTimeError } }"],
":\"",
)?;
@ -1179,15 +1195,19 @@ pub mod test_ed_update {
"ul",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: 1┃ } }"], ":1")?;
assert_insert_seq(
&["{ a: { zulu┃ } }"],
&["{ a: { zulu1┃: RunTimeError } }"],
":1",
)?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: 0┃ } }"],
&["{ abc: { camelCase0┃: RunTimeError } }"],
":0",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: 45┃ } }"],
&["{ camelCase: { z45┃: RunTimeError } }"],
":45",
)?;
@ -1198,15 +1218,19 @@ pub mod test_ed_update {
"77",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: { ┃ } } }"], ":{")?;
assert_insert_seq(
&["{ a: { zulu┃ } }"],
&["{ a: { zulu┃: RunTimeError } }"],
":{",
)?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: { ┃ } } }"],
&["{ abc: { camelCase┃: RunTimeError } }"],
":{",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: { ┃ } } }"],
&["{ camelCase: { z┃: RunTimeError } }"],
":{",
)?;
@ -1233,17 +1257,17 @@ pub mod test_ed_update {
assert_insert_seq(
&["{ a┃: { bcD: { eFgHij: { k15 } } } }"],
&["{ a4┃: { bcD: { eFgHij: { k15 } } } }"],
&["{ a4┃: { bcD: { eFgHij: { k15: RunTimeError } } } }"],
"4",
)?;
assert_insert_seq(
&["{ ┃a: { bcD: { eFgHij: { k15 } } } }"],
&["{ y┃a: { bcD: { eFgHij: { k15 } } } }"],
&["{ y┃a: { bcD: { eFgHij: { k15: RunTimeError } } } }"],
"y",
)?;
assert_insert_seq(
&["{ a: { bcD: { eF┃gHij: { k15 } } } }"],
&["{ a: { bcD: { eFxyz┃gHij: { k15 } } } }"],
&["{ a: { bcD: { eFxyz┃gHij: { k15: RunTimeError } } } }"],
"xyz",
)?;
@ -1268,23 +1292,23 @@ pub mod test_ed_update {
assert_insert_seq_ignore(&["{ ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ ┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃abc }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a: RunTimeError }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃abc: RunTimeError }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a:┃ RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a15: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15: ┃RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a15: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15:┃ RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: ┃RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase: RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase:┃ RunTimeError }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"\" }"], IGNORE_CHARS)?;
@ -1360,17 +1384,17 @@ pub mod test_ed_update {
assert_insert_seq_ignore(&["┃{ a: { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a: { } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: R┃unTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: Ru┃nTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: RunTimeError } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: RunTimeError } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: RunTimeError } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\"┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃\"\" } }"], IGNORE_NO_LTR)?;
@ -1460,39 +1484,39 @@ pub mod test_ed_update {
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase:┃ RunTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase: R┃unTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }┃"],
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase: RunTimeError } } } } } } } }┃"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase: RunTimeEr┃ror } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase: RunTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase: RunTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["{┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase: RunTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase: RunTimeError } } } } } } } }"],
IGNORE_NO_LTR,
)?;
assert_insert_seq_ignore(
&["{ ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
&["{ ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase: RunTimeError } } } } } } } }"],
"2",
)?;
Ok(())

View file

@ -1,11 +1,15 @@
#![allow(clippy::manual_map)]
use std::collections::{HashMap, HashSet};
use std::hash::BuildHasherDefault;
use crate::lang::pattern::{Pattern2, PatternId};
use crate::lang::pool::Pool;
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
use crate::lang::types::{Type2, TypeId};
use arraystring::{typenum::U30, ArrayString};
use roc_can::expr::Recursive;
use roc_collections::all::WyHash;
use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia;
use roc_module::symbol::Symbol;
@ -135,9 +139,9 @@ pub enum Expr2 {
body_id: NodeId<Expr2>, // 4B
},
LetFunction {
def: NodeId<FunctionDef>, // 4B
body_var: Variable, // 8B
body_id: NodeId<Expr2>, // 4B
def_id: NodeId<FunctionDef>, // 4B
body_var: Variable, // 8B
body_id: NodeId<Expr2>, // 4B
},
LetValue {
def_id: NodeId<ValueDef>, // 4B
@ -217,21 +221,46 @@ pub enum Expr2 {
}
#[derive(Debug)]
pub struct ValueDef {
pub pattern: PatternId, // 4B
pub expr_type: Option<(TypeId, Rigids)>, // ?
pub expr_var: Variable, // 4B
pub enum ValueDef {
WithAnnotation {
pattern_id: PatternId, // 4B
expr_id: ExprId, // 4B
type_id: TypeId,
rigids: Rigids,
expr_var: Variable, // 4B
},
NoAnnotation {
pattern_id: PatternId, // 4B
expr_id: ExprId, // 4B
expr_var: Variable, // 4B
},
}
impl ShallowClone for ValueDef {
fn shallow_clone(&self) -> Self {
Self {
pattern: self.pattern,
expr_type: match &self.expr_type {
Some((id, rigids)) => Some((*id, rigids.shallow_clone())),
None => None,
match self {
Self::WithAnnotation {
pattern_id,
expr_id,
type_id,
rigids,
expr_var,
} => Self::WithAnnotation {
pattern_id: *pattern_id,
expr_id: *expr_id,
type_id: *type_id,
rigids: rigids.shallow_clone(),
expr_var: *expr_var,
},
Self::NoAnnotation {
pattern_id,
expr_id,
expr_var,
} => Self::NoAnnotation {
pattern_id: *pattern_id,
expr_id: *expr_id,
expr_var: *expr_var,
},
expr_var: self.expr_var,
}
}
}
@ -287,8 +316,68 @@ impl ShallowClone for FunctionDef {
#[derive(Debug)]
pub struct Rigids {
pub named: PoolVec<(PoolStr, Variable)>, // 8B
pub unnamed: PoolVec<Variable>, // 8B
pub names: PoolVec<(Option<PoolStr>, Variable)>, // 8B
padding: [u8; 1],
}
#[allow(clippy::needless_collect)]
impl Rigids {
pub fn new(
named: HashMap<&str, Variable, BuildHasherDefault<WyHash>>,
unnamed: HashSet<Variable, BuildHasherDefault<WyHash>>,
pool: &mut Pool,
) -> Self {
let names = PoolVec::with_capacity((named.len() + unnamed.len()) as u32, pool);
let mut temp_names = Vec::new();
temp_names.extend(named.iter().map(|(name, var)| (Some(*name), *var)));
temp_names.extend(unnamed.iter().map(|var| (None, *var)));
for (node_id, (opt_name, variable)) in names.iter_node_ids().zip(temp_names) {
let poolstr = opt_name.map(|name| PoolStr::new(name, pool));
pool[node_id] = (poolstr, variable);
}
Self {
names,
padding: Default::default(),
}
}
pub fn named(&self, pool: &mut Pool) -> PoolVec<(PoolStr, Variable)> {
let named = self
.names
.iter(pool)
.filter_map(|(opt_pool_str, var)| {
if let Some(pool_str) = opt_pool_str {
Some((*pool_str, *var))
} else {
None
}
})
.collect::<Vec<(PoolStr, Variable)>>();
PoolVec::new(named.into_iter(), pool)
}
pub fn unnamed(&self, pool: &mut Pool) -> PoolVec<Variable> {
let unnamed = self
.names
.iter(pool)
.filter_map(|(opt_pool_str, var)| {
if opt_pool_str.is_none() {
Some(*var)
} else {
None
}
})
.collect::<Vec<Variable>>();
PoolVec::new(unnamed.into_iter(), pool)
}
}
/// This is overflow data from a Closure variant, which needs to store
@ -474,8 +563,8 @@ fn size_of_expr() {
impl ShallowClone for Rigids {
fn shallow_clone(&self) -> Self {
Self {
named: self.named.shallow_clone(),
unnamed: self.unnamed.shallow_clone(),
names: self.names.shallow_clone(),
padding: self.padding,
}
}
}

View file

@ -1,7 +1,7 @@
use bumpalo::{collections::Vec as BumpVec, Bump};
use crate::lang::{
ast::{Expr2, RecordField, WhenBranch},
ast::{Expr2, RecordField, ValueDef, WhenBranch},
expr::Env,
pattern::{DestructType, Pattern2, PatternState2, RecordDestruct},
pool::{Pool, PoolStr, PoolVec, ShallowClone},
@ -9,8 +9,11 @@ use crate::lang::{
};
use roc_can::expected::{Expected, PExpected};
use roc_collections::all::{BumpMap, BumpMapDefault, Index};
use roc_module::{ident::TagName, symbol::Symbol};
use roc_collections::all::{BumpMap, BumpMapDefault, Index, SendMap};
use roc_module::{
ident::{Lowercase, TagName},
symbol::Symbol,
};
use roc_region::all::Region;
use roc_types::{
subs::Variable,
@ -754,6 +757,156 @@ pub fn constrain_expr<'a>(
// exhautiveness checking happens when converting to mono::Expr
exists(arena, flex_vars, And(constraints))
}
Expr2::LetValue {
def_id,
body_id,
body_var,
} => {
let value_def = env.pool.get(*def_id);
let body = env.pool.get(*body_id);
let body_con = constrain_expr(arena, env, body, expected.shallow_clone(), region);
match value_def {
ValueDef::WithAnnotation { .. } => todo!("implement {:?}", value_def),
ValueDef::NoAnnotation {
pattern_id,
expr_id,
expr_var,
} => {
let pattern = env.pool.get(*pattern_id);
let mut flex_vars = BumpVec::with_capacity_in(1, arena);
flex_vars.push(*body_var);
let expr_type = Type2::Variable(*expr_var);
let pattern_expected = PExpected::NoExpectation(expr_type.shallow_clone());
let mut state = PatternState2 {
headers: BumpMap::new_in(arena),
vars: BumpVec::with_capacity_in(1, arena),
constraints: BumpVec::with_capacity_in(1, arena),
};
constrain_pattern(arena, env, pattern, region, pattern_expected, &mut state);
state.vars.push(*expr_var);
let def_expr = env.pool.get(*expr_id);
let constrained_def = Let(arena.alloc(LetConstraint {
rigid_vars: BumpVec::new_in(arena),
flex_vars: state.vars,
def_types: state.headers,
defs_constraint: Let(arena.alloc(LetConstraint {
rigid_vars: BumpVec::new_in(arena), // always empty
flex_vars: BumpVec::new_in(arena), // empty, because our functions have no arguments
def_types: BumpMap::new_in(arena), // empty, because our functions have no arguments!
defs_constraint: And(state.constraints),
ret_constraint: constrain_expr(
arena,
env,
def_expr,
Expected::NoExpectation(expr_type),
region,
),
})),
ret_constraint: body_con,
}));
let mut and_constraints = BumpVec::with_capacity_in(2, arena);
and_constraints.push(constrained_def);
and_constraints.push(Eq(
Type2::Variable(*body_var),
expected,
Category::Storage(std::file!(), std::line!()),
// TODO: needs to be ret region
region,
));
exists(arena, flex_vars, And(and_constraints))
}
}
}
Expr2::Update {
symbol,
updates,
ext_var,
record_var,
} => {
let field_types = PoolVec::with_capacity(updates.len() as u32, env.pool);
let mut flex_vars = BumpVec::with_capacity_in(updates.len() + 2, arena);
let mut cons = BumpVec::with_capacity_in(updates.len() + 1, arena);
let mut record_key_updates = SendMap::default();
for (record_field_id, field_type_node_id) in
updates.iter_node_ids().zip(field_types.iter_node_ids())
{
let record_field = env.pool.get(record_field_id);
match record_field {
RecordField::LabeledValue(pool_str, var, node_id) => {
let expr = env.pool.get(*node_id);
let (field_type, field_con) = constrain_field_update(
arena,
env,
*var,
pool_str.as_str(env.pool).into(),
expr,
);
let field_type_id = env.pool.add(field_type);
env.pool[field_type_node_id] =
(*pool_str, types::RecordField::Required(field_type_id));
record_key_updates.insert(pool_str.as_str(env.pool).into(), Region::zero());
flex_vars.push(*var);
cons.push(field_con);
}
e => todo!("{:?}", e),
}
}
let fields_type = Type2::Record(field_types, env.pool.add(Type2::Variable(*ext_var)));
let record_type = Type2::Variable(*record_var);
// NOTE from elm compiler: fields_type is separate so that Error propagates better
let fields_con = Eq(
record_type.shallow_clone(),
Expected::NoExpectation(fields_type),
Category::Record,
region,
);
let record_con = Eq(
record_type.shallow_clone(),
expected,
Category::Record,
region,
);
flex_vars.push(*record_var);
flex_vars.push(*ext_var);
let con = Lookup(
*symbol,
Expected::ForReason(
Reason::RecordUpdateKeys(*symbol, record_key_updates),
record_type,
region,
),
region,
);
// ensure constraints are solved in this order, gives better errors
cons.insert(0, fields_con);
cons.insert(1, con);
cons.insert(2, record_con);
exists(arena, flex_vars, And(cons))
}
_ => todo!("implement constraints for {:?}", expr),
}
}
@ -785,6 +938,22 @@ fn constrain_field<'a>(
(field_type, constraint)
}
#[inline(always)]
fn constrain_field_update<'a>(
arena: &'a Bump,
env: &mut Env,
field_var: Variable,
field: Lowercase,
expr: &Expr2,
) -> (Type2, Constraint<'a>) {
let field_type = Type2::Variable(field_var);
let reason = Reason::RecordUpdateValue(field);
let field_expected = Expected::ForReason(reason, field_type.shallow_clone(), Region::zero());
let con = constrain_expr(arena, env, expr, field_expected, Region::zero());
(field_type, con)
}
fn constrain_empty_record<'a>(expected: Expected<Type2>, region: Region) -> Constraint<'a> {
Constraint::Eq(Type2::EmptyRec, expected, Category::Record, region)
}

View file

@ -46,10 +46,13 @@ impl Def {
match self {
Def::AnnotationOnly { .. } => todo!("lost pattern information here ... "),
Def::Value(ValueDef { pattern, .. }) => {
let pattern2 = &pool[*pattern];
output.extend(symbols_from_pattern(pool, pattern2));
}
Def::Value(value_def) => match value_def {
ValueDef::WithAnnotation { pattern_id, .. }
| ValueDef::NoAnnotation { pattern_id, .. } => {
let pattern2 = &pool[*pattern_id];
output.extend(symbols_from_pattern(pool, pattern2));
}
},
Def::Function(function_def) => match function_def {
FunctionDef::NoAnnotation { name, .. }
| FunctionDef::WithAnnotation { name, .. } => {
@ -79,7 +82,7 @@ impl ShallowClone for Def {
/// but no Expr canonicalization has happened yet. Also, it has had spaces
/// and nesting resolved, and knows whether annotations are standalone or not.
#[derive(Debug)]
enum PendingDef<'a> {
pub enum PendingDef<'a> {
/// A standalone annotation with no body
AnnotationOnly(
&'a Located<ast::Pattern<'a>>,
@ -315,23 +318,7 @@ fn from_pending_alias<'a>(
}
}
let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool);
let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool);
for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) {
let poolstr = PoolStr::new(name, env.pool);
env.pool[node_id] = (poolstr, variable);
}
for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) {
env.pool[node_id] = rigid;
}
let rigids = Rigids {
named: named.shallow_clone(),
unnamed,
};
let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool);
let annotation = match signature {
Signature::Value { annotation } => annotation,
@ -355,6 +342,8 @@ fn from_pending_alias<'a>(
rec_type_union.substitute_alias(env.pool, symbol, Type2::Variable(rec_var));
let annotation_id = env.add(rec_type_union, ann.region);
let named = rigids.named(env.pool);
scope.add_alias(env.pool, symbol, named, annotation_id);
} else {
env.problem(Problem::CyclicAlias(symbol, name.region, vec![]));
@ -362,6 +351,8 @@ fn from_pending_alias<'a>(
}
} else {
let annotation_id = env.add(annotation, ann.region);
let named = rigids.named(env.pool);
scope.add_alias(env.pool, symbol, named, annotation_id);
}
@ -407,21 +398,7 @@ fn canonicalize_pending_def<'a>(
output.references.referenced_aliases.insert(symbol);
}
let rigids = {
let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool);
let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool);
for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) {
let poolstr = PoolStr::new(name, env.pool);
env.pool[node_id] = (poolstr, variable);
}
for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) {
env.pool[node_id] = rigid;
}
Rigids { named, unnamed }
};
let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool);
let annotation = match signature {
Signature::Value { annotation } => annotation,
@ -470,21 +447,7 @@ fn canonicalize_pending_def<'a>(
output.references.referenced_aliases.insert(symbol);
}
let rigids = {
let named = PoolVec::with_capacity(named_rigids.len() as u32, env.pool);
let unnamed = PoolVec::with_capacity(unnamed_rigids.len() as u32, env.pool);
for (node_id, (name, variable)) in named.iter_node_ids().zip(named_rigids) {
let poolstr = PoolStr::new(name, env.pool);
env.pool[node_id] = (poolstr, variable);
}
for (node_id, rigid) in unnamed.iter_node_ids().zip(unnamed_rigids) {
env.pool[node_id] = rigid;
}
Rigids { named, unnamed }
};
let rigids = Rigids::new(named_rigids, unnamed_rigids, env.pool);
// bookkeeping for tail-call detection. If we're assigning to an
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
@ -624,9 +587,11 @@ fn canonicalize_pending_def<'a>(
};
let annotation = env.add(annotation, loc_ann.region);
let value_def = ValueDef {
pattern: loc_can_pattern,
expr_type: Some((annotation, rigids)),
let value_def = ValueDef::WithAnnotation {
pattern_id: loc_can_pattern,
expr_id: env.pool.add(loc_can_expr),
type_id: annotation,
rigids: rigids,
expr_var: env.var_store.fresh(),
};
@ -745,9 +710,9 @@ fn canonicalize_pending_def<'a>(
}
_ => {
let value_def = ValueDef {
pattern: loc_can_pattern,
expr_type: None,
let value_def = ValueDef::NoAnnotation {
pattern_id: loc_can_pattern,
expr_id: env.pool.add(loc_can_expr),
expr_var: env.var_store.fresh(),
};

View file

@ -1,19 +1,28 @@
#![allow(clippy::all)]
#![allow(dead_code)]
#![allow(unused_imports)]
use crate::lang::ast::expr2_to_string;
use crate::lang::ast::RecordField;
use crate::lang::ast::{ClosureExtra, Expr2, ExprId, FloatVal, IntStyle, IntVal, WhenBranch};
use crate::lang::def::References;
use crate::lang::pattern::to_pattern2;
use bumpalo::{collections::Vec as BumpVec, Bump};
use inlinable_string::InlinableString;
use std::collections::HashMap;
use crate::lang::ast::{
expr2_to_string, ClosureExtra, Expr2, ExprId, FloatVal, IntStyle, IntVal, RecordField,
WhenBranch,
};
use crate::lang::def::{
canonicalize_defs, sort_can_defs, CanDefs, Declaration, Def, PendingDef, References,
};
use crate::lang::pattern::{to_pattern2, Pattern2, PatternId};
use crate::lang::pool::{NodeId, Pool, PoolStr, PoolVec, ShallowClone};
use crate::lang::scope::Scope;
use crate::lang::types::{Alias, Type2, TypeId};
use bumpalo::Bump;
use inlinable_string::InlinableString;
use crate::lang::types::{Alias, Annotation2, Type2, TypeId};
use roc_can::expr::Recursive;
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
use roc_can::operator::desugar_expr;
use roc_collections::all::default_hasher;
use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::Lowercase;
use roc_module::ident::ModuleName;
use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia;
@ -21,14 +30,63 @@ use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
use roc_parse::ast;
use roc_parse::ast::StrLiteral;
use roc_parse::parser::{loc, Parser, State, SyntaxError};
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Located, Region};
use roc_types::subs::{VarStore, Variable};
#[derive(Clone, Debug, PartialEq, Default)]
pub struct IntroducedVariables {
// Rigids must be unique within a type annoation.
// E.g. in `identity : a -> a`, there should only be one
// variable (a rigid one, with name "a").
// Hence `rigids : Map<Lowercase, Variable>`
//
// But then between annotations, the same name can occur multiple times,
// but a variable can only have one name. Therefore
// `ftv : Map<Variable, Lowercase>`.
pub wildcards: Vec<Variable>,
pub var_by_name: MutMap<Lowercase, Variable>,
pub name_by_var: MutMap<Variable, Lowercase>,
pub host_exposed_aliases: MutMap<Symbol, Variable>,
}
impl IntroducedVariables {
pub fn insert_named(&mut self, name: Lowercase, var: Variable) {
self.var_by_name.insert(name.clone(), var);
self.name_by_var.insert(var, name);
}
pub fn insert_wildcard(&mut self, var: Variable) {
self.wildcards.push(var);
}
pub fn insert_host_exposed_alias(&mut self, symbol: Symbol, var: Variable) {
self.host_exposed_aliases.insert(symbol, var);
}
pub fn union(&mut self, other: &Self) {
self.wildcards.extend(other.wildcards.iter().cloned());
self.var_by_name.extend(other.var_by_name.clone());
self.name_by_var.extend(other.name_by_var.clone());
self.host_exposed_aliases
.extend(other.host_exposed_aliases.clone());
}
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
self.var_by_name.get(name)
}
pub fn name_by_var(&self, var: Variable) -> Option<&Lowercase> {
self.name_by_var.get(&var)
}
}
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Output {
pub references: References,
pub tail_call: Option<Symbol>,
pub introduced_variables: IntroducedVariables,
pub aliases: MutMap<Symbol, NodeId<Alias>>,
pub non_closures: MutSet<Symbol>,
}
@ -53,6 +111,8 @@ pub struct Env<'a> {
pub pool: &'a mut Pool,
pub arena: &'a Bump,
pub problems: BumpVec<'a, Problem>,
pub dep_idents: MutMap<ModuleId, IdentIds>,
pub module_ids: &'a ModuleIds,
pub ident_ids: IdentIds,
@ -82,6 +142,7 @@ impl<'a> Env<'a> {
home,
arena,
pool,
problems: BumpVec::new_in(arena),
var_store,
dep_idents,
module_ids,
@ -102,8 +163,8 @@ impl<'a> Env<'a> {
id
}
pub fn problem(&mut self, _problem: Problem) {
todo!();
pub fn problem(&mut self, problem: Problem) {
self.problems.push(problem);
}
pub fn set_region<T>(&mut self, _node_id: NodeId<T>, _region: Region) {
@ -236,7 +297,16 @@ pub fn str_to_expr2<'a>(
region: Region,
) -> Result<(Expr2, self::Output), SyntaxError<'a>> {
match roc_parse::test_helpers::parse_loc_with(arena, input.trim()) {
Ok(loc_expr) => Ok(to_expr2(env, scope, arena.alloc(loc_expr.value), region)),
Ok(loc_expr) => {
let desugared_loc_expr = desugar_expr(arena, arena.alloc(loc_expr));
Ok(to_expr2(
env,
scope,
arena.alloc(desugared_loc_expr.value),
region,
))
}
Err(fail) => Err(fail),
}
}
@ -780,7 +850,50 @@ pub fn to_expr2<'a>(
}
Defs(loc_defs, loc_ret) => {
todo!("{:?} {:?}", loc_defs, loc_ret)
let (unsorted, mut scope, defs_output, symbols_introduced) = canonicalize_defs(
env,
Output::default(),
&scope,
loc_defs,
PatternType::DefExpr,
);
// The def as a whole is a tail call iff its return expression is a tail call.
// Use its output as a starting point because its tail_call already has the right answer!
let (ret_expr, mut output) = to_expr2(env, &mut scope, &loc_ret.value, loc_ret.region);
output
.introduced_variables
.union(&defs_output.introduced_variables);
output.references.union_mut(defs_output.references);
// Now that we've collected all the references, check to see if any of the new idents
// we defined went unused by the return expression. If any were unused, report it.
for (symbol, region) in symbols_introduced {
if !output.references.has_lookup(symbol) {
env.problem(Problem::UnusedDef(symbol, region));
}
}
let (can_defs, output) = sort_can_defs(env, unsorted, output);
match can_defs {
Ok(decls) => {
let mut expr = ret_expr;
for declaration in decls.into_iter().rev() {
expr = decl_to_let(env.pool, env.var_store, declaration, expr);
}
(expr, output)
}
Err(_err) => {
// TODO: fix this to be something from Expr2
// (RuntimeError(err), output)
todo!()
}
}
}
PrecedenceConflict { .. } => {
@ -1262,11 +1375,10 @@ fn canonicalize_lookup(
Var(symbol)
}
Err(_problem) => {
// env.problem(Problem::RuntimeError(problem.clone()));
Err(problem) => {
env.problem(Problem::RuntimeError(problem.clone()));
// RuntimeError(problem)
todo!()
RuntimeError()
}
}
} else {
@ -1278,14 +1390,12 @@ fn canonicalize_lookup(
Var(symbol)
}
Err(_problem) => {
Err(problem) => {
// Either the module wasn't imported, or
// it was imported but it doesn't expose this ident.
// env.problem(Problem::RuntimeError(problem.clone()));
env.problem(Problem::RuntimeError(problem.clone()));
// RuntimeError(problem)
todo!()
RuntimeError()
}
}
};
@ -1294,3 +1404,59 @@ fn canonicalize_lookup(
(can_expr, output)
}
fn decl_to_let(pool: &mut Pool, var_store: &mut VarStore, decl: Declaration, ret: Expr2) -> Expr2 {
match decl {
Declaration::Declare(def) => match def {
Def::AnnotationOnly { .. } => todo!(),
Def::Value(value_def) => {
let def_id = pool.add(value_def);
let body_id = pool.add(ret);
Expr2::LetValue {
def_id,
body_id,
body_var: var_store.fresh(),
}
}
Def::Function(function_def) => {
let def_id = pool.add(function_def);
let body_id = pool.add(ret);
Expr2::LetFunction {
def_id,
body_id,
body_var: var_store.fresh(),
}
}
},
Declaration::DeclareRec(defs) => {
let mut function_defs = vec![];
for def in defs {
match def {
Def::AnnotationOnly { .. } => todo!(),
Def::Function(function_def) => function_defs.push(function_def),
Def::Value(_) => unreachable!(),
}
}
let body_id = pool.add(ret);
Expr2::LetRec {
defs: PoolVec::new(function_defs.into_iter(), pool),
body_var: var_store.fresh(),
body_id,
}
}
Declaration::InvalidCycle(_entries, _) => {
// TODO: replace with something from Expr2
// Expr::RuntimeError(RuntimeError::CircularDef(entries))
todo!()
}
Declaration::Builtin(_) => {
// Builtins should only be added to top-level decls, not to let-exprs!
unreachable!()
}
}
}

View file

@ -2,7 +2,7 @@
#![allow(dead_code)]
#![allow(unused_imports)]
#![allow(unused_variables)]
use crate::lang::ast::{FunctionDef, ValueDef};
use crate::lang::ast::{Expr2, FunctionDef, ValueDef};
use crate::lang::def::{canonicalize_defs, sort_can_defs, Declaration, Def};
use crate::lang::expr::Env;
use crate::lang::expr::Output;
@ -249,10 +249,11 @@ pub fn canonicalize_module_defs<'a>(
let runtime_error = RuntimeError::ExposedButNotDefined(symbol);
let value_def = {
let pattern = env.pool.add(Pattern2::Identifier(symbol));
ValueDef {
pattern,
expr_type: None,
let pattern_id = env.pool.add(Pattern2::Identifier(symbol));
let expr_id = env.pool.add(Expr2::RuntimeError());
ValueDef::NoAnnotation {
pattern_id,
expr_id,
expr_var: env.var_store.fresh(),
}
};

View file

@ -117,7 +117,7 @@ fn infer_eq(actual: &str, expected_str: &str) {
let content = subs.get(var).content;
let interns = Interns {
module_ids,
module_ids: env.module_ids.clone(),
all_ident_ids: dep_idents,
};
@ -300,3 +300,31 @@ fn constrain_when() {
"[ Blue, Purple ]*",
)
}
#[test]
fn constrain_let_value() {
infer_eq(
indoc!(
r#"
person = { name: "roc" }
person
"#
),
"{ name : Str }",
)
}
#[test]
fn constrain_update() {
infer_eq(
indoc!(
r#"
person = { name: "roc" }
{ person & name: "bird" }
"#
),
"{ name : Str }",
)
}