mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into str-split
This commit is contained in:
commit
d41e940b7f
108 changed files with 3509 additions and 863 deletions
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -51,11 +51,6 @@ jobs:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
name: cargo test
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
name: cargo test -- --ignored
|
name: cargo test -- --ignored
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
# Building the Roc compiler from source
|
# Building the Roc compiler from source
|
||||||
|
|
||||||
|
|
||||||
## Installing LLVM, Zig, valgrind, libunwind, and libc++-dev
|
## Installing LLVM, Python, Zig, valgrind, libunwind, and libc++-dev
|
||||||
|
|
||||||
To build the compiler, you need these installed:
|
To build the compiler, you need these installed:
|
||||||
|
|
||||||
* `libunwind` (macOS should already have this one installed)
|
* `libunwind` (macOS should already have this one installed)
|
||||||
* `libc++-dev`
|
* `libc++-dev`
|
||||||
|
* Python 2.7 (Windows only), `python-is-python3` (Ubuntu)
|
||||||
* a particular version of Zig (see below)
|
* a particular version of Zig (see below)
|
||||||
* a particular version of LLVM (see below)
|
* a particular version of LLVM (see below)
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ We use a specific version of Zig, a build off the the commit `0088efc4b`. The la
|
||||||
tar xvf zig-linux-x86_64-0.6.0+0088efc4b.tar
|
tar xvf zig-linux-x86_64-0.6.0+0088efc4b.tar
|
||||||
# move the files into /opt:
|
# move the files into /opt:
|
||||||
sudo mkdir -p /opt/zig
|
sudo mkdir -p /opt/zig
|
||||||
sudo mv tar xvf zig-linux-x86_64-0.6.0+0088efc4b.tar/* /opt/zig/
|
sudo mv zig-linux-x86_64-0.6.0+0088efc4b/* /opt/zig/
|
||||||
```
|
```
|
||||||
Then add `/opt/zig/` to your `PATH` (e.g. in `~/.bashrc`).
|
Then add `/opt/zig/` to your `PATH` (e.g. in `~/.bashrc`).
|
||||||
|
|
||||||
|
|
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -644,6 +644,8 @@ dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
"handlebars",
|
"handlebars",
|
||||||
|
"maplit",
|
||||||
|
"pretty_assertions",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -661,9 +663,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
|
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
|
@ -2463,6 +2465,7 @@ name = "roc_gen"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"either",
|
||||||
"im",
|
"im",
|
||||||
"im-rc",
|
"im-rc",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
|
|
@ -194,42 +194,40 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
exposed_types,
|
exposed_types,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded = loaded.expect("failed to load module");
|
let mut loaded = loaded.expect("failed to load module");
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
can_problems,
|
|
||||||
type_problems,
|
|
||||||
mono_problems,
|
|
||||||
mut procedures,
|
mut procedures,
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
mut subs,
|
mut subs,
|
||||||
module_id: home,
|
module_id: home,
|
||||||
|
sources,
|
||||||
..
|
..
|
||||||
} = loaded;
|
} = loaded;
|
||||||
|
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
|
||||||
|
for (home, (module_path, src)) in sources {
|
||||||
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
|
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
||||||
|
|
||||||
if error_count > 0 {
|
if error_count == 0 {
|
||||||
// There were problems; report them and return.
|
continue;
|
||||||
let src_lines: Vec<&str> = module_src.split('\n').collect();
|
}
|
||||||
|
|
||||||
// Used for reporting where an error came from.
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
//
|
|
||||||
// TODO: maybe Reporting should have this be an Option?
|
|
||||||
let path = PathBuf::new();
|
|
||||||
|
|
||||||
// Report problems
|
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
// Report parsing and canonicalization problems
|
// Report parsing and canonicalization problems
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||||
|
|
||||||
let mut lines = Vec::with_capacity(error_count);
|
|
||||||
|
|
||||||
for problem in can_problems.into_iter() {
|
for problem in can_problems.into_iter() {
|
||||||
let report = can_problem(&alloc, path.clone(), problem);
|
let report = can_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -237,8 +235,8 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in type_problems.into_iter() {
|
for problem in type_problems {
|
||||||
let report = type_problem(&alloc, path.clone(), problem);
|
let report = type_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -246,15 +244,17 @@ fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<ReplOutput, Fa
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems.into_iter() {
|
for problem in mono_problems {
|
||||||
let report = mono_problem(&alloc, path.clone(), problem);
|
let report = mono_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lines.is_empty() {
|
||||||
Ok(ReplOutput::Problems(lines))
|
Ok(ReplOutput::Problems(lines))
|
||||||
} else {
|
} else {
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
|
|
|
@ -59,6 +59,11 @@ fn jit_to_ast_help<'a>(
|
||||||
content: &Content,
|
content: &Content,
|
||||||
) -> Expr<'a> {
|
) -> Expr<'a> {
|
||||||
match layout {
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int1) => {
|
||||||
|
// TODO this will not handle the case where this bool was really
|
||||||
|
// a 1-element recored, e.g. { x: True }, correctly
|
||||||
|
run_jit_function!(lib, main_fn_name, bool, |num| i1_to_ast(num))
|
||||||
|
}
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||||
env,
|
env,
|
||||||
|
@ -433,6 +438,15 @@ fn i64_to_ast(arena: &Bump, num: i64) -> Expr<'_> {
|
||||||
Expr::Num(arena.alloc(format!("{}", num)))
|
Expr::Num(arena.alloc(format!("{}", num)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is centralized in case we want to format it differently later,
|
||||||
|
/// e.g. adding underscores for large numbers
|
||||||
|
fn i1_to_ast<'a>(num: bool) -> Expr<'a> {
|
||||||
|
match num {
|
||||||
|
true => Expr::GlobalTag(&"True"),
|
||||||
|
false => Expr::GlobalTag(&"False"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is centralized in case we want to format it differently later,
|
/// This is centralized in case we want to format it differently later,
|
||||||
/// e.g. adding underscores for large numbers
|
/// e.g. adding underscores for large numbers
|
||||||
fn f64_to_ast(arena: &Bump, num: f64) -> Expr<'_> {
|
fn f64_to_ast(arena: &Bump, num: f64) -> Expr<'_> {
|
||||||
|
|
|
@ -12,24 +12,13 @@ mod helpers;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod cli_run {
|
mod cli_run {
|
||||||
use crate::helpers::{
|
use crate::helpers::{
|
||||||
example_file, extract_valgrind_errors, run_cmd, run_roc, run_with_valgrind,
|
example_file, extract_valgrind_errors, fixture_file, run_cmd, run_roc, run_with_valgrind,
|
||||||
};
|
};
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
fn check_output(
|
fn check_output(file: &Path, flags: &[&str], expected_ending: &str, use_valgrind: bool) {
|
||||||
folder: &str,
|
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
||||||
file: &str,
|
|
||||||
flags: &[&str],
|
|
||||||
expected_ending: &str,
|
|
||||||
use_valgrind: bool,
|
|
||||||
) {
|
|
||||||
let compile_out = run_roc(
|
|
||||||
&[
|
|
||||||
&["build", example_file(folder, file).to_str().unwrap()],
|
|
||||||
flags,
|
|
||||||
]
|
|
||||||
.concat(),
|
|
||||||
);
|
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
panic!(compile_out.stderr);
|
panic!(compile_out.stderr);
|
||||||
}
|
}
|
||||||
|
@ -37,14 +26,14 @@ mod cli_run {
|
||||||
|
|
||||||
let out = if use_valgrind {
|
let out = if use_valgrind {
|
||||||
let (valgrind_out, raw_xml) =
|
let (valgrind_out, raw_xml) =
|
||||||
run_with_valgrind(&[example_file(folder, "app").to_str().unwrap()]);
|
run_with_valgrind(&[file.with_file_name("app").to_str().unwrap()]);
|
||||||
let memory_errors = extract_valgrind_errors(&raw_xml);
|
let memory_errors = extract_valgrind_errors(&raw_xml);
|
||||||
if !memory_errors.is_empty() {
|
if !memory_errors.is_empty() {
|
||||||
panic!("{:?}", memory_errors);
|
panic!("{:?}", memory_errors);
|
||||||
}
|
}
|
||||||
valgrind_out
|
valgrind_out
|
||||||
} else {
|
} else {
|
||||||
run_cmd(example_file(folder, "app").to_str().unwrap(), &[])
|
run_cmd(file.with_file_name("app").to_str().unwrap(), &[])
|
||||||
};
|
};
|
||||||
if !&out.stdout.ends_with(expected_ending) {
|
if !&out.stdout.ends_with(expected_ending) {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -59,8 +48,7 @@ mod cli_run {
|
||||||
#[serial(hello_world)]
|
#[serial(hello_world)]
|
||||||
fn run_hello_world() {
|
fn run_hello_world() {
|
||||||
check_output(
|
check_output(
|
||||||
"hello-world",
|
&example_file("hello-world", "Hello.roc"),
|
||||||
"Hello.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"Hello, World!!!!!!!!!!!!!\n",
|
"Hello, World!!!!!!!!!!!!!\n",
|
||||||
true,
|
true,
|
||||||
|
@ -71,8 +59,7 @@ mod cli_run {
|
||||||
#[serial(hello_world)]
|
#[serial(hello_world)]
|
||||||
fn run_hello_world_optimized() {
|
fn run_hello_world_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
"hello-world",
|
&example_file("hello-world", "Hello.roc"),
|
||||||
"Hello.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"Hello, World!!!!!!!!!!!!!\n",
|
"Hello, World!!!!!!!!!!!!!\n",
|
||||||
true,
|
true,
|
||||||
|
@ -83,8 +70,7 @@ mod cli_run {
|
||||||
#[serial(quicksort)]
|
#[serial(quicksort)]
|
||||||
fn run_quicksort_not_optimized() {
|
fn run_quicksort_not_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
"quicksort",
|
&example_file("quicksort", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
false,
|
false,
|
||||||
|
@ -95,8 +81,7 @@ mod cli_run {
|
||||||
#[serial(quicksort)]
|
#[serial(quicksort)]
|
||||||
fn run_quicksort_optimized() {
|
fn run_quicksort_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
"quicksort",
|
&example_file("quicksort", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&["--optimize"],
|
&["--optimize"],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
false,
|
false,
|
||||||
|
@ -109,8 +94,7 @@ mod cli_run {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn run_quicksort_valgrind() {
|
fn run_quicksort_valgrind() {
|
||||||
check_output(
|
check_output(
|
||||||
"quicksort",
|
&example_file("quicksort", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
true,
|
true,
|
||||||
|
@ -123,8 +107,7 @@ mod cli_run {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn run_quicksort_optimized_valgrind() {
|
fn run_quicksort_optimized_valgrind() {
|
||||||
check_output(
|
check_output(
|
||||||
"quicksort",
|
&example_file("quicksort", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&["--optimize"],
|
&["--optimize"],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
true,
|
true,
|
||||||
|
@ -135,8 +118,7 @@ mod cli_run {
|
||||||
#[serial(multi_module)]
|
#[serial(multi_module)]
|
||||||
fn run_multi_module() {
|
fn run_multi_module() {
|
||||||
check_output(
|
check_output(
|
||||||
"multi-module",
|
&example_file("multi-module", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
false,
|
false,
|
||||||
|
@ -147,8 +129,7 @@ mod cli_run {
|
||||||
#[serial(multi_module)]
|
#[serial(multi_module)]
|
||||||
fn run_multi_module_optimized() {
|
fn run_multi_module_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
"multi-module",
|
&example_file("multi-module", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&["--optimize"],
|
&["--optimize"],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
false,
|
false,
|
||||||
|
@ -161,8 +142,7 @@ mod cli_run {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn run_multi_module_valgrind() {
|
fn run_multi_module_valgrind() {
|
||||||
check_output(
|
check_output(
|
||||||
"multi-module",
|
&example_file("multi-module", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&[],
|
&[],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
true,
|
true,
|
||||||
|
@ -175,11 +155,60 @@ mod cli_run {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn run_multi_module_optimized_valgrind() {
|
fn run_multi_module_optimized_valgrind() {
|
||||||
check_output(
|
check_output(
|
||||||
"multi-module",
|
&example_file("multi-module", "Quicksort.roc"),
|
||||||
"Quicksort.roc",
|
|
||||||
&["--optimize"],
|
&["--optimize"],
|
||||||
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
"[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2]\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_str)]
|
||||||
|
fn run_multi_dep_str_unoptimized() {
|
||||||
|
// if true {
|
||||||
|
// todo!(
|
||||||
|
// "fix this test so it no longer deadlocks and hangs during monomorphization! The test never shows the error; to see the panic error, run this: cargo run run cli/tests/fixtures/multi-dep-str/Main.roc"
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
check_output(
|
||||||
|
&fixture_file("multi-dep-str", "Main.roc"),
|
||||||
|
&[],
|
||||||
|
"I am Dep2.str2\n",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_str)]
|
||||||
|
fn run_multi_dep_str_optimized() {
|
||||||
|
check_output(
|
||||||
|
&fixture_file("multi-dep-str", "Main.roc"),
|
||||||
|
&[],
|
||||||
|
"I am Dep2.str2\n",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_thunk)]
|
||||||
|
fn run_multi_dep_thunk_unoptimized() {
|
||||||
|
check_output(
|
||||||
|
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||||
|
&[],
|
||||||
|
"I am Dep2.value2\n",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_str)]
|
||||||
|
fn run_multi_dep_thunk_optimized() {
|
||||||
|
check_output(
|
||||||
|
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||||
|
&[],
|
||||||
|
"I am Dep2.value2\n",
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
4
cli/tests/fixtures/.gitignore
vendored
Normal file
4
cli/tests/fixtures/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
app
|
||||||
|
host.o
|
||||||
|
c_host.o
|
||||||
|
app.dSYM
|
4
cli/tests/fixtures/multi-dep-str/Dep1.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-str/Dep1.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
interface Dep1 exposes [ str1 ] imports [ Dep2 ]
|
||||||
|
|
||||||
|
str1 : Str
|
||||||
|
str1 = Dep2.str2
|
4
cli/tests/fixtures/multi-dep-str/Dep2.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-str/Dep2.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
interface Dep2 exposes [ str2 ] imports []
|
||||||
|
|
||||||
|
str2 : Str
|
||||||
|
str2 = "I am Dep2.str2"
|
4
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-str/Main.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
app Main provides [ main ] imports [ Dep1 ]
|
||||||
|
|
||||||
|
main : Str
|
||||||
|
main = Dep1.str1
|
23
cli/tests/fixtures/multi-dep-str/platform/Cargo.lock
generated
vendored
Normal file
23
cli/tests/fixtures/multi-dep-str/platform/Cargo.lock
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
cli/tests/fixtures/multi-dep-str/platform/Cargo.toml
vendored
Normal file
13
cli/tests/fixtures/multi-dep-str/platform/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_std = { path = "../../../../../roc_std" }
|
||||||
|
|
||||||
|
[workspace]
|
8
cli/tests/fixtures/multi-dep-str/platform/README.md
vendored
Normal file
8
cli/tests/fixtures/multi-dep-str/platform/README.md
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Rebuilding the host from source
|
||||||
|
|
||||||
|
Run `build.sh` to manually rebuild this platform's host.
|
||||||
|
|
||||||
|
Note that the compiler currently has its own logic for rebuilding these hosts
|
||||||
|
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
||||||
|
hosts will be precompiled by platform authors and distributed in packages,
|
||||||
|
at which point only package authors will need to think about rebuilding hosts.
|
7
cli/tests/fixtures/multi-dep-str/platform/host.c
vendored
Normal file
7
cli/tests/fixtures/multi-dep-str/platform/host.c
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int rust_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return rust_main();
|
||||||
|
}
|
28
cli/tests/fixtures/multi-dep-str/platform/src/lib.rs
vendored
Normal file
28
cli/tests/fixtures/multi-dep-str/platform/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use roc_std::RocCallResult;
|
||||||
|
use roc_std::RocStr;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#[link_name = "main_1_exposed"]
|
||||||
|
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_main() -> isize {
|
||||||
|
let answer = unsafe {
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
let mut output: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
say_hello(&mut *output.as_mut_ptr());
|
||||||
|
|
||||||
|
match output.assume_init().into() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(msg) => panic!("roc failed with message {}", msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Roc says: {}", str::from_utf8(answer.as_slice()).unwrap());
|
||||||
|
|
||||||
|
// Exit code
|
||||||
|
0
|
||||||
|
}
|
4
cli/tests/fixtures/multi-dep-thunk/Dep1.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-thunk/Dep1.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
interface Dep1 exposes [ value1 ] imports [ Dep2 ]
|
||||||
|
|
||||||
|
value1 : {} -> Str
|
||||||
|
value1 = \_ -> Dep2.value2 {}
|
4
cli/tests/fixtures/multi-dep-thunk/Dep2.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-thunk/Dep2.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
interface Dep2 exposes [ value2 ] imports []
|
||||||
|
|
||||||
|
value2 : {} -> Str
|
||||||
|
value2 = \_ -> "I am Dep2.value2"
|
4
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
Normal file
4
cli/tests/fixtures/multi-dep-thunk/Main.roc
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
app Main provides [ main ] imports [ Dep1 ]
|
||||||
|
|
||||||
|
main : Str
|
||||||
|
main = Dep1.value1 {}
|
23
cli/tests/fixtures/multi-dep-thunk/platform/Cargo.lock
generated
vendored
Normal file
23
cli/tests/fixtures/multi-dep-thunk/platform/Cargo.lock
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
cli/tests/fixtures/multi-dep-thunk/platform/Cargo.toml
vendored
Normal file
13
cli/tests/fixtures/multi-dep-thunk/platform/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_std = { path = "../../../../../roc_std" }
|
||||||
|
|
||||||
|
[workspace]
|
8
cli/tests/fixtures/multi-dep-thunk/platform/README.md
vendored
Normal file
8
cli/tests/fixtures/multi-dep-thunk/platform/README.md
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Rebuilding the host from source
|
||||||
|
|
||||||
|
Run `build.sh` to manually rebuild this platform's host.
|
||||||
|
|
||||||
|
Note that the compiler currently has its own logic for rebuilding these hosts
|
||||||
|
(in `link.rs`). It's hardcoded for now, but the long-term goal is that
|
||||||
|
hosts will be precompiled by platform authors and distributed in packages,
|
||||||
|
at which point only package authors will need to think about rebuilding hosts.
|
12
cli/tests/fixtures/multi-dep-thunk/platform/build.sh
vendored
Executable file
12
cli/tests/fixtures/multi-dep-thunk/platform/build.sh
vendored
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# compile c_host.o and rust_host.o
|
||||||
|
clang -c host.c -o c_host.o
|
||||||
|
rustc host.rs -o rust_host.o
|
||||||
|
|
||||||
|
# link them together into host.o
|
||||||
|
ld -r c_host.o rust_host.o -o host.o
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
rm -f c_host.o
|
||||||
|
rm -f rust_host.o
|
7
cli/tests/fixtures/multi-dep-thunk/platform/host.c
vendored
Normal file
7
cli/tests/fixtures/multi-dep-thunk/platform/host.c
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int rust_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return rust_main();
|
||||||
|
}
|
28
cli/tests/fixtures/multi-dep-thunk/platform/src/lib.rs
vendored
Normal file
28
cli/tests/fixtures/multi-dep-thunk/platform/src/lib.rs
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use roc_std::RocCallResult;
|
||||||
|
use roc_std::RocStr;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#[link_name = "main_1_exposed"]
|
||||||
|
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_main() -> isize {
|
||||||
|
let answer = unsafe {
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
let mut output: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
say_hello(&mut *output.as_mut_ptr());
|
||||||
|
|
||||||
|
match output.assume_init().into() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(msg) => panic!("roc failed with message {}", msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Roc says: {}", str::from_utf8(answer.as_slice()).unwrap());
|
||||||
|
|
||||||
|
// Exit code
|
||||||
|
0
|
||||||
|
}
|
|
@ -207,6 +207,40 @@ pub fn example_file(dir_name: &str, file_name: &str) -> PathBuf {
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn fixtures_dir(dir_name: &str) -> PathBuf {
|
||||||
|
let mut path = env::current_exe().ok().unwrap();
|
||||||
|
|
||||||
|
// Get rid of the filename in target/debug/deps/cli_run-99c65e4e9a1fbd06
|
||||||
|
path.pop();
|
||||||
|
|
||||||
|
// If we're in deps/ get rid of deps/ in target/debug/deps/
|
||||||
|
if path.ends_with("deps") {
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of target/debug/ so we're back at the project root
|
||||||
|
path.pop();
|
||||||
|
path.pop();
|
||||||
|
|
||||||
|
// Descend into cli/tests/fixtures/{dir_name}
|
||||||
|
path.push("cli");
|
||||||
|
path.push("tests");
|
||||||
|
path.push("fixtures");
|
||||||
|
path.push(dir_name);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn fixture_file(dir_name: &str, file_name: &str) -> PathBuf {
|
||||||
|
let mut path = fixtures_dir(dir_name);
|
||||||
|
|
||||||
|
path.push(file_name);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn repl_eval(input: &str) -> Out {
|
pub fn repl_eval(input: &str) -> Out {
|
||||||
let mut cmd = Command::new(path_to_roc_binary());
|
let mut cmd = Command::new(path_to_roc_binary());
|
||||||
|
|
|
@ -175,6 +175,13 @@ mod repl_eval {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_contains() {
|
||||||
|
expect_success("List.contains [] 0", "False : Bool");
|
||||||
|
expect_success("List.contains [ 1, 2, 3 ] 2", "True : Bool");
|
||||||
|
expect_success("List.contains [ 1, 2, 3 ] 4", "False : Bool");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_1_field_i64_record() {
|
fn basic_1_field_i64_record() {
|
||||||
// Even though this gets unwrapped at runtime, the repl should still
|
// Even though this gets unwrapped at runtime, the repl should still
|
||||||
|
|
|
@ -6,7 +6,7 @@ use libloading::{Error, Library};
|
||||||
use roc_gen::llvm::build::OptLevel;
|
use roc_gen::llvm::build::OptLevel;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Child, Command};
|
use std::process::{Child, Command, Output};
|
||||||
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
use target_lexicon::{Architecture, OperatingSystem, Triple};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
let host_dest = host_input_path.with_file_name("host.o");
|
let host_dest = host_input_path.with_file_name("host.o");
|
||||||
|
|
||||||
// Compile host.c
|
// Compile host.c
|
||||||
Command::new("clang")
|
let output = Command::new("clang")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[
|
.args(&[
|
||||||
"-c",
|
"-c",
|
||||||
|
@ -58,18 +58,22 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
validate_output("host.c", "clang", output);
|
||||||
|
|
||||||
if cargo_host_src.exists() {
|
if cargo_host_src.exists() {
|
||||||
// Compile and link Cargo.toml, if it exists
|
// Compile and link Cargo.toml, if it exists
|
||||||
let cargo_dir = host_input_path.parent().unwrap();
|
let cargo_dir = host_input_path.parent().unwrap();
|
||||||
let libhost_dir = cargo_dir.join("target").join("release");
|
let libhost_dir = cargo_dir.join("target").join("release");
|
||||||
|
|
||||||
Command::new("cargo")
|
let output = Command::new("cargo")
|
||||||
.args(&["build", "--release"])
|
.args(&["build", "--release"])
|
||||||
.current_dir(cargo_dir)
|
.current_dir(cargo_dir)
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Command::new("ld")
|
validate_output("host.rs", "cargo build --release", output);
|
||||||
|
|
||||||
|
let output = Command::new("ld")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[
|
.args(&[
|
||||||
"-r",
|
"-r",
|
||||||
|
@ -82,9 +86,11 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
])
|
])
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
validate_output("c_host.o", "ld", output);
|
||||||
} else if rust_host_src.exists() {
|
} else if rust_host_src.exists() {
|
||||||
// Compile and link host.rs, if it exists
|
// Compile and link host.rs, if it exists
|
||||||
Command::new("rustc")
|
let output = Command::new("rustc")
|
||||||
.args(&[
|
.args(&[
|
||||||
rust_host_src.to_str().unwrap(),
|
rust_host_src.to_str().unwrap(),
|
||||||
"-o",
|
"-o",
|
||||||
|
@ -93,7 +99,9 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Command::new("ld")
|
validate_output("host.rs", "rustc", output);
|
||||||
|
|
||||||
|
let output = Command::new("ld")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[
|
.args(&[
|
||||||
"-r",
|
"-r",
|
||||||
|
@ -105,8 +113,10 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
validate_output("rust_host.o", "ld", output);
|
||||||
|
|
||||||
// Clean up rust_host.o
|
// Clean up rust_host.o
|
||||||
Command::new("rm")
|
let output = Command::new("rm")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[
|
.args(&[
|
||||||
"-f",
|
"-f",
|
||||||
|
@ -115,13 +125,17 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||||
])
|
])
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
validate_output("rust_host.o", "rm", output);
|
||||||
} else {
|
} else {
|
||||||
// Clean up rust_host.o
|
// Clean up rust_host.o
|
||||||
Command::new("mv")
|
let output = Command::new("mv")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[c_host_dest, host_dest])
|
.args(&[c_host_dest, host_dest])
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
validate_output("rust_host.o", "mv", output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,3 +317,18 @@ pub fn module_to_dylib(
|
||||||
|
|
||||||
Library::new(path)
|
Library::new(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_output(file_name: &str, cmd_name: &str, output: Output) {
|
||||||
|
if !output.status.success() {
|
||||||
|
match std::str::from_utf8(&output.stderr) {
|
||||||
|
Ok(stderr) => panic!(
|
||||||
|
"Failed to rebuild {} - stderr of the `{}` command was:\n{}",
|
||||||
|
file_name, cmd_name, stderr
|
||||||
|
),
|
||||||
|
Err(utf8_err) => panic!(
|
||||||
|
"Failed to rebuild {} - stderr of the `{}` command was invalid utf8 ({:?})",
|
||||||
|
file_name, cmd_name, utf8_err
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,24 +14,26 @@ use target_lexicon::Triple;
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn gen_from_mono_module(
|
pub fn gen_from_mono_module(
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
loaded: MonomorphizedModule,
|
mut loaded: MonomorphizedModule,
|
||||||
file_path: PathBuf,
|
_file_path: PathBuf,
|
||||||
target: Triple,
|
target: Triple,
|
||||||
app_o_file: &Path,
|
app_o_file: &Path,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
) {
|
) {
|
||||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE};
|
use roc_reporting::report::{
|
||||||
|
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||||
|
};
|
||||||
|
|
||||||
let src = loaded.src;
|
for (home, (module_path, src)) in loaded.sources {
|
||||||
let home = loaded.module_id;
|
|
||||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
// Report parsing and canonicalization problems
|
// Report parsing and canonicalization problems
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, &loaded.interns);
|
||||||
|
|
||||||
for problem in loaded.can_problems.into_iter() {
|
let problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
let report = can_problem(&alloc, file_path.clone(), problem);
|
for problem in problems.into_iter() {
|
||||||
|
let report = can_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -39,8 +41,9 @@ pub fn gen_from_mono_module(
|
||||||
println!("\n{}\n", buf);
|
println!("\n{}\n", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in loaded.type_problems.into_iter() {
|
let problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
let report = type_problem(&alloc, file_path.clone(), problem);
|
for problem in problems {
|
||||||
|
let report = type_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -48,6 +51,17 @@ pub fn gen_from_mono_module(
|
||||||
println!("\n{}\n", buf);
|
println!("\n{}\n", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||||
|
for problem in problems {
|
||||||
|
let report = mono_problem(&alloc, module_path.clone(), problem);
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
|
||||||
|
println!("\n{}\n", buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the binary
|
// Generate the binary
|
||||||
|
|
||||||
let context = Context::create();
|
let context = Context::create();
|
||||||
|
|
|
@ -459,6 +459,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// contains : List elem, elem -> Bool
|
||||||
|
add_type(
|
||||||
|
Symbol::LIST_CONTAINS,
|
||||||
|
top_level_function(
|
||||||
|
vec![list_type(flex(TVAR1)), flex(TVAR1)],
|
||||||
|
Box::new(bool_type()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_WALK_RIGHT,
|
Symbol::LIST_WALK_RIGHT,
|
||||||
|
|
|
@ -674,6 +674,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// contains : Attr * (List a)
|
||||||
|
// , a
|
||||||
|
// -> Attr * Bool
|
||||||
|
add_type(Symbol::LIST_CONTAINS, {
|
||||||
|
let_tvars! { a, star1, star2 };
|
||||||
|
|
||||||
|
unique_function(vec![list_type(star1, a), flex(a)], bool_type(star2))
|
||||||
|
});
|
||||||
|
|
||||||
// join : Attr * (List (Attr * (List a)))
|
// join : Attr * (List (Attr * (List a)))
|
||||||
// -> Attr * (List a)
|
// -> Attr * (List a)
|
||||||
add_type(Symbol::LIST_JOIN, {
|
add_type(Symbol::LIST_JOIN, {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use roc_collections::all::{ImMap, MutSet, SendMap};
|
use roc_collections::all::{ImMap, MutMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
use roc_parse::ast::{AssignedField, Tag, TypeAnnotation};
|
||||||
|
@ -31,6 +31,7 @@ pub struct IntroducedVariables {
|
||||||
pub wildcards: Vec<Variable>,
|
pub wildcards: Vec<Variable>,
|
||||||
pub var_by_name: SendMap<Lowercase, Variable>,
|
pub var_by_name: SendMap<Lowercase, Variable>,
|
||||||
pub name_by_var: SendMap<Variable, Lowercase>,
|
pub name_by_var: SendMap<Variable, Lowercase>,
|
||||||
|
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntroducedVariables {
|
impl IntroducedVariables {
|
||||||
|
@ -43,10 +44,16 @@ impl IntroducedVariables {
|
||||||
self.wildcards.push(var);
|
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) {
|
pub fn union(&mut self, other: &Self) {
|
||||||
self.wildcards.extend(other.wildcards.iter().cloned());
|
self.wildcards.extend(other.wildcards.iter().cloned());
|
||||||
self.var_by_name.extend(other.var_by_name.clone());
|
self.var_by_name.extend(other.var_by_name.clone());
|
||||||
self.name_by_var.extend(other.name_by_var.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> {
|
pub fn var_by_name(&self, name: &Lowercase) -> Option<&Variable> {
|
||||||
|
@ -220,7 +227,15 @@ fn can_annotation_help(
|
||||||
// instantiate variables
|
// instantiate variables
|
||||||
actual.substitute(&substitutions);
|
actual.substitute(&substitutions);
|
||||||
|
|
||||||
Type::Alias(symbol, vars, Box::new(actual))
|
// Type::Alias(symbol, vars, Box::new(actual))
|
||||||
|
let actual_var = var_store.fresh();
|
||||||
|
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||||
|
Type::HostExposedAlias {
|
||||||
|
name: symbol,
|
||||||
|
arguments: vars,
|
||||||
|
actual: Box::new(actual),
|
||||||
|
actual_var,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
|
@ -352,7 +367,16 @@ fn can_annotation_help(
|
||||||
let alias = scope.lookup_alias(symbol).unwrap();
|
let alias = scope.lookup_alias(symbol).unwrap();
|
||||||
local_aliases.insert(symbol, alias.clone());
|
local_aliases.insert(symbol, alias.clone());
|
||||||
|
|
||||||
Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
|
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
|
||||||
|
|
||||||
|
let actual_var = var_store.fresh();
|
||||||
|
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||||
|
Type::HostExposedAlias {
|
||||||
|
name: symbol,
|
||||||
|
arguments: vars,
|
||||||
|
actual: Box::new(alias.typ.clone()),
|
||||||
|
actual_var,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// This is a syntactically invalid type alias.
|
// This is a syntactically invalid type alias.
|
||||||
|
|
|
@ -63,6 +63,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::LIST_REPEAT => list_repeat,
|
Symbol::LIST_REPEAT => list_repeat,
|
||||||
Symbol::LIST_REVERSE => list_reverse,
|
Symbol::LIST_REVERSE => list_reverse,
|
||||||
Symbol::LIST_CONCAT => list_concat,
|
Symbol::LIST_CONCAT => list_concat,
|
||||||
|
Symbol::LIST_CONTAINS => list_contains,
|
||||||
Symbol::LIST_PREPEND => list_prepend,
|
Symbol::LIST_PREPEND => list_prepend,
|
||||||
Symbol::LIST_JOIN => list_join,
|
Symbol::LIST_JOIN => list_join,
|
||||||
Symbol::LIST_MAP => list_map,
|
Symbol::LIST_MAP => list_map,
|
||||||
|
@ -101,6 +102,36 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::NUM_ATAN => num_atan,
|
Symbol::NUM_ATAN => num_atan,
|
||||||
Symbol::NUM_ACOS => num_acos,
|
Symbol::NUM_ACOS => num_acos,
|
||||||
Symbol::NUM_ASIN => num_asin,
|
Symbol::NUM_ASIN => num_asin,
|
||||||
|
Symbol::NUM_MAX_INT => num_max_int,
|
||||||
|
Symbol::NUM_MIN_INT => num_min_int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.maxInt : Int
|
||||||
|
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let int_var = var_store.fresh();
|
||||||
|
let body = Int(int_var, i64::MAX);
|
||||||
|
|
||||||
|
Def {
|
||||||
|
annotation: None,
|
||||||
|
expr_var: int_var,
|
||||||
|
loc_expr: Located::at_zero(body),
|
||||||
|
loc_pattern: Located::at_zero(Pattern::Identifier(symbol)),
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.minInt : Int
|
||||||
|
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let int_var = var_store.fresh();
|
||||||
|
let body = Int(int_var, i64::MIN);
|
||||||
|
|
||||||
|
Def {
|
||||||
|
annotation: None,
|
||||||
|
expr_var: int_var,
|
||||||
|
loc_expr: Located::at_zero(body),
|
||||||
|
loc_pattern: Located::at_zero(Pattern::Identifier(symbol)),
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1291,6 +1322,30 @@ fn list_keep_if(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List.contains : List elem, elem, -> Bool
|
||||||
|
fn list_contains(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let elem_var = var_store.fresh();
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::ListContains,
|
||||||
|
args: vec![
|
||||||
|
(list_var, Var(Symbol::ARG_1)),
|
||||||
|
(elem_var, Var(Symbol::ARG_2)),
|
||||||
|
],
|
||||||
|
ret_var: bool_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(list_var, Symbol::ARG_1), (elem_var, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
bool_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.map : List before, (before -> after) -> List after
|
/// List.map : List before, (before -> after) -> List after
|
||||||
fn list_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
|
|
|
@ -1443,6 +1443,8 @@ fn to_pending_def<'a>(
|
||||||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => {
|
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => {
|
||||||
to_pending_def(env, var_store, sub_def, scope, pattern_type)
|
to_pending_def(env, var_store, sub_def, scope, pattern_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,9 +207,8 @@ pub fn canonicalize_expr<'a>(
|
||||||
let (can_update, update_out) =
|
let (can_update, update_out) =
|
||||||
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
|
canonicalize_expr(env, var_store, scope, loc_update.region, &loc_update.value);
|
||||||
if let Var(symbol) = &can_update.value {
|
if let Var(symbol) = &can_update.value {
|
||||||
let (can_fields, mut output) =
|
match canonicalize_fields(env, var_store, scope, region, fields) {
|
||||||
canonicalize_fields(env, var_store, scope, region, fields);
|
Ok((can_fields, mut output)) => {
|
||||||
|
|
||||||
output.references = output.references.union(update_out.references);
|
output.references = output.references.union(update_out.references);
|
||||||
|
|
||||||
let answer = Update {
|
let answer = Update {
|
||||||
|
@ -220,6 +219,20 @@ pub fn canonicalize_expr<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
(answer, output)
|
(answer, output)
|
||||||
|
}
|
||||||
|
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
}) => (
|
||||||
|
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
}),
|
||||||
|
Output::default(),
|
||||||
|
),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// only (optionally qualified) variables can be updated, not arbitrary expressions
|
// only (optionally qualified) variables can be updated, not arbitrary expressions
|
||||||
|
|
||||||
|
@ -241,16 +254,27 @@ pub fn canonicalize_expr<'a>(
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
(EmptyRecord, Output::default())
|
(EmptyRecord, Output::default())
|
||||||
} else {
|
} else {
|
||||||
let (can_fields, output) =
|
match canonicalize_fields(env, var_store, scope, region, fields) {
|
||||||
canonicalize_fields(env, var_store, scope, region, fields);
|
Ok((can_fields, output)) => (
|
||||||
|
|
||||||
(
|
|
||||||
Record {
|
Record {
|
||||||
record_var: var_store.fresh(),
|
record_var: var_store.fresh(),
|
||||||
fields: can_fields,
|
fields: can_fields,
|
||||||
},
|
},
|
||||||
output,
|
output,
|
||||||
)
|
),
|
||||||
|
Err(CanonicalizeRecordProblem::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
}) => (
|
||||||
|
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
}),
|
||||||
|
Output::default(),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
|
ast::Expr::Str(literal) => flatten_str_literal(env, var_store, scope, literal),
|
||||||
|
@ -971,20 +995,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CanonicalizeRecordProblem {
|
||||||
|
InvalidOptionalValue {
|
||||||
|
field_name: Lowercase,
|
||||||
|
field_region: Region,
|
||||||
|
record_region: Region,
|
||||||
|
},
|
||||||
|
}
|
||||||
fn canonicalize_fields<'a>(
|
fn canonicalize_fields<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
region: Region,
|
region: Region,
|
||||||
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
|
fields: &'a [Located<ast::AssignedField<'a, ast::Expr<'a>>>],
|
||||||
) -> (SendMap<Lowercase, Field>, Output) {
|
) -> Result<(SendMap<Lowercase, Field>, Output), CanonicalizeRecordProblem> {
|
||||||
let mut can_fields = SendMap::default();
|
let mut can_fields = SendMap::default();
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
|
|
||||||
for loc_field in fields.iter() {
|
for loc_field in fields.iter() {
|
||||||
let (label, field_expr, field_out, field_var) =
|
match canonicalize_field(env, var_store, scope, &loc_field.value, loc_field.region) {
|
||||||
canonicalize_field(env, var_store, scope, &loc_field.value, loc_field.region);
|
Ok((label, field_expr, field_out, field_var)) => {
|
||||||
|
|
||||||
let field = Field {
|
let field = Field {
|
||||||
var: field_var,
|
var: field_var,
|
||||||
region: loc_field.region,
|
region: loc_field.region,
|
||||||
|
@ -1004,17 +1034,40 @@ fn canonicalize_fields<'a>(
|
||||||
|
|
||||||
output.references = output.references.union(field_out.references);
|
output.references = output.references.union(field_out.references);
|
||||||
}
|
}
|
||||||
|
Err(CanonicalizeFieldProblem::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
}) => {
|
||||||
|
env.problems.push(Problem::InvalidOptionalValue {
|
||||||
|
field_name: field_name.clone(),
|
||||||
|
field_region,
|
||||||
|
record_region: region,
|
||||||
|
});
|
||||||
|
return Err(CanonicalizeRecordProblem::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region: region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(can_fields, output)
|
Ok((can_fields, output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CanonicalizeFieldProblem {
|
||||||
|
InvalidOptionalValue {
|
||||||
|
field_name: Lowercase,
|
||||||
|
field_region: Region,
|
||||||
|
},
|
||||||
|
}
|
||||||
fn canonicalize_field<'a>(
|
fn canonicalize_field<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
field: &'a ast::AssignedField<'a, ast::Expr<'a>>,
|
field: &'a ast::AssignedField<'a, ast::Expr<'a>>,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> (Lowercase, Located<Expr>, Output, Variable) {
|
) -> Result<(Lowercase, Located<Expr>, Output, Variable), CanonicalizeFieldProblem> {
|
||||||
use roc_parse::ast::AssignedField::*;
|
use roc_parse::ast::AssignedField::*;
|
||||||
|
|
||||||
match field {
|
match field {
|
||||||
|
@ -1024,17 +1077,18 @@ fn canonicalize_field<'a>(
|
||||||
let (loc_can_expr, output) =
|
let (loc_can_expr, output) =
|
||||||
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value);
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
Lowercase::from(label.value),
|
Lowercase::from(label.value),
|
||||||
loc_can_expr,
|
loc_can_expr,
|
||||||
output,
|
output,
|
||||||
field_var,
|
field_var,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionalValue(_, _, _) => {
|
OptionalValue(label, _, loc_expr) => Err(CanonicalizeFieldProblem::InvalidOptionalValue {
|
||||||
todo!("TODO gracefully handle an optional field being used in an Expr");
|
field_name: Lowercase::from(label.value),
|
||||||
}
|
field_region: Region::span_across(&label.region, &loc_expr.region),
|
||||||
|
}),
|
||||||
|
|
||||||
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
|
// A label with no value, e.g. `{ name }` (this is sugar for { name: name })
|
||||||
LabelOnly(_) => {
|
LabelOnly(_) => {
|
||||||
|
|
|
@ -47,6 +47,8 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
||||||
Nested(alias @ Alias { .. }) => Nested(alias),
|
Nested(alias @ Alias { .. }) => Nested(alias),
|
||||||
ann @ Annotation(_, _) => Nested(ann),
|
ann @ Annotation(_, _) => Nested(ann),
|
||||||
Nested(ann @ Annotation(_, _)) => Nested(ann),
|
Nested(ann @ Annotation(_, _)) => Nested(ann),
|
||||||
|
Nested(NotYetImplemented(s)) => todo!("{}", s),
|
||||||
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,6 +305,32 @@ mod test_can {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// OPTIONAL RECORDS
|
||||||
|
#[test]
|
||||||
|
fn incorrect_optional_value() {
|
||||||
|
let src = indoc!(
|
||||||
|
r#"
|
||||||
|
{ x ? 42 }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
let arena = Bump::new();
|
||||||
|
let CanExprOut {
|
||||||
|
problems, loc_expr, ..
|
||||||
|
} = can_expr_with(&arena, test_home(), src);
|
||||||
|
|
||||||
|
assert_eq!(problems.len(), 1);
|
||||||
|
assert!(problems.iter().all(|problem| match problem {
|
||||||
|
Problem::InvalidOptionalValue { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
assert!(match loc_expr.value {
|
||||||
|
Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue { .. }) => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TAIL CALLS
|
||||||
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
|
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
|
||||||
match expr {
|
match expr {
|
||||||
LetRec(assignments, body, _) => {
|
LetRec(assignments, body, _) => {
|
||||||
|
|
|
@ -2105,6 +2105,46 @@ fn annotation_to_attr_type(
|
||||||
|
|
||||||
let alias = Type::Alias(*symbol, new_fields, Box::new(actual_type));
|
let alias = Type::Alias(*symbol, new_fields, Box::new(actual_type));
|
||||||
|
|
||||||
|
(
|
||||||
|
actual_vars,
|
||||||
|
crate::builtins::builtin_type(Symbol::ATTR_ATTR, vec![uniq_type, alias]),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
panic!("lifted type is not Attr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
name: symbol,
|
||||||
|
arguments: fields,
|
||||||
|
actual_var,
|
||||||
|
actual,
|
||||||
|
} => {
|
||||||
|
let (mut actual_vars, lifted_actual) =
|
||||||
|
annotation_to_attr_type(var_store, actual, rigids, change_var_kind);
|
||||||
|
|
||||||
|
if let Type::Apply(attr_symbol, args) = lifted_actual {
|
||||||
|
debug_assert!(attr_symbol == Symbol::ATTR_ATTR);
|
||||||
|
|
||||||
|
let uniq_type = args[0].clone();
|
||||||
|
let actual_type = args[1].clone();
|
||||||
|
|
||||||
|
let mut new_fields = Vec::with_capacity(fields.len());
|
||||||
|
for (name, tipe) in fields {
|
||||||
|
let (lifted_vars, lifted) =
|
||||||
|
annotation_to_attr_type(var_store, tipe, rigids, change_var_kind);
|
||||||
|
|
||||||
|
actual_vars.extend(lifted_vars);
|
||||||
|
|
||||||
|
new_fields.push((name.clone(), lifted));
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias = Type::HostExposedAlias {
|
||||||
|
name: *symbol,
|
||||||
|
arguments: new_fields,
|
||||||
|
actual_var: *actual_var,
|
||||||
|
actual: Box::new(actual_type),
|
||||||
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
actual_vars,
|
actual_vars,
|
||||||
crate::builtins::builtin_type(Symbol::ATTR_ATTR, vec![uniq_type, alias]),
|
crate::builtins::builtin_type(Symbol::ATTR_ATTR, vec![uniq_type, alias]),
|
||||||
|
|
|
@ -20,6 +20,7 @@ impl<'a> Formattable<'a> for Def<'a> {
|
||||||
spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline()
|
spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline()
|
||||||
}
|
}
|
||||||
Nested(def) => def.is_multiline(),
|
Nested(def) => def.is_multiline(),
|
||||||
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ impl<'a> Formattable<'a> for Def<'a> {
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
fmt_spaces(buf, spaces.iter(), indent);
|
||||||
}
|
}
|
||||||
Nested(def) => def.format(buf, indent),
|
Nested(def) => def.format(buf, indent),
|
||||||
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
|
either = "1.6.1"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||||
#
|
#
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
# The reason for this fork is that the way Inkwell is designed, you have to use
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::layout_id::LayoutIds;
|
use crate::layout_id::LayoutIds;
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_get_unsafe,
|
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
||||||
list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat, list_reverse, list_set,
|
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
||||||
list_single, list_walk_right,
|
list_reverse, list_set, list_single, list_walk_right,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{str_concat, str_len, str_split, CHAR_LAYOUT};
|
use crate::llvm::build_str::{str_concat, str_len, str_split, CHAR_LAYOUT};
|
||||||
use crate::llvm::compare::{build_eq, build_neq};
|
use crate::llvm::compare::{build_eq, build_neq};
|
||||||
|
@ -34,7 +34,7 @@ use roc_collections::all::{ImMap, MutSet};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::ir::{JoinPointId, Wrapped};
|
use roc_mono::ir::{JoinPointId, Wrapped};
|
||||||
use roc_mono::layout::{Builtin, Layout, MemoryMode};
|
use roc_mono::layout::{Builtin, ClosureLayout, Layout, MemoryMode};
|
||||||
use target_lexicon::CallingConvention;
|
use target_lexicon::CallingConvention;
|
||||||
|
|
||||||
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
|
/// This is for Inkwell's FunctionValue::verify - we want to know the verification
|
||||||
|
@ -1842,6 +1842,286 @@ pub fn create_entry_block_alloca<'a, 'ctx>(
|
||||||
builder.build_alloca(basic_type, name)
|
builder.build_alloca(basic_type, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expose_function_to_host<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
roc_function: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let roc_wrapper_function = make_exception_catching_wrapper(env, roc_function);
|
||||||
|
|
||||||
|
let roc_function_type = roc_wrapper_function.get_type();
|
||||||
|
|
||||||
|
// STEP 1: turn `f : a,b,c -> d` into `f : a,b,c, &d -> {}`
|
||||||
|
let mut argument_types = roc_function_type.get_param_types();
|
||||||
|
let return_type = roc_function_type.get_return_type().unwrap();
|
||||||
|
let output_type = return_type.ptr_type(AddressSpace::Generic);
|
||||||
|
argument_types.push(output_type.into());
|
||||||
|
|
||||||
|
let c_function_type = env.context.void_type().fn_type(&argument_types, false);
|
||||||
|
let c_function_name: String = format!("{}_exposed", roc_function.get_name().to_str().unwrap());
|
||||||
|
|
||||||
|
let c_function = env.module.add_function(
|
||||||
|
c_function_name.as_str(),
|
||||||
|
c_function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
|
||||||
|
// STEP 2: build the exposed function's body
|
||||||
|
let builder = env.builder;
|
||||||
|
let context = env.context;
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(c_function, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
// drop the final argument, which is the pointer we write the result into
|
||||||
|
let args = c_function.get_params();
|
||||||
|
let output_arg_index = args.len() - 1;
|
||||||
|
let args = &args[..args.len() - 1];
|
||||||
|
|
||||||
|
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
||||||
|
debug_assert_eq!(args.len(), roc_wrapper_function.get_params().len());
|
||||||
|
|
||||||
|
let call_wrapped = builder.build_call(roc_wrapper_function, args, "call_wrapped_function");
|
||||||
|
call_wrapped.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
let call_result = call_wrapped.try_as_basic_value().left().unwrap();
|
||||||
|
|
||||||
|
let output_arg = c_function
|
||||||
|
.get_nth_param(output_arg_index as u32)
|
||||||
|
.unwrap()
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
builder.build_store(output_arg, call_result);
|
||||||
|
|
||||||
|
builder.build_return(None);
|
||||||
|
|
||||||
|
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
||||||
|
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||||
|
let size_function_name: String = format!("{}_size", roc_function.get_name().to_str().unwrap());
|
||||||
|
|
||||||
|
let size_function = env.module.add_function(
|
||||||
|
size_function_name.as_str(),
|
||||||
|
size_function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(size_function, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let size: BasicValueEnum = return_type.size_of().unwrap().into();
|
||||||
|
builder.build_return(Some(&size));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invoke_and_catch<'a, 'ctx, 'env, F, T>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
function: F,
|
||||||
|
arguments: &[BasicValueEnum<'ctx>],
|
||||||
|
return_type: T,
|
||||||
|
) -> BasicValueEnum<'ctx>
|
||||||
|
where
|
||||||
|
F: Into<either::Either<FunctionValue<'ctx>, PointerValue<'ctx>>>,
|
||||||
|
T: inkwell::types::BasicType<'ctx>,
|
||||||
|
{
|
||||||
|
let context = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
|
let call_result_type = context.struct_type(
|
||||||
|
&[context.i64_type().into(), return_type.as_basic_type_enum()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
|
let catch_block = context.append_basic_block(parent, "catch_block");
|
||||||
|
let cont_block = context.append_basic_block(parent, "cont_block");
|
||||||
|
|
||||||
|
let result_alloca = builder.build_alloca(call_result_type, "result");
|
||||||
|
|
||||||
|
// invoke instead of call, so that we can catch any exeptions thrown in Roc code
|
||||||
|
let call_result = {
|
||||||
|
let call = builder.build_invoke(
|
||||||
|
function,
|
||||||
|
&arguments,
|
||||||
|
then_block,
|
||||||
|
catch_block,
|
||||||
|
"call_roc_function",
|
||||||
|
);
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
call.try_as_basic_value().left().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// exception handling
|
||||||
|
{
|
||||||
|
builder.position_at_end(catch_block);
|
||||||
|
|
||||||
|
let landing_pad_type = {
|
||||||
|
let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into();
|
||||||
|
let selector_value = context.i32_type().into();
|
||||||
|
|
||||||
|
context.struct_type(&[exception_ptr, selector_value], false)
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = builder
|
||||||
|
.build_catch_all_landing_pad(
|
||||||
|
&landing_pad_type,
|
||||||
|
&BasicValueEnum::IntValue(context.i8_type().const_zero()),
|
||||||
|
context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
|
"main_landing_pad",
|
||||||
|
)
|
||||||
|
.into_struct_value();
|
||||||
|
|
||||||
|
let exception_ptr = builder
|
||||||
|
.build_extract_value(info, 0, "exception_ptr")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let thrown = cxa_begin_catch(env, exception_ptr);
|
||||||
|
|
||||||
|
let error_msg = {
|
||||||
|
let exception_type = u8_ptr;
|
||||||
|
let ptr = builder.build_bitcast(
|
||||||
|
thrown,
|
||||||
|
exception_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"cast",
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_load(ptr.into_pointer_value(), "error_msg")
|
||||||
|
};
|
||||||
|
|
||||||
|
let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false);
|
||||||
|
|
||||||
|
let return_value = {
|
||||||
|
let v1 = return_type.const_zero();
|
||||||
|
|
||||||
|
// flag is non-zero, indicating failure
|
||||||
|
let flag = context.i64_type().const_int(1, false);
|
||||||
|
|
||||||
|
let v2 = builder
|
||||||
|
.build_insert_value(v1, flag, 0, "set_error")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let v3 = builder
|
||||||
|
.build_insert_value(v2, error_msg, 1, "set_exception")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
v3
|
||||||
|
};
|
||||||
|
|
||||||
|
// bitcast result alloca so we can store our concrete type { flag, error_msg } in there
|
||||||
|
let result_alloca_bitcast = builder
|
||||||
|
.build_bitcast(
|
||||||
|
result_alloca,
|
||||||
|
return_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"result_alloca_bitcast",
|
||||||
|
)
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
// store our return value
|
||||||
|
builder.build_store(result_alloca_bitcast, return_value);
|
||||||
|
|
||||||
|
cxa_end_catch(env);
|
||||||
|
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
let return_value = {
|
||||||
|
let v1 = call_result_type.const_zero();
|
||||||
|
|
||||||
|
let v2 = builder
|
||||||
|
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
||||||
|
.unwrap();
|
||||||
|
let v3 = builder
|
||||||
|
.build_insert_value(v2, call_result, 1, "set_call_result")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
v3
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr = builder.build_bitcast(
|
||||||
|
result_alloca,
|
||||||
|
call_result_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"name",
|
||||||
|
);
|
||||||
|
builder.build_store(ptr.into_pointer_value(), return_value);
|
||||||
|
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.position_at_end(cont_block);
|
||||||
|
|
||||||
|
let result = builder.build_load(result_alloca, "result");
|
||||||
|
|
||||||
|
// MUST set the personality at the very end;
|
||||||
|
// doing it earlier can cause the personality to be ignored
|
||||||
|
let personality_func = get_gxx_personality_v0(env);
|
||||||
|
parent.set_personality_function(personality_func);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
roc_function: FunctionValue<'ctx>,
|
||||||
|
) -> FunctionValue<'ctx> {
|
||||||
|
// build the C calling convention wrapper
|
||||||
|
|
||||||
|
let context = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let roc_function_type = roc_function.get_type();
|
||||||
|
let argument_types = roc_function_type.get_param_types();
|
||||||
|
|
||||||
|
let wrapper_function_name = format!("{}_catcher", roc_function.get_name().to_str().unwrap());
|
||||||
|
|
||||||
|
let wrapper_return_type = context.struct_type(
|
||||||
|
&[
|
||||||
|
context.i64_type().into(),
|
||||||
|
roc_function_type.get_return_type().unwrap(),
|
||||||
|
],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let wrapper_function_type = wrapper_return_type.fn_type(&argument_types, false);
|
||||||
|
|
||||||
|
// Add main to the module.
|
||||||
|
let wrapper_function =
|
||||||
|
env.module
|
||||||
|
.add_function(&wrapper_function_name, wrapper_function_type, None);
|
||||||
|
|
||||||
|
// our exposed main function adheres to the C calling convention
|
||||||
|
wrapper_function.set_call_conventions(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
// invoke instead of call, so that we can catch any exeptions thrown in Roc code
|
||||||
|
let arguments = wrapper_function.get_params();
|
||||||
|
|
||||||
|
let basic_block = context.append_basic_block(wrapper_function, "entry");
|
||||||
|
builder.position_at_end(basic_block);
|
||||||
|
|
||||||
|
let result = invoke_and_catch(
|
||||||
|
env,
|
||||||
|
wrapper_function,
|
||||||
|
roc_function,
|
||||||
|
&arguments,
|
||||||
|
roc_function_type.get_return_type().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_return(Some(&result));
|
||||||
|
|
||||||
|
// MUST set the personality at the very end;
|
||||||
|
// doing it earlier can cause the personality to be ignored
|
||||||
|
let personality_func = get_gxx_personality_v0(env);
|
||||||
|
wrapper_function.set_personality_function(personality_func);
|
||||||
|
|
||||||
|
wrapper_function
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_proc_header<'a, 'ctx, 'env>(
|
pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -1853,6 +2133,32 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let context = &env.context;
|
let context = &env.context;
|
||||||
|
|
||||||
|
let fn_name = layout_ids
|
||||||
|
.get(symbol, layout)
|
||||||
|
.to_symbol_string(symbol, &env.interns);
|
||||||
|
|
||||||
|
use roc_mono::ir::HostExposedLayouts;
|
||||||
|
match &proc.host_exposed_layouts {
|
||||||
|
HostExposedLayouts::NotHostExposed => {}
|
||||||
|
HostExposedLayouts::HostExposed { rigids: _, aliases } => {
|
||||||
|
for (name, layout) in aliases {
|
||||||
|
match layout {
|
||||||
|
Layout::Closure(arguments, closure, result) => {
|
||||||
|
build_closure_caller(env, &fn_name, *name, arguments, closure, result)
|
||||||
|
}
|
||||||
|
Layout::FunctionPointer(_arguments, _result) => {
|
||||||
|
// TODO should this be considered a closure of size 0?
|
||||||
|
// or do we let the host call it directly?
|
||||||
|
// then we have no RocCallResult wrapping though
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes);
|
let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes);
|
||||||
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
@ -1864,26 +2170,199 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let fn_type = get_fn_type(&ret_type, &arg_basic_types);
|
let fn_type = get_fn_type(&ret_type, &arg_basic_types);
|
||||||
|
|
||||||
let fn_name = layout_ids
|
|
||||||
.get(symbol, layout)
|
|
||||||
.to_symbol_string(symbol, &env.interns);
|
|
||||||
let fn_val = env
|
let fn_val = env
|
||||||
.module
|
.module
|
||||||
.add_function(fn_name.as_str(), fn_type, Some(Linkage::Private));
|
.add_function(fn_name.as_str(), fn_type, Some(Linkage::Private));
|
||||||
|
|
||||||
if env.exposed_to_host.contains(&symbol) {
|
|
||||||
// If this is an external-facing function, it'll use the C calling convention
|
|
||||||
// and external linkage.
|
|
||||||
fn_val.set_linkage(Linkage::External);
|
|
||||||
fn_val.set_call_conventions(C_CALL_CONV);
|
|
||||||
} else {
|
|
||||||
// If it's an internal-only function, it should use the fast calling conention.
|
|
||||||
fn_val.set_call_conventions(FAST_CALL_CONV);
|
fn_val.set_call_conventions(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
if env.exposed_to_host.contains(&symbol) {
|
||||||
|
expose_function_to_host(env, fn_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_val
|
fn_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_closure_caller<'a, 'ctx, 'env>(
|
||||||
|
env: &'a Env<'a, 'ctx, 'env>,
|
||||||
|
def_name: &str,
|
||||||
|
alias_symbol: Symbol,
|
||||||
|
arguments: &[Layout<'a>],
|
||||||
|
closure: &ClosureLayout<'a>,
|
||||||
|
result: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let arena = env.arena;
|
||||||
|
let context = &env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
// STEP 1: build function header
|
||||||
|
|
||||||
|
let function_name = format!(
|
||||||
|
"{}_{}_caller",
|
||||||
|
def_name,
|
||||||
|
alias_symbol.ident_string(&env.interns)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut argument_types = Vec::with_capacity_in(arguments.len() + 3, env.arena);
|
||||||
|
|
||||||
|
for layout in arguments {
|
||||||
|
argument_types.push(basic_type_from_layout(
|
||||||
|
arena,
|
||||||
|
context,
|
||||||
|
layout,
|
||||||
|
env.ptr_bytes,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let function_pointer_type = {
|
||||||
|
let function_layout =
|
||||||
|
ClosureLayout::extend_function_layout(arena, arguments, closure.clone(), result);
|
||||||
|
|
||||||
|
// this is already a (function) pointer type
|
||||||
|
basic_type_from_layout(arena, context, &function_layout, env.ptr_bytes)
|
||||||
|
};
|
||||||
|
argument_types.push(function_pointer_type);
|
||||||
|
|
||||||
|
let closure_argument_type = {
|
||||||
|
let basic_type = basic_type_from_layout(
|
||||||
|
arena,
|
||||||
|
context,
|
||||||
|
&closure.as_block_of_memory_layout(),
|
||||||
|
env.ptr_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
basic_type.ptr_type(AddressSpace::Generic)
|
||||||
|
};
|
||||||
|
argument_types.push(closure_argument_type.into());
|
||||||
|
|
||||||
|
let result_type = basic_type_from_layout(arena, context, result, env.ptr_bytes);
|
||||||
|
|
||||||
|
let roc_call_result_type =
|
||||||
|
context.struct_type(&[context.i64_type().into(), result_type], false);
|
||||||
|
|
||||||
|
let output_type = { roc_call_result_type.ptr_type(AddressSpace::Generic) };
|
||||||
|
argument_types.push(output_type.into());
|
||||||
|
|
||||||
|
let function_type = context.void_type().fn_type(&argument_types, false);
|
||||||
|
|
||||||
|
let function_value = env.module.add_function(
|
||||||
|
function_name.as_str(),
|
||||||
|
function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
|
||||||
|
function_value.set_call_conventions(C_CALL_CONV);
|
||||||
|
|
||||||
|
// STEP 2: build function body
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(function_value, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let mut parameters = function_value.get_params();
|
||||||
|
let output = parameters.pop().unwrap().into_pointer_value();
|
||||||
|
let closure_data_ptr = parameters.pop().unwrap().into_pointer_value();
|
||||||
|
let function_ptr = parameters.pop().unwrap().into_pointer_value();
|
||||||
|
|
||||||
|
let closure_data = builder.build_load(closure_data_ptr, "load_closure_data");
|
||||||
|
|
||||||
|
let mut arguments = parameters;
|
||||||
|
arguments.push(closure_data);
|
||||||
|
|
||||||
|
let result = invoke_and_catch(env, function_value, function_ptr, &arguments, result_type);
|
||||||
|
|
||||||
|
builder.build_store(output, result);
|
||||||
|
|
||||||
|
builder.build_return(None);
|
||||||
|
|
||||||
|
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
||||||
|
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||||
|
let size_function_name: String = format!(
|
||||||
|
"{}_{}_size",
|
||||||
|
def_name,
|
||||||
|
alias_symbol.ident_string(&env.interns)
|
||||||
|
);
|
||||||
|
|
||||||
|
let size_function = env.module.add_function(
|
||||||
|
size_function_name.as_str(),
|
||||||
|
size_function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(size_function, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let size: BasicValueEnum = roc_call_result_type.size_of().unwrap().into();
|
||||||
|
builder.build_return(Some(&size));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn build_closure_caller_old<'a, 'ctx, 'env>(
|
||||||
|
env: &'a Env<'a, 'ctx, 'env>,
|
||||||
|
closure_function: FunctionValue<'ctx>,
|
||||||
|
) {
|
||||||
|
let context = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
// asuming the closure has type `a, b, closure_data -> c`
|
||||||
|
// change that into `a, b, *const closure_data, *mut output -> ()`
|
||||||
|
|
||||||
|
// a function `a, b, closure_data -> RocCallResult<c>`
|
||||||
|
let wrapped_function = make_exception_catching_wrapper(env, closure_function);
|
||||||
|
|
||||||
|
let closure_function_type = closure_function.get_type();
|
||||||
|
let wrapped_function_type = wrapped_function.get_type();
|
||||||
|
|
||||||
|
let mut arguments = closure_function_type.get_param_types();
|
||||||
|
|
||||||
|
// require that the closure data is passed by reference
|
||||||
|
let closure_data_type = arguments.pop().unwrap();
|
||||||
|
let closure_data_ptr_type = get_ptr_type(&closure_data_type, AddressSpace::Generic);
|
||||||
|
arguments.push(closure_data_ptr_type.into());
|
||||||
|
|
||||||
|
// require that a pointer is passed in to write the result into
|
||||||
|
let output_type = get_ptr_type(
|
||||||
|
&wrapped_function_type.get_return_type().unwrap(),
|
||||||
|
AddressSpace::Generic,
|
||||||
|
);
|
||||||
|
arguments.push(output_type.into());
|
||||||
|
|
||||||
|
let caller_function_type = env.context.void_type().fn_type(&arguments, false);
|
||||||
|
let caller_function_name: String =
|
||||||
|
format!("{}_caller", closure_function.get_name().to_str().unwrap());
|
||||||
|
|
||||||
|
let caller_function = env.module.add_function(
|
||||||
|
caller_function_name.as_str(),
|
||||||
|
caller_function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
|
||||||
|
caller_function.set_call_conventions(C_CALL_CONV);
|
||||||
|
|
||||||
|
let entry = context.append_basic_block(caller_function, "entry");
|
||||||
|
|
||||||
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
|
let mut parameters = caller_function.get_params();
|
||||||
|
let output = parameters.pop().unwrap();
|
||||||
|
let closure_data_ptr = parameters.pop().unwrap();
|
||||||
|
|
||||||
|
let closure_data =
|
||||||
|
builder.build_load(closure_data_ptr.into_pointer_value(), "load_closure_data");
|
||||||
|
parameters.push(closure_data);
|
||||||
|
|
||||||
|
let call = builder.build_call(wrapped_function, ¶meters, "call_wrapped_function");
|
||||||
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
let result = call.try_as_basic_value().left().unwrap();
|
||||||
|
|
||||||
|
builder.build_store(output.into_pointer_value(), result);
|
||||||
|
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_proc<'a, 'ctx, 'env>(
|
pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
env: &'a Env<'a, 'ctx, 'env>,
|
env: &'a Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -1907,6 +2386,10 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
||||||
// the closure argument (if any) comes in as an opaque sequence of bytes.
|
// the closure argument (if any) comes in as an opaque sequence of bytes.
|
||||||
// we need to cast that to the specific closure data layout that the body expects
|
// we need to cast that to the specific closure data layout that the body expects
|
||||||
let value = if let Symbol::ARG_CLOSURE = *arg_symbol {
|
let value = if let Symbol::ARG_CLOSURE = *arg_symbol {
|
||||||
|
// generate a caller function (to be used by the host)
|
||||||
|
// build_closure_caller(env, fn_val);
|
||||||
|
// builder.position_at_end(entry);
|
||||||
|
|
||||||
// blindly trust that there is a layout available for the closure data
|
// blindly trust that there is a layout available for the closure data
|
||||||
let layout = proc.closure_data_layout.clone().unwrap();
|
let layout = proc.closure_data_layout.clone().unwrap();
|
||||||
|
|
||||||
|
@ -1971,8 +2454,8 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||||
panic!("Unrecognized builtin function: {:?}", fn_name)
|
panic!("Unrecognized builtin function: {:?}", fn_name)
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"Unrecognized non-builtin function: {:?} {:?}",
|
"Unrecognized non-builtin function: {:?} (symbol: {:?}, layout: {:?})",
|
||||||
fn_name, layout
|
fn_name, symbol, layout
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2126,6 +2609,16 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
list_keep_if(env, inplace, parent, func, func_layout, list, list_layout)
|
list_keep_if(env, inplace, parent, func, func_layout, list, list_layout)
|
||||||
}
|
}
|
||||||
|
ListContains => {
|
||||||
|
// List.contains : List elem, elem -> Bool
|
||||||
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
|
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
|
||||||
|
let (elem, elem_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||||
|
|
||||||
|
list_contains(env, parent, elem, elem_layout, list, list_layout)
|
||||||
|
}
|
||||||
ListWalkRight => {
|
ListWalkRight => {
|
||||||
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
debug_assert_eq!(args.len(), 3);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
@ -2920,7 +3413,7 @@ fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
||||||
let name = "__cxa_rethrow";
|
let name = "__gxx_personality_v0";
|
||||||
|
|
||||||
let module = env.module;
|
let module = env.module;
|
||||||
let context = env.context;
|
let context = env.context;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::llvm::build::{Env, InPlace};
|
use crate::llvm::build::{Env, InPlace};
|
||||||
|
use crate::llvm::compare::build_eq;
|
||||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
use crate::llvm::convert::{basic_type_from_layout, collection, get_ptr_type, ptr_int};
|
||||||
use inkwell::builder::Builder;
|
use inkwell::builder::Builder;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
|
@ -819,6 +820,116 @@ pub fn list_walk_right<'a, 'ctx, 'env>(
|
||||||
builder.build_load(accum_alloca, "load_final_acum")
|
builder.build_load(accum_alloca, "load_final_acum")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.contains : List elem, elem -> Bool
|
||||||
|
pub fn list_contains<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
elem: BasicValueEnum<'ctx>,
|
||||||
|
elem_layout: &Layout<'a>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
list_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
use inkwell::types::BasicType;
|
||||||
|
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let wrapper_struct = list.into_struct_value();
|
||||||
|
let list_elem_layout = match &list_layout {
|
||||||
|
// this pointer will never actually be dereferenced
|
||||||
|
Layout::Builtin(Builtin::EmptyList) => &Layout::Builtin(Builtin::Int64),
|
||||||
|
Layout::Builtin(Builtin::List(_, element_layout)) => element_layout,
|
||||||
|
_ => unreachable!("Invalid layout {:?} in List.contains", list_layout),
|
||||||
|
};
|
||||||
|
|
||||||
|
let list_elem_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, list_elem_layout, env.ptr_bytes);
|
||||||
|
|
||||||
|
let list_ptr = load_list_ptr(
|
||||||
|
builder,
|
||||||
|
wrapper_struct,
|
||||||
|
list_elem_type.ptr_type(AddressSpace::Generic),
|
||||||
|
);
|
||||||
|
|
||||||
|
let length = list_len(builder, list.into_struct_value());
|
||||||
|
|
||||||
|
list_contains_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
length,
|
||||||
|
list_ptr,
|
||||||
|
list_elem_layout,
|
||||||
|
elem,
|
||||||
|
elem_layout,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_contains_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
length: IntValue<'ctx>,
|
||||||
|
source_ptr: PointerValue<'ctx>,
|
||||||
|
list_elem_layout: &Layout<'a>,
|
||||||
|
elem: BasicValueEnum<'ctx>,
|
||||||
|
elem_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
let ctx = env.context;
|
||||||
|
|
||||||
|
let bool_alloca = builder.build_alloca(ctx.bool_type(), "bool_alloca");
|
||||||
|
let index_alloca = builder.build_alloca(ctx.i64_type(), "index_alloca");
|
||||||
|
let next_free_index_alloca = builder.build_alloca(ctx.i64_type(), "next_free_index_alloca");
|
||||||
|
|
||||||
|
builder.build_store(bool_alloca, ctx.bool_type().const_zero());
|
||||||
|
builder.build_store(index_alloca, ctx.i64_type().const_zero());
|
||||||
|
builder.build_store(next_free_index_alloca, ctx.i64_type().const_zero());
|
||||||
|
|
||||||
|
let condition_bb = ctx.append_basic_block(parent, "condition");
|
||||||
|
builder.build_unconditional_branch(condition_bb);
|
||||||
|
builder.position_at_end(condition_bb);
|
||||||
|
|
||||||
|
let index = builder.build_load(index_alloca, "index").into_int_value();
|
||||||
|
|
||||||
|
let condition = builder.build_int_compare(IntPredicate::SGT, length, index, "loopcond");
|
||||||
|
|
||||||
|
let body_bb = ctx.append_basic_block(parent, "body");
|
||||||
|
let cont_bb = ctx.append_basic_block(parent, "cont");
|
||||||
|
builder.build_conditional_branch(condition, body_bb, cont_bb);
|
||||||
|
|
||||||
|
// loop body
|
||||||
|
builder.position_at_end(body_bb);
|
||||||
|
|
||||||
|
let current_elem_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[index], "elem_ptr") };
|
||||||
|
|
||||||
|
let current_elem = builder.build_load(current_elem_ptr, "load_elem");
|
||||||
|
|
||||||
|
let has_found = build_eq(env, current_elem, elem, list_elem_layout, elem_layout);
|
||||||
|
|
||||||
|
builder.build_store(bool_alloca, has_found.into_int_value());
|
||||||
|
|
||||||
|
let one = ctx.i64_type().const_int(1, false);
|
||||||
|
|
||||||
|
let next_free_index = builder
|
||||||
|
.build_load(next_free_index_alloca, "load_next_free")
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
|
builder.build_store(
|
||||||
|
next_free_index_alloca,
|
||||||
|
builder.build_int_add(next_free_index, one, "incremented_next_free_index"),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_store(
|
||||||
|
index_alloca,
|
||||||
|
builder.build_int_add(index, one, "incremented_index"),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_conditional_branch(has_found.into_int_value(), cont_bb, condition_bb);
|
||||||
|
|
||||||
|
// continuation
|
||||||
|
builder.position_at_end(cont_bb);
|
||||||
|
|
||||||
|
builder.build_load(bool_alloca, "answer")
|
||||||
|
}
|
||||||
|
|
||||||
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
/// List.keepIf : List elem, (elem -> Bool) -> List elem
|
||||||
pub fn list_keep_if<'a, 'ctx, 'env>(
|
pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -849,7 +960,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||||
elem_layout.clone(),
|
elem_layout.clone(),
|
||||||
),
|
),
|
||||||
|
|
||||||
_ => unreachable!("Invalid layout {:?} in List.reverse", list_layout),
|
_ => unreachable!("Invalid layout {:?} in List.keepIf", list_layout),
|
||||||
};
|
};
|
||||||
|
|
||||||
let list_type = basic_type_from_layout(env.arena, env.context, &list_layout, env.ptr_bytes);
|
let list_type = basic_type_from_layout(env.arena, env.context, &list_layout, env.ptr_bytes);
|
||||||
|
|
|
@ -285,6 +285,7 @@ mod gen_list {
|
||||||
r#"
|
r#"
|
||||||
Bit : [ Zero, One ]
|
Bit : [ Zero, One ]
|
||||||
|
|
||||||
|
byte : List Bit
|
||||||
byte = [ Zero, One, Zero, One, Zero, Zero, One, Zero ]
|
byte = [ Zero, One, Zero, One, Zero, Zero, One, Zero ]
|
||||||
|
|
||||||
initialCounts = { zeroes: 0, ones: 0 }
|
initialCounts = { zeroes: 0, ones: 0 }
|
||||||
|
@ -313,7 +314,7 @@ mod gen_list {
|
||||||
empty =
|
empty =
|
||||||
[]
|
[]
|
||||||
|
|
||||||
List.keepIf empty (\x -> True)
|
List.keepIf empty (\_ -> True)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
RocList::from_slice(&[]),
|
RocList::from_slice(&[]),
|
||||||
|
@ -345,7 +346,7 @@ mod gen_list {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
alwaysTrue : Int -> Bool
|
alwaysTrue : Int -> Bool
|
||||||
alwaysTrue = \i ->
|
alwaysTrue = \_ ->
|
||||||
True
|
True
|
||||||
|
|
||||||
oneThroughEight : List Int
|
oneThroughEight : List Int
|
||||||
|
@ -366,7 +367,7 @@ mod gen_list {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
alwaysFalse : Int -> Bool
|
alwaysFalse : Int -> Bool
|
||||||
alwaysFalse = \i ->
|
alwaysFalse = \_ ->
|
||||||
False
|
False
|
||||||
|
|
||||||
List.keepIf [1,2,3,4,5,6,7,8] alwaysFalse
|
List.keepIf [1,2,3,4,5,6,7,8] alwaysFalse
|
||||||
|
@ -1602,4 +1603,13 @@ mod gen_list {
|
||||||
RocList<i64>
|
RocList<i64>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_contains() {
|
||||||
|
assert_evals_to!(indoc!("List.contains [1,2,3] 1"), true, bool);
|
||||||
|
|
||||||
|
assert_evals_to!(indoc!("List.contains [1,2,3] 4"), false, bool);
|
||||||
|
|
||||||
|
assert_evals_to!(indoc!("List.contains [] 4"), false, bool);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -557,7 +557,7 @@ mod gen_num {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
always42 : Num.Num Num.Integer -> Num.Num Num.Integer
|
always42 : Num.Num Num.Integer -> Num.Num Num.Integer
|
||||||
always42 = \num -> 42
|
always42 = \_ -> 42
|
||||||
|
|
||||||
always42 5
|
always42 5
|
||||||
"#
|
"#
|
||||||
|
@ -788,4 +788,30 @@ mod gen_num {
|
||||||
// f64
|
// f64
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_max_int() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.maxInt
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
i64::MAX,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn num_min_int() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.minInt
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
i64::MIN,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,7 +294,7 @@ mod gen_primitives {
|
||||||
r#"
|
r#"
|
||||||
wrapper = \{} ->
|
wrapper = \{} ->
|
||||||
alwaysFloatIdentity : Int -> (Float -> Float)
|
alwaysFloatIdentity : Int -> (Float -> Float)
|
||||||
alwaysFloatIdentity = \num ->
|
alwaysFloatIdentity = \_ ->
|
||||||
(\a -> a)
|
(\a -> a)
|
||||||
|
|
||||||
(alwaysFloatIdentity 2) 3.14
|
(alwaysFloatIdentity 2) 3.14
|
||||||
|
@ -362,7 +362,7 @@ mod gen_primitives {
|
||||||
|
|
||||||
pi = 3.14
|
pi = 3.14
|
||||||
|
|
||||||
answer
|
if pi > 3 then answer else answer
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
42,
|
42,
|
||||||
|
@ -376,7 +376,7 @@ mod gen_primitives {
|
||||||
|
|
||||||
pi = 3.14
|
pi = 3.14
|
||||||
|
|
||||||
pi
|
if answer > 3 then pi else pi
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
3.14,
|
3.14,
|
||||||
|
@ -384,87 +384,89 @@ mod gen_primitives {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// These tests caught a bug in how Defs are converted to the mono IR
|
||||||
fn gen_chained_defs() {
|
// but they have UnusedDef or UnusedArgument problems, and don't run any more
|
||||||
assert_evals_to!(
|
// #[test]
|
||||||
indoc!(
|
// fn gen_chained_defs() {
|
||||||
r#"
|
// assert_evals_to!(
|
||||||
x = i1
|
// indoc!(
|
||||||
i3 = i2
|
// r#"
|
||||||
i1 = 1337
|
// x = i1
|
||||||
i2 = i1
|
// i3 = i2
|
||||||
y = 12.4
|
// i1 = 1337
|
||||||
|
// i2 = i1
|
||||||
i3
|
// y = 12.4
|
||||||
"#
|
//
|
||||||
),
|
// i3
|
||||||
1337,
|
// "#
|
||||||
i64
|
// ),
|
||||||
);
|
// 1337,
|
||||||
}
|
// i64
|
||||||
|
// );
|
||||||
#[test]
|
// }
|
||||||
fn gen_nested_defs_old() {
|
//
|
||||||
assert_evals_to!(
|
// #[test]
|
||||||
indoc!(
|
// fn gen_nested_defs_old() {
|
||||||
r#"
|
// assert_evals_to!(
|
||||||
x = 5
|
// indoc!(
|
||||||
|
// r#"
|
||||||
answer =
|
// x = 5
|
||||||
i3 = i2
|
//
|
||||||
|
// answer =
|
||||||
nested =
|
// i3 = i2
|
||||||
a = 1.0
|
//
|
||||||
b = 5
|
// nested =
|
||||||
|
// a = 1.0
|
||||||
i1
|
// b = 5
|
||||||
|
//
|
||||||
i1 = 1337
|
// i1
|
||||||
i2 = i1
|
//
|
||||||
|
// i1 = 1337
|
||||||
|
// i2 = i1
|
||||||
nested
|
//
|
||||||
|
//
|
||||||
# None of this should affect anything, even though names
|
// nested
|
||||||
# overlap with the previous nested defs
|
//
|
||||||
unused =
|
// # None of this should affect anything, even though names
|
||||||
nested = 17
|
// # overlap with the previous nested defs
|
||||||
|
// unused =
|
||||||
i1 = 84.2
|
// nested = 17
|
||||||
|
//
|
||||||
nested
|
// i1 = 84.2
|
||||||
|
//
|
||||||
y = 12.4
|
// nested
|
||||||
|
//
|
||||||
answer
|
// y = 12.4
|
||||||
"#
|
//
|
||||||
),
|
// answer
|
||||||
1337,
|
// "#
|
||||||
i64
|
// ),
|
||||||
);
|
// 1337,
|
||||||
}
|
// i64
|
||||||
|
// );
|
||||||
#[test]
|
// }
|
||||||
fn let_x_in_x() {
|
//
|
||||||
assert_evals_to!(
|
// #[test]
|
||||||
indoc!(
|
// fn let_x_in_x() {
|
||||||
r#"
|
// assert_evals_to!(
|
||||||
x = 5
|
// indoc!(
|
||||||
|
// r#"
|
||||||
answer =
|
// x = 5
|
||||||
1337
|
//
|
||||||
|
// answer =
|
||||||
unused =
|
// 1337
|
||||||
nested = 17
|
//
|
||||||
nested
|
// unused =
|
||||||
|
// nested = 17
|
||||||
answer
|
// nested
|
||||||
"#
|
//
|
||||||
),
|
// answer
|
||||||
1337,
|
// "#
|
||||||
i64
|
// ),
|
||||||
);
|
// 1337,
|
||||||
}
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn factorial() {
|
fn factorial() {
|
||||||
|
|
|
@ -254,11 +254,11 @@ mod gen_records {
|
||||||
r#"
|
r#"
|
||||||
v = {}
|
v = {}
|
||||||
|
|
||||||
1
|
v
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
(),
|
||||||
i64
|
()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -23,11 +23,12 @@ mod gen_tags {
|
||||||
x : Maybe Int
|
x : Maybe Int
|
||||||
x = Nothing
|
x = Nothing
|
||||||
|
|
||||||
0x1
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
i64
|
(i64, i64),
|
||||||
|
|(tag, _)| tag
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,11 +42,12 @@ mod gen_tags {
|
||||||
x : Maybe Int
|
x : Maybe Int
|
||||||
x = Nothing
|
x = Nothing
|
||||||
|
|
||||||
0x1
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
i64
|
(i64, i64),
|
||||||
|
|(tag, _)| tag
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +61,11 @@ mod gen_tags {
|
||||||
y : Maybe Int
|
y : Maybe Int
|
||||||
y = Just 0x4
|
y = Just 0x4
|
||||||
|
|
||||||
0x1
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
(0, 0x4),
|
||||||
i64
|
(i64, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,11 +79,11 @@ mod gen_tags {
|
||||||
y : Maybe Int
|
y : Maybe Int
|
||||||
y = Just 0x4
|
y = Just 0x4
|
||||||
|
|
||||||
0x1
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
(0, 0x4),
|
||||||
i64
|
(i64, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,11 +101,11 @@ mod gen_tags {
|
||||||
y : Maybe Fruit
|
y : Maybe Fruit
|
||||||
y = Just orange
|
y = Just orange
|
||||||
|
|
||||||
0x1
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
(0, 2),
|
||||||
i64
|
(i64, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +352,7 @@ mod gen_tags {
|
||||||
|
|
||||||
when x is
|
when x is
|
||||||
These a b -> a + b
|
These a b -> a + b
|
||||||
That v -> 8
|
That v -> v
|
||||||
This v -> v
|
This v -> v
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -616,10 +618,10 @@ mod gen_tags {
|
||||||
x : [ Pair Int ]
|
x : [ Pair Int ]
|
||||||
x = Pair 2
|
x = Pair 2
|
||||||
|
|
||||||
0x3
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
3,
|
2,
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -637,11 +639,11 @@ mod gen_tags {
|
||||||
x = Just (Just 41)
|
x = Just (Just 41)
|
||||||
|
|
||||||
main =
|
main =
|
||||||
5
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
5,
|
(0, (0, 41)),
|
||||||
i64
|
(i64, (i64, i64))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -654,11 +656,11 @@ mod gen_tags {
|
||||||
v : Unit
|
v : Unit
|
||||||
v = Unit
|
v = Unit
|
||||||
|
|
||||||
1
|
v
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
(),
|
||||||
i64
|
()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,8 +669,6 @@ mod gen_tags {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
Maybe a : [ Nothing, Just a ]
|
|
||||||
|
|
||||||
x = { a : { b : 0x5 } }
|
x = { a : { b : 0x5 } }
|
||||||
|
|
||||||
y = x.a
|
y = x.a
|
||||||
|
|
|
@ -50,14 +50,10 @@ pub fn helper<'a>(
|
||||||
exposed_types,
|
exposed_types,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded = loaded.expect("failed to load module");
|
let mut loaded = loaded.expect("failed to load module");
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
module_id: home,
|
|
||||||
can_problems,
|
|
||||||
type_problems,
|
|
||||||
mono_problems,
|
|
||||||
mut procedures,
|
mut procedures,
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
|
@ -76,47 +72,52 @@ pub fn helper<'a>(
|
||||||
let target = target_lexicon::Triple::host();
|
let target = target_lexicon::Triple::host();
|
||||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||||
|
|
||||||
// don't panic based on the errors here, so we can test that RuntimeError generates the correct code
|
let mut lines = Vec::new();
|
||||||
let errors = can_problems
|
// errors whose reporting we delay (so we can see that code gen generates runtime errors)
|
||||||
.into_iter()
|
let mut delayed_errors = Vec::new();
|
||||||
.filter(|problem| {
|
|
||||||
use roc_problem::can::Problem::*;
|
|
||||||
|
|
||||||
// Ignore "unused" problems
|
|
||||||
match problem {
|
|
||||||
UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => false,
|
|
||||||
_ => true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<roc_problem::can::Problem>>();
|
|
||||||
|
|
||||||
|
for (home, (module_path, src)) in loaded.sources {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let error_count = errors.len() + type_problems.len() + mono_problems.len();
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
let fatal_error_count = type_problems.len() + mono_problems.len();
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
|
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
if error_count > 0 {
|
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
||||||
// There were problems; report them and return.
|
|
||||||
let src_lines: Vec<&str> = module_src.split('\n').collect();
|
|
||||||
|
|
||||||
// Used for reporting where an error came from.
|
if error_count == 0 {
|
||||||
//
|
continue;
|
||||||
// TODO: maybe Reporting should have this be an Option?
|
}
|
||||||
let path = PathBuf::new();
|
|
||||||
|
|
||||||
// Report problems
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
// Report parsing and canonicalization problems
|
// Report parsing and canonicalization problems
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||||||
|
|
||||||
let mut lines = Vec::with_capacity(error_count);
|
use roc_problem::can::Problem::*;
|
||||||
|
|
||||||
let can_problems = errors.clone();
|
|
||||||
for problem in can_problems.into_iter() {
|
for problem in can_problems.into_iter() {
|
||||||
let report = can_problem(&alloc, path.clone(), problem);
|
// Ignore "unused" problems
|
||||||
|
match problem {
|
||||||
|
UnusedDef(_, _) | UnusedArgument(_, _, _) | UnusedImport(_, _) => {
|
||||||
|
delayed_errors.push(problem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let report = can_problem(&alloc, module_path.clone(), problem);
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
|
||||||
|
lines.push(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for problem in type_problems {
|
||||||
|
let report = type_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
@ -124,31 +125,19 @@ pub fn helper<'a>(
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in type_problems.into_iter() {
|
for problem in mono_problems {
|
||||||
let report = type_problem(&alloc, path.clone(), problem);
|
let report = mono_problem(&alloc, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
report.render_color_terminal(&mut buf, &alloc, &palette);
|
||||||
|
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems.into_iter() {
|
|
||||||
let report = mono_problem(&alloc, path.clone(), problem);
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
report.render_color_terminal(&mut buf, &alloc, &palette);
|
|
||||||
|
|
||||||
lines.push(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", (&lines).join("\n"));
|
if !lines.is_empty() {
|
||||||
|
println!("{}", lines.join("\n"));
|
||||||
// we want to continue onward only for canonical problems at the moment,
|
assert_eq!(0, 1, "Mistakes were made");
|
||||||
// to check that they codegen into runtime exceptions
|
|
||||||
if fatal_error_count > 0 {
|
|
||||||
assert_eq!(0, 1, "problems occured");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let module = roc_gen::llvm::build::module_from_builtins(context, "app");
|
let module = roc_gen::llvm::build::module_from_builtins(context, "app");
|
||||||
|
@ -261,7 +250,7 @@ pub fn helper<'a>(
|
||||||
let lib = module_to_dylib(&env.module, &target, opt_level)
|
let lib = module_to_dylib(&env.module, &target, opt_level)
|
||||||
.expect("Error loading compiled dylib for test");
|
.expect("Error loading compiled dylib for test");
|
||||||
|
|
||||||
(main_fn_name, errors, lib)
|
(main_fn_name, delayed_errors, lib)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is almost all code duplication with assert_llvm_evals_to
|
// TODO this is almost all code duplication with assert_llvm_evals_to
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
// This file was copied from file.rs and modified to expose information
|
|
||||||
// required to auto-generate documentation
|
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_module::ident::ModuleName;
|
use roc_module::ident::ModuleName;
|
||||||
use roc_module::symbol::IdentIds;
|
use roc_module::symbol::IdentIds;
|
||||||
|
@ -71,7 +69,7 @@ fn generate_module_doc<'a>(
|
||||||
// If there are comments before, attach to this definition
|
// If there are comments before, attach to this definition
|
||||||
generate_module_doc(exposed_ident_ids, acc, before_comments_or_new_lines, sub_def);
|
generate_module_doc(exposed_ident_ids, acc, before_comments_or_new_lines, sub_def);
|
||||||
|
|
||||||
// Comments after a definition are attached to the next defition
|
// Comments after a definition are attached to the next definition
|
||||||
(new_acc, Some(comments_or_new_lines))
|
(new_acc, Some(comments_or_new_lines))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +96,15 @@ fn generate_module_doc<'a>(
|
||||||
name: _,
|
name: _,
|
||||||
vars: _,
|
vars: _,
|
||||||
ann: _,
|
ann: _,
|
||||||
} => (acc, None),
|
} =>
|
||||||
|
// TODO
|
||||||
|
{
|
||||||
|
(acc, None)
|
||||||
|
}
|
||||||
|
|
||||||
Body(_, _) | Nested(_) => (acc, None),
|
Body(_, _) | Nested(_) => (acc, None),
|
||||||
|
|
||||||
|
NotYetImplemented(s) => todo!("{}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,9 +117,11 @@ fn comments_or_new_lines_to_docs<'a>(
|
||||||
|
|
||||||
for comment_or_new_line in comments_or_new_lines.iter() {
|
for comment_or_new_line in comments_or_new_lines.iter() {
|
||||||
match comment_or_new_line {
|
match comment_or_new_line {
|
||||||
Newline => {}
|
DocComment(doc_str) => {
|
||||||
LineComment(_) => {}
|
docs.push_str(doc_str);
|
||||||
DocComment(doc_str) => docs.push_str(doc_str),
|
docs.push_str("\n");
|
||||||
|
}
|
||||||
|
Newline | LineComment(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if docs.is_empty() {
|
if docs.is_empty() {
|
||||||
|
|
|
@ -13,11 +13,10 @@ use roc_constrain::module::{
|
||||||
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
||||||
};
|
};
|
||||||
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
||||||
use roc_module::ident::{Ident, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
CapturedSymbols, ExternalSpecializations, MonoProblem, PartialProc, PendingSpecialization,
|
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
|
||||||
Proc, Procs,
|
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Layout, LayoutCache};
|
use roc_mono::layout::{Layout, LayoutCache};
|
||||||
use roc_parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
use roc_parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
||||||
|
@ -201,15 +200,24 @@ impl Dependencies {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct ModuleCache<'a> {
|
struct ModuleCache<'a> {
|
||||||
module_names: MutMap<ModuleId, ModuleName>,
|
module_names: MutMap<ModuleId, ModuleName>,
|
||||||
|
|
||||||
|
/// Phases
|
||||||
headers: MutMap<ModuleId, ModuleHeader<'a>>,
|
headers: MutMap<ModuleId, ModuleHeader<'a>>,
|
||||||
parsed: MutMap<ModuleId, ParsedModule<'a>>,
|
parsed: MutMap<ModuleId, ParsedModule<'a>>,
|
||||||
canonicalized: MutMap<ModuleId, CanonicalizedModule<'a>>,
|
canonicalized: MutMap<ModuleId, CanonicalizedModule<'a>>,
|
||||||
aliases: MutMap<ModuleId, MutMap<Symbol, Alias>>,
|
aliases: MutMap<ModuleId, MutMap<Symbol, Alias>>,
|
||||||
constrained: MutMap<ModuleId, ConstrainedModule<'a>>,
|
constrained: MutMap<ModuleId, ConstrainedModule>,
|
||||||
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
|
||||||
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
||||||
external_specializations_requested: MutMap<ModuleId, ExternalSpecializations>,
|
external_specializations_requested: MutMap<ModuleId, ExternalSpecializations>,
|
||||||
|
|
||||||
|
/// Various information
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
|
can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
|
type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||||
|
mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||||
|
|
||||||
|
sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) -> BuildTask<'a> {
|
fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) -> BuildTask<'a> {
|
||||||
|
@ -314,7 +322,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
src,
|
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
|
@ -326,7 +333,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
src,
|
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
|
@ -344,7 +350,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
module_timing,
|
module_timing,
|
||||||
solved_subs,
|
solved_subs,
|
||||||
decls,
|
decls,
|
||||||
finished_info,
|
|
||||||
ident_ids,
|
ident_ids,
|
||||||
} = typechecked;
|
} = typechecked;
|
||||||
|
|
||||||
|
@ -354,7 +359,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
module_timing,
|
module_timing,
|
||||||
solved_subs,
|
solved_subs,
|
||||||
decls,
|
decls,
|
||||||
finished_info,
|
|
||||||
ident_ids,
|
ident_ids,
|
||||||
exposed_to_host: state.exposed_to_host.clone(),
|
exposed_to_host: state.exposed_to_host.clone(),
|
||||||
}
|
}
|
||||||
|
@ -378,7 +382,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
subs,
|
subs,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
finished_info,
|
|
||||||
} = found_specializations;
|
} = found_specializations;
|
||||||
|
|
||||||
BuildTask::MakeSpecializations {
|
BuildTask::MakeSpecializations {
|
||||||
|
@ -388,7 +391,6 @@ fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) ->
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
finished_info,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,11 +401,11 @@ pub struct LoadedModule {
|
||||||
pub module_id: ModuleId,
|
pub module_id: ModuleId,
|
||||||
pub interns: Interns,
|
pub interns: Interns,
|
||||||
pub solved: Solved<Subs>,
|
pub solved: Solved<Subs>,
|
||||||
pub can_problems: Vec<roc_problem::can::Problem>,
|
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
pub type_problems: Vec<solve::TypeError>,
|
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||||
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
|
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
|
||||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
pub src: Box<str>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
}
|
}
|
||||||
|
@ -417,6 +419,7 @@ pub enum BuildProblem<'a> {
|
||||||
struct ModuleHeader<'a> {
|
struct ModuleHeader<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
|
module_path: PathBuf,
|
||||||
exposed_ident_ids: IdentIds,
|
exposed_ident_ids: IdentIds,
|
||||||
deps_by_name: MutMap<ModuleName, ModuleId>,
|
deps_by_name: MutMap<ModuleName, ModuleId>,
|
||||||
imported_modules: MutSet<ModuleId>,
|
imported_modules: MutSet<ModuleId>,
|
||||||
|
@ -427,11 +430,10 @@ struct ModuleHeader<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ConstrainedModule<'a> {
|
struct ConstrainedModule {
|
||||||
module: Module,
|
module: Module,
|
||||||
declarations: Vec<Declaration>,
|
declarations: Vec<Declaration>,
|
||||||
imported_modules: MutSet<ModuleId>,
|
imported_modules: MutSet<ModuleId>,
|
||||||
src: &'a str,
|
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
|
@ -446,7 +448,6 @@ pub struct TypeCheckedModule<'a> {
|
||||||
pub solved_subs: Solved<Subs>,
|
pub solved_subs: Solved<Subs>,
|
||||||
pub decls: Vec<Declaration>,
|
pub decls: Vec<Declaration>,
|
||||||
pub ident_ids: IdentIds,
|
pub ident_ids: IdentIds,
|
||||||
pub finished_info: FinishedInfo<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -456,7 +457,6 @@ pub struct FoundSpecializationsModule<'a> {
|
||||||
pub layout_cache: LayoutCache<'a>,
|
pub layout_cache: LayoutCache<'a>,
|
||||||
pub procs: Procs<'a>,
|
pub procs: Procs<'a>,
|
||||||
pub subs: Subs,
|
pub subs: Subs,
|
||||||
pub finished_info: FinishedInfo<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -464,19 +464,26 @@ pub struct MonomorphizedModule<'a> {
|
||||||
pub module_id: ModuleId,
|
pub module_id: ModuleId,
|
||||||
pub interns: Interns,
|
pub interns: Interns,
|
||||||
pub subs: Subs,
|
pub subs: Subs,
|
||||||
pub can_problems: Vec<roc_problem::can::Problem>,
|
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
pub type_problems: Vec<solve::TypeError>,
|
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||||
pub mono_problems: Vec<roc_mono::ir::MonoProblem>,
|
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||||
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
pub src: Box<str>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct VariablySizedLayouts<'a> {
|
||||||
|
rigids: MutMap<Lowercase, Layout<'a>>,
|
||||||
|
aliases: MutMap<Symbol, Layout<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ParsedModule<'a> {
|
struct ParsedModule<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
|
module_path: PathBuf,
|
||||||
src: &'a str,
|
src: &'a str,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
deps_by_name: MutMap<ModuleName, ModuleId>,
|
deps_by_name: MutMap<ModuleName, ModuleId>,
|
||||||
|
@ -498,12 +505,11 @@ enum Msg<'a> {
|
||||||
Header(ModuleHeader<'a>),
|
Header(ModuleHeader<'a>),
|
||||||
Parsed(ParsedModule<'a>),
|
Parsed(ParsedModule<'a>),
|
||||||
CanonicalizedAndConstrained {
|
CanonicalizedAndConstrained {
|
||||||
constrained_module: ConstrainedModule<'a>,
|
constrained_module: ConstrainedModule,
|
||||||
canonicalization_problems: Vec<roc_problem::can::Problem>,
|
canonicalization_problems: Vec<roc_problem::can::Problem>,
|
||||||
module_docs: ModuleDocumentation,
|
module_docs: ModuleDocumentation,
|
||||||
},
|
},
|
||||||
SolvedTypes {
|
SolvedTypes {
|
||||||
src: &'a str,
|
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
solved_module: SolvedModule,
|
solved_module: SolvedModule,
|
||||||
|
@ -515,7 +521,6 @@ enum Msg<'a> {
|
||||||
solved_subs: Solved<Subs>,
|
solved_subs: Solved<Subs>,
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
src: &'a str,
|
|
||||||
},
|
},
|
||||||
FoundSpecializations {
|
FoundSpecializations {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
|
@ -524,7 +529,6 @@ enum Msg<'a> {
|
||||||
procs: Procs<'a>,
|
procs: Procs<'a>,
|
||||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||||
solved_subs: Solved<Subs>,
|
solved_subs: Solved<Subs>,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
},
|
},
|
||||||
MadeSpecializations {
|
MadeSpecializations {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
|
@ -534,7 +538,6 @@ enum Msg<'a> {
|
||||||
procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/// The task is to only typecheck AND monomorphize modules
|
/// The task is to only typecheck AND monomorphize modules
|
||||||
|
@ -542,16 +545,9 @@ enum Msg<'a> {
|
||||||
FinishedAllSpecialization {
|
FinishedAllSpecialization {
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
exposed_to_host: MutMap<Symbol, Variable>,
|
exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
src: &'a str,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FinishedInfo<'a> {
|
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
|
||||||
src: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct State<'a> {
|
struct State<'a> {
|
||||||
pub root_id: ModuleId,
|
pub root_id: ModuleId,
|
||||||
|
@ -559,10 +555,7 @@ struct State<'a> {
|
||||||
pub stdlib: StdLib,
|
pub stdlib: StdLib,
|
||||||
pub exposed_types: SubsByModule,
|
pub exposed_types: SubsByModule,
|
||||||
|
|
||||||
pub can_problems: std::vec::Vec<roc_problem::can::Problem>,
|
|
||||||
pub mono_problems: std::vec::Vec<MonoProblem>,
|
|
||||||
pub headers_parsed: MutSet<ModuleId>,
|
pub headers_parsed: MutSet<ModuleId>,
|
||||||
pub type_problems: std::vec::Vec<solve::TypeError>,
|
|
||||||
|
|
||||||
pub module_cache: ModuleCache<'a>,
|
pub module_cache: ModuleCache<'a>,
|
||||||
pub dependencies: Dependencies,
|
pub dependencies: Dependencies,
|
||||||
|
@ -702,7 +695,6 @@ enum BuildTask<'a> {
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
declarations: Vec<Declaration>,
|
declarations: Vec<Declaration>,
|
||||||
src: &'a str,
|
|
||||||
},
|
},
|
||||||
BuildPendingSpecializations {
|
BuildPendingSpecializations {
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
@ -711,7 +703,6 @@ enum BuildTask<'a> {
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
decls: Vec<Declaration>,
|
decls: Vec<Declaration>,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
exposed_to_host: MutMap<Symbol, Variable>,
|
exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
},
|
},
|
||||||
MakeSpecializations {
|
MakeSpecializations {
|
||||||
|
@ -720,7 +711,6 @@ enum BuildTask<'a> {
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
procs: Procs<'a>,
|
procs: Procs<'a>,
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
specializations_we_must_make: ExternalSpecializations,
|
specializations_we_must_make: ExternalSpecializations,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1105,9 +1095,6 @@ where
|
||||||
exposed_types,
|
exposed_types,
|
||||||
headers_parsed,
|
headers_parsed,
|
||||||
loading_started,
|
loading_started,
|
||||||
can_problems: std::vec::Vec::new(),
|
|
||||||
type_problems: std::vec::Vec::new(),
|
|
||||||
mono_problems: std::vec::Vec::new(),
|
|
||||||
arc_modules,
|
arc_modules,
|
||||||
constrained_ident_ids: IdentIds::exposed_builtins(0),
|
constrained_ident_ids: IdentIds::exposed_builtins(0),
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
|
@ -1140,7 +1127,6 @@ where
|
||||||
solved_subs,
|
solved_subs,
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
documentation,
|
documentation,
|
||||||
src,
|
|
||||||
} => {
|
} => {
|
||||||
// We're done! There should be no more messages pending.
|
// We're done! There should be no more messages pending.
|
||||||
debug_assert!(msg_rx.is_empty());
|
debug_assert!(msg_rx.is_empty());
|
||||||
|
@ -1157,13 +1143,11 @@ where
|
||||||
solved_subs,
|
solved_subs,
|
||||||
exposed_vars_by_symbol,
|
exposed_vars_by_symbol,
|
||||||
documentation,
|
documentation,
|
||||||
src,
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Msg::FinishedAllSpecialization {
|
Msg::FinishedAllSpecialization {
|
||||||
subs,
|
subs,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
src,
|
|
||||||
} => {
|
} => {
|
||||||
// We're done! There should be no more messages pending.
|
// We're done! There should be no more messages pending.
|
||||||
debug_assert!(msg_rx.is_empty());
|
debug_assert!(msg_rx.is_empty());
|
||||||
|
@ -1179,7 +1163,6 @@ where
|
||||||
state,
|
state,
|
||||||
subs,
|
subs,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
src,
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
msg => {
|
msg => {
|
||||||
|
@ -1267,6 +1250,11 @@ fn update<'a>(
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
Parsed(parsed) => {
|
Parsed(parsed) => {
|
||||||
|
state
|
||||||
|
.module_cache
|
||||||
|
.sources
|
||||||
|
.insert(parsed.module_id, (parsed.module_path.clone(), parsed.src));
|
||||||
|
|
||||||
let module_id = parsed.module_id;
|
let module_id = parsed.module_id;
|
||||||
|
|
||||||
state.module_cache.parsed.insert(parsed.module_id, parsed);
|
state.module_cache.parsed.insert(parsed.module_id, parsed);
|
||||||
|
@ -1288,7 +1276,10 @@ fn update<'a>(
|
||||||
} => {
|
} => {
|
||||||
let module_id = constrained_module.module.module_id;
|
let module_id = constrained_module.module.module_id;
|
||||||
log!("generated constraints for {:?}", module_id);
|
log!("generated constraints for {:?}", module_id);
|
||||||
state.can_problems.extend(canonicalization_problems);
|
state
|
||||||
|
.module_cache
|
||||||
|
.can_problems
|
||||||
|
.insert(module_id, canonicalization_problems);
|
||||||
|
|
||||||
state
|
state
|
||||||
.module_cache
|
.module_cache
|
||||||
|
@ -1318,7 +1309,6 @@ fn update<'a>(
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
SolvedTypes {
|
SolvedTypes {
|
||||||
src,
|
|
||||||
module_id,
|
module_id,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
solved_module,
|
solved_module,
|
||||||
|
@ -1329,7 +1319,10 @@ fn update<'a>(
|
||||||
log!("solved types for {:?}", module_id);
|
log!("solved types for {:?}", module_id);
|
||||||
module_timing.end_time = SystemTime::now();
|
module_timing.end_time = SystemTime::now();
|
||||||
|
|
||||||
state.type_problems.extend(solved_module.problems);
|
state
|
||||||
|
.module_cache
|
||||||
|
.type_problems
|
||||||
|
.insert(module_id, solved_module.problems);
|
||||||
|
|
||||||
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
||||||
|
|
||||||
|
@ -1357,7 +1350,6 @@ fn update<'a>(
|
||||||
solved_subs,
|
solved_subs,
|
||||||
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
|
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
|
||||||
documentation,
|
documentation,
|
||||||
src,
|
|
||||||
})
|
})
|
||||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||||
|
|
||||||
|
@ -1382,11 +1374,6 @@ fn update<'a>(
|
||||||
if state.goal_phase > Phase::SolveTypes {
|
if state.goal_phase > Phase::SolveTypes {
|
||||||
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
||||||
|
|
||||||
let finished_info = FinishedInfo {
|
|
||||||
src,
|
|
||||||
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
|
|
||||||
};
|
|
||||||
|
|
||||||
let typechecked = TypeCheckedModule {
|
let typechecked = TypeCheckedModule {
|
||||||
module_id,
|
module_id,
|
||||||
decls,
|
decls,
|
||||||
|
@ -1394,7 +1381,6 @@ fn update<'a>(
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
finished_info,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
|
@ -1417,7 +1403,6 @@ fn update<'a>(
|
||||||
FoundSpecializations {
|
FoundSpecializations {
|
||||||
module_id,
|
module_id,
|
||||||
procs,
|
procs,
|
||||||
finished_info,
|
|
||||||
solved_subs,
|
solved_subs,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -1443,7 +1428,6 @@ fn update<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
module_id,
|
module_id,
|
||||||
procs,
|
procs,
|
||||||
finished_info,
|
|
||||||
ident_ids,
|
ident_ids,
|
||||||
subs,
|
subs,
|
||||||
};
|
};
|
||||||
|
@ -1468,7 +1452,6 @@ fn update<'a>(
|
||||||
module_id,
|
module_id,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
subs,
|
subs,
|
||||||
finished_info,
|
|
||||||
procedures,
|
procedures,
|
||||||
external_specializations_requested,
|
external_specializations_requested,
|
||||||
problems,
|
problems,
|
||||||
|
@ -1476,7 +1459,7 @@ fn update<'a>(
|
||||||
} => {
|
} => {
|
||||||
log!("made specializations for {:?}", module_id);
|
log!("made specializations for {:?}", module_id);
|
||||||
|
|
||||||
state.mono_problems.extend(problems);
|
state.module_cache.mono_problems.insert(module_id, problems);
|
||||||
|
|
||||||
for (module_id, requested) in external_specializations_requested {
|
for (module_id, requested) in external_specializations_requested {
|
||||||
let existing = match state
|
let existing = match state
|
||||||
|
@ -1512,7 +1495,6 @@ fn update<'a>(
|
||||||
subs,
|
subs,
|
||||||
// TODO thread through mono problems
|
// TODO thread through mono problems
|
||||||
exposed_to_host: state.exposed_to_host.clone(),
|
exposed_to_host: state.exposed_to_host.clone(),
|
||||||
src: finished_info.src,
|
|
||||||
})
|
})
|
||||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||||
|
|
||||||
|
@ -1542,7 +1524,6 @@ fn finish_specialization<'a>(
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
exposed_to_host: MutMap<Symbol, Variable>,
|
exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
src: &'a str,
|
|
||||||
) -> MonomorphizedModule<'a> {
|
) -> MonomorphizedModule<'a> {
|
||||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||||
|
@ -1554,12 +1535,23 @@ fn finish_specialization<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let State {
|
let State {
|
||||||
|
procedures,
|
||||||
|
module_cache,
|
||||||
|
..
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
let ModuleCache {
|
||||||
mono_problems,
|
mono_problems,
|
||||||
type_problems,
|
type_problems,
|
||||||
can_problems,
|
can_problems,
|
||||||
procedures,
|
sources,
|
||||||
..
|
..
|
||||||
} = state;
|
} = module_cache;
|
||||||
|
|
||||||
|
let sources = sources
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
|
.collect();
|
||||||
|
|
||||||
MonomorphizedModule {
|
MonomorphizedModule {
|
||||||
can_problems,
|
can_problems,
|
||||||
|
@ -1570,7 +1562,7 @@ fn finish_specialization<'a>(
|
||||||
subs,
|
subs,
|
||||||
interns,
|
interns,
|
||||||
procedures,
|
procedures,
|
||||||
src: src.into(),
|
sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1580,7 +1572,6 @@ fn finish<'a>(
|
||||||
solved: Solved<Subs>,
|
solved: Solved<Subs>,
|
||||||
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
src: &'a str,
|
|
||||||
) -> LoadedModule {
|
) -> LoadedModule {
|
||||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||||
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
.unwrap_or_else(|_| panic!("There were still outstanding Arc references to module_ids"))
|
||||||
|
@ -1591,15 +1582,22 @@ fn finish<'a>(
|
||||||
all_ident_ids: state.constrained_ident_ids,
|
all_ident_ids: state.constrained_ident_ids,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sources = state
|
||||||
|
.module_cache
|
||||||
|
.sources
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
|
.collect();
|
||||||
|
|
||||||
LoadedModule {
|
LoadedModule {
|
||||||
module_id: state.root_id,
|
module_id: state.root_id,
|
||||||
interns,
|
interns,
|
||||||
solved,
|
solved,
|
||||||
can_problems: state.can_problems,
|
can_problems: state.module_cache.can_problems,
|
||||||
type_problems: state.type_problems,
|
type_problems: state.module_cache.type_problems,
|
||||||
declarations_by_id: state.declarations_by_id,
|
declarations_by_id: state.declarations_by_id,
|
||||||
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
||||||
src: src.into(),
|
sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
documentation,
|
documentation,
|
||||||
}
|
}
|
||||||
|
@ -1693,6 +1691,7 @@ fn parse_header<'a>(
|
||||||
match parsed {
|
match parsed {
|
||||||
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
Ok((ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
||||||
header.name,
|
header.name,
|
||||||
|
filename,
|
||||||
header.exposes.into_bump_slice(),
|
header.exposes.into_bump_slice(),
|
||||||
header.imports.into_bump_slice(),
|
header.imports.into_bump_slice(),
|
||||||
parse_state,
|
parse_state,
|
||||||
|
@ -1702,6 +1701,7 @@ fn parse_header<'a>(
|
||||||
)),
|
)),
|
||||||
Ok((ast::Module::App { header }, parse_state)) => Ok(send_header(
|
Ok((ast::Module::App { header }, parse_state)) => Ok(send_header(
|
||||||
header.name,
|
header.name,
|
||||||
|
filename,
|
||||||
header.provides.into_bump_slice(),
|
header.provides.into_bump_slice(),
|
||||||
header.imports.into_bump_slice(),
|
header.imports.into_bump_slice(),
|
||||||
parse_state,
|
parse_state,
|
||||||
|
@ -1776,6 +1776,7 @@ fn load_from_str<'a>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn send_header<'a>(
|
fn send_header<'a>(
|
||||||
name: Located<roc_parse::header::ModuleName<'a>>,
|
name: Located<roc_parse::header::ModuleName<'a>>,
|
||||||
|
filename: PathBuf,
|
||||||
exposes: &'a [Located<ExposesEntry<'a>>],
|
exposes: &'a [Located<ExposesEntry<'a>>],
|
||||||
imports: &'a [Located<ImportsEntry<'a>>],
|
imports: &'a [Located<ImportsEntry<'a>>],
|
||||||
parse_state: parser::State<'a>,
|
parse_state: parser::State<'a>,
|
||||||
|
@ -1895,6 +1896,7 @@ fn send_header<'a>(
|
||||||
home,
|
home,
|
||||||
Msg::Header(ModuleHeader {
|
Msg::Header(ModuleHeader {
|
||||||
module_id: home,
|
module_id: home,
|
||||||
|
module_path: filename,
|
||||||
exposed_ident_ids: ident_ids,
|
exposed_ident_ids: ident_ids,
|
||||||
module_name: declared_name,
|
module_name: declared_name,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
|
@ -1914,7 +1916,6 @@ impl<'a> BuildTask<'a> {
|
||||||
module: Module,
|
module: Module,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
src: &'a str,
|
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
imported_modules: MutSet<ModuleId>,
|
imported_modules: MutSet<ModuleId>,
|
||||||
|
@ -1954,7 +1955,6 @@ impl<'a> BuildTask<'a> {
|
||||||
imported_symbols,
|
imported_symbols,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
src,
|
|
||||||
declarations,
|
declarations,
|
||||||
module_timing,
|
module_timing,
|
||||||
}
|
}
|
||||||
|
@ -1970,7 +1970,6 @@ fn run_solve<'a>(
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
mut var_store: VarStore,
|
mut var_store: VarStore,
|
||||||
decls: Vec<Declaration>,
|
decls: Vec<Declaration>,
|
||||||
src: &'a str,
|
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
// We have more constraining work to do now, so we'll add it to our timings.
|
// We have more constraining work to do now, so we'll add it to our timings.
|
||||||
let constrain_start = SystemTime::now();
|
let constrain_start = SystemTime::now();
|
||||||
|
@ -2012,7 +2011,6 @@ fn run_solve<'a>(
|
||||||
|
|
||||||
// Send the subs to the main thread for processing,
|
// Send the subs to the main thread for processing,
|
||||||
Msg::SolvedTypes {
|
Msg::SolvedTypes {
|
||||||
src,
|
|
||||||
module_id,
|
module_id,
|
||||||
solved_subs,
|
solved_subs,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
@ -2041,7 +2039,6 @@ fn canonicalize_and_constrain<'a>(
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
mut module_timing,
|
mut module_timing,
|
||||||
src,
|
|
||||||
..
|
..
|
||||||
} = parsed;
|
} = parsed;
|
||||||
|
|
||||||
|
@ -2094,7 +2091,6 @@ fn canonicalize_and_constrain<'a>(
|
||||||
module,
|
module,
|
||||||
declarations: module_output.declarations,
|
declarations: module_output.declarations,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
src,
|
|
||||||
var_store,
|
var_store,
|
||||||
constraint,
|
constraint,
|
||||||
ident_ids: module_output.ident_ids,
|
ident_ids: module_output.ident_ids,
|
||||||
|
@ -2145,12 +2141,14 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
||||||
deps_by_name,
|
deps_by_name,
|
||||||
exposed_ident_ids,
|
exposed_ident_ids,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
|
module_path,
|
||||||
..
|
..
|
||||||
} = header;
|
} = header;
|
||||||
|
|
||||||
let parsed = ParsedModule {
|
let parsed = ParsedModule {
|
||||||
module_id,
|
module_id,
|
||||||
module_name,
|
module_name,
|
||||||
|
module_path,
|
||||||
deps_by_name,
|
deps_by_name,
|
||||||
exposed_ident_ids,
|
exposed_ident_ids,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
|
@ -2202,7 +2200,6 @@ fn make_specializations<'a>(
|
||||||
mut procs: Procs<'a>,
|
mut procs: Procs<'a>,
|
||||||
mut layout_cache: LayoutCache<'a>,
|
mut layout_cache: LayoutCache<'a>,
|
||||||
specializations_we_must_make: ExternalSpecializations,
|
specializations_we_must_make: ExternalSpecializations,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let mut mono_problems = Vec::new();
|
let mut mono_problems = Vec::new();
|
||||||
// do the thing
|
// do the thing
|
||||||
|
@ -2238,7 +2235,6 @@ fn make_specializations<'a>(
|
||||||
procedures,
|
procedures,
|
||||||
problems: mono_problems,
|
problems: mono_problems,
|
||||||
subs,
|
subs,
|
||||||
finished_info,
|
|
||||||
external_specializations_requested,
|
external_specializations_requested,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2255,7 +2251,6 @@ fn build_pending_specializations<'a>(
|
||||||
mut layout_cache: LayoutCache<'a>,
|
mut layout_cache: LayoutCache<'a>,
|
||||||
// TODO remove
|
// TODO remove
|
||||||
exposed_to_host: MutMap<Symbol, Variable>,
|
exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
finished_info: FinishedInfo<'a>,
|
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let mut procs = Procs::default();
|
let mut procs = Procs::default();
|
||||||
|
|
||||||
|
@ -2309,7 +2304,6 @@ fn build_pending_specializations<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
procs,
|
procs,
|
||||||
problems,
|
problems,
|
||||||
finished_info,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2366,7 +2360,13 @@ fn add_def_to_module<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
procs.insert_exposed(symbol, layout, mono_env.subs, annotation);
|
procs.insert_exposed(
|
||||||
|
symbol,
|
||||||
|
layout,
|
||||||
|
mono_env.subs,
|
||||||
|
def.annotation,
|
||||||
|
annotation,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
procs.insert_named(
|
procs.insert_named(
|
||||||
|
@ -2392,7 +2392,13 @@ fn add_def_to_module<'a>(
|
||||||
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err)
|
todo!("TODO gracefully handle the situation where we expose a function to the host which doesn't have a valid layout (e.g. maybe the function wasn't monomorphic): {:?}", err)
|
||||||
);
|
);
|
||||||
|
|
||||||
procs.insert_exposed(symbol, layout, mono_env.subs, annotation);
|
procs.insert_exposed(
|
||||||
|
symbol,
|
||||||
|
layout,
|
||||||
|
mono_env.subs,
|
||||||
|
def.annotation,
|
||||||
|
annotation,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let proc = PartialProc {
|
let proc = PartialProc {
|
||||||
|
@ -2457,7 +2463,6 @@ fn run_task<'a>(
|
||||||
var_store,
|
var_store,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
declarations,
|
declarations,
|
||||||
src,
|
|
||||||
} => Ok(run_solve(
|
} => Ok(run_solve(
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
@ -2466,7 +2471,6 @@ fn run_task<'a>(
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
declarations,
|
declarations,
|
||||||
src,
|
|
||||||
)),
|
)),
|
||||||
BuildPendingSpecializations {
|
BuildPendingSpecializations {
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -2475,7 +2479,6 @@ fn run_task<'a>(
|
||||||
module_timing,
|
module_timing,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
solved_subs,
|
solved_subs,
|
||||||
finished_info,
|
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
} => Ok(build_pending_specializations(
|
} => Ok(build_pending_specializations(
|
||||||
arena,
|
arena,
|
||||||
|
@ -2486,7 +2489,6 @@ fn run_task<'a>(
|
||||||
module_timing,
|
module_timing,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
finished_info,
|
|
||||||
)),
|
)),
|
||||||
MakeSpecializations {
|
MakeSpecializations {
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -2495,7 +2497,6 @@ fn run_task<'a>(
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
finished_info,
|
|
||||||
} => Ok(make_specializations(
|
} => Ok(make_specializations(
|
||||||
arena,
|
arena,
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -2504,7 +2505,6 @@ fn run_task<'a>(
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
finished_info,
|
|
||||||
)),
|
)),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,21 @@ mod test_load {
|
||||||
src_dir.as_path(),
|
src_dir.as_path(),
|
||||||
subs_by_module,
|
subs_by_module,
|
||||||
);
|
);
|
||||||
let loaded_module = loaded.expect("Test module failed to load");
|
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
let home = loaded_module.module_id;
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
let expected_name = loaded_module
|
let expected_name = loaded_module
|
||||||
.interns
|
.interns
|
||||||
|
@ -87,8 +98,17 @@ mod test_load {
|
||||||
let home = loaded_module.module_id;
|
let home = loaded_module.module_id;
|
||||||
let mut subs = loaded_module.solved.into_inner();
|
let mut subs = loaded_module.solved.into_inner();
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
assert_eq!(
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
for decl in loaded_module.declarations_by_id.remove(&home).unwrap() {
|
for decl in loaded_module.declarations_by_id.remove(&home).unwrap() {
|
||||||
match decl {
|
match decl {
|
||||||
|
@ -141,9 +161,19 @@ mod test_load {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||||
|
let home = loaded_module.module_id;
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
assert_eq!(
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
let def_count: usize = loaded_module
|
let def_count: usize = loaded_module
|
||||||
.declarations_by_id
|
.declarations_by_id
|
||||||
|
|
|
@ -44,10 +44,21 @@ mod test_uniq_load {
|
||||||
src_dir.as_path(),
|
src_dir.as_path(),
|
||||||
subs_by_module,
|
subs_by_module,
|
||||||
);
|
);
|
||||||
let loaded_module = loaded.expect("Test module failed to load");
|
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
let home = loaded_module.module_id;
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
let expected_name = loaded_module
|
let expected_name = loaded_module
|
||||||
.interns
|
.interns
|
||||||
|
@ -88,8 +99,17 @@ mod test_uniq_load {
|
||||||
let home = loaded_module.module_id;
|
let home = loaded_module.module_id;
|
||||||
let mut subs = loaded_module.solved.into_inner();
|
let mut subs = loaded_module.solved.into_inner();
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
assert_eq!(
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
for decl in loaded_module.declarations_by_id.remove(&home).unwrap() {
|
for decl in loaded_module.declarations_by_id.remove(&home).unwrap() {
|
||||||
match decl {
|
match decl {
|
||||||
|
@ -142,9 +162,19 @@ mod test_uniq_load {
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut loaded_module = loaded.expect("Test module failed to load");
|
let mut loaded_module = loaded.expect("Test module failed to load");
|
||||||
|
let home = loaded_module.module_id;
|
||||||
|
|
||||||
assert_eq!(loaded_module.can_problems, Vec::new());
|
assert_eq!(
|
||||||
assert_eq!(loaded_module.type_problems, Vec::new());
|
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loaded_module
|
||||||
|
.type_problems
|
||||||
|
.remove(&home)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Vec::new()
|
||||||
|
);
|
||||||
|
|
||||||
let def_count: usize = loaded_module
|
let def_count: usize = loaded_module
|
||||||
.declarations_by_id
|
.declarations_by_id
|
||||||
|
|
|
@ -14,6 +14,7 @@ pub enum LowLevel {
|
||||||
ListRepeat,
|
ListRepeat,
|
||||||
ListReverse,
|
ListReverse,
|
||||||
ListConcat,
|
ListConcat,
|
||||||
|
ListContains,
|
||||||
ListAppend,
|
ListAppend,
|
||||||
ListPrepend,
|
ListPrepend,
|
||||||
ListJoin,
|
ListJoin,
|
||||||
|
|
|
@ -691,6 +691,7 @@ define_builtins! {
|
||||||
15 LIST_PREPEND: "prepend"
|
15 LIST_PREPEND: "prepend"
|
||||||
16 LIST_JOIN: "join"
|
16 LIST_JOIN: "join"
|
||||||
17 LIST_KEEP_IF: "keepIf"
|
17 LIST_KEEP_IF: "keepIf"
|
||||||
|
18 LIST_CONTAINS: "contains"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -520,6 +520,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
ListMap => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListMap => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
|
ListContains => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
||||||
|
|
|
@ -147,7 +147,7 @@ type LiveVarSet = MutSet<Symbol>;
|
||||||
type JPLiveVarMap = MutMap<JoinPointId, LiveVarSet>;
|
type JPLiveVarMap = MutMap<JoinPointId, LiveVarSet>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Context<'a> {
|
struct Context<'a> {
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
vars: VarMap,
|
vars: VarMap,
|
||||||
jp_live_vars: JPLiveVarMap, // map: join point => live variables
|
jp_live_vars: JPLiveVarMap, // map: join point => live variables
|
||||||
|
|
|
@ -28,6 +28,12 @@ pub struct PartialProc<'a> {
|
||||||
pub is_self_recursive: bool,
|
pub is_self_recursive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
|
pub struct HostExposedVariables {
|
||||||
|
rigids: MutMap<Lowercase, Variable>,
|
||||||
|
aliases: MutMap<Symbol, Variable>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum CapturedSymbols<'a> {
|
pub enum CapturedSymbols<'a> {
|
||||||
None,
|
None,
|
||||||
|
@ -46,12 +52,34 @@ impl<'a> CapturedSymbols<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PendingSpecialization {
|
pub struct PendingSpecialization {
|
||||||
solved_type: SolvedType,
|
solved_type: SolvedType,
|
||||||
|
host_exposed_aliases: MutMap<Symbol, SolvedType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingSpecialization {
|
impl PendingSpecialization {
|
||||||
pub fn from_var(subs: &Subs, var: Variable) -> Self {
|
pub fn from_var(subs: &Subs, var: Variable) -> Self {
|
||||||
let solved_type = SolvedType::from_var(subs, var);
|
let solved_type = SolvedType::from_var(subs, var);
|
||||||
PendingSpecialization { solved_type }
|
PendingSpecialization {
|
||||||
|
solved_type,
|
||||||
|
host_exposed_aliases: MutMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_var_host_exposed(
|
||||||
|
subs: &Subs,
|
||||||
|
var: Variable,
|
||||||
|
host_exposed_aliases: &MutMap<Symbol, Variable>,
|
||||||
|
) -> Self {
|
||||||
|
let solved_type = SolvedType::from_var(subs, var);
|
||||||
|
|
||||||
|
let host_exposed_aliases = host_exposed_aliases
|
||||||
|
.iter()
|
||||||
|
.map(|(symbol, variable)| (*symbol, SolvedType::from_var(subs, *variable)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
PendingSpecialization {
|
||||||
|
solved_type,
|
||||||
|
host_exposed_aliases,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +91,16 @@ pub struct Proc<'a> {
|
||||||
pub closure_data_layout: Option<Layout<'a>>,
|
pub closure_data_layout: Option<Layout<'a>>,
|
||||||
pub ret_layout: Layout<'a>,
|
pub ret_layout: Layout<'a>,
|
||||||
pub is_self_recursive: SelfRecursive,
|
pub is_self_recursive: SelfRecursive,
|
||||||
|
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum HostExposedLayouts<'a> {
|
||||||
|
NotHostExposed,
|
||||||
|
HostExposed {
|
||||||
|
rigids: MutMap<Lowercase, Layout<'a>>,
|
||||||
|
aliases: MutMap<Symbol, Layout<'a>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -398,8 +436,7 @@ impl<'a> Procs<'a> {
|
||||||
// Changing it to use .entry() would necessarily make it incorrect.
|
// Changing it to use .entry() would necessarily make it incorrect.
|
||||||
#[allow(clippy::map_entry)]
|
#[allow(clippy::map_entry)]
|
||||||
if !already_specialized {
|
if !already_specialized {
|
||||||
let solved_type = SolvedType::from_var(env.subs, annotation);
|
let pending = PendingSpecialization::from_var(env.subs, annotation);
|
||||||
let pending = PendingSpecialization { solved_type };
|
|
||||||
|
|
||||||
let pattern_symbols = pattern_symbols.into_bump_slice();
|
let pattern_symbols = pattern_symbols.into_bump_slice();
|
||||||
match &mut self.pending_specializations {
|
match &mut self.pending_specializations {
|
||||||
|
@ -478,6 +515,7 @@ impl<'a> Procs<'a> {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
layout: Layout<'a>,
|
layout: Layout<'a>,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
|
opt_annotation: Option<roc_can::def::Annotation>,
|
||||||
fn_var: Variable,
|
fn_var: Variable,
|
||||||
) {
|
) {
|
||||||
let tuple = (name, layout);
|
let tuple = (name, layout);
|
||||||
|
@ -489,7 +527,14 @@ impl<'a> Procs<'a> {
|
||||||
|
|
||||||
// We're done with that tuple, so move layout back out to avoid cloning it.
|
// We're done with that tuple, so move layout back out to avoid cloning it.
|
||||||
let (name, layout) = tuple;
|
let (name, layout) = tuple;
|
||||||
let pending = PendingSpecialization::from_var(subs, fn_var);
|
let pending = match opt_annotation {
|
||||||
|
None => PendingSpecialization::from_var(subs, fn_var),
|
||||||
|
Some(annotation) => PendingSpecialization::from_var_host_exposed(
|
||||||
|
subs,
|
||||||
|
fn_var,
|
||||||
|
&annotation.introduced_variables.host_exposed_aliases,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
// This should only be called when pending_specializations is Some.
|
// This should only be called when pending_specializations is Some.
|
||||||
// Otherwise, it's being called in the wrong pass!
|
// Otherwise, it's being called in the wrong pass!
|
||||||
|
@ -971,10 +1016,11 @@ impl<'a> Stmt<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
var: Variable,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
from_can(env, can_expr, procs, layout_cache)
|
from_can(env, var, can_expr, procs, layout_cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A>
|
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A>
|
||||||
|
@ -1301,6 +1347,7 @@ pub fn specialize_all<'a>(
|
||||||
name,
|
name,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
solved_type,
|
solved_type,
|
||||||
|
MutMap::default(),
|
||||||
partial_proc,
|
partial_proc,
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
|
@ -1386,6 +1433,7 @@ fn specialize_external<'a>(
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
fn_var: Variable,
|
fn_var: Variable,
|
||||||
|
host_exposed_variables: &[(Symbol, Variable)],
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<Proc<'a>, LayoutProblem> {
|
) -> Result<Proc<'a>, LayoutProblem> {
|
||||||
let PartialProc {
|
let PartialProc {
|
||||||
|
@ -1405,7 +1453,7 @@ fn specialize_external<'a>(
|
||||||
let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||||
debug_assert!(is_valid);
|
debug_assert!(is_valid);
|
||||||
|
|
||||||
let mut specialized_body = from_can(env, body, procs, layout_cache);
|
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||||
|
|
||||||
// if this is a closure, add the closure record argument
|
// if this is a closure, add the closure record argument
|
||||||
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
||||||
|
@ -1450,6 +1498,25 @@ fn specialize_external<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determine the layout of aliases/rigids exposed to the host
|
||||||
|
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
||||||
|
HostExposedLayouts::NotHostExposed
|
||||||
|
} else {
|
||||||
|
let mut aliases = MutMap::default();
|
||||||
|
|
||||||
|
for (symbol, variable) in host_exposed_variables {
|
||||||
|
let layout = layout_cache
|
||||||
|
.from_var(env.arena, *variable, env.subs)
|
||||||
|
.unwrap();
|
||||||
|
aliases.insert(*symbol, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostExposedLayouts::HostExposed {
|
||||||
|
rigids: MutMap::default(),
|
||||||
|
aliases,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// reset subs, so we don't get type errors when specializing for a different signature
|
// reset subs, so we don't get type errors when specializing for a different signature
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
layout_cache.rollback_to(cache_snapshot);
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
|
@ -1472,6 +1539,7 @@ fn specialize_external<'a>(
|
||||||
closure_data_layout,
|
closure_data_layout,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
is_self_recursive: recursivity,
|
is_self_recursive: recursivity,
|
||||||
|
host_exposed_layouts,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(proc)
|
Ok(proc)
|
||||||
|
@ -1692,51 +1760,80 @@ fn specialize<'a>(
|
||||||
pending: PendingSpecialization,
|
pending: PendingSpecialization,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
||||||
let PendingSpecialization { solved_type } = pending;
|
let PendingSpecialization {
|
||||||
|
solved_type,
|
||||||
|
host_exposed_aliases,
|
||||||
|
} = pending;
|
||||||
|
|
||||||
specialize_solved_type(
|
specialize_solved_type(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
proc_name,
|
proc_name,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
solved_type,
|
solved_type,
|
||||||
|
host_exposed_aliases,
|
||||||
partial_proc,
|
partial_proc,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn introduce_solved_type_to_subs<'a>(env: &mut Env<'a, '_>, solved_type: &SolvedType) -> Variable {
|
||||||
|
use roc_solve::solve::insert_type_into_subs;
|
||||||
|
use roc_types::solved_types::{to_type, FreeVars};
|
||||||
|
use roc_types::subs::VarStore;
|
||||||
|
let mut free_vars = FreeVars::default();
|
||||||
|
let mut var_store = VarStore::new_from_subs(env.subs);
|
||||||
|
|
||||||
|
let before = var_store.peek();
|
||||||
|
|
||||||
|
let normal_type = to_type(solved_type, &mut free_vars, &mut var_store);
|
||||||
|
|
||||||
|
let after = var_store.peek();
|
||||||
|
let variables_introduced = after - before;
|
||||||
|
|
||||||
|
env.subs.extend_by(variables_introduced as usize);
|
||||||
|
|
||||||
|
insert_type_into_subs(env.subs, &normal_type)
|
||||||
|
}
|
||||||
|
|
||||||
fn specialize_solved_type<'a>(
|
fn specialize_solved_type<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
solved_type: SolvedType,
|
solved_type: SolvedType,
|
||||||
|
host_exposed_aliases: MutMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
||||||
// add the specializations that other modules require of us
|
// add the specializations that other modules require of us
|
||||||
use roc_solve::solve::{insert_type_into_subs, instantiate_rigids};
|
use roc_solve::solve::instantiate_rigids;
|
||||||
use roc_types::solved_types::{to_type, FreeVars};
|
|
||||||
use roc_types::subs::VarStore;
|
|
||||||
|
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
let cache_snapshot = layout_cache.snapshot();
|
||||||
|
|
||||||
let mut free_vars = FreeVars::default();
|
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
||||||
let mut var_store = VarStore::new_from_subs(env.subs);
|
|
||||||
|
|
||||||
let before = var_store.peek();
|
|
||||||
|
|
||||||
let normal_type = to_type(&solved_type, &mut free_vars, &mut var_store);
|
|
||||||
|
|
||||||
let after = var_store.peek();
|
|
||||||
let variables_introduced = after - before;
|
|
||||||
|
|
||||||
env.subs.extend_by(variables_introduced as usize);
|
|
||||||
|
|
||||||
let fn_var = insert_type_into_subs(env.subs, &normal_type);
|
|
||||||
|
|
||||||
// make sure rigid variables in the annotation are converted to flex variables
|
// make sure rigid variables in the annotation are converted to flex variables
|
||||||
instantiate_rigids(env.subs, partial_proc.annotation);
|
instantiate_rigids(env.subs, partial_proc.annotation);
|
||||||
|
|
||||||
match specialize_external(env, procs, proc_name, layout_cache, fn_var, partial_proc) {
|
let mut host_exposed_variables = Vec::with_capacity_in(host_exposed_aliases.len(), env.arena);
|
||||||
|
|
||||||
|
for (symbol, solved_type) in host_exposed_aliases {
|
||||||
|
let alias_var = introduce_solved_type_to_subs(env, &solved_type);
|
||||||
|
|
||||||
|
host_exposed_variables.push((symbol, alias_var));
|
||||||
|
}
|
||||||
|
|
||||||
|
let specialized = specialize_external(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
proc_name,
|
||||||
|
layout_cache,
|
||||||
|
fn_var,
|
||||||
|
&host_exposed_variables,
|
||||||
|
partial_proc,
|
||||||
|
);
|
||||||
|
|
||||||
|
match specialized {
|
||||||
Ok(proc) => {
|
Ok(proc) => {
|
||||||
let layout = layout_cache
|
let layout = layout_cache
|
||||||
.from_var(&env.arena, fn_var, env.subs)
|
.from_var(&env.arena, fn_var, env.subs)
|
||||||
|
@ -1789,6 +1886,7 @@ impl<'a> FunctionLayouts<'a> {
|
||||||
pub fn with_hole<'a>(
|
pub fn with_hole<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
|
variable: Variable,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
assigned: Symbol,
|
assigned: Symbol,
|
||||||
|
@ -1865,7 +1963,15 @@ pub fn with_hole<'a>(
|
||||||
return_type,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
return with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
return with_hole(
|
||||||
|
env,
|
||||||
|
cont.value,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1877,6 +1983,7 @@ pub fn with_hole<'a>(
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned,
|
assigned,
|
||||||
|
@ -1887,7 +1994,15 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue with the default path
|
// continue with the default path
|
||||||
let mut stmt = with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
let mut stmt = with_hole(
|
||||||
|
env,
|
||||||
|
cont.value,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
|
||||||
// a variable is aliased
|
// a variable is aliased
|
||||||
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
||||||
|
@ -1898,6 +2013,7 @@ pub fn with_hole<'a>(
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -1927,7 +2043,15 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert the continuation
|
// convert the continuation
|
||||||
let mut stmt = with_hole(env, cont.value, procs, layout_cache, assigned, hole);
|
let mut stmt = with_hole(
|
||||||
|
env,
|
||||||
|
cont.value,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
|
||||||
let outer_symbol = env.unique_symbol();
|
let outer_symbol = env.unique_symbol();
|
||||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||||
|
@ -1937,6 +2061,7 @@ pub fn with_hole<'a>(
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
outer_symbol,
|
outer_symbol,
|
||||||
|
@ -1983,7 +2108,15 @@ pub fn with_hole<'a>(
|
||||||
unreachable!("recursive value does not have Identifier pattern")
|
unreachable!("recursive value does not have Identifier pattern")
|
||||||
}
|
}
|
||||||
|
|
||||||
with_hole(env, cont.value, procs, layout_cache, assigned, hole)
|
with_hole(
|
||||||
|
env,
|
||||||
|
cont.value,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Var(symbol) => {
|
Var(symbol) => {
|
||||||
if procs.module_thunks.contains(&symbol) {
|
if procs.module_thunks.contains(&symbol) {
|
||||||
|
@ -2003,6 +2136,27 @@ pub fn with_hole<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
} else if symbol.module_id() != env.home && symbol.module_id() != ModuleId::ATTR {
|
||||||
|
match layout_cache.from_var(env.arena, variable, env.subs) {
|
||||||
|
Err(e) => panic!("invalid layout {:?}", e),
|
||||||
|
Ok(Layout::FunctionPointer(_, _)) => {
|
||||||
|
add_needed_external(procs, env, variable, symbol);
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
// this is a 0-arity thunk
|
||||||
|
let result = call_by_name(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
variable,
|
||||||
|
symbol,
|
||||||
|
std::vec::Vec::new(),
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
env.arena.alloc(Stmt::Ret(assigned)),
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A bit ugly, but it does the job
|
// A bit ugly, but it does the job
|
||||||
|
@ -2031,9 +2185,6 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Var(symbol) => panic!("reached Var {}", symbol),
|
|
||||||
// assigned,
|
|
||||||
// Stmt::Ret(symbol),
|
|
||||||
Tag {
|
Tag {
|
||||||
variant_var,
|
variant_var,
|
||||||
name: tag_name,
|
name: tag_name,
|
||||||
|
@ -2151,12 +2302,9 @@ pub fn with_hole<'a>(
|
||||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||||
|
|
||||||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
let mut field_layouts = Vec::with_capacity_in(fields.len(), env.arena);
|
|
||||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
|
||||||
for (label, layout) in sorted_fields.into_iter() {
|
for (label, _, _) in sorted_fields.into_iter() {
|
||||||
field_layouts.push(layout);
|
|
||||||
|
|
||||||
// TODO how should function pointers be handled here?
|
// TODO how should function pointers be handled here?
|
||||||
match fields.remove(&label) {
|
match fields.remove(&label) {
|
||||||
Some(field) => match can_reuse_symbol(procs, &field.loc_expr.value) {
|
Some(field) => match can_reuse_symbol(procs, &field.loc_expr.value) {
|
||||||
|
@ -2190,6 +2338,7 @@ pub fn with_hole<'a>(
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
field.loc_expr.value,
|
field.loc_expr.value,
|
||||||
|
field.var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -2226,6 +2375,7 @@ pub fn with_hole<'a>(
|
||||||
let mut stmt = with_hole(
|
let mut stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
final_else.value,
|
final_else.value,
|
||||||
|
branch_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned,
|
assigned,
|
||||||
|
@ -2237,6 +2387,7 @@ pub fn with_hole<'a>(
|
||||||
let then = with_hole(
|
let then = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_then.value,
|
loc_then.value,
|
||||||
|
branch_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned,
|
assigned,
|
||||||
|
@ -2257,6 +2408,7 @@ pub fn with_hole<'a>(
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_cond.value,
|
loc_cond.value,
|
||||||
|
cond_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
branching_symbol,
|
branching_symbol,
|
||||||
|
@ -2275,6 +2427,7 @@ pub fn with_hole<'a>(
|
||||||
let mut stmt = with_hole(
|
let mut stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
final_else.value,
|
final_else.value,
|
||||||
|
branch_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned_in_jump,
|
assigned_in_jump,
|
||||||
|
@ -2286,6 +2439,7 @@ pub fn with_hole<'a>(
|
||||||
let then = with_hole(
|
let then = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_then.value,
|
loc_then.value,
|
||||||
|
branch_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned_in_jump,
|
assigned_in_jump,
|
||||||
|
@ -2306,6 +2460,7 @@ pub fn with_hole<'a>(
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_cond.value,
|
loc_cond.value,
|
||||||
|
cond_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
branching_symbol,
|
branching_symbol,
|
||||||
|
@ -2441,7 +2596,7 @@ pub fn with_hole<'a>(
|
||||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
|
||||||
let mut current = 0;
|
let mut current = 0;
|
||||||
for (label, opt_field_layout) in sorted_fields.into_iter() {
|
for (label, _, opt_field_layout) in sorted_fields.into_iter() {
|
||||||
match opt_field_layout {
|
match opt_field_layout {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// this was an optional field, and now does not exist!
|
// this was an optional field, and now does not exist!
|
||||||
|
@ -2582,7 +2737,7 @@ pub fn with_hole<'a>(
|
||||||
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
|
||||||
let mut current = 0;
|
let mut current = 0;
|
||||||
for (label, opt_field_layout) in sorted_fields.into_iter() {
|
for (label, _, opt_field_layout) in sorted_fields.into_iter() {
|
||||||
match opt_field_layout {
|
match opt_field_layout {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug_assert!(!updates.contains_key(&label));
|
debug_assert!(!updates.contains_key(&label));
|
||||||
|
@ -2957,6 +3112,7 @@ pub fn with_hole<'a>(
|
||||||
result = with_hole(
|
result = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
|
fn_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
function_symbol,
|
function_symbol,
|
||||||
|
@ -3004,6 +3160,7 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
pub fn from_can<'a>(
|
pub fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
variable: Variable,
|
||||||
can_expr: roc_can::expr::Expr,
|
can_expr: roc_can::expr::Expr,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
@ -3056,11 +3213,11 @@ pub fn from_can<'a>(
|
||||||
.from_var(env.arena, cond_var, env.subs)
|
.from_var(env.arena, cond_var, env.subs)
|
||||||
.expect("invalid cond_layout");
|
.expect("invalid cond_layout");
|
||||||
|
|
||||||
let mut stmt = from_can(env, final_else.value, procs, layout_cache);
|
let mut stmt = from_can(env, branch_var, final_else.value, procs, layout_cache);
|
||||||
|
|
||||||
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
||||||
let branching_symbol = env.unique_symbol();
|
let branching_symbol = env.unique_symbol();
|
||||||
let then = from_can(env, loc_then.value, procs, layout_cache);
|
let then = from_can(env, branch_var, loc_then.value, procs, layout_cache);
|
||||||
|
|
||||||
stmt = Stmt::Cond {
|
stmt = Stmt::Cond {
|
||||||
cond_symbol: branching_symbol,
|
cond_symbol: branching_symbol,
|
||||||
|
@ -3076,6 +3233,7 @@ pub fn from_can<'a>(
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_cond.value,
|
loc_cond.value,
|
||||||
|
cond_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
branching_symbol,
|
branching_symbol,
|
||||||
|
@ -3128,7 +3286,7 @@ pub fn from_can<'a>(
|
||||||
unreachable!("recursive value does not have Identifier pattern")
|
unreachable!("recursive value does not have Identifier pattern")
|
||||||
}
|
}
|
||||||
|
|
||||||
from_can(env, cont.value, procs, layout_cache)
|
from_can(env, variable, cont.value, procs, layout_cache)
|
||||||
}
|
}
|
||||||
LetNonRec(def, cont, outer_annotation) => {
|
LetNonRec(def, cont, outer_annotation) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
|
@ -3178,7 +3336,7 @@ pub fn from_can<'a>(
|
||||||
return_type,
|
return_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
return from_can(env, cont.value, procs, layout_cache);
|
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -3186,7 +3344,7 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(original) => {
|
roc_can::expr::Expr::Var(original) => {
|
||||||
let mut rest = from_can(env, cont.value, procs, layout_cache);
|
let mut rest = from_can(env, def.expr_var, cont.value, procs, layout_cache);
|
||||||
// a variable is aliased
|
// a variable is aliased
|
||||||
substitute_in_exprs(env.arena, &mut rest, *symbol, original);
|
substitute_in_exprs(env.arena, &mut rest, *symbol, original);
|
||||||
|
|
||||||
|
@ -3231,7 +3389,7 @@ pub fn from_can<'a>(
|
||||||
nested_annotation,
|
nested_annotation,
|
||||||
);
|
);
|
||||||
|
|
||||||
return from_can(env, new_outer, procs, layout_cache);
|
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||||
}
|
}
|
||||||
roc_can::expr::Expr::LetRec(nested_defs, nested_cont, nested_annotation) => {
|
roc_can::expr::Expr::LetRec(nested_defs, nested_cont, nested_annotation) => {
|
||||||
use roc_can::expr::Expr::*;
|
use roc_can::expr::Expr::*;
|
||||||
|
@ -3272,13 +3430,14 @@ pub fn from_can<'a>(
|
||||||
nested_annotation,
|
nested_annotation,
|
||||||
);
|
);
|
||||||
|
|
||||||
return from_can(env, new_outer, procs, layout_cache);
|
return from_can(env, variable, new_outer, procs, layout_cache);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let rest = from_can(env, cont.value, procs, layout_cache);
|
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -3292,11 +3451,19 @@ pub fn from_can<'a>(
|
||||||
let mono_pattern = from_can_pattern(env, layout_cache, &def.loc_pattern.value);
|
let mono_pattern = from_can_pattern(env, layout_cache, &def.loc_pattern.value);
|
||||||
|
|
||||||
if let Pattern::Identifier(symbol) = mono_pattern {
|
if let Pattern::Identifier(symbol) = mono_pattern {
|
||||||
let hole = env
|
let hole =
|
||||||
.arena
|
env.arena
|
||||||
.alloc(from_can(env, cont.value, procs, layout_cache));
|
.alloc(from_can(env, variable, cont.value, procs, layout_cache));
|
||||||
|
|
||||||
with_hole(env, def.loc_expr.value, procs, layout_cache, symbol, hole)
|
with_hole(
|
||||||
|
env,
|
||||||
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
symbol,
|
||||||
|
hole,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let context = crate::exhaustive::Context::BadDestruct;
|
let context = crate::exhaustive::Context::BadDestruct;
|
||||||
match crate::exhaustive::check(
|
match crate::exhaustive::check(
|
||||||
|
@ -3317,7 +3484,7 @@ pub fn from_can<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert the continuation
|
// convert the continuation
|
||||||
let mut stmt = from_can(env, cont.value, procs, layout_cache);
|
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||||
|
@ -3332,6 +3499,7 @@ pub fn from_can<'a>(
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
outer_symbol,
|
outer_symbol,
|
||||||
|
@ -3344,7 +3512,7 @@ pub fn from_can<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
let hole = env.arena.alloc(Stmt::Ret(symbol));
|
let hole = env.arena.alloc(Stmt::Ret(symbol));
|
||||||
with_hole(env, can_expr, procs, layout_cache, symbol, hole)
|
with_hole(env, can_expr, variable, procs, layout_cache, symbol, hole)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3464,13 +3632,13 @@ fn from_can_when<'a>(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(pattern, opt_guard, can_expr)| {
|
.map(|(pattern, opt_guard, can_expr)| {
|
||||||
let branch_stmt = match join_point {
|
let branch_stmt = match join_point {
|
||||||
None => from_can(env, can_expr, procs, layout_cache),
|
None => from_can(env, expr_var, can_expr, procs, layout_cache),
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
let arguments = bumpalo::vec![in env.arena; symbol].into_bump_slice();
|
let arguments = bumpalo::vec![in env.arena; symbol].into_bump_slice();
|
||||||
let jump = env.arena.alloc(Stmt::Jump(id, arguments));
|
let jump = env.arena.alloc(Stmt::Jump(id, arguments));
|
||||||
|
|
||||||
with_hole(env, can_expr, procs, layout_cache, symbol, jump)
|
with_hole(env, can_expr, expr_var, procs, layout_cache, symbol, jump)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3480,7 +3648,15 @@ fn from_can_when<'a>(
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
let jump = env.arena.alloc(Stmt::Jump(id, env.arena.alloc([symbol])));
|
let jump = env.arena.alloc(Stmt::Jump(id, env.arena.alloc([symbol])));
|
||||||
|
|
||||||
let guard_stmt = with_hole(env, loc_expr.value, procs, layout_cache, symbol, jump);
|
let guard_stmt = with_hole(
|
||||||
|
env,
|
||||||
|
loc_expr.value,
|
||||||
|
cond_var,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
symbol,
|
||||||
|
jump,
|
||||||
|
);
|
||||||
|
|
||||||
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, guard_stmt) {
|
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, guard_stmt) {
|
||||||
Ok(new_guard_stmt) => (
|
Ok(new_guard_stmt) => (
|
||||||
|
@ -4026,6 +4202,7 @@ fn store_record_destruct<'a>(
|
||||||
stmt = with_hole(
|
stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
expr.clone(),
|
expr.clone(),
|
||||||
|
destruct.variable,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
destruct.symbol,
|
destruct.symbol,
|
||||||
|
@ -4250,6 +4427,7 @@ fn assign_to_symbol<'a>(
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
loc_arg.value,
|
loc_arg.value,
|
||||||
|
arg_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -4275,6 +4453,24 @@ where
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_needed_external<'a>(
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
fn_var: Variable,
|
||||||
|
name: Symbol,
|
||||||
|
) {
|
||||||
|
// call of a function that is not in this module
|
||||||
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
|
|
||||||
|
let existing = match procs.externals_we_need.entry(name.module_id()) {
|
||||||
|
Vacant(entry) => entry.insert(ExternalSpecializations::default()),
|
||||||
|
Occupied(entry) => entry.into_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let solved_type = SolvedType::from_var(env.subs, fn_var);
|
||||||
|
existing.insert(name, solved_type);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn call_by_name<'a>(
|
fn call_by_name<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -4358,6 +4554,12 @@ fn call_by_name<'a>(
|
||||||
// exactly once.
|
// exactly once.
|
||||||
match &mut procs.pending_specializations {
|
match &mut procs.pending_specializations {
|
||||||
Some(pending_specializations) => {
|
Some(pending_specializations) => {
|
||||||
|
let is_imported = assigned.module_id() != proc_name.module_id();
|
||||||
|
// builtins are currently (re)defined in each module, so not really imported
|
||||||
|
let is_builtin = proc_name.is_builtin();
|
||||||
|
if is_imported && !is_builtin {
|
||||||
|
add_needed_external(procs, env, original_fn_var, proc_name);
|
||||||
|
} else {
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
add_pending(
|
add_pending(
|
||||||
pending_specializations,
|
pending_specializations,
|
||||||
|
@ -4365,6 +4567,7 @@ fn call_by_name<'a>(
|
||||||
full_layout.clone(),
|
full_layout.clone(),
|
||||||
pending,
|
pending,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let call = Expr::FunctionCall {
|
let call = Expr::FunctionCall {
|
||||||
call_type: CallType::ByName(proc_name),
|
call_type: CallType::ByName(proc_name),
|
||||||
|
@ -4447,21 +4650,7 @@ fn call_by_name<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
None if assigned.module_id() != proc_name.module_id() => {
|
None if assigned.module_id() != proc_name.module_id() => {
|
||||||
let fn_var = original_fn_var;
|
add_needed_external(procs, env, original_fn_var, proc_name);
|
||||||
|
|
||||||
// call of a function that is not not in this module
|
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
|
||||||
|
|
||||||
let existing =
|
|
||||||
match procs.externals_we_need.entry(proc_name.module_id()) {
|
|
||||||
Vacant(entry) => {
|
|
||||||
entry.insert(ExternalSpecializations::default())
|
|
||||||
}
|
|
||||||
Occupied(entry) => entry.into_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let solved_type = SolvedType::from_var(env.subs, fn_var);
|
|
||||||
existing.insert(proc_name, solved_type);
|
|
||||||
|
|
||||||
let call = Expr::FunctionCall {
|
let call = Expr::FunctionCall {
|
||||||
call_type: CallType::ByName(proc_name),
|
call_type: CallType::ByName(proc_name),
|
||||||
|
@ -4548,6 +4737,7 @@ pub enum Pattern<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct RecordDestruct<'a> {
|
pub struct RecordDestruct<'a> {
|
||||||
pub label: Lowercase,
|
pub label: Lowercase,
|
||||||
|
pub variable: Variable,
|
||||||
pub layout: Layout<'a>,
|
pub layout: Layout<'a>,
|
||||||
pub symbol: Symbol,
|
pub symbol: Symbol,
|
||||||
pub typ: DestructType<'a>,
|
pub typ: DestructType<'a>,
|
||||||
|
@ -4774,7 +4964,7 @@ pub fn from_can_pattern<'a>(
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match (opt_sorted, opt_destruct) {
|
match (opt_sorted, opt_destruct) {
|
||||||
(Some((label, Ok(field_layout))), Some(destruct)) => {
|
(Some((label, variable, Ok(field_layout))), Some(destruct)) => {
|
||||||
if destruct.value.label == label {
|
if destruct.value.label == label {
|
||||||
mono_destructs.push(from_can_record_destruct(
|
mono_destructs.push(from_can_record_destruct(
|
||||||
env,
|
env,
|
||||||
|
@ -4790,6 +4980,7 @@ pub fn from_can_pattern<'a>(
|
||||||
mono_destructs.push(RecordDestruct {
|
mono_destructs.push(RecordDestruct {
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
symbol: env.unique_symbol(),
|
symbol: env.unique_symbol(),
|
||||||
|
variable,
|
||||||
layout: field_layout.clone(),
|
layout: field_layout.clone(),
|
||||||
typ: DestructType::Guard(Pattern::Underscore),
|
typ: DestructType::Guard(Pattern::Underscore),
|
||||||
});
|
});
|
||||||
|
@ -4798,7 +4989,7 @@ pub fn from_can_pattern<'a>(
|
||||||
}
|
}
|
||||||
field_layouts.push(field_layout);
|
field_layouts.push(field_layout);
|
||||||
}
|
}
|
||||||
(Some((label, Err(field_layout))), Some(destruct)) => {
|
(Some((label, variable, Err(field_layout))), Some(destruct)) => {
|
||||||
if destruct.value.label == label {
|
if destruct.value.label == label {
|
||||||
opt_destruct = it2.next();
|
opt_destruct = it2.next();
|
||||||
|
|
||||||
|
@ -4806,6 +4997,7 @@ pub fn from_can_pattern<'a>(
|
||||||
label: destruct.value.label.clone(),
|
label: destruct.value.label.clone(),
|
||||||
symbol: destruct.value.symbol,
|
symbol: destruct.value.symbol,
|
||||||
layout: field_layout,
|
layout: field_layout,
|
||||||
|
variable,
|
||||||
typ: match &destruct.value.typ {
|
typ: match &destruct.value.typ {
|
||||||
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
|
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
|
||||||
// if we reach this stage, the optional field is not present
|
// if we reach this stage, the optional field is not present
|
||||||
|
@ -4821,12 +5013,13 @@ pub fn from_can_pattern<'a>(
|
||||||
opt_sorted = it1.next();
|
opt_sorted = it1.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
(Some((label, Err(field_layout))), None) => {
|
(Some((label, variable, Err(field_layout))), None) => {
|
||||||
// the remainder of the fields (from the type) is not matched on in
|
// the remainder of the fields (from the type) is not matched on in
|
||||||
// this pattern; to fill it out, we put underscores
|
// this pattern; to fill it out, we put underscores
|
||||||
mono_destructs.push(RecordDestruct {
|
mono_destructs.push(RecordDestruct {
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
symbol: env.unique_symbol(),
|
symbol: env.unique_symbol(),
|
||||||
|
variable,
|
||||||
layout: field_layout.clone(),
|
layout: field_layout.clone(),
|
||||||
typ: DestructType::Guard(Pattern::Underscore),
|
typ: DestructType::Guard(Pattern::Underscore),
|
||||||
});
|
});
|
||||||
|
@ -4834,12 +5027,13 @@ pub fn from_can_pattern<'a>(
|
||||||
opt_sorted = it1.next();
|
opt_sorted = it1.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
(Some((label, Ok(field_layout))), None) => {
|
(Some((label, variable, Ok(field_layout))), None) => {
|
||||||
// the remainder of the fields (from the type) is not matched on in
|
// the remainder of the fields (from the type) is not matched on in
|
||||||
// this pattern; to fill it out, we put underscores
|
// this pattern; to fill it out, we put underscores
|
||||||
mono_destructs.push(RecordDestruct {
|
mono_destructs.push(RecordDestruct {
|
||||||
label: label.clone(),
|
label: label.clone(),
|
||||||
symbol: env.unique_symbol(),
|
symbol: env.unique_symbol(),
|
||||||
|
variable,
|
||||||
layout: field_layout.clone(),
|
layout: field_layout.clone(),
|
||||||
typ: DestructType::Guard(Pattern::Underscore),
|
typ: DestructType::Guard(Pattern::Underscore),
|
||||||
});
|
});
|
||||||
|
@ -4861,6 +5055,7 @@ pub fn from_can_pattern<'a>(
|
||||||
mono_destructs.push(RecordDestruct {
|
mono_destructs.push(RecordDestruct {
|
||||||
label: destruct.value.label.clone(),
|
label: destruct.value.label.clone(),
|
||||||
symbol: destruct.value.symbol,
|
symbol: destruct.value.symbol,
|
||||||
|
variable: destruct.value.var,
|
||||||
layout: field_layout,
|
layout: field_layout,
|
||||||
typ: DestructType::Optional(loc_expr.value.clone()),
|
typ: DestructType::Optional(loc_expr.value.clone()),
|
||||||
})
|
})
|
||||||
|
@ -4894,6 +5089,7 @@ fn from_can_record_destruct<'a>(
|
||||||
RecordDestruct {
|
RecordDestruct {
|
||||||
label: can_rd.label.clone(),
|
label: can_rd.label.clone(),
|
||||||
symbol: can_rd.symbol,
|
symbol: can_rd.symbol,
|
||||||
|
variable: can_rd.var,
|
||||||
layout: field_layout,
|
layout: field_layout,
|
||||||
typ: match &can_rd.typ {
|
typ: match &can_rd.typ {
|
||||||
roc_can::pattern::DestructType::Required => DestructType::Required,
|
roc_can::pattern::DestructType::Required => DestructType::Required,
|
||||||
|
|
|
@ -822,7 +822,7 @@ pub fn sort_record_fields<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
) -> Vec<'a, (Lowercase, Result<Layout<'a>, Layout<'a>>)> {
|
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
||||||
let mut fields_map = MutMap::default();
|
let mut fields_map = MutMap::default();
|
||||||
|
|
||||||
let mut env = Env {
|
let mut env = Env {
|
||||||
|
@ -844,7 +844,7 @@ pub fn sort_record_fields<'a>(
|
||||||
RecordField::Optional(v) => {
|
RecordField::Optional(v) => {
|
||||||
let layout =
|
let layout =
|
||||||
Layout::from_var(&mut env, v).expect("invalid layout from var");
|
Layout::from_var(&mut env, v).expect("invalid layout from var");
|
||||||
sorted_fields.push((label, Err(layout)));
|
sorted_fields.push((label, v, Err(layout)));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -853,11 +853,11 @@ pub fn sort_record_fields<'a>(
|
||||||
|
|
||||||
// Drop any zero-sized fields like {}
|
// Drop any zero-sized fields like {}
|
||||||
if !layout.is_zero_sized() {
|
if !layout.is_zero_sized() {
|
||||||
sorted_fields.push((label, Ok(layout)));
|
sorted_fields.push((label, var, Ok(layout)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
|
sorted_fields.sort_by(|(label1, _, _), (label2, _, _)| label1.cmp(label2));
|
||||||
|
|
||||||
sorted_fields
|
sorted_fields
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,18 +62,20 @@ mod test_mono {
|
||||||
exposed_types,
|
exposed_types,
|
||||||
);
|
);
|
||||||
|
|
||||||
let loaded = loaded.expect("failed to load module");
|
let mut loaded = loaded.expect("failed to load module");
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
can_problems,
|
module_id: home,
|
||||||
type_problems,
|
|
||||||
mono_problems,
|
|
||||||
procedures,
|
procedures,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
..
|
..
|
||||||
} = loaded;
|
} = loaded;
|
||||||
|
|
||||||
|
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
||||||
|
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
||||||
|
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
if !can_problems.is_empty() {
|
if !can_problems.is_empty() {
|
||||||
println!("Ignoring {} canonicalization problems", can_problems.len());
|
println!("Ignoring {} canonicalization problems", can_problems.len());
|
||||||
}
|
}
|
||||||
|
|
4
compiler/parse/fuzz/.gitignore
vendored
Normal file
4
compiler/parse/fuzz/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
182
compiler/parse/fuzz/Cargo.lock
generated
Normal file
182
compiler/parse/fuzz/Cargo.lock
generated
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitmaps"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "im"
|
||||||
|
version = "14.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7"
|
||||||
|
dependencies = [
|
||||||
|
"bitmaps",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"rand_xoshiro",
|
||||||
|
"sized-chunks",
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "im-rc"
|
||||||
|
version = "14.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "303f7e6256d546e01979071417432425f15c1891fb309a5f2d724ee908fabd6e"
|
||||||
|
dependencies = [
|
||||||
|
"bitmaps",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"rand_xoshiro",
|
||||||
|
"sized-chunks",
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inlinable_string"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb6ee2a7da03bfc3b66ca47c92c2e392fcc053ea040a85561749b026f7aad09a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xoshiro"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_collections"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"im",
|
||||||
|
"im-rc",
|
||||||
|
"wyhash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_module"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"inlinable_string",
|
||||||
|
"lazy_static",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_region",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_parse"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"encode_unicode",
|
||||||
|
"inlinable_string",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_module",
|
||||||
|
"roc_region",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_parse-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"roc_parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_region"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sized-chunks"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718"
|
||||||
|
dependencies = [
|
||||||
|
"bitmaps",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyhash"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "782a50f48ac4336916227cd199c61c7b42f38d0ad705421b49eb12c74c53ae00"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
39
compiler/parse/fuzz/Cargo.toml
Normal file
39
compiler/parse/fuzz/Cargo.toml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "roc_parse-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["Automatically generated"]
|
||||||
|
publish = false
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
cargo-fuzz = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libfuzzer-sys = "0.3"
|
||||||
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
|
|
||||||
|
[dependencies.roc_parse]
|
||||||
|
path = ".."
|
||||||
|
|
||||||
|
# Prevent this from interfering with workspaces
|
||||||
|
[workspace]
|
||||||
|
members = ["."]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_expr"
|
||||||
|
path = "fuzz_targets/fuzz_expr.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_defs"
|
||||||
|
path = "fuzz_targets/fuzz_defs.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_header"
|
||||||
|
path = "fuzz_targets/fuzz_header.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
11
compiler/parse/fuzz/README.md
Normal file
11
compiler/parse/fuzz/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
To setup fuzzing you will need to install cargo-fuzz and run with rust nightly:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cargo install cargo-fuzz
|
||||||
|
$ cargo +nightly fuzz run -j<cores> <target> -- -dict=dict.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
The different targets can be found by running `cargo fuzz list`.
|
||||||
|
|
||||||
|
When a bug is found, it will be reported with commands to run it again and look for a minimized version.
|
||||||
|
If you are going to file a bug, please minimize the input before filing the bug.
|
36
compiler/parse/fuzz/dict.txt
Normal file
36
compiler/parse/fuzz/dict.txt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"if"
|
||||||
|
"then"
|
||||||
|
"else"
|
||||||
|
"when"
|
||||||
|
"as"
|
||||||
|
"is"
|
||||||
|
"expect"
|
||||||
|
|
||||||
|
"app"
|
||||||
|
"platform"
|
||||||
|
"provides"
|
||||||
|
"requires"
|
||||||
|
"exposes"
|
||||||
|
"imports"
|
||||||
|
"effects"
|
||||||
|
"interface"
|
||||||
|
|
||||||
|
"|>"
|
||||||
|
"=="
|
||||||
|
"!="
|
||||||
|
"&&"
|
||||||
|
"||"
|
||||||
|
"+"
|
||||||
|
"*"
|
||||||
|
"-"
|
||||||
|
"//"
|
||||||
|
"/"
|
||||||
|
"<="
|
||||||
|
"<"
|
||||||
|
">="
|
||||||
|
">"
|
||||||
|
"^"
|
||||||
|
"%%"
|
||||||
|
"%"
|
||||||
|
|
||||||
|
"->"
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#![no_main]
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use roc_parse::test_helpers::parse_defs_with;
|
||||||
|
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
if let Ok(input) = std::str::from_utf8(data) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let _actual = parse_defs_with(&arena, input.trim());
|
||||||
|
}
|
||||||
|
});
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#![no_main]
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use roc_parse::test_helpers::parse_expr_with;
|
||||||
|
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
if let Ok(input) = std::str::from_utf8(data) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let _actual = parse_expr_with(&arena, input.trim());
|
||||||
|
}
|
||||||
|
});
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_header.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_header.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#![no_main]
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
|
use roc_parse::test_helpers::parse_header_with;
|
||||||
|
|
||||||
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
if let Ok(input) = std::str::from_utf8(data) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let _actual = parse_header_with(&arena, input.trim());
|
||||||
|
}
|
||||||
|
});
|
|
@ -277,6 +277,8 @@ pub enum Def<'a> {
|
||||||
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
|
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
|
||||||
/// It lets us take a (&Def) and create a plain (Def) from it.
|
/// It lets us take a (&Def) and create a plain (Def) from it.
|
||||||
Nested(&'a Def<'a>),
|
Nested(&'a Def<'a>),
|
||||||
|
|
||||||
|
NotYetImplemented(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
@ -317,12 +317,27 @@ fn spaces<'a>(
|
||||||
'\n' => {
|
'\n' => {
|
||||||
state = state.newline()?;
|
state = state.newline()?;
|
||||||
|
|
||||||
// This was a newline, so end this line comment.
|
match (comment_line_buf.len(), comment_line_buf.chars().next())
|
||||||
space_list.push(LineComment(comment_line_buf.into_bump_str()));
|
{
|
||||||
|
(1, Some('#')) => {
|
||||||
|
// This is a line with `##` - that is,
|
||||||
|
// a doc comment new line.
|
||||||
|
space_list.push(DocComment(""));
|
||||||
comment_line_buf = String::new_in(arena);
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
line_state = LineState::Normal;
|
line_state = LineState::Normal;
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
// This was a newline, so end this line comment.
|
||||||
|
space_list.push(LineComment(
|
||||||
|
comment_line_buf.into_bump_str(),
|
||||||
|
));
|
||||||
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
|
line_state = LineState::Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
nonblank => {
|
nonblank => {
|
||||||
// Chars can have btye lengths of more than 1!
|
// Chars can have btye lengths of more than 1!
|
||||||
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
state = state.advance_without_indenting(nonblank.len_utf8())?;
|
||||||
|
|
|
@ -561,7 +561,7 @@ fn annotation_or_alias<'a>(
|
||||||
ann: loc_ann,
|
ann: loc_ann,
|
||||||
},
|
},
|
||||||
Apply(_, _) => {
|
Apply(_, _) => {
|
||||||
panic!("TODO gracefully handle invalid Apply in type annotation");
|
Def::NotYetImplemented("TODO gracefully handle invalid Apply in type annotation")
|
||||||
}
|
}
|
||||||
SpaceAfter(value, spaces_before) => Def::SpaceAfter(
|
SpaceAfter(value, spaces_before) => Def::SpaceAfter(
|
||||||
arena.alloc(annotation_or_alias(arena, value, region, loc_ann)),
|
arena.alloc(annotation_or_alias(arena, value, region, loc_ann)),
|
||||||
|
@ -574,19 +574,19 @@ fn annotation_or_alias<'a>(
|
||||||
Nested(value) => annotation_or_alias(arena, value, region, loc_ann),
|
Nested(value) => annotation_or_alias(arena, value, region, loc_ann),
|
||||||
|
|
||||||
PrivateTag(_) => {
|
PrivateTag(_) => {
|
||||||
panic!("TODO gracefully handle trying to use a private tag as an annotation.");
|
Def::NotYetImplemented("TODO gracefully handle trying to use a private tag as an annotation.")
|
||||||
}
|
}
|
||||||
QualifiedIdentifier { .. } => {
|
QualifiedIdentifier { .. } => {
|
||||||
panic!("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`");
|
Def::NotYetImplemented("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`")
|
||||||
}
|
}
|
||||||
NumLiteral(_) | NonBase10Literal { .. } | FloatLiteral(_) | StrLiteral(_) => {
|
NumLiteral(_) | NonBase10Literal { .. } | FloatLiteral(_) | StrLiteral(_) => {
|
||||||
panic!("TODO gracefully handle trying to annotate a litera");
|
Def::NotYetImplemented("TODO gracefully handle trying to annotate a litera")
|
||||||
}
|
}
|
||||||
Underscore => {
|
Underscore => {
|
||||||
panic!("TODO gracefully handle trying to give a type annotation to an undrscore");
|
Def::NotYetImplemented("TODO gracefully handle trying to give a type annotation to an undrscore")
|
||||||
}
|
}
|
||||||
Malformed(_) => {
|
Malformed(_) => {
|
||||||
panic!("TODO translate a malformed pattern into a malformed annotation");
|
Def::NotYetImplemented("TODO translate a malformed pattern into a malformed annotation")
|
||||||
}
|
}
|
||||||
Identifier(ident) => {
|
Identifier(ident) => {
|
||||||
// This is a regular Annotation
|
// This is a regular Annotation
|
||||||
|
@ -633,7 +633,13 @@ fn parse_def_expr<'a>(
|
||||||
))
|
))
|
||||||
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
||||||
} else if equals_sign_indent < def_start_col {
|
} else if equals_sign_indent < def_start_col {
|
||||||
todo!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col);
|
Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(format!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col)),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
// Indented more beyond the original indent of the entire def-expr.
|
// Indented more beyond the original indent of the entire def-expr.
|
||||||
let indented_more = def_start_col + 1;
|
let indented_more = def_start_col + 1;
|
||||||
|
@ -720,7 +726,15 @@ fn parse_def_signature<'a>(
|
||||||
))
|
))
|
||||||
// `<` because ':' should be same indent or greater
|
// `<` because ':' should be same indent or greater
|
||||||
} else if colon_indent < original_indent {
|
} else if colon_indent < original_indent {
|
||||||
panic!("TODO the : in this declaration seems outdented");
|
Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(
|
||||||
|
"TODO the : in this declaration seems outdented".to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
// Indented more beyond the original indent.
|
// Indented more beyond the original indent.
|
||||||
let indented_more = original_indent + 1;
|
let indented_more = original_indent + 1;
|
||||||
|
@ -1069,11 +1083,12 @@ fn loc_ident_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>
|
||||||
} else {
|
} else {
|
||||||
format!("{}.{}", module_name, parts.join("."))
|
format!("{}.{}", module_name, parts.join("."))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Located {
|
Located {
|
||||||
region: loc_ident.region,
|
region: loc_ident.region,
|
||||||
value: Pattern::Malformed(arena.alloc(malformed_str)),
|
value: Pattern::Malformed(
|
||||||
|
String::from_str_in(&malformed_str, &arena).into_bump_str(),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
|
@ -1120,7 +1135,15 @@ mod when {
|
||||||
),
|
),
|
||||||
move |arena, state, (case_indent, loc_condition)| {
|
move |arena, state, (case_indent, loc_condition)| {
|
||||||
if case_indent < min_indent {
|
if case_indent < min_indent {
|
||||||
panic!("TODO case wasn't indented enough");
|
return Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(
|
||||||
|
"TODO case wasn't indented enough".to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything in the branches must be indented at least as much as the case itself.
|
// Everything in the branches must be indented at least as much as the case itself.
|
||||||
|
@ -1178,9 +1201,15 @@ mod when {
|
||||||
if alternatives_indented_correctly(&loc_patterns, original_indent) {
|
if alternatives_indented_correctly(&loc_patterns, original_indent) {
|
||||||
Ok(((loc_patterns, loc_guard), state))
|
Ok(((loc_patterns, loc_guard), state))
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
Err((
|
||||||
"TODO additional branch didn't have same indentation as first branch"
|
Fail {
|
||||||
);
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(
|
||||||
|
"TODO additional branch didn't have same indentation as first branch".to_string(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1490,10 +1519,16 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(malformed) => {
|
Err(malformed) => {
|
||||||
panic!(
|
return Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(format!(
|
||||||
"TODO early return malformed pattern {:?}",
|
"TODO early return malformed pattern {:?}",
|
||||||
malformed
|
malformed
|
||||||
);
|
)),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,5 @@ pub mod number_literal;
|
||||||
pub mod pattern;
|
pub mod pattern;
|
||||||
pub mod problems;
|
pub mod problems;
|
||||||
pub mod string_literal;
|
pub mod string_literal;
|
||||||
|
pub mod test_helpers;
|
||||||
pub mod type_annotation;
|
pub mod type_annotation;
|
||||||
|
|
|
@ -224,6 +224,7 @@ pub enum FailReason {
|
||||||
BadUtf8,
|
BadUtf8,
|
||||||
ReservedKeyword(Region),
|
ReservedKeyword(Region),
|
||||||
ArgumentsBeforeEquals(Region),
|
ArgumentsBeforeEquals(Region),
|
||||||
|
NotYetImplemented(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment};
|
use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment};
|
||||||
use crate::expr;
|
use crate::expr;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof,
|
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Fail,
|
||||||
ParseResult, Parser, State,
|
FailReason, ParseResult, Parser, State,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -279,7 +279,16 @@ where
|
||||||
// lines.push(line);
|
// lines.push(line);
|
||||||
|
|
||||||
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
||||||
todo!("TODO parse this line in a block string: {:?}", line);
|
Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented(format!(
|
||||||
|
"TODO parse this line in a block string: {:?}",
|
||||||
|
line
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Err(reason) => state.fail(reason),
|
Err(reason) => state.fail(reason),
|
||||||
};
|
};
|
||||||
|
|
45
compiler/parse/src/test_helpers.rs
Normal file
45
compiler/parse/src/test_helpers.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use crate::ast::{self, Attempting};
|
||||||
|
use crate::blankspace::space0_before;
|
||||||
|
use crate::expr::expr;
|
||||||
|
use crate::module::{header, module_defs};
|
||||||
|
use crate::parser::{loc, Fail, Parser, State};
|
||||||
|
use bumpalo::collections::Vec;
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_region::all::Located;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||||
|
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Fail> {
|
||||||
|
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||||
|
let answer = header().parse(arena, state);
|
||||||
|
answer
|
||||||
|
.map(|(loc_expr, _)| loc_expr)
|
||||||
|
.map_err(|(fail, _)| fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse_defs_with<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
input: &'a str,
|
||||||
|
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Fail> {
|
||||||
|
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||||
|
let answer = module_defs().parse(arena, state);
|
||||||
|
answer
|
||||||
|
.map(|(loc_expr, _)| loc_expr)
|
||||||
|
.map_err(|(fail, _)| fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||||
|
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||||
|
let parser = space0_before(loc(expr(0)), 0);
|
||||||
|
let answer = parser.parse(&arena, state);
|
||||||
|
|
||||||
|
answer
|
||||||
|
.map(|(loc_expr, _)| loc_expr)
|
||||||
|
.map_err(|(fail, _)| fail)
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ use crate::expr::{global_tag, private_tag};
|
||||||
use crate::ident::join_module_parts;
|
use crate::ident::join_module_parts;
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either,
|
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either, Fail,
|
||||||
ParseResult, Parser, State,
|
FailReason, ParseResult, Parser, State,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
|
@ -239,7 +239,13 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
||||||
Ok((first, state))
|
Ok((first, state))
|
||||||
} else {
|
} else {
|
||||||
// e.g. `Int,Int` without an arrow and return type
|
// e.g. `Int,Int` without an arrow and return type
|
||||||
panic!("Invalid function signature")
|
Err((
|
||||||
|
Fail {
|
||||||
|
attempting: state.attempting,
|
||||||
|
reason: FailReason::NotYetImplemented("TODO: Decide the correct error to return for 'Invalid function signature'".to_string()),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
extern crate bumpalo;
|
|
||||||
|
|
||||||
use self::bumpalo::Bump;
|
|
||||||
use roc_parse::ast::{self, Attempting};
|
|
||||||
use roc_parse::blankspace::space0_before;
|
|
||||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
|
||||||
use roc_region::all::Located;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
|
||||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
|
||||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
|
||||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
|
||||||
let answer = parser.parse(&arena, state);
|
|
||||||
|
|
||||||
answer
|
|
||||||
.map(|(loc_expr, _)| loc_expr)
|
|
||||||
.map_err(|(fail, _)| fail)
|
|
||||||
}
|
|
|
@ -11,11 +11,8 @@ extern crate quickcheck_macros;
|
||||||
extern crate roc_module;
|
extern crate roc_module;
|
||||||
extern crate roc_parse;
|
extern crate roc_parse;
|
||||||
|
|
||||||
mod helpers;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_parse {
|
mod test_parse {
|
||||||
use crate::helpers::parse_with;
|
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::{self, Bump};
|
use bumpalo::{self, Bump};
|
||||||
use roc_module::operator::BinOp::*;
|
use roc_module::operator::BinOp::*;
|
||||||
|
@ -33,19 +30,20 @@ mod test_parse {
|
||||||
use roc_parse::header::ModuleName;
|
use roc_parse::header::ModuleName;
|
||||||
use roc_parse::module::{interface_header, module_defs};
|
use roc_parse::module::{interface_header, module_defs};
|
||||||
use roc_parse::parser::{Fail, FailReason, Parser, State};
|
use roc_parse::parser::{Fail, FailReason, Parser, State};
|
||||||
|
use roc_parse::test_helpers::parse_expr_with;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use std::{f64, i64};
|
use std::{f64, i64};
|
||||||
|
|
||||||
fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) {
|
fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual = parse_with(&arena, input.trim());
|
let actual = parse_expr_with(&arena, input.trim());
|
||||||
|
|
||||||
assert_eq!(Ok(expected_expr), actual);
|
assert_eq!(Ok(expected_expr), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_parsing_fails<'a>(input: &'a str, reason: FailReason, attempting: Attempting) {
|
fn assert_parsing_fails<'a>(input: &'a str, reason: FailReason, attempting: Attempting) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual = parse_with(&arena, input);
|
let actual = parse_expr_with(&arena, input);
|
||||||
let expected_fail = Fail { reason, attempting };
|
let expected_fail = Fail { reason, attempting };
|
||||||
|
|
||||||
assert_eq!(Err(expected_fail), actual);
|
assert_eq!(Err(expected_fail), actual);
|
||||||
|
@ -53,7 +51,7 @@ mod test_parse {
|
||||||
|
|
||||||
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
|
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual = parse_with(&arena, arena.alloc(input));
|
let actual = parse_expr_with(&arena, arena.alloc(input));
|
||||||
let expected_slice = to_expected(&arena);
|
let expected_slice = to_expected(&arena);
|
||||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||||
|
|
||||||
|
@ -77,7 +75,7 @@ mod test_parse {
|
||||||
("\\t", EscapedChar::Tab),
|
("\\t", EscapedChar::Tab),
|
||||||
("\\\"", EscapedChar::Quote),
|
("\\\"", EscapedChar::Quote),
|
||||||
] {
|
] {
|
||||||
let actual = parse_with(&arena, arena.alloc(to_input(string)));
|
let actual = parse_expr_with(&arena, arena.alloc(to_input(string)));
|
||||||
let expected_slice = to_expected(*escaped, &arena);
|
let expected_slice = to_expected(*escaped, &arena);
|
||||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||||
|
|
||||||
|
@ -423,7 +421,7 @@ mod test_parse {
|
||||||
fields: &[],
|
fields: &[],
|
||||||
update: None,
|
update: None,
|
||||||
};
|
};
|
||||||
let actual = parse_with(&arena, "{}");
|
let actual = parse_expr_with(&arena, "{}");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -455,7 +453,7 @@ mod test_parse {
|
||||||
fields,
|
fields,
|
||||||
};
|
};
|
||||||
|
|
||||||
let actual = parse_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
|
let actual = parse_expr_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +468,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 2, 3, Num("2")),
|
Located::new(0, 0, 2, 3, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1+2");
|
let actual = parse_expr_with(&arena, "1+2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -484,7 +482,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 2, 3, Num("2")),
|
Located::new(0, 0, 2, 3, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1-2");
|
let actual = parse_expr_with(&arena, "1-2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -498,7 +496,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 7, 8, Num("2")),
|
Located::new(0, 0, 7, 8, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1 + 2");
|
let actual = parse_expr_with(&arena, "1 + 2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -512,7 +510,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 7, 8, Num("2")),
|
Located::new(0, 0, 7, 8, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "1 - 2");
|
let actual = parse_expr_with(&arena, "1 - 2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -535,7 +533,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 4, 5, Num("2")),
|
Located::new(0, 0, 4, 5, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x + 2");
|
let actual = parse_expr_with(&arena, "x + 2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -557,7 +555,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 4, 5, Num("2")),
|
Located::new(0, 0, 4, 5, Num("2")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x - 2");
|
let actual = parse_expr_with(&arena, "x - 2");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -572,7 +570,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, Num("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n+ 4");
|
let actual = parse_expr_with(&arena, "3 \n+ 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -587,7 +585,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, Num("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n- 4");
|
let actual = parse_expr_with(&arena, "3 \n- 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -602,7 +600,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, spaced_int),
|
Located::new(1, 1, 2, 3, spaced_int),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 *\n 4");
|
let actual = parse_expr_with(&arena, "3 *\n 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -617,7 +615,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, spaced_int),
|
Located::new(1, 1, 2, 3, spaced_int),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 -\n 4");
|
let actual = parse_expr_with(&arena, "3 -\n 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -632,7 +630,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, Num("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 # 2 × 2\n+ 4");
|
let actual = parse_expr_with(&arena, "3 # 2 × 2\n+ 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -647,7 +645,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 2, 3, Num("4")),
|
Located::new(1, 1, 2, 3, Num("4")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
let actual = parse_expr_with(&arena, "3 # test!\n+ 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -662,7 +660,7 @@ mod test_parse {
|
||||||
Located::new(1, 1, 1, 3, spaced_int),
|
Located::new(1, 1, 1, 3, spaced_int),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "12 * # test!\n 92");
|
let actual = parse_expr_with(&arena, "12 * # test!\n 92");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -678,7 +676,7 @@ mod test_parse {
|
||||||
Located::new(3, 3, 2, 3, spaced_int2),
|
Located::new(3, 3, 2, 3, spaced_int2),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "3 \n+ \n\n 4");
|
let actual = parse_expr_with(&arena, "3 \n+ \n\n 4");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -702,7 +700,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 4, var2),
|
Located::new(0, 0, 3, 4, var2),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x- y");
|
let actual = parse_expr_with(&arena, "x- y");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -716,7 +714,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 4, 5, Num("5")),
|
Located::new(0, 0, 4, 5, Num("5")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "-12-5");
|
let actual = parse_expr_with(&arena, "-12-5");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -730,7 +728,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 5, Num("11")),
|
Located::new(0, 0, 3, 5, Num("11")),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "10*11");
|
let actual = parse_expr_with(&arena, "10*11");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -749,7 +747,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 9, BinOp(inner)),
|
Located::new(0, 0, 3, 9, BinOp(inner)),
|
||||||
));
|
));
|
||||||
let expected = BinOp(outer);
|
let expected = BinOp(outer);
|
||||||
let actual = parse_with(&arena, "31*42+534");
|
let actual = parse_expr_with(&arena, "31*42+534");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -771,7 +769,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 3, 4, var2),
|
Located::new(0, 0, 3, 4, var2),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x==y");
|
let actual = parse_expr_with(&arena, "x==y");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -793,7 +791,7 @@ mod test_parse {
|
||||||
Located::new(0, 0, 5, 6, var2),
|
Located::new(0, 0, 5, 6, var2),
|
||||||
));
|
));
|
||||||
let expected = BinOp(tuple);
|
let expected = BinOp(tuple);
|
||||||
let actual = parse_with(&arena, "x == y");
|
let actual = parse_expr_with(&arena, "x == y");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -807,7 +805,7 @@ mod test_parse {
|
||||||
module_name: "",
|
module_name: "",
|
||||||
ident: "whee",
|
ident: "whee",
|
||||||
};
|
};
|
||||||
let actual = parse_with(&arena, "whee");
|
let actual = parse_expr_with(&arena, "whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -819,7 +817,7 @@ mod test_parse {
|
||||||
module_name: "",
|
module_name: "",
|
||||||
ident: "whee",
|
ident: "whee",
|
||||||
}));
|
}));
|
||||||
let actual = parse_with(&arena, "(whee)");
|
let actual = parse_expr_with(&arena, "(whee)");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -831,7 +829,7 @@ mod test_parse {
|
||||||
module_name: "One.Two",
|
module_name: "One.Two",
|
||||||
ident: "whee",
|
ident: "whee",
|
||||||
};
|
};
|
||||||
let actual = parse_with(&arena, "One.Two.whee");
|
let actual = parse_expr_with(&arena, "One.Two.whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -842,7 +840,7 @@ mod test_parse {
|
||||||
fn basic_global_tag() {
|
fn basic_global_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let expected = Expr::GlobalTag("Whee");
|
let expected = Expr::GlobalTag("Whee");
|
||||||
let actual = parse_with(&arena, "Whee");
|
let actual = parse_expr_with(&arena, "Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -851,7 +849,7 @@ mod test_parse {
|
||||||
fn basic_private_tag() {
|
fn basic_private_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let expected = Expr::PrivateTag("@Whee");
|
let expected = Expr::PrivateTag("@Whee");
|
||||||
let actual = parse_with(&arena, "@Whee");
|
let actual = parse_expr_with(&arena, "@Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -867,7 +865,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "@Whee 12 34");
|
let actual = parse_expr_with(&arena, "@Whee 12 34");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -883,7 +881,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "Whee 12 34");
|
let actual = parse_expr_with(&arena, "Whee 12 34");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -901,7 +899,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "Whee (12) (34)");
|
let actual = parse_expr_with(&arena, "Whee (12) (34)");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -910,7 +908,7 @@ mod test_parse {
|
||||||
fn qualified_global_tag() {
|
fn qualified_global_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let expected = Expr::MalformedIdent("One.Two.Whee");
|
let expected = Expr::MalformedIdent("One.Two.Whee");
|
||||||
let actual = parse_with(&arena, "One.Two.Whee");
|
let actual = parse_expr_with(&arena, "One.Two.Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -920,7 +918,7 @@ mod test_parse {
|
||||||
// fn qualified_private_tag() {
|
// fn qualified_private_tag() {
|
||||||
// let arena = Bump::new();
|
// let arena = Bump::new();
|
||||||
// let expected = Expr::MalformedIdent("One.Two.@Whee");
|
// let expected = Expr::MalformedIdent("One.Two.@Whee");
|
||||||
// let actual = parse_with(&arena, "One.Two.@Whee");
|
// let actual = parse_expr_with(&arena, "One.Two.@Whee");
|
||||||
|
|
||||||
// assert_eq!(Ok(expected), actual);
|
// assert_eq!(Ok(expected), actual);
|
||||||
// }
|
// }
|
||||||
|
@ -931,7 +929,7 @@ mod test_parse {
|
||||||
let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing"));
|
let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing"));
|
||||||
let patterns = &[pattern];
|
let patterns = &[pattern];
|
||||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42"))));
|
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42"))));
|
||||||
let actual = parse_with(&arena, "\\Thing -> 42");
|
let actual = parse_expr_with(&arena, "\\Thing -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -940,7 +938,7 @@ mod test_parse {
|
||||||
fn private_qualified_tag() {
|
fn private_qualified_tag() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let expected = Expr::MalformedIdent("@One.Two.Whee");
|
let expected = Expr::MalformedIdent("@One.Two.Whee");
|
||||||
let actual = parse_with(&arena, "@One.Two.Whee");
|
let actual = parse_expr_with(&arena, "@One.Two.Whee");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -952,7 +950,7 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = &[];
|
let elems = &[];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[]");
|
let actual = parse_expr_with(&arena, "[]");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -963,7 +961,7 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = &[];
|
let elems = &[];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[ ]");
|
let actual = parse_expr_with(&arena, "[ ]");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -973,7 +971,7 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[1]");
|
let actual = parse_expr_with(&arena, "[1]");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -983,7 +981,7 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||||
let expected = List(elems);
|
let expected = List(elems);
|
||||||
let actual = parse_with(&arena, "[ 1 ]");
|
let actual = parse_expr_with(&arena, "[ 1 ]");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -998,7 +996,7 @@ mod test_parse {
|
||||||
ident: "rec",
|
ident: "rec",
|
||||||
};
|
};
|
||||||
let expected = Access(arena.alloc(var), "field");
|
let expected = Access(arena.alloc(var), "field");
|
||||||
let actual = parse_with(&arena, "rec.field");
|
let actual = parse_expr_with(&arena, "rec.field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1011,7 +1009,7 @@ mod test_parse {
|
||||||
ident: "rec",
|
ident: "rec",
|
||||||
}));
|
}));
|
||||||
let expected = Access(arena.alloc(paren_var), "field");
|
let expected = Access(arena.alloc(paren_var), "field");
|
||||||
let actual = parse_with(&arena, "(rec).field");
|
let actual = parse_expr_with(&arena, "(rec).field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1024,7 +1022,7 @@ mod test_parse {
|
||||||
ident: "rec",
|
ident: "rec",
|
||||||
}));
|
}));
|
||||||
let expected = Access(arena.alloc(paren_var), "field");
|
let expected = Access(arena.alloc(paren_var), "field");
|
||||||
let actual = parse_with(&arena, "(One.Two.rec).field");
|
let actual = parse_expr_with(&arena, "(One.Two.rec).field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1040,7 +1038,7 @@ mod test_parse {
|
||||||
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
||||||
"ghi",
|
"ghi",
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "rec.abc.def.ghi");
|
let actual = parse_expr_with(&arena, "rec.abc.def.ghi");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1056,7 +1054,7 @@ mod test_parse {
|
||||||
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
||||||
"ghi",
|
"ghi",
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi");
|
let actual = parse_expr_with(&arena, "One.Two.rec.abc.def.ghi");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1077,7 +1075,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "whee 1");
|
let actual = parse_expr_with(&arena, "whee 1");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1100,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "whee 12 34");
|
let actual = parse_expr_with(&arena, "whee 12 34");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1155,7 +1153,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "a b c d");
|
let actual = parse_expr_with(&arena, "a b c d");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1174,7 +1172,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "(whee) 1");
|
let actual = parse_expr_with(&arena, "(whee) 1");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1191,7 +1189,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_arg1_expr = Located::new(0, 0, 1, 4, arg1_expr);
|
let loc_arg1_expr = Located::new(0, 0, 1, 4, arg1_expr);
|
||||||
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
||||||
let actual = parse_with(&arena, "-foo");
|
let actual = parse_expr_with(&arena, "-foo");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1206,7 +1204,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_arg1_expr = Located::new(0, 0, 1, 5, arg1_expr);
|
let loc_arg1_expr = Located::new(0, 0, 1, 5, arg1_expr);
|
||||||
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
||||||
let actual = parse_with(&arena, "!blah");
|
let actual = parse_expr_with(&arena, "!blah");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1242,7 +1240,7 @@ mod test_parse {
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
||||||
let actual = parse_with(&arena, "-whee 12 foo");
|
let actual = parse_expr_with(&arena, "-whee 12 foo");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1276,7 @@ mod test_parse {
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
||||||
let actual = parse_with(&arena, "!whee 12 foo");
|
let actual = parse_expr_with(&arena, "!whee 12 foo");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1314,7 +1312,7 @@ mod test_parse {
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
)));
|
)));
|
||||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
||||||
let actual = parse_with(&arena, "-(whee 12 foo)");
|
let actual = parse_expr_with(&arena, "-(whee 12 foo)");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1350,7 +1348,7 @@ mod test_parse {
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
)));
|
)));
|
||||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
||||||
let actual = parse_with(&arena, "!(whee 12 foo)");
|
let actual = parse_expr_with(&arena, "!(whee 12 foo)");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1377,7 +1375,7 @@ mod test_parse {
|
||||||
args,
|
args,
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "whee 12 -foo");
|
let actual = parse_expr_with(&arena, "whee 12 -foo");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1394,7 +1392,7 @@ mod test_parse {
|
||||||
let access = Access(arena.alloc(var), "field");
|
let access = Access(arena.alloc(var), "field");
|
||||||
let loc_access = Located::new(0, 0, 1, 11, access);
|
let loc_access = Located::new(0, 0, 1, 11, access);
|
||||||
let expected = UnaryOp(arena.alloc(loc_access), loc_op);
|
let expected = UnaryOp(arena.alloc(loc_access), loc_op);
|
||||||
let actual = parse_with(&arena, "-rec1.field");
|
let actual = parse_expr_with(&arena, "-rec1.field");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1407,7 +1405,7 @@ mod test_parse {
|
||||||
let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
|
let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||||
let patterns = &[pattern];
|
let patterns = &[pattern];
|
||||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||||
let actual = parse_with(&arena, "\\a -> 42");
|
let actual = parse_expr_with(&arena, "\\a -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1418,7 +1416,7 @@ mod test_parse {
|
||||||
let pattern = Located::new(0, 0, 1, 2, Underscore);
|
let pattern = Located::new(0, 0, 1, 2, Underscore);
|
||||||
let patterns = &[pattern];
|
let patterns = &[pattern];
|
||||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||||
let actual = parse_with(&arena, "\\_ -> 42");
|
let actual = parse_expr_with(&arena, "\\_ -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1429,7 +1427,7 @@ mod test_parse {
|
||||||
// underscore in an argument name, it would parse as three arguments
|
// underscore in an argument name, it would parse as three arguments
|
||||||
// (and would ignore the underscore as if it had been blank space).
|
// (and would ignore the underscore as if it had been blank space).
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual = parse_with(&arena, "\\the_answer -> 42");
|
let actual = parse_expr_with(&arena, "\\the_answer -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(MalformedClosure), actual);
|
assert_eq!(Ok(MalformedClosure), actual);
|
||||||
}
|
}
|
||||||
|
@ -1441,7 +1439,7 @@ mod test_parse {
|
||||||
let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
|
let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
|
||||||
let patterns = &[arg1, arg2];
|
let patterns = &[arg1, arg2];
|
||||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42"))));
|
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42"))));
|
||||||
let actual = parse_with(&arena, "\\a, b -> 42");
|
let actual = parse_expr_with(&arena, "\\a, b -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1457,7 +1455,7 @@ mod test_parse {
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 12, 14, Num("42"))),
|
arena.alloc(Located::new(0, 0, 12, 14, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\a, b, c -> 42");
|
let actual = parse_expr_with(&arena, "\\a, b, c -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -1472,7 +1470,7 @@ mod test_parse {
|
||||||
arena.alloc(patterns),
|
arena.alloc(patterns),
|
||||||
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
||||||
);
|
);
|
||||||
let actual = parse_with(&arena, "\\_, _ -> 42");
|
let actual = parse_expr_with(&arena, "\\_, _ -> 42");
|
||||||
|
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
@ -2040,7 +2038,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -2084,7 +2082,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -2133,7 +2131,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -2183,7 +2181,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -2347,18 +2345,20 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let newlines = &[Newline, Newline];
|
let newlines = &[Newline, Newline];
|
||||||
let def = Def::Body(
|
let def = Def::Body(
|
||||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
arena.alloc(Located::new(6, 6, 0, 1, Identifier("x"))),
|
||||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
arena.alloc(Located::new(6, 6, 4, 5, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def));
|
let loc_def = &*arena.alloc(Located::new(6, 6, 0, 1, def));
|
||||||
let defs = &[loc_def];
|
let defs = &[loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||||
let loc_ret = Located::new(6, 6, 0, 2, ret);
|
let loc_ret = Located::new(8, 8, 0, 2, ret);
|
||||||
let reset_indentation = &[
|
let reset_indentation = &[
|
||||||
DocComment("first line of docs"),
|
DocComment("first line of docs"),
|
||||||
DocComment(" second line"),
|
DocComment(" second line"),
|
||||||
DocComment(" third line"),
|
DocComment(" third line"),
|
||||||
DocComment("fourth line"),
|
DocComment("fourth line"),
|
||||||
|
DocComment(""),
|
||||||
|
DocComment("sixth line after doc new line"),
|
||||||
];
|
];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||||
|
@ -2372,6 +2372,8 @@ mod test_parse {
|
||||||
## second line
|
## second line
|
||||||
## third line
|
## third line
|
||||||
## fourth line
|
## fourth line
|
||||||
|
##
|
||||||
|
## sixth line after doc new line
|
||||||
x = 5
|
x = 5
|
||||||
|
|
||||||
42
|
42
|
||||||
|
@ -2490,7 +2492,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -2535,7 +2537,7 @@ mod test_parse {
|
||||||
};
|
};
|
||||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
let actual = parse_with(
|
let actual = parse_expr_with(
|
||||||
&arena,
|
&arena,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -40,6 +40,12 @@ pub enum Problem {
|
||||||
field_region: Region,
|
field_region: Region,
|
||||||
replaced_region: Region,
|
replaced_region: Region,
|
||||||
},
|
},
|
||||||
|
InvalidOptionalValue {
|
||||||
|
field_name: Lowercase,
|
||||||
|
record_region: Region,
|
||||||
|
field_region: Region,
|
||||||
|
},
|
||||||
|
|
||||||
DuplicateTag {
|
DuplicateTag {
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
tag_union_region: Region,
|
tag_union_region: Region,
|
||||||
|
@ -101,6 +107,11 @@ pub enum RuntimeError {
|
||||||
original_region: Region,
|
original_region: Region,
|
||||||
shadow: Located<Ident>,
|
shadow: Located<Ident>,
|
||||||
},
|
},
|
||||||
|
InvalidOptionalValue {
|
||||||
|
field_name: Lowercase,
|
||||||
|
record_region: Region,
|
||||||
|
field_region: Region,
|
||||||
|
},
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
// Example: when 1 is 1.X -> 32
|
// Example: when 1 is 1.X -> 32
|
||||||
|
|
|
@ -179,6 +179,25 @@ pub fn can_problem<'b>(
|
||||||
alloc.reflow(" definitions from this record."),
|
alloc.reflow(" definitions from this record."),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
Problem::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This record uses an optional value for the "),
|
||||||
|
alloc.record_field(field_name),
|
||||||
|
alloc.reflow(" field in an incorrect context!"),
|
||||||
|
]),
|
||||||
|
alloc.region_all_the_things(
|
||||||
|
record_region,
|
||||||
|
field_region,
|
||||||
|
field_region,
|
||||||
|
Annotation::Error,
|
||||||
|
),
|
||||||
|
alloc.reflow("You can only use optional values in record destructuring, for example in affectation:"),
|
||||||
|
alloc.reflow("{ answer ? 42, otherField } = myRecord").indent(4),
|
||||||
|
]),
|
||||||
Problem::DuplicateRecordFieldType {
|
Problem::DuplicateRecordFieldType {
|
||||||
field_name,
|
field_name,
|
||||||
field_region,
|
field_region,
|
||||||
|
@ -534,6 +553,25 @@ fn pretty_runtime_error<'b>(
|
||||||
tip,
|
tip,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
RuntimeError::InvalidOptionalValue {
|
||||||
|
field_name,
|
||||||
|
field_region,
|
||||||
|
record_region,
|
||||||
|
} => alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This record uses an optional value for the "),
|
||||||
|
alloc.record_field(field_name),
|
||||||
|
alloc.reflow(" field in an incorrect context!"),
|
||||||
|
]),
|
||||||
|
alloc.region_all_the_things(
|
||||||
|
record_region,
|
||||||
|
field_region,
|
||||||
|
field_region,
|
||||||
|
Annotation::Error,
|
||||||
|
),
|
||||||
|
alloc.reflow("You can only use optional values in record destructuring, for exemple in affectation:"),
|
||||||
|
alloc.reflow("{ answer ? 42, otherField } = myRecord"),
|
||||||
|
]),
|
||||||
RuntimeError::InvalidRecordUpdate { region } => alloc.stack(vec![
|
RuntimeError::InvalidRecordUpdate { region } => alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow("This expression cannot be updated"),
|
alloc.reflow("This expression cannot be updated"),
|
||||||
|
|
|
@ -95,8 +95,13 @@ mod test_reporting {
|
||||||
home,
|
home,
|
||||||
ident_ids: &mut ident_ids,
|
ident_ids: &mut ident_ids,
|
||||||
};
|
};
|
||||||
let _mono_expr =
|
let _mono_expr = Stmt::new(
|
||||||
Stmt::new(&mut mono_env, loc_expr.value, &mut procs, &mut layout_cache);
|
&mut mono_env,
|
||||||
|
loc_expr.value,
|
||||||
|
var,
|
||||||
|
&mut procs,
|
||||||
|
&mut layout_cache,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((unify_problems, can_problems, mono_problems, home, interns))
|
Ok((unify_problems, can_problems, mono_problems, home, interns))
|
||||||
|
@ -3806,6 +3811,32 @@ mod test_reporting {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incorrect_optional_field() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 5, y ? 42 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This record uses an optional value for the `.y` field in an incorrect
|
||||||
|
context!
|
||||||
|
|
||||||
|
1│ { x: 5, y ? 42 }
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
You can only use optional values in record destructuring, for example
|
||||||
|
in affectation:
|
||||||
|
|
||||||
|
{ answer ? 42, otherField } = myRecord
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn first_wildcard_is_required() {
|
fn first_wildcard_is_required() {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
|
|
|
@ -813,6 +813,45 @@ fn type_to_variable(
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
name: symbol,
|
||||||
|
arguments: args,
|
||||||
|
actual: alias_type,
|
||||||
|
actual_var,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
|
let mut new_aliases = ImMap::default();
|
||||||
|
|
||||||
|
for (arg, arg_type) in args {
|
||||||
|
let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
|
||||||
|
|
||||||
|
arg_vars.push((arg.clone(), arg_var));
|
||||||
|
new_aliases.insert(arg.clone(), arg_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
|
||||||
|
|
||||||
|
// unify the actual_var with the result var
|
||||||
|
// this can be used to access the type of the actual_var
|
||||||
|
// to determine its layout later
|
||||||
|
// subs.set_content(*actual_var, descriptor.content);
|
||||||
|
|
||||||
|
//subs.set(*actual_var, descriptor.clone());
|
||||||
|
let content = Content::Alias(*symbol, arg_vars, alias_var);
|
||||||
|
|
||||||
|
let result = register(subs, rank, pools, content);
|
||||||
|
|
||||||
|
// We only want to unify the actual_var with the alias once
|
||||||
|
// if it's already redirected (and therefore, redundant)
|
||||||
|
// don't do it again
|
||||||
|
if !subs.redundant(*actual_var) {
|
||||||
|
let descriptor = subs.get(result);
|
||||||
|
subs.union(result, *actual_var, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
Erroneous(problem) => {
|
Erroneous(problem) => {
|
||||||
let content = Content::Structure(FlatType::Erroneous(problem.clone()));
|
let content = Content::Structure(FlatType::Erroneous(problem.clone()));
|
||||||
|
|
||||||
|
|
|
@ -75,13 +75,16 @@ mod solve_expr {
|
||||||
let LoadedModule {
|
let LoadedModule {
|
||||||
module_id: home,
|
module_id: home,
|
||||||
mut can_problems,
|
mut can_problems,
|
||||||
type_problems,
|
mut type_problems,
|
||||||
interns,
|
interns,
|
||||||
mut solved,
|
mut solved,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
..
|
..
|
||||||
} = loaded;
|
} = loaded;
|
||||||
|
|
||||||
|
let mut can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||||
|
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||||
|
|
||||||
let mut subs = solved.inner_mut();
|
let mut subs = solved.inner_mut();
|
||||||
|
|
||||||
// assert!(can_problems.is_empty());
|
// assert!(can_problems.is_empty());
|
||||||
|
|
|
@ -53,6 +53,13 @@ pub enum SolvedType {
|
||||||
/// A type alias
|
/// A type alias
|
||||||
Alias(Symbol, Vec<(Lowercase, SolvedType)>, Box<SolvedType>),
|
Alias(Symbol, Vec<(Lowercase, SolvedType)>, Box<SolvedType>),
|
||||||
|
|
||||||
|
HostExposedAlias {
|
||||||
|
name: Symbol,
|
||||||
|
arguments: Vec<(Lowercase, SolvedType)>,
|
||||||
|
actual_var: VarId,
|
||||||
|
actual: Box<SolvedType>,
|
||||||
|
},
|
||||||
|
|
||||||
/// a boolean algebra Bool
|
/// a boolean algebra Bool
|
||||||
Boolean(SolvedBool),
|
Boolean(SolvedBool),
|
||||||
|
|
||||||
|
@ -194,6 +201,26 @@ impl SolvedType {
|
||||||
|
|
||||||
SolvedType::Alias(*symbol, solved_args, Box::new(solved_type))
|
SolvedType::Alias(*symbol, solved_args, Box::new(solved_type))
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
actual_var,
|
||||||
|
actual,
|
||||||
|
} => {
|
||||||
|
let solved_type = Self::from_type(solved_subs, actual);
|
||||||
|
let mut solved_args = Vec::with_capacity(arguments.len());
|
||||||
|
|
||||||
|
for (name, var) in arguments {
|
||||||
|
solved_args.push((name.clone(), Self::from_type(solved_subs, var)));
|
||||||
|
}
|
||||||
|
|
||||||
|
SolvedType::HostExposedAlias {
|
||||||
|
name: *name,
|
||||||
|
arguments: solved_args,
|
||||||
|
actual_var: VarId::from_var(*actual_var, solved_subs.inner()),
|
||||||
|
actual: Box::new(solved_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
Boolean(val) => SolvedType::Boolean(SolvedBool::from_bool(&val, solved_subs.inner())),
|
Boolean(val) => SolvedType::Boolean(SolvedBool::from_bool(&val, solved_subs.inner())),
|
||||||
Variable(var) => Self::from_var(solved_subs.inner(), *var),
|
Variable(var) => Self::from_var(solved_subs.inner(), *var),
|
||||||
}
|
}
|
||||||
|
@ -486,6 +513,27 @@ pub fn to_type(
|
||||||
|
|
||||||
Type::Alias(*symbol, type_variables, Box::new(actual))
|
Type::Alias(*symbol, type_variables, Box::new(actual))
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
name,
|
||||||
|
arguments: solved_type_variables,
|
||||||
|
actual_var,
|
||||||
|
actual: solved_actual,
|
||||||
|
} => {
|
||||||
|
let mut type_variables = Vec::with_capacity(solved_type_variables.len());
|
||||||
|
|
||||||
|
for (lowercase, solved_arg) in solved_type_variables {
|
||||||
|
type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = to_type(solved_actual, free_vars, var_store);
|
||||||
|
|
||||||
|
Type::HostExposedAlias {
|
||||||
|
name: *name,
|
||||||
|
arguments: type_variables,
|
||||||
|
actual_var: var_id_to_flex_var(*actual_var, free_vars, var_store),
|
||||||
|
actual: Box::new(actual),
|
||||||
|
}
|
||||||
|
}
|
||||||
Error => Type::Erroneous(Problem::SolvedTypeError),
|
Error => Type::Erroneous(Problem::SolvedTypeError),
|
||||||
Erroneous(problem) => Type::Erroneous(problem.clone()),
|
Erroneous(problem) => Type::Erroneous(problem.clone()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,12 @@ pub enum Type {
|
||||||
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
Record(SendMap<Lowercase, RecordField<Type>>, Box<Type>),
|
||||||
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
TagUnion(Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||||
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
Alias(Symbol, Vec<(Lowercase, Type)>, Box<Type>),
|
||||||
|
HostExposedAlias {
|
||||||
|
name: Symbol,
|
||||||
|
arguments: Vec<(Lowercase, Type)>,
|
||||||
|
actual_var: Variable,
|
||||||
|
actual: Box<Type>,
|
||||||
|
},
|
||||||
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, Box<Type>),
|
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, Box<Type>),
|
||||||
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
/// Applying a type to some arguments (e.g. Map.Map String Int)
|
||||||
Apply(Symbol, Vec<Type>),
|
Apply(Symbol, Vec<Type>),
|
||||||
|
@ -206,6 +212,20 @@ impl fmt::Debug for Type {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Type::HostExposedAlias {
|
||||||
|
name, arguments, ..
|
||||||
|
} => {
|
||||||
|
write!(f, "HostExposedAlias {:?}", name)?;
|
||||||
|
|
||||||
|
for (_, arg) in arguments {
|
||||||
|
write!(f, " {:?}", arg)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes it's useful to see the expansion of the alias
|
||||||
|
// write!(f, "[ but actually {:?} ]", _actual)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Type::Record(fields, ext) => {
|
Type::Record(fields, ext) => {
|
||||||
write!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
|
|
||||||
|
@ -405,6 +425,16 @@ impl Type {
|
||||||
}
|
}
|
||||||
actual_type.substitute(substitutions);
|
actual_type.substitute(substitutions);
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
arguments,
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
for (_, value) in arguments.iter_mut() {
|
||||||
|
value.substitute(substitutions);
|
||||||
|
}
|
||||||
|
actual_type.substitute(substitutions);
|
||||||
|
}
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.substitute(substitutions);
|
arg.substitute(substitutions);
|
||||||
|
@ -453,6 +483,12 @@ impl Type {
|
||||||
Alias(_, _, actual_type) => {
|
Alias(_, _, actual_type) => {
|
||||||
actual_type.substitute_alias(rep_symbol, actual);
|
actual_type.substitute_alias(rep_symbol, actual);
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
actual_type.substitute_alias(rep_symbol, actual);
|
||||||
|
}
|
||||||
Apply(symbol, _) if *symbol == rep_symbol => {
|
Apply(symbol, _) if *symbol == rep_symbol => {
|
||||||
*self = actual.clone();
|
*self = actual.clone();
|
||||||
|
|
||||||
|
@ -496,6 +532,9 @@ impl Type {
|
||||||
Alias(alias_symbol, _, actual_type) => {
|
Alias(alias_symbol, _, actual_type) => {
|
||||||
alias_symbol == &rep_symbol || actual_type.contains_symbol(rep_symbol)
|
alias_symbol == &rep_symbol || actual_type.contains_symbol(rep_symbol)
|
||||||
}
|
}
|
||||||
|
HostExposedAlias { name, actual, .. } => {
|
||||||
|
name == &rep_symbol || actual.contains_symbol(rep_symbol)
|
||||||
|
}
|
||||||
Apply(symbol, _) if *symbol == rep_symbol => true,
|
Apply(symbol, _) if *symbol == rep_symbol => true,
|
||||||
Apply(_, args) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
Apply(_, args) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => false,
|
EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => false,
|
||||||
|
@ -528,6 +567,7 @@ impl Type {
|
||||||
.any(|arg| arg.contains_variable(rep_variable))
|
.any(|arg| arg.contains_variable(rep_variable))
|
||||||
}
|
}
|
||||||
Alias(_, _, actual_type) => actual_type.contains_variable(rep_variable),
|
Alias(_, _, actual_type) => actual_type.contains_variable(rep_variable),
|
||||||
|
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
|
||||||
Apply(_, args) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
Apply(_, args) => args.iter().any(|arg| arg.contains_variable(rep_variable)),
|
||||||
EmptyRec | EmptyTagUnion | Erroneous(_) | Boolean(_) => false,
|
EmptyRec | EmptyTagUnion | Erroneous(_) | Boolean(_) => false,
|
||||||
}
|
}
|
||||||
|
@ -579,7 +619,12 @@ impl Type {
|
||||||
}
|
}
|
||||||
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
ext.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
}
|
}
|
||||||
Alias(_, type_args, actual_type) => {
|
HostExposedAlias {
|
||||||
|
arguments: type_args,
|
||||||
|
actual: actual_type,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| Alias(_, type_args, actual_type) => {
|
||||||
for arg in type_args {
|
for arg in type_args {
|
||||||
arg.1
|
arg.1
|
||||||
.instantiate_aliases(region, aliases, var_store, introduced);
|
.instantiate_aliases(region, aliases, var_store, introduced);
|
||||||
|
@ -761,6 +806,10 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {
|
||||||
accum.insert(*alias_symbol);
|
accum.insert(*alias_symbol);
|
||||||
symbols_help(&actual_type, accum);
|
symbols_help(&actual_type, accum);
|
||||||
}
|
}
|
||||||
|
HostExposedAlias { name, actual, .. } => {
|
||||||
|
accum.insert(*name);
|
||||||
|
symbols_help(&actual, accum);
|
||||||
|
}
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
accum.insert(*symbol);
|
accum.insert(*symbol);
|
||||||
args.iter().for_each(|arg| symbols_help(arg, accum));
|
args.iter().for_each(|arg| symbols_help(arg, accum));
|
||||||
|
@ -830,6 +879,14 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
}
|
}
|
||||||
variables_help(actual, accum);
|
variables_help(actual, accum);
|
||||||
}
|
}
|
||||||
|
HostExposedAlias {
|
||||||
|
arguments, actual, ..
|
||||||
|
} => {
|
||||||
|
for (_, arg) in arguments {
|
||||||
|
variables_help(arg, accum);
|
||||||
|
}
|
||||||
|
variables_help(actual, accum);
|
||||||
|
}
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
for x in args {
|
for x in args {
|
||||||
variables_help(x, accum);
|
variables_help(x, accum);
|
||||||
|
|
|
@ -17,3 +17,7 @@ roc_load = { path = "../compiler/load" }
|
||||||
roc_builtins = { path = "../compiler/builtins" }
|
roc_builtins = { path = "../compiler/builtins" }
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "0.5.1"
|
||||||
|
maplit = "1.0.1"
|
||||||
|
|
286
docs/src/main.rs
286
docs/src/main.rs
|
@ -5,7 +5,10 @@ extern crate serde;
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate pulldown_cmark;
|
extern crate pulldown_cmark;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
use std::error::Error;
|
use roc_builtins::std::StdLib;
|
||||||
|
use roc_load::docs::Documentation;
|
||||||
|
use roc_load::docs::ModuleDocumentation;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
extern crate roc_load;
|
extern crate roc_load;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -22,7 +25,7 @@ pub struct Template {
|
||||||
module_links: Vec<TemplateLink>,
|
module_links: Vec<TemplateLink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone, Debug, PartialEq)]
|
||||||
pub struct ModuleEntry {
|
pub struct ModuleEntry {
|
||||||
name: String,
|
name: String,
|
||||||
docs: String,
|
docs: String,
|
||||||
|
@ -41,13 +44,9 @@ pub struct TemplateLinkEntry {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() {
|
||||||
let std_lib = roc_builtins::std::standard_stdlib();
|
generate(
|
||||||
let subs_by_module = MutMap::default();
|
vec![
|
||||||
let arena = Bump::new();
|
|
||||||
|
|
||||||
let src_dir = Path::new("../compiler/builtins/docs");
|
|
||||||
let files = vec![
|
|
||||||
PathBuf::from(r"../compiler/builtins/docs/Bool.roc"),
|
PathBuf::from(r"../compiler/builtins/docs/Bool.roc"),
|
||||||
PathBuf::from(r"../compiler/builtins/docs/Map.roc"),
|
PathBuf::from(r"../compiler/builtins/docs/Map.roc"),
|
||||||
// Not working
|
// Not working
|
||||||
|
@ -56,76 +55,109 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// PathBuf::from(r"../compiler/builtins/docs/Num.roc"),
|
// PathBuf::from(r"../compiler/builtins/docs/Num.roc"),
|
||||||
PathBuf::from(r"../compiler/builtins/docs/Set.roc"),
|
PathBuf::from(r"../compiler/builtins/docs/Set.roc"),
|
||||||
PathBuf::from(r"../compiler/builtins/docs/Str.roc"),
|
PathBuf::from(r"../compiler/builtins/docs/Str.roc"),
|
||||||
];
|
],
|
||||||
|
roc_builtins::std::standard_stdlib(),
|
||||||
|
Path::new("../compiler/builtins/docs"),
|
||||||
|
Path::new("./build"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let mut modules_docs = vec![];
|
pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, src_dir: &Path, build_dir: &Path) {
|
||||||
|
let files_docs = files_to_documentations(filenames, std_lib, src_dir);
|
||||||
|
|
||||||
// Load each file is files vector
|
// TODO: get info from a file like "elm.json"
|
||||||
for filename in files {
|
let package = roc_load::docs::Documentation {
|
||||||
|
name: "roc/builtins".to_string(),
|
||||||
|
version: "1.0.0".to_string(),
|
||||||
|
docs: "Package introduction or README.".to_string(),
|
||||||
|
modules: files_docs,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Remove old build folder, if exists
|
||||||
|
let _ = fs::remove_dir_all(build_dir);
|
||||||
|
|
||||||
|
let version_folder = build_dir
|
||||||
|
.join(package.name.clone())
|
||||||
|
.join(package.version.clone());
|
||||||
|
|
||||||
|
// Make sure the output directories exists
|
||||||
|
fs::create_dir_all(&version_folder)
|
||||||
|
.expect("TODO gracefully handle creating directories failing");
|
||||||
|
|
||||||
|
// Register handlebars template
|
||||||
|
let mut handlebars = handlebars::Handlebars::new();
|
||||||
|
handlebars
|
||||||
|
.register_template_file("page", "./src/templates/page.hbs")
|
||||||
|
.expect("TODO gracefully handle registering template failing");
|
||||||
|
|
||||||
|
// Write each package's module docs html file
|
||||||
|
for module in &package.modules {
|
||||||
|
let template = documentation_to_template_data(&package, module);
|
||||||
|
|
||||||
|
let handlebars_data = handlebars::to_json(&template);
|
||||||
|
let filepath = version_folder.join(format!("{}.html", module.name));
|
||||||
|
let mut output_file =
|
||||||
|
fs::File::create(filepath).expect("TODO gracefully handle creating file failing");
|
||||||
|
handlebars
|
||||||
|
.render_to_write("page", &handlebars_data, &mut output_file)
|
||||||
|
.expect("TODO gracefully handle writing file failing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy /static folder content to /build
|
||||||
|
let copy_options = fs_extra::dir::CopyOptions {
|
||||||
|
overwrite: true,
|
||||||
|
skip_exist: false,
|
||||||
|
buffer_size: 64000, //64kb
|
||||||
|
copy_inside: false,
|
||||||
|
content_only: true,
|
||||||
|
depth: 0,
|
||||||
|
};
|
||||||
|
fs_extra::dir::copy("./src/static/", &build_dir, ©_options)
|
||||||
|
.expect("TODO gracefully handle copying static content failing");
|
||||||
|
println!("Docs generated at {}", build_dir.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files_to_documentations(
|
||||||
|
filenames: Vec<PathBuf>,
|
||||||
|
std_lib: StdLib,
|
||||||
|
src_dir: &Path,
|
||||||
|
) -> Vec<ModuleDocumentation> {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let mut files_docs = vec![];
|
||||||
|
|
||||||
|
for filename in filenames {
|
||||||
let mut loaded = roc_load::file::load_and_typecheck(
|
let mut loaded = roc_load::file::load_and_typecheck(
|
||||||
&arena,
|
&arena,
|
||||||
filename,
|
filename,
|
||||||
std_lib.clone(),
|
std_lib.clone(),
|
||||||
src_dir,
|
src_dir,
|
||||||
subs_by_module.clone(),
|
MutMap::default(),
|
||||||
)
|
)
|
||||||
.expect("TODO gracefully handle load failing");
|
.expect("TODO gracefully handle load failing");
|
||||||
modules_docs.extend(loaded.documentation.drain().map(|x| x.1));
|
files_docs.extend(loaded.documentation.drain().map(|x| x.1));
|
||||||
}
|
}
|
||||||
|
files_docs
|
||||||
|
}
|
||||||
|
|
||||||
let package = roc_load::docs::Documentation {
|
fn documentation_to_template_data(doc: &Documentation, module: &ModuleDocumentation) -> Template {
|
||||||
name: "roc/builtins".to_string(),
|
Template {
|
||||||
version: "1.0.0".to_string(),
|
package_name: doc.name.clone(),
|
||||||
docs: "Package introduction or README.".to_string(),
|
package_version: doc.version.clone(),
|
||||||
modules: modules_docs,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove old build folder
|
|
||||||
fs::remove_dir_all("./build")?;
|
|
||||||
|
|
||||||
// Make sure the output directories exists
|
|
||||||
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
|
|
||||||
|
|
||||||
// Register handlebars template
|
|
||||||
let mut handlebars = handlebars::Handlebars::new();
|
|
||||||
assert!(handlebars
|
|
||||||
.register_template_file("page", "./src/templates/page.hbs")
|
|
||||||
.is_ok());
|
|
||||||
|
|
||||||
let markdown_options = pulldown_cmark::Options::all();
|
|
||||||
|
|
||||||
// Write each package's module docs
|
|
||||||
for module in &package.modules {
|
|
||||||
// Convert module docs from markdown to html
|
|
||||||
let docs_parser = pulldown_cmark::Parser::new_ext(&module.docs, markdown_options);
|
|
||||||
let mut docs_html: String = String::with_capacity(module.docs.len() * 3 / 2);
|
|
||||||
pulldown_cmark::html::push_html(&mut docs_html, docs_parser);
|
|
||||||
|
|
||||||
let template = Template {
|
|
||||||
package_name: package.name.clone(),
|
|
||||||
package_version: package.version.clone(),
|
|
||||||
module_name: module.name.clone(),
|
module_name: module.name.clone(),
|
||||||
module_docs: docs_html,
|
module_docs: markdown_to_html(module.docs.clone()),
|
||||||
module_entries: module
|
module_entries: module
|
||||||
.entries
|
.entries
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|entry| {
|
.map(|entry| ModuleEntry {
|
||||||
// Convert entry docs from markdown to html
|
|
||||||
let mut entry_docs_html: String = String::new();
|
|
||||||
if let Some(docs) = entry.docs {
|
|
||||||
let entry_docs_parser =
|
|
||||||
pulldown_cmark::Parser::new_ext(&docs, markdown_options);
|
|
||||||
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleEntry {
|
|
||||||
name: entry.name.clone(),
|
name: entry.name.clone(),
|
||||||
docs: entry_docs_html,
|
docs: match entry.docs {
|
||||||
}
|
Some(docs) => markdown_to_html(docs),
|
||||||
|
None => String::new(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
module_links: package
|
module_links: doc
|
||||||
.modules
|
.modules
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -140,26 +172,118 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.collect(),
|
.collect(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
|
||||||
|
|
||||||
let handlebars_data = handlebars::to_json(&template);
|
|
||||||
let mut output_file = fs::File::create(format!(
|
|
||||||
"./build/{}/{}/{}.html",
|
|
||||||
package.name, package.version, module.name
|
|
||||||
))?;
|
|
||||||
handlebars.render_to_write("page", &handlebars_data, &mut output_file)?;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Copy /static folder content to /build
|
|
||||||
let copy_options = fs_extra::dir::CopyOptions {
|
fn markdown_to_html(markdown: String) -> String {
|
||||||
overwrite: true,
|
use pulldown_cmark::CodeBlockKind::*;
|
||||||
skip_exist: false,
|
use pulldown_cmark::CowStr::*;
|
||||||
buffer_size: 64000, //64kb
|
use pulldown_cmark::Event::*;
|
||||||
copy_inside: false,
|
use pulldown_cmark::Tag::*;
|
||||||
content_only: true,
|
|
||||||
depth: 0,
|
let markdown_options = pulldown_cmark::Options::all();
|
||||||
};
|
let mut docs_parser = vec![];
|
||||||
fs_extra::dir::copy("./src/static/", "./build", ©_options)?;
|
let (_, _) = pulldown_cmark::Parser::new_ext(&markdown, markdown_options).fold(
|
||||||
println!("Docs generated at /build");
|
(0, 0),
|
||||||
Ok(())
|
|(start_quote_count, end_quote_count), event| match event {
|
||||||
|
// Replace this sequence (`>>>` syntax):
|
||||||
|
// Start(BlockQuote)
|
||||||
|
// Start(BlockQuote)
|
||||||
|
// Start(BlockQuote)
|
||||||
|
// Start(Paragraph)
|
||||||
|
// For `Start(CodeBlock(Fenced(Borrowed("roc"))))`
|
||||||
|
Start(BlockQuote) => {
|
||||||
|
docs_parser.push(event);
|
||||||
|
(start_quote_count + 1, 0)
|
||||||
|
}
|
||||||
|
Start(Paragraph) => {
|
||||||
|
if start_quote_count == 3 {
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.push(Start(CodeBlock(Fenced(Borrowed("roc")))));
|
||||||
|
} else {
|
||||||
|
docs_parser.push(event);
|
||||||
|
}
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
// Replace this sequence (`>>>` syntax):
|
||||||
|
// End(Paragraph)
|
||||||
|
// End(BlockQuote)
|
||||||
|
// End(BlockQuote)
|
||||||
|
// End(BlockQuote)
|
||||||
|
// For `End(CodeBlock(Fenced(Borrowed("roc"))))`
|
||||||
|
End(Paragraph) => {
|
||||||
|
docs_parser.push(event);
|
||||||
|
(0, 1)
|
||||||
|
}
|
||||||
|
End(BlockQuote) => {
|
||||||
|
if end_quote_count == 3 {
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.pop();
|
||||||
|
docs_parser.push(End(CodeBlock(Fenced(Borrowed("roc")))));
|
||||||
|
(0, 0)
|
||||||
|
} else {
|
||||||
|
docs_parser.push(event);
|
||||||
|
(0, end_quote_count + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
docs_parser.push(event);
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let mut docs_html = String::new();
|
||||||
|
pulldown_cmark::html::push_html(&mut docs_html, docs_parser.into_iter());
|
||||||
|
docs_html
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_docs {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn internal() {
|
||||||
|
let files_docs = files_to_documentations(
|
||||||
|
vec![PathBuf::from(r"tests/fixtures/Interface.roc")],
|
||||||
|
roc_builtins::std::standard_stdlib(),
|
||||||
|
Path::new("tests/fixtures"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let package = roc_load::docs::Documentation {
|
||||||
|
name: "roc/builtins".to_string(),
|
||||||
|
version: "1.0.0".to_string(),
|
||||||
|
docs: "Package introduction or README.".to_string(),
|
||||||
|
modules: files_docs,
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_entries = vec![
|
||||||
|
ModuleEntry {
|
||||||
|
name: "singleline".to_string(),
|
||||||
|
docs: "<p>Single line documentation.</p>\n".to_string(),
|
||||||
|
},
|
||||||
|
ModuleEntry {
|
||||||
|
name: "multiline".to_string(),
|
||||||
|
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n".to_string(),
|
||||||
|
}, ModuleEntry {
|
||||||
|
name: "multiparagraph".to_string(),
|
||||||
|
docs: "<p>Multiparagraph documentation.</p>\n<p>Without any complex syntax yet!</p>\n".to_string(),
|
||||||
|
}, ModuleEntry {
|
||||||
|
name: "codeblock".to_string(),
|
||||||
|
docs: "<p>Turns >>> into code block for now.</p>\n<pre><code class=\"language-roc\">codeblock</code></pre>\n".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for module in &package.modules {
|
||||||
|
let template = documentation_to_template_data(&package, module);
|
||||||
|
assert_eq!(template.module_name, "Test");
|
||||||
|
template
|
||||||
|
.module_entries
|
||||||
|
.iter()
|
||||||
|
.zip(expected_entries.iter())
|
||||||
|
.for_each(|(x, y)| assert_eq!(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
23
docs/tests/fixtures/Interface.roc
vendored
Normal file
23
docs/tests/fixtures/Interface.roc
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
interface Test
|
||||||
|
exposes [ singleline, multiline, multiparagraph, codeblock ]
|
||||||
|
imports []
|
||||||
|
|
||||||
|
## Single line documentation.
|
||||||
|
singleline : Bool -> Bool
|
||||||
|
|
||||||
|
## Multiline documentation.
|
||||||
|
## Without any complex syntax yet!
|
||||||
|
multiline : Bool -> Bool
|
||||||
|
|
||||||
|
## Multiparagraph documentation.
|
||||||
|
##
|
||||||
|
## Without any complex syntax yet!
|
||||||
|
multiparagraph : Bool -> Bool
|
||||||
|
|
||||||
|
## No documentation for not exposed function.
|
||||||
|
notExposed : Bool -> Bool
|
||||||
|
|
||||||
|
## Turns >>> into code block for now.
|
||||||
|
##
|
||||||
|
## >>> codeblock
|
||||||
|
codeblock : Bool -> Bool
|
1
editor/.gitignore
vendored
Normal file
1
editor/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.spv
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
app
|
app
|
||||||
host.o
|
host.o
|
||||||
c_host.o
|
c_host.o
|
||||||
|
roc_app.o
|
||||||
app.dSYM
|
app.dSYM
|
||||||
|
|
9
examples/closure/Closure.roc
Normal file
9
examples/closure/Closure.roc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
app Closure provides [ makeClosure ] imports []
|
||||||
|
|
||||||
|
makeClosure : ({} -> Int) as MyClosure
|
||||||
|
makeClosure =
|
||||||
|
x = 42
|
||||||
|
y = 42
|
||||||
|
|
||||||
|
\{} -> x + y
|
||||||
|
|
23
examples/closure/platform/Cargo.lock
generated
Normal file
23
examples/closure/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/closure/platform/Cargo.toml
Normal file
13
examples/closure/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_std = { path = "../../../roc_std" }
|
||||||
|
|
||||||
|
[workspace]
|
7
examples/closure/platform/host.c
Normal file
7
examples/closure/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int rust_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return rust_main();
|
||||||
|
}
|
89
examples/closure/platform/src/lib.rs
Normal file
89
examples/closure/platform/src/lib.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use roc_std::alloca;
|
||||||
|
use roc_std::RocCallResult;
|
||||||
|
use std::alloc::Layout;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#[link_name = "makeClosure_1_exposed"]
|
||||||
|
fn make_closure(output: *mut u8) -> ();
|
||||||
|
|
||||||
|
#[link_name = "makeClosure_1_size"]
|
||||||
|
fn closure_size() -> i64;
|
||||||
|
|
||||||
|
#[link_name = "makeClosure_1_MyClosure_caller"]
|
||||||
|
fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||||
|
|
||||||
|
#[link_name = "makeClosure_1_MyClosure_size"]
|
||||||
|
fn size_MyClosure() -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
|
||||||
|
let size = size_MyClosure() as usize;
|
||||||
|
|
||||||
|
alloca::with_stack_bytes(size, |buffer| {
|
||||||
|
let buffer: *mut std::ffi::c_void = buffer;
|
||||||
|
let buffer: *mut u8 = buffer as *mut u8;
|
||||||
|
|
||||||
|
call_MyClosure(
|
||||||
|
function_pointer,
|
||||||
|
closure_data_ptr as *const u8,
|
||||||
|
buffer as *mut u8,
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = &*(buffer as *mut RocCallResult<i64>);
|
||||||
|
|
||||||
|
match output.into() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => panic!("failed with {}", e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_main() -> isize {
|
||||||
|
println!("Running Roc closure");
|
||||||
|
let start_time = SystemTime::now();
|
||||||
|
|
||||||
|
let size = unsafe { closure_size() } as usize;
|
||||||
|
let layout = Layout::array::<u8>(size).unwrap();
|
||||||
|
let answer = unsafe {
|
||||||
|
let buffer = std::alloc::alloc(layout);
|
||||||
|
|
||||||
|
make_closure(buffer);
|
||||||
|
|
||||||
|
let output = &*(buffer as *mut RocCallResult<()>);
|
||||||
|
|
||||||
|
match output.into() {
|
||||||
|
Ok(()) => {
|
||||||
|
let function_pointer = {
|
||||||
|
// this is a pointer to the location where the function pointer is stored
|
||||||
|
// we pass just the function pointer
|
||||||
|
let temp = buffer.offset(8) as *const i64;
|
||||||
|
|
||||||
|
(*temp) as *const u8
|
||||||
|
};
|
||||||
|
|
||||||
|
let closure_data_ptr = buffer.offset(16);
|
||||||
|
|
||||||
|
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8)
|
||||||
|
}
|
||||||
|
Err(msg) => {
|
||||||
|
std::alloc::dealloc(buffer, layout);
|
||||||
|
|
||||||
|
panic!("Roc failed with message: {}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let end_time = SystemTime::now();
|
||||||
|
let duration = end_time.duration_since(start_time).unwrap();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Roc closure took {:.4} ms to compute this answer: {:?}",
|
||||||
|
duration.as_secs_f64() * 1000.0,
|
||||||
|
// truncate the answer, so stdout is not swamped
|
||||||
|
answer
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exit code
|
||||||
|
0
|
||||||
|
}
|
|
@ -1,17 +1,27 @@
|
||||||
|
use roc_std::RocCallResult;
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "main_1"]
|
#[link_name = "main_1_exposed"]
|
||||||
fn main() -> RocStr;
|
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn rust_main() -> isize {
|
pub fn rust_main() -> isize {
|
||||||
println!(
|
let answer = unsafe {
|
||||||
"Roc says: {}",
|
use std::mem::MaybeUninit;
|
||||||
str::from_utf8(unsafe { main().as_slice() }).unwrap()
|
let mut output: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||||
);
|
|
||||||
|
say_hello(&mut *output.as_mut_ptr());
|
||||||
|
|
||||||
|
match output.assume_init().into() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(msg) => panic!("roc failed with message {}", msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Roc says: {}", str::from_utf8(answer.as_slice()).unwrap());
|
||||||
|
|
||||||
// Exit code
|
// Exit code
|
||||||
0
|
0
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
use roc_std::RocCallResult;
|
||||||
use roc_std::RocList;
|
use roc_std::RocList;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "quicksort_1"]
|
#[link_name = "quicksort_1_exposed"]
|
||||||
fn quicksort(list: RocList<i64>) -> RocList<i64>;
|
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
const NUM_NUMS: usize = 100;
|
const NUM_NUMS: usize = 100;
|
||||||
|
@ -24,7 +25,17 @@ pub fn rust_main() -> isize {
|
||||||
|
|
||||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||||
let start_time = SystemTime::now();
|
let start_time = SystemTime::now();
|
||||||
let answer = unsafe { quicksort(nums) };
|
let answer = unsafe {
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
let mut output = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
quicksort(nums, &mut *output.as_mut_ptr());
|
||||||
|
|
||||||
|
match output.assume_init().into() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(msg) => panic!("roc failed with message: {}", msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
let end_time = SystemTime::now();
|
let end_time = SystemTime::now();
|
||||||
let duration = end_time.duration_since(start_time).unwrap();
|
let duration = end_time.duration_since(start_time).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
use roc_std::RocCallResult;
|
||||||
use roc_std::RocList;
|
use roc_std::RocList;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "quicksort_1"]
|
#[link_name = "quicksort_1_exposed"]
|
||||||
fn quicksort(list: RocList<i64>) -> RocList<i64>;
|
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
const NUM_NUMS: usize = 100;
|
const NUM_NUMS: usize = 100;
|
||||||
|
@ -24,7 +25,18 @@ pub fn rust_main() -> isize {
|
||||||
|
|
||||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||||
let start_time = SystemTime::now();
|
let start_time = SystemTime::now();
|
||||||
let answer = unsafe { quicksort(nums) };
|
let answer = unsafe {
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
let mut output = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
quicksort(nums, &mut *output.as_mut_ptr());
|
||||||
|
|
||||||
|
match output.assume_init().into() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(msg) => panic!("roc failed with message {}", msg),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let end_time = SystemTime::now();
|
let end_time = SystemTime::now();
|
||||||
let duration = end_time.duration_since(start_time).unwrap();
|
let duration = end_time.duration_since(start_time).unwrap();
|
||||||
|
|
||||||
|
|
23
examples/shared-quicksort/platform/Cargo.lock
generated
Normal file
23
examples/shared-quicksort/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/shared-quicksort/platform/Cargo.toml
Normal file
13
examples/shared-quicksort/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_std = { path = "../../../roc_std" }
|
||||||
|
|
||||||
|
[workspace]
|
|
@ -1,49 +0,0 @@
|
||||||
# Rebuilding the host from source
|
|
||||||
|
|
||||||
Here are the current steps to rebuild this host. These
|
|
||||||
steps can likely be moved into a `build.rs` script after
|
|
||||||
turning `host.rs` into a `cargo` project, but that hasn't
|
|
||||||
been attempted yet.
|
|
||||||
|
|
||||||
## Compile the Rust and C sources
|
|
||||||
|
|
||||||
Currently this host has both a `host.rs` and a `host.c`.
|
|
||||||
This is only because we haven't figured out a way to convince
|
|
||||||
Rust to emit a `.o` file that doesn't define a `main` entrypoint,
|
|
||||||
but which is capable of being linked into one later.
|
|
||||||
|
|
||||||
As a workaround, we have `host.rs` expose a function called
|
|
||||||
`rust_main` instead of `main`, and all `host.c` does is provide
|
|
||||||
an actual `main` which imports and then calls `rust_main` from
|
|
||||||
the compiled `host.rs`. It's not the most elegant workaround,
|
|
||||||
but [asking on `users.rust-lang.org`](https://users.rust-lang.org/t/error-when-compiling-linking-with-o-files/49635/4)
|
|
||||||
didn't turn up any nicer approaches. Maybe they're out there though!
|
|
||||||
|
|
||||||
To make this workaround happen, we need to compile both `host.rs`
|
|
||||||
and `host.c`. First, `cd` into `platform/host/src/` and then run:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ clang -c host.c -o c_host.o
|
|
||||||
$ rustc host.rs -o rust_host.o
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we should have `c_host.o` and `rust_host.o` in the curent directory.
|
|
||||||
|
|
||||||
## Link together the `.o` files
|
|
||||||
|
|
||||||
Next, combine `c_host.o` and `rust_host.o` into `host.o` using `ld -r` like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ld -r c_host.o rust_host.o -o host.o
|
|
||||||
```
|
|
||||||
|
|
||||||
Move `host.o` into the appropriate `platform/` subdirectory
|
|
||||||
based on your architecture and operating system. For example,
|
|
||||||
on macOS, you'd move `host.o` into the `platform/host/x86_64-unknown-darwin10/` directory.
|
|
||||||
|
|
||||||
## All done!
|
|
||||||
|
|
||||||
Congratulations! You now have an updated host.
|
|
||||||
|
|
||||||
It's now fine to delete `c_host.o` and `rust_host.o`,
|
|
||||||
since they were only needed to produce `host.o`.
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue