mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge branch 'docs-exposed-values' of github.com:rtfeldman/roc into docs-exposed-values
This commit is contained in:
commit
c23d77a931
14 changed files with 824 additions and 303 deletions
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3150,13 +3150,11 @@ 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(_));
|
||||
|
@ -3220,7 +3218,8 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
|
||||
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
||||
let branching_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||
let branching_symbol =
|
||||
possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||
|
||||
let then = with_hole(
|
||||
env,
|
||||
|
@ -3248,7 +3247,9 @@ pub fn with_hole<'a>(
|
|||
|
||||
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));
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
let param = Param {
|
||||
symbol: assigned,
|
||||
|
@ -3264,6 +3265,10 @@ pub fn with_hole<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
(Err(_), _) => Stmt::RuntimeError("invalid ret_layout"),
|
||||
(_, Err(_)) => Stmt::RuntimeError("invalid cond_layout"),
|
||||
}
|
||||
}
|
||||
|
||||
When {
|
||||
cond_var,
|
||||
|
@ -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(
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -89,6 +89,26 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
|||
* 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.
|
||||
* 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
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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,7 +139,7 @@ pub enum Expr2 {
|
|||
body_id: NodeId<Expr2>, // 4B
|
||||
},
|
||||
LetFunction {
|
||||
def: NodeId<FunctionDef>, // 4B
|
||||
def_id: NodeId<FunctionDef>, // 4B
|
||||
body_var: Variable, // 8B
|
||||
body_id: NodeId<Expr2>, // 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -46,10 +46,13 @@ impl Def {
|
|||
|
||||
match self {
|
||||
Def::AnnotationOnly { .. } => todo!("lost pattern information here ... "),
|
||||
Def::Value(ValueDef { pattern, .. }) => {
|
||||
let pattern2 = &pool[*pattern];
|
||||
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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 }",
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue