mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'roc-lang:main' into main
This commit is contained in:
commit
dc65298f9d
157 changed files with 7365 additions and 5476 deletions
9
.github/workflows/nightly_macos_x86_64.yml
vendored
9
.github/workflows/nightly_macos_x86_64.yml
vendored
|
@ -14,16 +14,15 @@ jobs:
|
||||||
|
|
||||||
- name: write version to file
|
- name: write version to file
|
||||||
run: ./ci/write_version.sh
|
run: ./ci/write_version.sh
|
||||||
|
|
||||||
# build has to be done before tests #2572
|
|
||||||
- name: build release
|
|
||||||
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
|
||||||
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
|
||||||
|
|
||||||
- name: execute rust tests
|
- name: execute rust tests
|
||||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
|
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
|
||||||
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
|
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
|
||||||
# this issue may be caused by using older versions of XCode
|
# this issue may be caused by using older versions of XCode
|
||||||
|
|
||||||
|
- name: build release
|
||||||
|
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
||||||
|
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
||||||
|
|
||||||
- name: get commit SHA
|
- name: get commit SHA
|
||||||
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
||||||
|
|
3
.github/workflows/ubuntu_x86_64.yml
vendored
3
.github/workflows/ubuntu_x86_64.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
|
|
||||||
- name: zig fmt check, zig tests
|
- name: zig fmt check, zig tests
|
||||||
run: cd crates/compiler/builtins/bitcode && ./run-tests.sh
|
run: cd crates/compiler/builtins/bitcode && ./run-tests.sh
|
||||||
|
|
||||||
- name: roc format check on builtins
|
- name: roc format check on builtins
|
||||||
run: cargo run --locked --release format --check crates/compiler/builtins/roc
|
run: cargo run --locked --release format --check crates/compiler/builtins/roc
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ jobs:
|
||||||
- name: run `roc test` on Dict builtins
|
- name: run `roc test` on Dict builtins
|
||||||
run: cargo run --locked --release -- test crates/compiler/builtins/roc/Dict.roc && sccache --show-stats
|
run: cargo run --locked --release -- test crates/compiler/builtins/roc/Dict.roc && sccache --show-stats
|
||||||
|
|
||||||
#TODO pass --locked into the script here as well, this avoids rebuilding dependencies unnecessarily
|
|
||||||
- name: wasm repl test
|
- name: wasm repl test
|
||||||
run: crates/repl_test/test_wasm.sh && sccache --show-stats
|
run: crates/repl_test/test_wasm.sh && sccache --show-stats
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,6 @@ To build the compiler, you need these installed:
|
||||||
- On Debian/Ubuntu `sudo apt-get install pkg-config`
|
- On Debian/Ubuntu `sudo apt-get install pkg-config`
|
||||||
- LLVM, see below for version
|
- LLVM, see below for version
|
||||||
- [rust](https://rustup.rs/)
|
- [rust](https://rustup.rs/)
|
||||||
- Also run `cargo install bindgen` after installing rust. You may need to open a new terminal.
|
|
||||||
|
|
||||||
To run the test suite (via `cargo test`), you additionally need to install:
|
To run the test suite (via `cargo test`), you additionally need to install:
|
||||||
|
|
||||||
|
|
925
Cargo.lock
generated
925
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -113,7 +113,6 @@ target-lexicon = "0.12.3"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
unicode-segmentation = "1.10.0"
|
unicode-segmentation = "1.10.0"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
wasm3 = { git = "https://github.com/roc-lang/wasm3-rs", rev = "ba0cdab7404f7f2995a8c18e614ce020dabd6da0" }
|
|
||||||
wyhash = "0.5.0"
|
wyhash = "0.5.0"
|
||||||
|
|
||||||
# TODO: Deal with the update of object to 0.27.
|
# TODO: Deal with the update of object to 0.27.
|
||||||
|
|
|
@ -22,7 +22,7 @@ i386-cli-run = ["target-x86"]
|
||||||
|
|
||||||
editor = ["roc_editor"]
|
editor = ["roc_editor"]
|
||||||
|
|
||||||
run-wasm32 = ["wasmer", "wasmer-wasi"]
|
run-wasm32 = ["roc_wasm_interp"]
|
||||||
|
|
||||||
# Compiling for a different target than the current machine can cause linker errors.
|
# Compiling for a different target than the current machine can cause linker errors.
|
||||||
target-arm = ["roc_build/target-arm", "roc_repl_cli/target-arm"]
|
target-arm = ["roc_build/target-arm", "roc_repl_cli/target-arm"]
|
||||||
|
@ -39,6 +39,8 @@ target-all = [
|
||||||
"target-wasm32"
|
"target-wasm32"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
sanitizers = ["roc_build/sanitizers"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
|
@ -63,11 +65,10 @@ roc_repl_cli = { path = "../repl_cli", optional = true }
|
||||||
roc_tracing = { path = "../tracing" }
|
roc_tracing = { path = "../tracing" }
|
||||||
roc_intern = { path = "../compiler/intern" }
|
roc_intern = { path = "../compiler/intern" }
|
||||||
roc_gen_llvm = {path = "../compiler/gen_llvm"}
|
roc_gen_llvm = {path = "../compiler/gen_llvm"}
|
||||||
|
roc_wasm_interp = { path = "../wasm_interp", optional = true }
|
||||||
|
|
||||||
ven_pretty = { path = "../vendor/pretty" }
|
ven_pretty = { path = "../vendor/pretty" }
|
||||||
|
|
||||||
wasmer-wasi = { version = "2.2.1", optional = true }
|
|
||||||
|
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
const_format.workspace = true
|
const_format.workspace = true
|
||||||
mimalloc.workspace = true
|
mimalloc.workspace = true
|
||||||
|
@ -86,15 +87,8 @@ inkwell.workspace = true
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
roc_repl_expect = { path = "../repl_expect" }
|
roc_repl_expect = { path = "../repl_expect" }
|
||||||
|
|
||||||
# Wasmer singlepass compiler only works on x86_64.
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
|
||||||
wasmer = { version = "2.2.1", optional = true, default-features = false, features = ["singlepass", "universal"] }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "x86_64"))'.dependencies]
|
|
||||||
wasmer = { version = "2.2.1", optional = true, default-features = false, features = ["cranelift", "universal"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasmer-wasi = "2.2.1"
|
|
||||||
pretty_assertions = "1.3.0"
|
pretty_assertions = "1.3.0"
|
||||||
roc_test_utils = { path = "../test_utils" }
|
roc_test_utils = { path = "../test_utils" }
|
||||||
roc_utils = { path = "../utils" }
|
roc_utils = { path = "../utils" }
|
||||||
|
@ -105,13 +99,6 @@ cli_utils = { path = "../cli_utils" }
|
||||||
once_cell = "1.15.0"
|
once_cell = "1.15.0"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
|
||||||
# Wasmer singlepass compiler only works on x86_64.
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
|
||||||
wasmer = { version = "2.2.1", default-features = false, features = ["singlepass", "universal"] }
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "x86_64"))'.dev-dependencies]
|
|
||||||
wasmer = { version = "2.2.1", default-features = false, features = ["cranelift", "universal"] }
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "time_bench"
|
name = "time_bench"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
|
@ -186,7 +186,9 @@ pub fn build_file<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// We don't need to spawn a rebuild thread when using a prebuilt host.
|
// We don't need to spawn a rebuild thread when using a prebuilt host.
|
||||||
let rebuild_thread = if is_prebuilt {
|
let rebuild_thread = if matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||||
|
None
|
||||||
|
} else if is_prebuilt {
|
||||||
if !preprocessed_host_path.exists() {
|
if !preprocessed_host_path.exists() {
|
||||||
if prebuilt_requested {
|
if prebuilt_requested {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
@ -378,10 +380,11 @@ pub fn build_file<'a>(
|
||||||
|
|
||||||
std::fs::write(app_o_file, &*roc_app_bytes).unwrap();
|
std::fs::write(app_o_file, &*roc_app_bytes).unwrap();
|
||||||
|
|
||||||
let mut inputs = vec![
|
let mut inputs = vec![app_o_file.to_str().unwrap()];
|
||||||
host_input_path.as_path().to_str().unwrap(),
|
|
||||||
app_o_file.to_str().unwrap(),
|
if !matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||||
];
|
inputs.push(host_input_path.as_path().to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
let builtins_host_tempfile = {
|
let builtins_host_tempfile = {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
|
@ -853,7 +853,7 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||||
{
|
{
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
run_with_wasmer(
|
run_wasm(
|
||||||
generated_filename,
|
generated_filename,
|
||||||
args.into_iter().map(|os_str| os_str.as_bytes()),
|
args.into_iter().map(|os_str| os_str.as_bytes()),
|
||||||
);
|
);
|
||||||
|
@ -861,11 +861,11 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||||
|
|
||||||
#[cfg(not(target_family = "unix"))]
|
#[cfg(not(target_family = "unix"))]
|
||||||
{
|
{
|
||||||
run_with_wasmer(
|
run_wasm(
|
||||||
generated_filename,
|
generated_filename,
|
||||||
args.into_iter().map(|os_str| {
|
args.into_iter().map(|os_str| {
|
||||||
os_str.to_str().expect(
|
os_str.to_str().expect(
|
||||||
"Roc does not currently support passing non-UTF8 arguments to Wasmer.",
|
"Roc does not currently support passing non-UTF8 arguments to Wasm.",
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1239,38 +1239,33 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "run-wasm32")]
|
#[cfg(feature = "run-wasm32")]
|
||||||
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(wasm_path: &std::path::Path, args: I) {
|
fn run_wasm<I: Iterator<Item = S>, S: AsRef<[u8]>>(wasm_path: &std::path::Path, args: I) {
|
||||||
use wasmer::{Instance, Module, Store};
|
use bumpalo::collections::Vec;
|
||||||
|
use roc_wasm_interp::{DefaultImportDispatcher, Instance};
|
||||||
|
|
||||||
let store = Store::default();
|
let bytes = std::fs::read(wasm_path).unwrap();
|
||||||
let module = Module::from_file(&store, &wasm_path).unwrap();
|
let arena = Bump::new();
|
||||||
|
|
||||||
// First, we create the `WasiEnv`
|
let mut argv = Vec::<&[u8]>::new_in(&arena);
|
||||||
use wasmer_wasi::WasiState;
|
for arg in args {
|
||||||
let mut wasi_env = WasiState::new("hello").args(args).finalize().unwrap();
|
let mut arg_copy = Vec::<u8>::new_in(&arena);
|
||||||
|
arg_copy.extend_from_slice(arg.as_ref());
|
||||||
// Then, we get the import object related to our WASI
|
argv.push(arg_copy.into_bump_slice());
|
||||||
// and attach it to the Wasm instance.
|
|
||||||
let import_object = wasi_env.import_object(&module).unwrap();
|
|
||||||
|
|
||||||
let instance = Instance::new(&module, &import_object).unwrap();
|
|
||||||
|
|
||||||
let start = instance.exports.get_function("_start").unwrap();
|
|
||||||
|
|
||||||
use wasmer_wasi::WasiError;
|
|
||||||
match start.call(&[]) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => match e.downcast::<WasiError>() {
|
|
||||||
Ok(WasiError::Exit(0)) => {
|
|
||||||
// we run the `_start` function, so exit(0) is expected
|
|
||||||
}
|
|
||||||
other => panic!("Wasmer error: {:?}", other),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
let import_dispatcher = DefaultImportDispatcher::new(&argv);
|
||||||
|
|
||||||
|
let mut instance = Instance::from_bytes(&arena, &bytes, import_dispatcher, false).unwrap();
|
||||||
|
|
||||||
|
instance
|
||||||
|
.call_export("_start", [])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.expect_i32()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "run-wasm32"))]
|
#[cfg(not(feature = "run-wasm32"))]
|
||||||
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(_wasm_path: &std::path::Path, _args: I) {
|
fn run_wasm<I: Iterator<Item = S>, S: AsRef<[u8]>>(_wasm_path: &std::path::Path, _args: I) {
|
||||||
println!("Running wasm files is not supported on this target.");
|
println!("Running wasm files is not supported on this target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -560,7 +560,7 @@ mod cli_run {
|
||||||
test_roc_app(
|
test_roc_app(
|
||||||
"crates/cli_testing_examples/expects",
|
"crates/cli_testing_examples/expects",
|
||||||
"expects.roc",
|
"expects.roc",
|
||||||
"expects",
|
"expects-test",
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
@ -568,7 +568,7 @@ mod cli_run {
|
||||||
r#"
|
r#"
|
||||||
This expectation failed:
|
This expectation failed:
|
||||||
|
|
||||||
14│ expect x != x
|
18│ expect x != x
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
When it failed, these variables had these values:
|
When it failed, these variables had these values:
|
||||||
|
@ -576,8 +576,11 @@ mod cli_run {
|
||||||
x : Num *
|
x : Num *
|
||||||
x = 42
|
x = 42
|
||||||
|
|
||||||
[<ignored for tests> 15:9] 42
|
[<ignored for tests> 19:9] 42
|
||||||
[<ignored for tests> 16:9] "Fjoer en ferdjer frieten oan dyn geve lea"
|
[<ignored for tests> 20:9] "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||||
|
[<ignored for tests> 13:9] "abc"
|
||||||
|
[<ignored for tests> 13:9] 10
|
||||||
|
[<ignored for tests> 13:9] A (B C)
|
||||||
Program finished!
|
Program finished!
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -588,7 +591,7 @@ mod cli_run {
|
||||||
test_roc_app(
|
test_roc_app(
|
||||||
"crates/cli_testing_examples/expects",
|
"crates/cli_testing_examples/expects",
|
||||||
"expects.roc",
|
"expects.roc",
|
||||||
"expects",
|
"expects-test",
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
&[],
|
&[],
|
||||||
|
@ -609,7 +612,7 @@ mod cli_run {
|
||||||
|
|
||||||
b : Num *
|
b : Num *
|
||||||
b = 2
|
b = 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1 failed and 0 passed in <ignored for test> ms."#
|
1 failed and 0 passed in <ignored for test> ms."#
|
||||||
|
@ -713,6 +716,16 @@ mod cli_run {
|
||||||
assert!(out.status.success());
|
assert!(out.status.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: write a new test once mono bugs are resolved in investigation
|
||||||
|
#[test]
|
||||||
|
#[serial(cli_platform)]
|
||||||
|
#[cfg_attr(windows, ignore)]
|
||||||
|
fn cli_virtual_dom_check() {
|
||||||
|
let path = file_path_from_root("examples/virtual-dom-wip", "app-server.roc");
|
||||||
|
let out = run_roc(&[CMD_CHECK, path.to_str().unwrap()], &[], &[]);
|
||||||
|
assert!(out.status.success());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(windows, ignore)]
|
#[cfg_attr(windows, ignore)]
|
||||||
fn interactive_effects() {
|
fn interactive_effects() {
|
||||||
|
@ -1002,7 +1015,7 @@ mod cli_run {
|
||||||
let mut path = file.with_file_name(executable_filename);
|
let mut path = file.with_file_name(executable_filename);
|
||||||
path.set_extension("wasm");
|
path.set_extension("wasm");
|
||||||
|
|
||||||
let stdout = crate::run_with_wasmer(&path, stdin);
|
let stdout = crate::run_wasm(&path, stdin);
|
||||||
|
|
||||||
if !stdout.ends_with(expected_ending) {
|
if !stdout.ends_with(expected_ending) {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -1227,6 +1240,40 @@ mod cli_run {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_thunk)]
|
||||||
|
#[cfg_attr(windows, ignore)]
|
||||||
|
fn run_packages_unoptimized() {
|
||||||
|
check_output_with_stdin(
|
||||||
|
&fixture_file("packages", "app.roc"),
|
||||||
|
&[],
|
||||||
|
"packages-test",
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
"Hello, World! This text came from a package! This text came from a CSV package!\n",
|
||||||
|
UseValgrind::Yes,
|
||||||
|
TestCliCommands::Run,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(multi_dep_thunk)]
|
||||||
|
#[cfg_attr(windows, ignore)]
|
||||||
|
fn run_packages_optimized() {
|
||||||
|
check_output_with_stdin(
|
||||||
|
&fixture_file("packages", "app.roc"),
|
||||||
|
&[],
|
||||||
|
"packages-test",
|
||||||
|
&[OPTIMIZE_FLAG],
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
"Hello, World! This text came from a package! This text came from a CSV package!\n",
|
||||||
|
UseValgrind::Yes,
|
||||||
|
TestCliCommands::Run,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn known_type_error() {
|
fn known_type_error() {
|
||||||
check_compile_error(
|
check_compile_error(
|
||||||
|
@ -1351,75 +1398,49 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String {
|
fn run_wasm(wasm_path: &std::path::Path, stdin: &[&str]) -> String {
|
||||||
use std::io::Write;
|
use bumpalo::Bump;
|
||||||
use wasmer::{Instance, Module, Store};
|
use roc_wasm_interp::{DefaultImportDispatcher, Instance, Value, WasiFile};
|
||||||
|
|
||||||
// std::process::Command::new("cp")
|
let wasm_bytes = std::fs::read(wasm_path).unwrap();
|
||||||
// .args(&[
|
let arena = Bump::new();
|
||||||
// wasm_path.to_str().unwrap(),
|
|
||||||
// "/home/folkertdev/roc/wasm/nqueens.wasm",
|
|
||||||
// ])
|
|
||||||
// .output()
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
let store = Store::default();
|
let mut instance = {
|
||||||
let module = Module::from_file(&store, wasm_path).unwrap();
|
let mut fake_stdin = vec![];
|
||||||
|
let fake_stdout = vec![];
|
||||||
|
let fake_stderr = vec![];
|
||||||
|
for s in stdin {
|
||||||
|
fake_stdin.extend_from_slice(s.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
let mut fake_stdin = wasmer_wasi::Pipe::new();
|
let mut dispatcher = DefaultImportDispatcher::default();
|
||||||
let fake_stdout = wasmer_wasi::Pipe::new();
|
dispatcher.wasi.files = vec![
|
||||||
let fake_stderr = wasmer_wasi::Pipe::new();
|
WasiFile::ReadOnly(fake_stdin),
|
||||||
|
WasiFile::WriteOnly(fake_stdout),
|
||||||
|
WasiFile::WriteOnly(fake_stderr),
|
||||||
|
];
|
||||||
|
|
||||||
for line in stdin {
|
Instance::from_bytes(&arena, &wasm_bytes, dispatcher, false).unwrap()
|
||||||
write!(fake_stdin, "{}", line).unwrap();
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// First, we create the `WasiEnv`
|
let result = instance.call_export("_start", []);
|
||||||
use wasmer_wasi::WasiState;
|
|
||||||
let mut wasi_env = WasiState::new("hello")
|
|
||||||
.stdin(Box::new(fake_stdin))
|
|
||||||
.stdout(Box::new(fake_stdout))
|
|
||||||
.stderr(Box::new(fake_stderr))
|
|
||||||
.finalize()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Then, we get the import object related to our WASI
|
match result {
|
||||||
// and attach it to the Wasm instance.
|
Ok(Some(Value::I32(0))) => match &instance.import_dispatcher.wasi.files[1] {
|
||||||
let import_object = wasi_env
|
WasiFile::WriteOnly(fake_stdout) => String::from_utf8(fake_stdout.clone())
|
||||||
.import_object(&module)
|
.unwrap_or_else(|_| "Wasm test printed invalid UTF-8".into()),
|
||||||
.unwrap_or_else(|_| wasmer::imports!());
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
let instance = Instance::new(&module, &import_object).unwrap();
|
Ok(Some(Value::I32(exit_code))) => {
|
||||||
|
format!("WASI app exit code {}", exit_code)
|
||||||
let start = instance.exports.get_function("_start").unwrap();
|
}
|
||||||
|
Ok(Some(val)) => {
|
||||||
match start.call(&[]) {
|
format!("WASI _start returned an unexpected number type {:?}", val)
|
||||||
Ok(_) => read_wasi_stdout(wasi_env),
|
}
|
||||||
|
Ok(None) => "WASI _start returned no value".into(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
use wasmer_wasi::WasiError;
|
format!("WASI error {}", e)
|
||||||
match e.downcast::<WasiError>() {
|
|
||||||
Ok(WasiError::Exit(0)) => {
|
|
||||||
// we run the `_start` function, so exit(0) is expected
|
|
||||||
read_wasi_stdout(wasi_env)
|
|
||||||
}
|
|
||||||
other => format!("Something went wrong running a wasm test: {:?}", other),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn read_wasi_stdout(wasi_env: wasmer_wasi::WasiEnv) -> String {
|
|
||||||
let mut state = wasi_env.state.lock().unwrap();
|
|
||||||
|
|
||||||
match state.fs.stdout_mut() {
|
|
||||||
Ok(Some(stdout)) => {
|
|
||||||
let mut buf = String::new();
|
|
||||||
stdout.read_to_string(&mut buf).unwrap();
|
|
||||||
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
1
crates/cli/tests/fixtures/.gitignore
vendored
1
crates/cli/tests/fixtures/.gitignore
vendored
|
@ -5,3 +5,4 @@ dynhost
|
||||||
libapp.so
|
libapp.so
|
||||||
metadata
|
metadata
|
||||||
preprocessedhost
|
preprocessedhost
|
||||||
|
packages-test
|
||||||
|
|
6
crates/cli/tests/fixtures/packages/app.roc
vendored
Normal file
6
crates/cli/tests/fixtures/packages/app.roc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
app "packages-test"
|
||||||
|
packages { pf: "platform/main.roc", json: "json/main.roc", csv: "csv/main.roc" }
|
||||||
|
imports [json.JsonParser, csv.Csv]
|
||||||
|
provides [main] to pf
|
||||||
|
|
||||||
|
main = "Hello, World! \(JsonParser.example) \(Csv.example)"
|
6
crates/cli/tests/fixtures/packages/csv/Csv.roc
vendored
Normal file
6
crates/cli/tests/fixtures/packages/csv/Csv.roc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
interface Csv
|
||||||
|
exposes [example]
|
||||||
|
imports []
|
||||||
|
|
||||||
|
example : Str
|
||||||
|
example = "This text came from a CSV package!"
|
3
crates/cli/tests/fixtures/packages/csv/main.roc
vendored
Normal file
3
crates/cli/tests/fixtures/packages/csv/main.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package "csv"
|
||||||
|
exposes [Csv]
|
||||||
|
packages {}
|
6
crates/cli/tests/fixtures/packages/json/JsonParser.roc
vendored
Normal file
6
crates/cli/tests/fixtures/packages/json/JsonParser.roc
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
interface JsonParser
|
||||||
|
exposes [example]
|
||||||
|
imports []
|
||||||
|
|
||||||
|
example : Str
|
||||||
|
example = "This text came from a package!"
|
3
crates/cli/tests/fixtures/packages/json/main.roc
vendored
Normal file
3
crates/cli/tests/fixtures/packages/json/main.roc
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package "json"
|
||||||
|
exposes [JsonParser]
|
||||||
|
packages {}
|
127
crates/cli/tests/fixtures/packages/platform/host.zig
vendored
Normal file
127
crates/cli/tests/fixtures/packages/platform/host.zig
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const str = @import("str");
|
||||||
|
const RocStr = str.RocStr;
|
||||||
|
const testing = std.testing;
|
||||||
|
const expectEqual = testing.expectEqual;
|
||||||
|
const expect = testing.expect;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||||
|
// which is only necessary on macOS.
|
||||||
|
//
|
||||||
|
// Once that issue is fixed, we can undo the changes in
|
||||||
|
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||||
|
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||||
|
// workaround is present in many host.zig files, so make sure to undo
|
||||||
|
// it everywhere!
|
||||||
|
if (builtin.os.tag == .macos) {
|
||||||
|
_ = @import("compiler_rt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mem = std.mem;
|
||||||
|
const Allocator = mem.Allocator;
|
||||||
|
|
||||||
|
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
|
||||||
|
|
||||||
|
const Align = 2 * @alignOf(usize);
|
||||||
|
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
|
||||||
|
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||||
|
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
|
||||||
|
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
|
||||||
|
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
|
||||||
|
|
||||||
|
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
_ = alignment;
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
|
||||||
|
_ = old_size;
|
||||||
|
_ = alignment;
|
||||||
|
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
|
||||||
|
_ = alignment;
|
||||||
|
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
|
||||||
|
return memcpy(dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
|
||||||
|
return memset(dst, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
|
||||||
|
_ = tag_id;
|
||||||
|
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||||
|
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||||
|
std.process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn kill(pid: c_int, sig: c_int) c_int;
|
||||||
|
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
||||||
|
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
||||||
|
extern fn getppid() c_int;
|
||||||
|
|
||||||
|
fn roc_getppid() callconv(.C) c_int {
|
||||||
|
return getppid();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc_getppid_windows_stub() callconv(.C) c_int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
|
||||||
|
return shm_open(name, oflag, mode);
|
||||||
|
}
|
||||||
|
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
|
||||||
|
return mmap(addr, length, prot, flags, fd, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||||
|
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
|
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
|
||||||
|
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Unit = extern struct {};
|
||||||
|
|
||||||
|
pub export fn main() i32 {
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
const stderr = std.io.getStdErr().writer();
|
||||||
|
|
||||||
|
var timer = std.time.Timer.start() catch unreachable;
|
||||||
|
|
||||||
|
// actually call roc to populate the callresult
|
||||||
|
var callresult = RocStr.empty();
|
||||||
|
roc__mainForHost_1_exposed_generic(&callresult);
|
||||||
|
|
||||||
|
const nanos = timer.read();
|
||||||
|
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
|
||||||
|
|
||||||
|
// stdout the result
|
||||||
|
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
||||||
|
|
||||||
|
callresult.deinit();
|
||||||
|
|
||||||
|
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_seconds(tms: std.os.timespec) f64 {
|
||||||
|
return @intToFloat(f64, tms.tv_sec) + (@intToFloat(f64, tms.tv_nsec) / 1_000_000_000.0);
|
||||||
|
}
|
9
crates/cli/tests/fixtures/packages/platform/main.roc
vendored
Normal file
9
crates/cli/tests/fixtures/packages/platform/main.roc
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
platform "multi-module"
|
||||||
|
requires {}{ main : Str }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [mainForHost]
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
1
crates/cli_testing_examples/.gitignore
vendored
1
crates/cli_testing_examples/.gitignore
vendored
|
@ -4,3 +4,4 @@ libapp.so
|
||||||
dynhost
|
dynhost
|
||||||
preprocessedhost
|
preprocessedhost
|
||||||
metadata
|
metadata
|
||||||
|
expects-test
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
app "expects"
|
app "expects-test"
|
||||||
packages { pf: "zig-platform/main.roc" }
|
packages { pf: "zig-platform/main.roc" }
|
||||||
imports []
|
imports []
|
||||||
provides [main] to pf
|
provides [main] to pf
|
||||||
|
@ -9,9 +9,17 @@ expect
|
||||||
|
|
||||||
a == b
|
a == b
|
||||||
|
|
||||||
|
polyDbg = \x ->
|
||||||
|
dbg x
|
||||||
|
x
|
||||||
|
|
||||||
main =
|
main =
|
||||||
x = 42
|
x = 42
|
||||||
expect x != x
|
expect x != x
|
||||||
dbg x
|
dbg x
|
||||||
dbg "Fjoer en ferdjer frieten oan dyn geve lea"
|
dbg "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||||
"Program finished!\n"
|
|
||||||
|
r = {x : polyDbg "abc", y: polyDbg 10u8, z : polyDbg (A (B C))}
|
||||||
|
|
||||||
|
when r is
|
||||||
|
_ -> "Program finished!\n"
|
||||||
|
|
|
@ -5,6 +5,7 @@ extern crate roc_module;
|
||||||
extern crate tempfile;
|
extern crate tempfile;
|
||||||
|
|
||||||
use roc_utils::cargo;
|
use roc_utils::cargo;
|
||||||
|
use roc_utils::pretty_command_string;
|
||||||
use roc_utils::root_dir;
|
use roc_utils::root_dir;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_xml_rs::from_str;
|
use serde_xml_rs::from_str;
|
||||||
|
@ -447,15 +448,3 @@ pub fn known_bad_file(file_name: &str) -> PathBuf {
|
||||||
|
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pretty_command_string(command: &Command) -> OsString {
|
|
||||||
let mut command_string = std::ffi::OsString::new();
|
|
||||||
command_string.push(command.get_program());
|
|
||||||
|
|
||||||
for arg in command.get_args() {
|
|
||||||
command_string.push(" ");
|
|
||||||
command_string.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
command_string
|
|
||||||
}
|
|
||||||
|
|
|
@ -1287,6 +1287,14 @@ fn lowlevel_spec<'a>(
|
||||||
|
|
||||||
builder.add_make_tuple(block, &[byte_index, string, is_ok, problem_code])
|
builder.add_make_tuple(block, &[byte_index, string, is_ok, problem_code])
|
||||||
}
|
}
|
||||||
|
Dbg => {
|
||||||
|
let arguments = [env.symbols[&arguments[0]]];
|
||||||
|
|
||||||
|
let result_type =
|
||||||
|
layout_spec(env, builder, interner, layout, &WhenRecursive::Unreachable)?;
|
||||||
|
|
||||||
|
builder.add_unknown_with(block, &arguments, result_type)
|
||||||
|
}
|
||||||
_other => {
|
_other => {
|
||||||
// println!("missing {:?}", _other);
|
// println!("missing {:?}", _other);
|
||||||
// TODO overly pessimstic
|
// TODO overly pessimstic
|
||||||
|
|
|
@ -47,3 +47,7 @@ target-aarch64 = ["roc_gen_dev/target-aarch64"]
|
||||||
target-x86 = []
|
target-x86 = []
|
||||||
target-x86_64 = ["roc_gen_dev/target-x86_64"]
|
target-x86_64 = ["roc_gen_dev/target-x86_64"]
|
||||||
target-wasm32 = []
|
target-wasm32 = []
|
||||||
|
|
||||||
|
# This is used to enable fuzzing and sanitizers.
|
||||||
|
# Example use is describe here: https://github.com/bhansconnect/roc-fuzz
|
||||||
|
sanitizers = []
|
||||||
|
|
|
@ -1248,14 +1248,17 @@ fn link_macos(
|
||||||
input_paths: &[&str],
|
input_paths: &[&str],
|
||||||
link_type: LinkType,
|
link_type: LinkType,
|
||||||
) -> io::Result<(Child, PathBuf)> {
|
) -> io::Result<(Child, PathBuf)> {
|
||||||
let (link_type_arg, output_path) = match link_type {
|
let (link_type_args, output_path) = match link_type {
|
||||||
LinkType::Executable => ("-execute", output_path),
|
LinkType::Executable => (vec!["-execute"], output_path),
|
||||||
LinkType::Dylib => {
|
LinkType::Dylib => {
|
||||||
let mut output_path = output_path;
|
let mut output_path = output_path;
|
||||||
|
|
||||||
output_path.set_extension("dylib");
|
output_path.set_extension("dylib");
|
||||||
|
|
||||||
("-dylib", output_path)
|
(
|
||||||
|
vec!["-dylib", "-undefined", "dynamic_lookup", "-no_fixup_chains"],
|
||||||
|
output_path,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
||||||
};
|
};
|
||||||
|
@ -1272,13 +1275,13 @@ fn link_macos(
|
||||||
// The `-l` flags should go after the `.o` arguments
|
// The `-l` flags should go after the `.o` arguments
|
||||||
// Don't allow LD_ env vars to affect this
|
// Don't allow LD_ env vars to affect this
|
||||||
.env_clear()
|
.env_clear()
|
||||||
|
.args(&link_type_args)
|
||||||
.args([
|
.args([
|
||||||
// NOTE: we don't do --gc-sections on macOS because the default
|
// NOTE: we don't do --gc-sections on macOS because the default
|
||||||
// macOS linker doesn't support it, but it's a performance
|
// macOS linker doesn't support it, but it's a performance
|
||||||
// optimization, so if we ever switch to a different linker,
|
// optimization, so if we ever switch to a different linker,
|
||||||
// we'd like to re-enable it on macOS!
|
// we'd like to re-enable it on macOS!
|
||||||
// "--gc-sections",
|
// "--gc-sections",
|
||||||
link_type_arg,
|
|
||||||
"-arch",
|
"-arch",
|
||||||
&arch,
|
&arch,
|
||||||
"-macos_version_min",
|
"-macos_version_min",
|
||||||
|
|
|
@ -237,15 +237,94 @@ fn gen_from_mono_module_llvm<'a>(
|
||||||
|
|
||||||
// annotate the LLVM IR output with debug info
|
// annotate the LLVM IR output with debug info
|
||||||
// so errors are reported with the line number of the LLVM source
|
// so errors are reported with the line number of the LLVM source
|
||||||
let memory_buffer = if emit_debug_info {
|
let memory_buffer = if cfg!(feature = "sanitizers") && std::env::var("ROC_SANITIZERS").is_ok() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let dir = dir.into_path();
|
||||||
|
|
||||||
|
let app_ll_file = dir.join("app.ll");
|
||||||
|
let app_bc_file = dir.join("app.bc");
|
||||||
|
let app_o_file = dir.join("app.o");
|
||||||
|
|
||||||
|
// write the ll code to a file, so we can modify it
|
||||||
|
module.print_to_file(&app_ll_file).unwrap();
|
||||||
|
|
||||||
|
// Apply coverage passes.
|
||||||
|
// Note, this is specifically tailored for `cargo afl` and afl++.
|
||||||
|
// It most likely will not work with other fuzzer setups without modification.
|
||||||
|
let mut passes = vec![];
|
||||||
|
let mut extra_args = vec![];
|
||||||
|
let mut unrecognized = vec![];
|
||||||
|
for sanitizer in std::env::var("ROC_SANITIZERS")
|
||||||
|
.unwrap()
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.trim())
|
||||||
|
{
|
||||||
|
match sanitizer {
|
||||||
|
"address" => passes.push("asan-module"),
|
||||||
|
"memory" => passes.push("msan-module"),
|
||||||
|
"thread" => passes.push("tsan-module"),
|
||||||
|
"fuzzer" => {
|
||||||
|
passes.push("sancov-module");
|
||||||
|
extra_args.extend_from_slice(&[
|
||||||
|
"-sanitizer-coverage-level=3",
|
||||||
|
"-sanitizer-coverage-prune-blocks=0",
|
||||||
|
"-sanitizer-coverage-trace-pc-guard",
|
||||||
|
// This can be used instead of the line above to enable working with `cargo fuzz` and libFuzzer.
|
||||||
|
// "-sanitizer-coverage-inline-8bit-counters",
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
x => unrecognized.push(x.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !unrecognized.is_empty() {
|
||||||
|
let out = unrecognized
|
||||||
|
.iter()
|
||||||
|
.map(|x| format!("{:?}", x))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ");
|
||||||
|
eprintln!("Unrecognized sanitizer: {}\nSupported options are \"address\", \"memory\", \"thread\", and \"fuzzer\"", out);
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
let mut opt = Command::new("opt");
|
||||||
|
opt.args([
|
||||||
|
app_ll_file.to_str().unwrap(),
|
||||||
|
"-o",
|
||||||
|
app_bc_file.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.args(extra_args);
|
||||||
|
if !passes.is_empty() {
|
||||||
|
opt.arg(format!("-passes={}", passes.join(",")));
|
||||||
|
}
|
||||||
|
let opt = opt.output().unwrap();
|
||||||
|
|
||||||
|
assert!(opt.stderr.is_empty(), "{:#?}", opt);
|
||||||
|
|
||||||
|
// write the .o file. Note that this builds the .o for the local machine,
|
||||||
|
// and ignores the `target_machine` entirely.
|
||||||
|
//
|
||||||
|
// different systems name this executable differently, so we shotgun for
|
||||||
|
// the most common ones and then give up.
|
||||||
|
let bc_to_object = Command::new("llc")
|
||||||
|
.args(&[
|
||||||
|
"-relocation-model=pic",
|
||||||
|
"-filetype=obj",
|
||||||
|
app_bc_file.to_str().unwrap(),
|
||||||
|
"-o",
|
||||||
|
app_o_file.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(bc_to_object.status.success(), "{:#?}", bc_to_object);
|
||||||
|
|
||||||
|
MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works")
|
||||||
|
} else if emit_debug_info {
|
||||||
module.strip_debug_info();
|
module.strip_debug_info();
|
||||||
|
|
||||||
let mut app_ll_dbg_file = PathBuf::from(roc_file_path);
|
let mut app_ll_dbg_file = PathBuf::from(roc_file_path);
|
||||||
app_ll_dbg_file.set_extension("dbg.ll");
|
app_ll_dbg_file.set_extension("dbg.ll");
|
||||||
|
|
||||||
let mut app_bc_file = PathBuf::from(roc_file_path);
|
|
||||||
app_bc_file.set_extension("bc");
|
|
||||||
|
|
||||||
let mut app_o_file = PathBuf::from(roc_file_path);
|
let mut app_o_file = PathBuf::from(roc_file_path);
|
||||||
app_o_file.set_extension("o");
|
app_o_file.set_extension("o");
|
||||||
|
|
||||||
|
@ -277,33 +356,23 @@ fn gen_from_mono_module_llvm<'a>(
|
||||||
| Architecture::X86_32(_)
|
| Architecture::X86_32(_)
|
||||||
| Architecture::Aarch64(_)
|
| Architecture::Aarch64(_)
|
||||||
| Architecture::Wasm32 => {
|
| Architecture::Wasm32 => {
|
||||||
let ll_to_bc = Command::new("llvm-as")
|
|
||||||
.args([
|
|
||||||
app_ll_dbg_file.to_str().unwrap(),
|
|
||||||
"-o",
|
|
||||||
app_bc_file.to_str().unwrap(),
|
|
||||||
])
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(ll_to_bc.stderr.is_empty(), "{:#?}", ll_to_bc);
|
|
||||||
|
|
||||||
let llc_args = &[
|
|
||||||
"-relocation-model=pic",
|
|
||||||
"-filetype=obj",
|
|
||||||
app_bc_file.to_str().unwrap(),
|
|
||||||
"-o",
|
|
||||||
app_o_file.to_str().unwrap(),
|
|
||||||
];
|
|
||||||
|
|
||||||
// write the .o file. Note that this builds the .o for the local machine,
|
// write the .o file. Note that this builds the .o for the local machine,
|
||||||
// and ignores the `target_machine` entirely.
|
// and ignores the `target_machine` entirely.
|
||||||
//
|
//
|
||||||
// different systems name this executable differently, so we shotgun for
|
// different systems name this executable differently, so we shotgun for
|
||||||
// the most common ones and then give up.
|
// the most common ones and then give up.
|
||||||
let bc_to_object = Command::new("llc").args(llc_args).output().unwrap();
|
let ll_to_object = Command::new("llc")
|
||||||
|
.args(&[
|
||||||
|
"-relocation-model=pic",
|
||||||
|
"-filetype=obj",
|
||||||
|
app_ll_dbg_file.to_str().unwrap(),
|
||||||
|
"-o",
|
||||||
|
app_o_file.to_str().unwrap(),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert!(bc_to_object.stderr.is_empty(), "{:#?}", bc_to_object);
|
assert!(ll_to_object.stderr.is_empty(), "{:#?}", ll_to_object);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
# Test failures will always point at the _start function
|
# For non-native binaries, Zig test needs a "test command" it can use
|
||||||
# Make sure to look at the rest of the stack trace!
|
cargo build --locked --release -p roc_wasm_interp
|
||||||
|
zig test -target wasm32-wasi-musl -O ReleaseFast src/main.zig --test-cmd ../../../../target/release/roc_wasm_interp --test-cmd-bin
|
||||||
# Zig will try to run the test binary it produced, but it is a wasm object and hence your OS won't
|
|
||||||
# know how to run it. In the error message, it prints the binary it tried to run. We use some fun
|
|
||||||
# unix tools to get that path, then feed it to wasmer
|
|
||||||
zig test -target wasm32-wasi-musl -O ReleaseFast src/main.zig --test-cmd wasmer --test-cmd-bin
|
|
||||||
|
|
|
@ -90,19 +90,19 @@ fn testing_roc_memcpy(dest: *anyopaque, src: *anyopaque, bytes: usize) callconv(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc(size: usize, alignment: u32) ?[*]u8 {
|
pub fn alloc(size: usize, alignment: u32) ?[*]u8 {
|
||||||
return @ptrCast(?[*]u8, @call(.{ .modifier = always_inline }, roc_alloc, .{ size, alignment }));
|
return @ptrCast(?[*]u8, roc_alloc(size, alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn realloc(c_ptr: [*]u8, new_size: usize, old_size: usize, alignment: u32) [*]u8 {
|
pub fn realloc(c_ptr: [*]u8, new_size: usize, old_size: usize, alignment: u32) [*]u8 {
|
||||||
return @ptrCast([*]u8, @call(.{ .modifier = always_inline }, roc_realloc, .{ c_ptr, new_size, old_size, alignment }));
|
return @ptrCast([*]u8, roc_realloc(c_ptr, new_size, old_size, alignment));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
|
pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
|
||||||
return @call(.{ .modifier = always_inline }, roc_dealloc, .{ c_ptr, alignment });
|
return roc_dealloc(c_ptr, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
|
pub fn memcpy(dst: [*]u8, src: [*]u8, size: usize) void {
|
||||||
@call(.{ .modifier = always_inline }, roc_memcpy, .{ dst, src, size });
|
roc_memcpy(dst, src, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// indirection because otherwise zig creates an alias to the panic function which our LLVM code
|
// indirection because otherwise zig creates an alias to the panic function which our LLVM code
|
||||||
|
@ -275,7 +275,8 @@ pub inline fn calculateCapacity(
|
||||||
} else {
|
} else {
|
||||||
new_capacity = (old_capacity * 3 + 1) / 2;
|
new_capacity = (old_capacity * 3 + 1) / 2;
|
||||||
}
|
}
|
||||||
return @maximum(new_capacity, requested_length);
|
|
||||||
|
return std.math.max(new_capacity, requested_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocateWithRefcountC(
|
pub fn allocateWithRefcountC(
|
||||||
|
|
|
@ -192,7 +192,8 @@ fn cp_unless_zig_cache(src_dir: &Path, target_dir: &Path) -> io::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_command(mut command: Command, flaky_fail_counter: usize) {
|
fn run_command(mut command: Command, flaky_fail_counter: usize) {
|
||||||
let command_str = format!("{:?}", &command);
|
let command_str = roc_utils::pretty_command_string(&command);
|
||||||
|
let command_str = command_str.to_string_lossy();
|
||||||
|
|
||||||
let output_result = command.output();
|
let output_result = command.output();
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,6 @@ pub enum DecWidth {
|
||||||
pub enum FloatWidth {
|
pub enum FloatWidth {
|
||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
F128,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatWidth {
|
impl FloatWidth {
|
||||||
|
@ -86,7 +85,6 @@ impl FloatWidth {
|
||||||
match self {
|
match self {
|
||||||
F32 => 4,
|
F32 => 4,
|
||||||
F64 => 8,
|
F64 => 8,
|
||||||
F128 => 16,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +97,7 @@ impl FloatWidth {
|
||||||
// the compiler is targeting (e.g. what the Roc code will be compiled to).
|
// the compiler is targeting (e.g. what the Roc code will be compiled to).
|
||||||
match self {
|
match self {
|
||||||
F32 => 4,
|
F32 => 4,
|
||||||
F64 | F128 => match target_info.architecture {
|
F64 => match target_info.architecture {
|
||||||
X86_64 | Aarch64 | Wasm32 => 8,
|
X86_64 | Aarch64 | Wasm32 => 8,
|
||||||
X86_32 | Aarch32 => 4,
|
X86_32 | Aarch32 => 4,
|
||||||
},
|
},
|
||||||
|
@ -225,7 +223,6 @@ impl Index<FloatWidth> for IntrinsicName {
|
||||||
match index {
|
match index {
|
||||||
FloatWidth::F32 => self.options[1],
|
FloatWidth::F32 => self.options[1],
|
||||||
FloatWidth::F64 => self.options[2],
|
FloatWidth::F64 => self.options[2],
|
||||||
FloatWidth::F128 => self.options[3],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,7 +253,6 @@ macro_rules! float_intrinsic {
|
||||||
|
|
||||||
output.options[1] = concat!($name, ".f32");
|
output.options[1] = concat!($name, ".f32");
|
||||||
output.options[2] = concat!($name, ".f64");
|
output.options[2] = concat!($name, ".f64");
|
||||||
output.options[3] = concat!($name, ".f128");
|
|
||||||
|
|
||||||
output
|
output
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -21,6 +21,8 @@ bumpalo.workspace = true
|
||||||
static_assertions.workspace = true
|
static_assertions.workspace = true
|
||||||
bitvec.workspace = true
|
bitvec.workspace = true
|
||||||
|
|
||||||
|
ven_pretty = { path = "../../vendor/pretty" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions.workspace = true
|
pretty_assertions.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
|
|
5
crates/compiler/can/src/debug.rs
Normal file
5
crates/compiler/can/src/debug.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod pretty_print;
|
||||||
|
|
||||||
|
pub use pretty_print::pretty_print_declarations;
|
||||||
|
pub use pretty_print::pretty_print_def;
|
||||||
|
pub use pretty_print::Ctx as PPCtx;
|
|
@ -1,16 +1,50 @@
|
||||||
//! Pretty-prints the canonical AST back to check our work - do things look reasonable?
|
//! Pretty-prints the canonical AST back to check our work - do things look reasonable?
|
||||||
|
|
||||||
use roc_can::def::Def;
|
use crate::def::Def;
|
||||||
use roc_can::expr::Expr::{self, *};
|
use crate::expr::Expr::{self, *};
|
||||||
use roc_can::expr::{ClosureData, OpaqueWrapFunctionData, WhenBranch};
|
use crate::expr::{
|
||||||
use roc_can::pattern::{Pattern, RecordDestruct};
|
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch,
|
||||||
|
};
|
||||||
|
use crate::pattern::{Pattern, RecordDestruct};
|
||||||
|
|
||||||
use roc_module::symbol::Interns;
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
|
||||||
use ven_pretty::{Arena, DocAllocator, DocBuilder};
|
use ven_pretty::{Arena, DocAllocator, DocBuilder};
|
||||||
|
|
||||||
pub struct Ctx<'a> {
|
pub struct Ctx<'a> {
|
||||||
|
pub home: ModuleId,
|
||||||
pub interns: &'a Interns,
|
pub interns: &'a Interns,
|
||||||
|
pub print_lambda_names: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print_declarations(c: &Ctx, declarations: &Declarations) -> String {
|
||||||
|
let f = Arena::new();
|
||||||
|
let mut defs = Vec::with_capacity(declarations.len());
|
||||||
|
for (index, tag) in declarations.iter_bottom_up() {
|
||||||
|
let symbol = declarations.symbols[index].value;
|
||||||
|
let body = &declarations.expressions[index];
|
||||||
|
|
||||||
|
let def = match tag {
|
||||||
|
DeclarationTag::Value => def_symbol_help(c, &f, symbol, &body.value),
|
||||||
|
DeclarationTag::Function(f_index)
|
||||||
|
| DeclarationTag::Recursive(f_index)
|
||||||
|
| DeclarationTag::TailRecursive(f_index) => {
|
||||||
|
let function_def = &declarations.function_bodies[f_index.index()].value;
|
||||||
|
toplevel_function(c, &f, symbol, function_def, &body.value)
|
||||||
|
}
|
||||||
|
DeclarationTag::Expectation => todo!(),
|
||||||
|
DeclarationTag::ExpectationFx => todo!(),
|
||||||
|
DeclarationTag::Destructure(_) => todo!(),
|
||||||
|
DeclarationTag::MutualRecursion { .. } => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
defs.push(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.intersperse(defs, f.hardline().append(f.hardline()))
|
||||||
|
.1
|
||||||
|
.pretty(80)
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
||||||
|
@ -40,10 +74,58 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> {
|
||||||
annotation: _,
|
annotation: _,
|
||||||
} = d;
|
} = d;
|
||||||
|
|
||||||
pattern(c, PPrec::Free, f, &loc_pattern.value)
|
def_help(c, f, &loc_pattern.value, &loc_expr.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_symbol_help<'a>(
|
||||||
|
c: &Ctx,
|
||||||
|
f: &'a Arena<'a>,
|
||||||
|
sym: Symbol,
|
||||||
|
body: &'a Expr,
|
||||||
|
) -> DocBuilder<'a, Arena<'a>> {
|
||||||
|
pp_sym(c, f, sym)
|
||||||
.append(f.text(" ="))
|
.append(f.text(" ="))
|
||||||
.append(f.line())
|
.append(f.line())
|
||||||
.append(expr(c, EPrec::Free, f, &loc_expr.value))
|
.append(expr(c, EPrec::Free, f, body))
|
||||||
|
.nest(2)
|
||||||
|
.group()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_help<'a>(
|
||||||
|
c: &Ctx,
|
||||||
|
f: &'a Arena<'a>,
|
||||||
|
pat: &'a Pattern,
|
||||||
|
body: &'a Expr,
|
||||||
|
) -> DocBuilder<'a, Arena<'a>> {
|
||||||
|
pattern(c, PPrec::Free, f, pat)
|
||||||
|
.append(f.text(" ="))
|
||||||
|
.append(f.line())
|
||||||
|
.append(expr(c, EPrec::Free, f, body))
|
||||||
|
.nest(2)
|
||||||
|
.group()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toplevel_function<'a>(
|
||||||
|
c: &Ctx,
|
||||||
|
f: &'a Arena<'a>,
|
||||||
|
sym: Symbol,
|
||||||
|
function_def: &'a FunctionDef,
|
||||||
|
body: &'a Expr,
|
||||||
|
) -> DocBuilder<'a, Arena<'a>> {
|
||||||
|
let FunctionDef { arguments, .. } = function_def;
|
||||||
|
|
||||||
|
let args = arguments
|
||||||
|
.iter()
|
||||||
|
.map(|arg| pattern(c, PPrec::Free, f, &arg.2.value));
|
||||||
|
|
||||||
|
pp_sym(c, f, sym)
|
||||||
|
.append(f.text(" ="))
|
||||||
|
.append(f.line())
|
||||||
|
.append(f.text("\\"))
|
||||||
|
.append(f.intersperse(args, f.text(", ")))
|
||||||
|
.append(f.text("->"))
|
||||||
|
.append(f.line())
|
||||||
|
.append(expr(c, EPrec::Free, f, body))
|
||||||
.nest(2)
|
.nest(2)
|
||||||
.group()
|
.group()
|
||||||
}
|
}
|
||||||
|
@ -87,11 +169,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||||
.append("]")
|
.append("]")
|
||||||
.group(),
|
.group(),
|
||||||
),
|
),
|
||||||
Var(sym, _) | AbilityMember(sym, _, _) => f.text(format!(
|
Var(sym, _) | AbilityMember(sym, _, _) => pp_sym(c, f, *sym),
|
||||||
"{}.{}",
|
|
||||||
sym.module_string(c.interns),
|
|
||||||
sym.as_str(c.interns),
|
|
||||||
)),
|
|
||||||
When {
|
When {
|
||||||
loc_cond, branches, ..
|
loc_cond, branches, ..
|
||||||
} => maybe_paren!(
|
} => maybe_paren!(
|
||||||
|
@ -184,6 +262,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||||
Closure(ClosureData {
|
Closure(ClosureData {
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
|
name,
|
||||||
..
|
..
|
||||||
}) => f
|
}) => f
|
||||||
.text("\\")
|
.text("\\")
|
||||||
|
@ -195,7 +274,13 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||||
f.text(", "),
|
f.text(", "),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.append(f.text(" ->"))
|
.append(if c.print_lambda_names {
|
||||||
|
f.text(" -[")
|
||||||
|
.append(pp_sym(c, f, *name))
|
||||||
|
.append(f.text("]->"))
|
||||||
|
} else {
|
||||||
|
f.text(" ->")
|
||||||
|
})
|
||||||
.append(f.line())
|
.append(f.line())
|
||||||
.append(expr(c, Free, f, &loc_body.value))
|
.append(expr(c, Free, f, &loc_body.value))
|
||||||
.nest(2)
|
.nest(2)
|
||||||
|
@ -290,6 +375,18 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pp_sym<'a>(c: &Ctx, f: &'a Arena<'a>, sym: Symbol) -> DocBuilder<'a, Arena<'a>> {
|
||||||
|
if sym.module_id() == c.home {
|
||||||
|
f.text(sym.as_str(c.interns).to_owned())
|
||||||
|
} else {
|
||||||
|
f.text(format!(
|
||||||
|
"{}.{}",
|
||||||
|
sym.module_string(c.interns),
|
||||||
|
sym.as_str(c.interns),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn branch<'a>(c: &Ctx, f: &'a Arena<'a>, b: &'a WhenBranch) -> DocBuilder<'a, Arena<'a>> {
|
fn branch<'a>(c: &Ctx, f: &'a Arena<'a>, b: &'a WhenBranch) -> DocBuilder<'a, Arena<'a>> {
|
||||||
let WhenBranch {
|
let WhenBranch {
|
||||||
patterns,
|
patterns,
|
||||||
|
@ -333,11 +430,7 @@ fn pattern<'a>(
|
||||||
Identifier(sym)
|
Identifier(sym)
|
||||||
| AbilityMemberSpecialization {
|
| AbilityMemberSpecialization {
|
||||||
specializes: sym, ..
|
specializes: sym, ..
|
||||||
} => f.text(format!(
|
} => pp_sym(c, f, *sym),
|
||||||
"{}.{}",
|
|
||||||
sym.module_string(c.interns),
|
|
||||||
sym.as_str(c.interns),
|
|
||||||
)),
|
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
tag_name,
|
tag_name,
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -373,12 +466,12 @@ fn pattern<'a>(
|
||||||
f.intersperse(
|
f.intersperse(
|
||||||
destructs.iter().map(|l| &l.value).map(
|
destructs.iter().map(|l| &l.value).map(
|
||||||
|RecordDestruct { label, typ, .. }| match typ {
|
|RecordDestruct { label, typ, .. }| match typ {
|
||||||
roc_can::pattern::DestructType::Required => f.text(label.as_str()),
|
crate::pattern::DestructType::Required => f.text(label.as_str()),
|
||||||
roc_can::pattern::DestructType::Optional(_, e) => f
|
crate::pattern::DestructType::Optional(_, e) => f
|
||||||
.text(label.as_str())
|
.text(label.as_str())
|
||||||
.append(f.text(" ? "))
|
.append(f.text(" ? "))
|
||||||
.append(expr(c, EPrec::Free, f, &e.value)),
|
.append(expr(c, EPrec::Free, f, &e.value)),
|
||||||
roc_can::pattern::DestructType::Guard(_, p) => f
|
crate::pattern::DestructType::Guard(_, p) => f
|
||||||
.text(label.as_str())
|
.text(label.as_str())
|
||||||
.append(f.text(": "))
|
.append(f.text(": "))
|
||||||
.append(pattern(c, Free, f, &p.value)),
|
.append(pattern(c, Free, f, &p.value)),
|
|
@ -28,3 +28,5 @@ pub mod string;
|
||||||
pub mod traverse;
|
pub mod traverse;
|
||||||
|
|
||||||
pub use derive::DERIVED_REGION;
|
pub use derive::DERIVED_REGION;
|
||||||
|
|
||||||
|
pub mod debug;
|
||||||
|
|
|
@ -205,6 +205,7 @@ impl GeneratedInfo {
|
||||||
generates,
|
generates,
|
||||||
generates_with,
|
generates_with,
|
||||||
name: _,
|
name: _,
|
||||||
|
exposes: _,
|
||||||
} => {
|
} => {
|
||||||
let name: &str = generates.into();
|
let name: &str = generates.into();
|
||||||
let (generated_functions, unknown_generated) =
|
let (generated_functions, unknown_generated) =
|
||||||
|
@ -240,6 +241,7 @@ impl GeneratedInfo {
|
||||||
HeaderType::Builtin {
|
HeaderType::Builtin {
|
||||||
generates_with,
|
generates_with,
|
||||||
name: _,
|
name: _,
|
||||||
|
exposes: _,
|
||||||
} => {
|
} => {
|
||||||
debug_assert!(generates_with.is_empty());
|
debug_assert!(generates_with.is_empty());
|
||||||
GeneratedInfo::Builtin
|
GeneratedInfo::Builtin
|
||||||
|
|
|
@ -705,7 +705,7 @@ pub fn constrain_expr(
|
||||||
expected,
|
expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
constraints.exists_many([], [cond_con, continuation_con])
|
constraints.exists_many([*variable], [cond_con, continuation_con])
|
||||||
}
|
}
|
||||||
|
|
||||||
If {
|
If {
|
||||||
|
|
|
@ -153,6 +153,9 @@ flags! {
|
||||||
/// Writes a `final.wasm` file to /tmp
|
/// Writes a `final.wasm` file to /tmp
|
||||||
ROC_WRITE_FINAL_WASM
|
ROC_WRITE_FINAL_WASM
|
||||||
|
|
||||||
|
/// Prints Wasm interpreter debug log in test_gen
|
||||||
|
ROC_LOG_WASM_INTERP
|
||||||
|
|
||||||
// ===Load===
|
// ===Load===
|
||||||
|
|
||||||
/// Print load phases as they complete.
|
/// Print load phases as they complete.
|
||||||
|
|
|
@ -8,8 +8,8 @@ use bumpalo::Bump;
|
||||||
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
|
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
|
||||||
use roc_parse::header::{
|
use roc_parse::header::{
|
||||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||||
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry,
|
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry, PackageHeader,
|
||||||
PackageKeyword, PackagePath, PackagesKeyword, PlatformHeader, PlatformRequires,
|
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||||
};
|
};
|
||||||
use roc_parse::ident::UppercaseIdent;
|
use roc_parse::ident::UppercaseIdent;
|
||||||
|
@ -24,6 +24,9 @@ pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
|
||||||
Header::App(header) => {
|
Header::App(header) => {
|
||||||
fmt_app_header(buf, header);
|
fmt_app_header(buf, header);
|
||||||
}
|
}
|
||||||
|
Header::Package(header) => {
|
||||||
|
fmt_package_header(buf, header);
|
||||||
|
}
|
||||||
Header::Platform(header) => {
|
Header::Platform(header) => {
|
||||||
fmt_platform_header(buf, header);
|
fmt_platform_header(buf, header);
|
||||||
}
|
}
|
||||||
|
@ -226,6 +229,20 @@ pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>)
|
||||||
header.provides.format(buf, indent);
|
header.provides.format(buf, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fmt_package_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PackageHeader<'a>) {
|
||||||
|
buf.indent(0);
|
||||||
|
buf.push_str("package");
|
||||||
|
let indent = INDENT;
|
||||||
|
fmt_default_spaces(buf, header.before_name, indent);
|
||||||
|
|
||||||
|
fmt_package_name(buf, header.name.value, indent);
|
||||||
|
|
||||||
|
header.exposes.keyword.format(buf, indent);
|
||||||
|
fmt_exposes(buf, header.exposes.item, indent);
|
||||||
|
header.packages.keyword.format(buf, indent);
|
||||||
|
fmt_packages(buf, header.packages.item, indent);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
|
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
|
||||||
buf.indent(0);
|
buf.indent(0);
|
||||||
buf.push_str("platform");
|
buf.push_str("platform");
|
||||||
|
@ -276,7 +293,7 @@ impl<'a> Formattable for TypedIdent<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackagePath, _indent: u16) {
|
fn fmt_package_name<'buf>(buf: &mut Buf<'buf>, name: PackageName, _indent: u16) {
|
||||||
buf.push('"');
|
buf.push('"');
|
||||||
buf.push_str_allow_spaces(name.to_str());
|
buf.push_str_allow_spaces(name.to_str());
|
||||||
buf.push('"');
|
buf.push('"');
|
||||||
|
@ -453,7 +470,7 @@ fn fmt_packages_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &PackageEntry<'a>, i
|
||||||
buf.push_str(entry.shorthand);
|
buf.push_str(entry.shorthand);
|
||||||
buf.push(':');
|
buf.push(':');
|
||||||
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
|
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
|
||||||
fmt_package_name(buf, entry.package_path.value, indent);
|
fmt_package_name(buf, entry.package_name.value, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
fn fmt_imports_entry<'a, 'buf>(buf: &mut Buf<'buf>, entry: &ImportsEntry<'a>, indent: u16) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ use roc_parse::{
|
||||||
},
|
},
|
||||||
header::{
|
header::{
|
||||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
||||||
ModuleName, PackageEntry, PackagePath, PlatformHeader, PlatformRequires, ProvidesTo, To,
|
ModuleName, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
|
||||||
TypedIdent,
|
ProvidesTo, To, TypedIdent,
|
||||||
},
|
},
|
||||||
ident::UppercaseIdent,
|
ident::UppercaseIdent,
|
||||||
};
|
};
|
||||||
|
@ -290,6 +290,12 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||||
imports: header.imports.remove_spaces(arena),
|
imports: header.imports.remove_spaces(arena),
|
||||||
provides: header.provides.remove_spaces(arena),
|
provides: header.provides.remove_spaces(arena),
|
||||||
}),
|
}),
|
||||||
|
Header::Package(header) => Header::Package(PackageHeader {
|
||||||
|
before_name: &[],
|
||||||
|
name: header.name.remove_spaces(arena),
|
||||||
|
exposes: header.exposes.remove_spaces(arena),
|
||||||
|
packages: header.packages.remove_spaces(arena),
|
||||||
|
}),
|
||||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||||
before_name: &[],
|
before_name: &[],
|
||||||
name: header.name.remove_spaces(arena),
|
name: header.name.remove_spaces(arena),
|
||||||
|
@ -349,7 +355,7 @@ impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PackagePath<'a> {
|
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
|
@ -394,7 +400,7 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||||
PackageEntry {
|
PackageEntry {
|
||||||
shorthand: self.shorthand,
|
shorthand: self.shorthand,
|
||||||
spaces_after_shorthand: &[],
|
spaces_after_shorthand: &[],
|
||||||
package_path: self.package_path.remove_spaces(arena),
|
package_name: self.package_name.remove_spaces(arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,10 +270,9 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
(true, layout) => {
|
(true, layout) => {
|
||||||
let closure_type = basic_type_from_layout(env, &layout).ptr_type(AddressSpace::Generic);
|
let closure_type = basic_type_from_layout(env, &layout).ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let closure_cast = env
|
let closure_cast =
|
||||||
.builder
|
env.builder
|
||||||
.build_bitcast(closure_ptr, closure_type, "cast_opaque_closure")
|
.build_pointer_cast(closure_ptr, closure_type, "cast_opaque_closure");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let closure_data = load_roc_value(env, layout, closure_cast, "load_closure");
|
let closure_data = load_roc_value(env, layout, closure_cast, "load_closure");
|
||||||
|
|
||||||
|
@ -389,23 +388,23 @@ fn build_rc_wrapper<'a, 'ctx, 'env>(
|
||||||
debug_info_init!(env, function_value);
|
debug_info_init!(env, function_value);
|
||||||
|
|
||||||
let mut it = function_value.get_param_iter();
|
let mut it = function_value.get_param_iter();
|
||||||
let value_ptr = it.next().unwrap().into_pointer_value();
|
let generic_value_ptr = it.next().unwrap().into_pointer_value();
|
||||||
|
|
||||||
value_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
generic_value_ptr.set_name(Symbol::ARG_1.as_str(&env.interns));
|
||||||
|
|
||||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
let value_ptr_type =
|
||||||
|
basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
||||||
let value = if layout.is_passed_by_reference(env.layout_interner, env.target_info) {
|
let value_ptr =
|
||||||
env.builder
|
env.builder
|
||||||
.build_pointer_cast(value_ptr, value_type, "cast_ptr_to_tag_build_rc_wrapper")
|
.build_pointer_cast(generic_value_ptr, value_ptr_type, "load_opaque");
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
let value_cast = env
|
|
||||||
.builder
|
|
||||||
.build_bitcast(value_ptr, value_type, "load_opaque")
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
env.builder.build_load(value_cast, "load_opaque")
|
// even though this looks like a `load_roc_value`, that gives segfaults in practice.
|
||||||
|
// I suspect it has something to do with the lifetime of the alloca that is created by
|
||||||
|
// `load_roc_value`
|
||||||
|
let value = if layout.is_passed_by_reference(env.layout_interner, env.target_info) {
|
||||||
|
value_ptr.into()
|
||||||
|
} else {
|
||||||
|
env.builder.build_load(value_ptr, "load_opaque")
|
||||||
};
|
};
|
||||||
|
|
||||||
match rc_operation {
|
match rc_operation {
|
||||||
|
@ -486,13 +485,11 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let value_cast1 = env
|
let value_cast1 = env
|
||||||
.builder
|
.builder
|
||||||
.build_bitcast(value_ptr1, value_type, "load_opaque")
|
.build_pointer_cast(value_ptr1, value_type, "load_opaque");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let value_cast2 = env
|
let value_cast2 = env
|
||||||
.builder
|
.builder
|
||||||
.build_bitcast(value_ptr2, value_type, "load_opaque")
|
.build_pointer_cast(value_ptr2, value_type, "load_opaque");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// load_roc_value(env, *element_layout, elem_ptr, "get_elem")
|
// load_roc_value(env, *element_layout, elem_ptr, "get_elem")
|
||||||
let value1 = load_roc_value(env, *layout, value_cast1, "load_opaque");
|
let value1 = load_roc_value(env, *layout, value_cast1, "load_opaque");
|
||||||
|
@ -568,15 +565,13 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||||
let value_type = basic_type_from_layout(env, layout);
|
let value_type = basic_type_from_layout(env, layout);
|
||||||
let value_ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
let value_ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let value_cast1 = env
|
let value_cast1 =
|
||||||
.builder
|
env.builder
|
||||||
.build_bitcast(value_ptr1, value_ptr_type, "load_opaque")
|
.build_pointer_cast(value_ptr1, value_ptr_type, "load_opaque");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let value_cast2 = env
|
let value_cast2 =
|
||||||
.builder
|
env.builder
|
||||||
.build_bitcast(value_ptr2, value_ptr_type, "load_opaque")
|
.build_pointer_cast(value_ptr2, value_ptr_type, "load_opaque");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let value1 = env.builder.build_load(value_cast1, "load_opaque");
|
let value1 = env.builder.build_load(value_cast1, "load_opaque");
|
||||||
let value2 = env.builder.build_load(value_cast2, "load_opaque");
|
let value2 = env.builder.build_load(value_cast2, "load_opaque");
|
||||||
|
@ -592,13 +587,14 @@ pub fn build_compare_wrapper<'a, 'ctx, 'env>(
|
||||||
&default
|
&default
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
let closure_type =
|
let closure_type = basic_type_from_layout(env, &other);
|
||||||
basic_type_from_layout(env, &other).ptr_type(AddressSpace::Generic);
|
let closure_ptr_type = closure_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let closure_cast = env
|
let closure_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
closure_ptr,
|
||||||
.build_bitcast(closure_ptr, closure_type, "load_opaque")
|
closure_ptr_type,
|
||||||
.into_pointer_value();
|
"load_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
let closure_data = env.builder.build_load(closure_cast, "load_opaque");
|
||||||
|
|
||||||
|
|
|
@ -709,7 +709,6 @@ fn float_with_precision<'a, 'ctx, 'env>(
|
||||||
match float_width {
|
match float_width {
|
||||||
FloatWidth::F64 => env.context.f64_type().const_float(value).into(),
|
FloatWidth::F64 => env.context.f64_type().const_float(value).into(),
|
||||||
FloatWidth::F32 => env.context.f32_type().const_float(value).into(),
|
FloatWidth::F32 => env.context.f32_type().const_float(value).into(),
|
||||||
FloatWidth::F128 => todo!("F128 is not implemented"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,14 +1164,11 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
let struct_layout = Layout::struct_no_name_order(fields);
|
let struct_layout = Layout::struct_no_name_order(fields);
|
||||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
let struct_type = basic_type_from_layout(env, &struct_layout);
|
||||||
|
|
||||||
let cast_argument = env
|
let cast_argument = env.builder.build_pointer_cast(
|
||||||
.builder
|
argument,
|
||||||
.build_bitcast(
|
struct_type.ptr_type(AddressSpace::Generic),
|
||||||
argument,
|
"cast_rosetree_like",
|
||||||
struct_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"cast_rosetree_like",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let ptr = env
|
let ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1402,11 +1398,13 @@ fn build_tag_field_value<'a, 'ctx, 'env>(
|
||||||
debug_assert!(value.is_pointer_value());
|
debug_assert!(value.is_pointer_value());
|
||||||
|
|
||||||
// we store recursive pointers as `i64*`
|
// we store recursive pointers as `i64*`
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
value,
|
.build_pointer_cast(
|
||||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
value.into_pointer_value(),
|
||||||
"cast_recursive_pointer",
|
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||||
)
|
"cast_recursive_pointer",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
} else if tag_field_layout.is_passed_by_reference(env.layout_interner, env.target_info) {
|
} else if tag_field_layout.is_passed_by_reference(env.layout_interner, env.target_info) {
|
||||||
debug_assert!(value.is_pointer_value());
|
debug_assert!(value.is_pointer_value());
|
||||||
|
|
||||||
|
@ -1839,14 +1837,11 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>(
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
let ptr = env
|
let ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
value,
|
||||||
.build_bitcast(
|
struct_type.ptr_type(AddressSpace::Generic),
|
||||||
value,
|
"cast_lookup_at_index_ptr",
|
||||||
struct_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"cast_lookup_at_index_ptr",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let elem_ptr = builder
|
let elem_ptr = builder
|
||||||
.build_struct_gep(ptr, index as u32, "at_index_struct_gep")
|
.build_struct_gep(ptr, index as u32, "at_index_struct_gep")
|
||||||
|
@ -1861,11 +1856,13 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>(
|
||||||
let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout));
|
let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout));
|
||||||
debug_assert!(actual_type.is_pointer_type());
|
debug_assert!(actual_type.is_pointer_type());
|
||||||
|
|
||||||
builder.build_bitcast(
|
builder
|
||||||
result,
|
.build_pointer_cast(
|
||||||
actual_type,
|
result.into_pointer_value(),
|
||||||
"cast_rec_pointer_lookup_at_index_ptr_old",
|
actual_type.into_pointer_type(),
|
||||||
)
|
"cast_rec_pointer_lookup_at_index_ptr_old",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -1883,14 +1880,11 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
|
||||||
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
let struct_layout = Layout::struct_no_name_order(field_layouts);
|
||||||
let struct_type = basic_type_from_layout(env, &struct_layout);
|
let struct_type = basic_type_from_layout(env, &struct_layout);
|
||||||
|
|
||||||
let data_ptr = env
|
let data_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
value,
|
||||||
.build_bitcast(
|
struct_type.ptr_type(AddressSpace::Generic),
|
||||||
value,
|
"cast_lookup_at_index_ptr",
|
||||||
struct_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"cast_lookup_at_index_ptr",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let elem_ptr = builder
|
let elem_ptr = builder
|
||||||
.build_struct_gep(data_ptr, index as u32, "at_index_struct_gep_data")
|
.build_struct_gep(data_ptr, index as u32, "at_index_struct_gep_data")
|
||||||
|
@ -1906,11 +1900,13 @@ fn lookup_at_index_ptr2<'a, 'ctx, 'env>(
|
||||||
let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout));
|
let actual_type = basic_type_from_layout(env, &Layout::Union(*union_layout));
|
||||||
debug_assert!(actual_type.is_pointer_type());
|
debug_assert!(actual_type.is_pointer_type());
|
||||||
|
|
||||||
builder.build_bitcast(
|
builder
|
||||||
result,
|
.build_pointer_cast(
|
||||||
actual_type,
|
result.into_pointer_value(),
|
||||||
"cast_rec_pointer_lookup_at_index_ptr_new",
|
actual_type.into_pointer_type(),
|
||||||
)
|
"cast_rec_pointer_lookup_at_index_ptr_new",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -1994,8 +1990,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||||
let ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
let ptr_type = value_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
env.builder
|
env.builder
|
||||||
.build_bitcast(ptr, ptr_type, "alloc_cast_to_desired")
|
.build_pointer_cast(ptr, ptr_type, "alloc_cast_to_desired")
|
||||||
.into_pointer_value()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_literal<'a, 'ctx, 'env>(
|
fn list_literal<'a, 'ctx, 'env>(
|
||||||
|
@ -2586,7 +2581,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
condition: cond_symbol,
|
condition: cond_symbol,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts: _,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
@ -2621,6 +2616,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
*cond_symbol,
|
*cond_symbol,
|
||||||
*region,
|
*region,
|
||||||
lookups,
|
lookups,
|
||||||
|
variables,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let LlvmBackendMode::BinaryDev = env.mode {
|
if let LlvmBackendMode::BinaryDev = env.mode {
|
||||||
|
@ -2655,7 +2651,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
condition: cond_symbol,
|
condition: cond_symbol,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts: _,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
@ -2690,6 +2686,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||||
*cond_symbol,
|
*cond_symbol,
|
||||||
*region,
|
*region,
|
||||||
lookups,
|
lookups,
|
||||||
|
variables,
|
||||||
);
|
);
|
||||||
|
|
||||||
bd.build_unconditional_branch(then_block);
|
bd.build_unconditional_branch(then_block);
|
||||||
|
@ -2815,7 +2812,13 @@ pub fn complex_bitcast<'ctx>(
|
||||||
// we can't use the more straightforward bitcast in all cases
|
// we can't use the more straightforward bitcast in all cases
|
||||||
// it seems like a bitcast only works on integers and pointers
|
// it seems like a bitcast only works on integers and pointers
|
||||||
// and crucially does not work not on arrays
|
// and crucially does not work not on arrays
|
||||||
return builder.build_bitcast(from_value, to_type, name);
|
return builder
|
||||||
|
.build_pointer_cast(
|
||||||
|
from_value.into_pointer_value(),
|
||||||
|
to_type.into_pointer_type(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
complex_bitcast_from_bigger_than_to(builder, from_value, to_type, name)
|
complex_bitcast_from_bigger_than_to(builder, from_value, to_type, name)
|
||||||
|
@ -2835,7 +2838,14 @@ pub fn complex_bitcast_check_size<'a, 'ctx, 'env>(
|
||||||
// we can't use the more straightforward bitcast in all cases
|
// we can't use the more straightforward bitcast in all cases
|
||||||
// it seems like a bitcast only works on integers and pointers
|
// it seems like a bitcast only works on integers and pointers
|
||||||
// and crucially does not work not on arrays
|
// and crucially does not work not on arrays
|
||||||
return env.builder.build_bitcast(from_value, to_type, name);
|
return env
|
||||||
|
.builder
|
||||||
|
.build_pointer_cast(
|
||||||
|
from_value.into_pointer_value(),
|
||||||
|
to_type.into_pointer_type(),
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
@ -2891,13 +2901,11 @@ fn complex_bitcast_from_bigger_than_to<'ctx>(
|
||||||
builder.build_store(argument_pointer, from_value);
|
builder.build_store(argument_pointer, from_value);
|
||||||
|
|
||||||
// then read it back as a different type
|
// then read it back as a different type
|
||||||
let to_type_pointer = builder
|
let to_type_pointer = builder.build_pointer_cast(
|
||||||
.build_bitcast(
|
argument_pointer,
|
||||||
argument_pointer,
|
to_type.ptr_type(inkwell::AddressSpace::Generic),
|
||||||
to_type.ptr_type(inkwell::AddressSpace::Generic),
|
name,
|
||||||
name,
|
);
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
builder.build_load(to_type_pointer, "cast_value")
|
builder.build_load(to_type_pointer, "cast_value")
|
||||||
}
|
}
|
||||||
|
@ -2914,15 +2922,13 @@ fn complex_bitcast_to_bigger_than_from<'ctx>(
|
||||||
let storage = builder.build_alloca(to_type, "cast_alloca");
|
let storage = builder.build_alloca(to_type, "cast_alloca");
|
||||||
|
|
||||||
// then cast the pointer to our desired type
|
// then cast the pointer to our desired type
|
||||||
let from_type_pointer = builder
|
let from_type_pointer = builder.build_pointer_cast(
|
||||||
.build_bitcast(
|
storage,
|
||||||
storage,
|
from_value
|
||||||
from_value
|
.get_type()
|
||||||
.get_type()
|
.ptr_type(inkwell::AddressSpace::Generic),
|
||||||
.ptr_type(inkwell::AddressSpace::Generic),
|
name,
|
||||||
name,
|
);
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// store the value in memory
|
// store the value in memory
|
||||||
builder.build_store(from_type_pointer, from_value);
|
builder.build_store(from_type_pointer, from_value);
|
||||||
|
@ -3036,7 +3042,6 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
let int_type = match float_width {
|
let int_type = match float_width {
|
||||||
FloatWidth::F32 => env.context.i32_type(),
|
FloatWidth::F32 => env.context.i32_type(),
|
||||||
FloatWidth::F64 => env.context.i64_type(),
|
FloatWidth::F64 => env.context.i64_type(),
|
||||||
FloatWidth::F128 => env.context.i128_type(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
builder
|
builder
|
||||||
|
@ -3314,14 +3319,11 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
|
||||||
// not pretty, but seems to cover all our current cases
|
// not pretty, but seems to cover all our current cases
|
||||||
if arg_type.is_pointer_type() && !fastcc_type.is_pointer_type() {
|
if arg_type.is_pointer_type() && !fastcc_type.is_pointer_type() {
|
||||||
// bitcast the ptr
|
// bitcast the ptr
|
||||||
let fastcc_ptr = env
|
let fastcc_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
arg.into_pointer_value(),
|
||||||
.build_bitcast(
|
fastcc_type.ptr_type(AddressSpace::Generic),
|
||||||
*arg,
|
"bitcast_arg",
|
||||||
fastcc_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"bitcast_arg",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let loaded = env.builder.build_load(fastcc_ptr, "load_arg");
|
let loaded = env.builder.build_load(fastcc_ptr, "load_arg");
|
||||||
arguments_for_call.push(loaded);
|
arguments_for_call.push(loaded);
|
||||||
|
@ -3643,14 +3645,11 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
|
||||||
c_function.add_attribute(AttributeLoc::Param(param_index), nonnull);
|
c_function.add_attribute(AttributeLoc::Param(param_index), nonnull);
|
||||||
}
|
}
|
||||||
// bitcast the ptr
|
// bitcast the ptr
|
||||||
let fastcc_ptr = env
|
let fastcc_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
arg.into_pointer_value(),
|
||||||
.build_bitcast(
|
fastcc_type.ptr_type(AddressSpace::Generic),
|
||||||
*arg,
|
"bitcast_arg",
|
||||||
fastcc_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"bitcast_arg",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
env.builder.build_load(fastcc_ptr, "load_arg")
|
env.builder.build_load(fastcc_ptr, "load_arg")
|
||||||
} else {
|
} else {
|
||||||
|
@ -3808,13 +3807,11 @@ pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValu
|
||||||
|
|
||||||
global.set_initializer(&type_.const_zero());
|
global.set_initializer(&type_.const_zero());
|
||||||
|
|
||||||
env.builder
|
env.builder.build_pointer_cast(
|
||||||
.build_bitcast(
|
global.as_pointer_value(),
|
||||||
global.as_pointer_value(),
|
env.context.i32_type().ptr_type(AddressSpace::Generic),
|
||||||
env.context.i32_type().ptr_type(AddressSpace::Generic),
|
"cast_sjlj_buffer",
|
||||||
"cast_sjlj_buffer",
|
)
|
||||||
)
|
|
||||||
.into_pointer_value()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
||||||
|
@ -3826,18 +3823,15 @@ pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||||
// Anywhere else, use the LLVM intrinsic.
|
// Anywhere else, use the LLVM intrinsic.
|
||||||
// https://llvm.org/docs/ExceptionHandling.html#llvm-eh-sjlj-setjmp
|
// https://llvm.org/docs/ExceptionHandling.html#llvm-eh-sjlj-setjmp
|
||||||
|
|
||||||
let jmp_buf_i8p_arr = env
|
let jmp_buf_i8p_arr = env.builder.build_pointer_cast(
|
||||||
.builder
|
jmp_buf,
|
||||||
.build_bitcast(
|
env.context
|
||||||
jmp_buf,
|
.i8_type()
|
||||||
env.context
|
.ptr_type(AddressSpace::Generic)
|
||||||
.i8_type()
|
.array_type(5)
|
||||||
.ptr_type(AddressSpace::Generic)
|
.ptr_type(AddressSpace::Generic),
|
||||||
.array_type(5)
|
"jmp_buf [5 x i8*]",
|
||||||
.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"jmp_buf [5 x i8*]",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// LLVM asks us to please store the frame pointer in the first word.
|
// LLVM asks us to please store the frame pointer in the first word.
|
||||||
let frame_address = env.call_intrinsic(
|
let frame_address = env.call_intrinsic(
|
||||||
|
@ -3867,11 +3861,14 @@ pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||||
let stack_save = env.call_intrinsic(LLVM_STACK_SAVE, &[]);
|
let stack_save = env.call_intrinsic(LLVM_STACK_SAVE, &[]);
|
||||||
env.builder.build_store(ss, stack_save);
|
env.builder.build_store(ss, stack_save);
|
||||||
|
|
||||||
let jmp_buf_i8p = env.builder.build_bitcast(
|
let jmp_buf_i8p = env
|
||||||
jmp_buf,
|
.builder
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
.build_pointer_cast(
|
||||||
"jmp_buf i8*",
|
jmp_buf,
|
||||||
);
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
|
"jmp_buf i8*",
|
||||||
|
)
|
||||||
|
.into();
|
||||||
env.call_intrinsic(LLVM_SETJMP, &[jmp_buf_i8p])
|
env.call_intrinsic(LLVM_SETJMP, &[jmp_buf_i8p])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5397,7 +5394,7 @@ fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
env.builder.build_alloca(param.get_type(), "param_alloca");
|
env.builder.build_alloca(param.get_type(), "param_alloca");
|
||||||
env.builder.build_store(param_alloca, param);
|
env.builder.build_store(param_alloca, param);
|
||||||
|
|
||||||
let as_cc_type = env.builder.build_bitcast(
|
let as_cc_type = env.builder.build_pointer_cast(
|
||||||
param_alloca,
|
param_alloca,
|
||||||
cc_type.into_pointer_type(),
|
cc_type.into_pointer_type(),
|
||||||
"to_cc_type_ptr",
|
"to_cc_type_ptr",
|
||||||
|
@ -5467,14 +5464,11 @@ fn define_global_str_literal_ptr<'a, 'ctx, 'env>(
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let global = define_global_str_literal(env, message);
|
let global = define_global_str_literal(env, message);
|
||||||
|
|
||||||
let ptr = env
|
let ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
global.as_pointer_value(),
|
||||||
.build_bitcast(
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
global,
|
"to_opaque",
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
);
|
||||||
"to_opaque",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// a pointer to the first actual data (skipping over the refcount)
|
// a pointer to the first actual data (skipping over the refcount)
|
||||||
let ptr = unsafe {
|
let ptr = unsafe {
|
||||||
|
|
|
@ -1,844 +0,0 @@
|
||||||
use crate::debug_info_init;
|
|
||||||
use crate::llvm::bitcode::{
|
|
||||||
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, call_bitcode_fn, call_void_bitcode_fn,
|
|
||||||
};
|
|
||||||
use crate::llvm::build::{
|
|
||||||
complex_bitcast, load_roc_value, load_symbol, load_symbol_and_layout, Env, RocFunctionCall,
|
|
||||||
Scope,
|
|
||||||
};
|
|
||||||
use crate::llvm::build_list::{layout_width, pass_as_opaque};
|
|
||||||
use crate::llvm::convert::{basic_type_from_layout, zig_dict_type};
|
|
||||||
use crate::llvm::refcounting::Mode;
|
|
||||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
|
||||||
use inkwell::builder::Builder;
|
|
||||||
use inkwell::context::Context;
|
|
||||||
use inkwell::types::BasicType;
|
|
||||||
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, StructValue};
|
|
||||||
use inkwell::AddressSpace;
|
|
||||||
use roc_builtins::bitcode;
|
|
||||||
use roc_module::symbol::Symbol;
|
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
|
||||||
use roc_target::TargetInfo;
|
|
||||||
|
|
||||||
use super::bitcode::call_list_bitcode_fn;
|
|
||||||
use super::build::store_roc_value;
|
|
||||||
use super::build_list::list_to_c_abi;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct Alignment(u8);
|
|
||||||
|
|
||||||
impl Alignment {
|
|
||||||
fn from_key_value_layout(key: &Layout, value: &Layout, target_info: TargetInfo) -> Alignment {
|
|
||||||
let key_align = key.alignment_bytes(target_info);
|
|
||||||
let value_align = value.alignment_bytes(target_info);
|
|
||||||
|
|
||||||
let mut bits = key_align.max(value_align) as u8;
|
|
||||||
|
|
||||||
// alignment must be a power of 2
|
|
||||||
debug_assert!(bits.is_power_of_two());
|
|
||||||
|
|
||||||
let value_before_key_flag = 0b1000_0000;
|
|
||||||
|
|
||||||
if key_align < value_align {
|
|
||||||
bits |= value_before_key_flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
Alignment(bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_int_value<'ctx>(&self, context: &'ctx Context) -> IntValue<'ctx> {
|
|
||||||
context.i8_type().const_int(self.0 as u64, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dict_len<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
scope: &Scope<'a, 'ctx>,
|
|
||||||
dict_symbol: Symbol,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let (_, dict_layout) = load_symbol_and_layout(scope, &dict_symbol);
|
|
||||||
|
|
||||||
match dict_layout {
|
|
||||||
Layout::Builtin(Builtin::Dict(_, _)) =>
|
|
||||||
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dict_empty<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> {
|
|
||||||
// get the RocDict type defined by zig
|
|
||||||
let roc_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
|
||||||
|
|
||||||
// we must give a pointer for the bitcode function to write the result into
|
|
||||||
let result_alloc = env.builder.build_alloca(roc_dict_type, "dict_empty");
|
|
||||||
|
|
||||||
call_void_bitcode_fn(env, &[result_alloc.into()], bitcode::DICT_EMPTY);
|
|
||||||
|
|
||||||
env.builder.build_load(result_alloc, "load_result")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_insert<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value: BasicValueEnum<'ctx>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let key_type = basic_type_from_layout(env, key_layout);
|
|
||||||
let value_type = basic_type_from_layout(env, value_layout);
|
|
||||||
|
|
||||||
let key_ptr = builder.build_alloca(key_type, "key_ptr");
|
|
||||||
let value_ptr = builder.build_alloca(value_type, "value_ptr");
|
|
||||||
|
|
||||||
store_roc_value(env, *key_layout, key_ptr, key);
|
|
||||||
store_roc_value(env, *value_layout, value_ptr, value);
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
|
|
||||||
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
|
||||||
key_width.into(),
|
|
||||||
env.builder.build_bitcast(value_ptr, u8_ptr, "to_u8_ptr"),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
result_ptr.into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_INSERT,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(result_ptr, "load_result")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_remove<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
|
||||||
|
|
||||||
env.builder.build_store(key_ptr, key);
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
|
|
||||||
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
result_ptr.into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_REMOVE,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(result_ptr, "load_result")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_contains<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
|
||||||
|
|
||||||
env.builder.build_store(key_ptr, key);
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
call_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_CONTAINS,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_get<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
|
||||||
|
|
||||||
env.builder.build_store(key_ptr, key);
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_bt = basic_type_from_layout(env, value_layout);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
// { flag: bool, value: *const u8 }
|
|
||||||
let result = call_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_GET,
|
|
||||||
)
|
|
||||||
.into_struct_value();
|
|
||||||
|
|
||||||
let flag_u8 = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(result, 1, "get_flag")
|
|
||||||
.unwrap()
|
|
||||||
.into_int_value();
|
|
||||||
|
|
||||||
let flag = env
|
|
||||||
.builder
|
|
||||||
.build_int_cast(flag_u8, env.context.bool_type(), "to_bool");
|
|
||||||
|
|
||||||
let value_u8_ptr_int = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(result, 0, "get_value_ptr_int")
|
|
||||||
.unwrap()
|
|
||||||
.into_int_value();
|
|
||||||
|
|
||||||
let ptr_type = value_bt.ptr_type(AddressSpace::Generic);
|
|
||||||
let value_u8_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_int_to_ptr(value_u8_ptr_int, ptr_type, "opaque_value_ptr");
|
|
||||||
|
|
||||||
let start_block = env.builder.get_insert_block().unwrap();
|
|
||||||
let parent = start_block.get_parent().unwrap();
|
|
||||||
|
|
||||||
let if_not_null = env.context.append_basic_block(parent, "if_not_null");
|
|
||||||
let done_block = env.context.append_basic_block(parent, "done");
|
|
||||||
|
|
||||||
let default = value_bt.const_zero();
|
|
||||||
|
|
||||||
env.builder
|
|
||||||
.build_conditional_branch(flag, if_not_null, done_block);
|
|
||||||
|
|
||||||
env.builder.position_at_end(if_not_null);
|
|
||||||
let value_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_bitcast(
|
|
||||||
value_u8_ptr,
|
|
||||||
value_bt.ptr_type(AddressSpace::Generic),
|
|
||||||
"from_opaque",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
let loaded = env.builder.build_load(value_ptr, "load_value");
|
|
||||||
env.builder.build_unconditional_branch(done_block);
|
|
||||||
|
|
||||||
env.builder.position_at_end(done_block);
|
|
||||||
let result_phi = env.builder.build_phi(value_bt, "result");
|
|
||||||
|
|
||||||
result_phi.add_incoming(&[(&default, start_block), (&loaded, if_not_null)]);
|
|
||||||
|
|
||||||
let value = result_phi.as_basic_value();
|
|
||||||
|
|
||||||
let result = env
|
|
||||||
.context
|
|
||||||
.struct_type(&[value_bt, env.context.bool_type().into()], false)
|
|
||||||
.const_zero();
|
|
||||||
|
|
||||||
let result = env
|
|
||||||
.builder
|
|
||||||
.build_insert_value(result, flag, 1, "insert_flag")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
env.builder
|
|
||||||
.build_insert_value(result, value, 0, "insert_value")
|
|
||||||
.unwrap()
|
|
||||||
.into_struct_value()
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_elements_rc<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
rc_operation: Mode,
|
|
||||||
) {
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let (key_fn, value_fn) = match rc_operation {
|
|
||||||
Mode::Inc => (
|
|
||||||
build_inc_wrapper(env, layout_ids, key_layout),
|
|
||||||
build_inc_wrapper(env, layout_ids, value_layout),
|
|
||||||
),
|
|
||||||
Mode::Dec => (
|
|
||||||
build_dec_wrapper(env, layout_ids, key_layout),
|
|
||||||
build_dec_wrapper(env, layout_ids, value_layout),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_ELEMENTS_RC,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_keys<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
call_list_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_KEYS,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pass_dict_c_abi<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
match env.target_info.ptr_width() {
|
|
||||||
roc_target::PtrWidth::Bytes4 => {
|
|
||||||
let target_type = env.context.custom_width_int_type(96).into();
|
|
||||||
|
|
||||||
complex_bitcast(env.builder, dict, target_type, "to_i96")
|
|
||||||
}
|
|
||||||
roc_target::PtrWidth::Bytes8 => {
|
|
||||||
let dict_ptr = env.builder.build_alloca(zig_dict_type(env), "dict_ptr");
|
|
||||||
env.builder.build_store(dict_ptr, dict);
|
|
||||||
|
|
||||||
dict_ptr.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_union<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict1: BasicValueEnum<'ctx>,
|
|
||||||
dict2: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
|
||||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
let output_ptr = builder.build_alloca(zig_dict_type(env), "output_ptr");
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict1),
|
|
||||||
pass_dict_c_abi(env, dict2),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
inc_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
output_ptr.into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_UNION,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(output_ptr, "load_output_ptr")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_difference<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict1: BasicValueEnum<'ctx>,
|
|
||||||
dict2: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
dict_intersect_or_difference(
|
|
||||||
env,
|
|
||||||
layout_ids,
|
|
||||||
dict1,
|
|
||||||
dict2,
|
|
||||||
key_layout,
|
|
||||||
value_layout,
|
|
||||||
bitcode::DICT_DIFFERENCE,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_intersection<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict1: BasicValueEnum<'ctx>,
|
|
||||||
dict2: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
dict_intersect_or_difference(
|
|
||||||
env,
|
|
||||||
layout_ids,
|
|
||||||
dict1,
|
|
||||||
dict2,
|
|
||||||
key_layout,
|
|
||||||
value_layout,
|
|
||||||
bitcode::DICT_INTERSECTION,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn dict_intersect_or_difference<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict1: BasicValueEnum<'ctx>,
|
|
||||||
dict2: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
op: &str,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
|
|
||||||
let dec_value_fn = build_dec_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
let output_ptr = builder.build_alloca(zig_dict_type, "output_ptr");
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict1),
|
|
||||||
pass_dict_c_abi(env, dict2),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
output_ptr.into(),
|
|
||||||
],
|
|
||||||
op,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(output_ptr, "load_output_ptr")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_walk<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
roc_function_call: RocFunctionCall<'ctx>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
accum: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
accum_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let accum_bt = basic_type_from_layout(env, accum_layout);
|
|
||||||
let accum_ptr = builder.build_alloca(accum_bt, "accum_ptr");
|
|
||||||
env.builder.build_store(accum_ptr, accum);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
roc_function_call.caller.into(),
|
|
||||||
pass_as_opaque(env, roc_function_call.data),
|
|
||||||
roc_function_call.inc_n_data.into(),
|
|
||||||
roc_function_call.data_is_owned.into(),
|
|
||||||
env.builder.build_bitcast(accum_ptr, u8_ptr, "to_opaque"),
|
|
||||||
alignment_iv.into(),
|
|
||||||
layout_width(env, key_layout),
|
|
||||||
layout_width(env, value_layout),
|
|
||||||
layout_width(env, accum_layout),
|
|
||||||
env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"),
|
|
||||||
],
|
|
||||||
bitcode::DICT_WALK,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(output_ptr, "load_output_ptr")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn dict_values<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
dict: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
value_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(value_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
|
||||||
|
|
||||||
call_list_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
pass_dict_c_abi(env, dict),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
inc_value_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
],
|
|
||||||
bitcode::DICT_VALUES,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dict.capacity : Dict * * -> Nat
|
|
||||||
pub fn dict_capacity<'ctx>(
|
|
||||||
builder: &Builder<'ctx>,
|
|
||||||
wrapper_struct: StructValue<'ctx>,
|
|
||||||
) -> IntValue<'ctx> {
|
|
||||||
builder
|
|
||||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "dict_capacity")
|
|
||||||
.unwrap()
|
|
||||||
.into_int_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set.capacity : Set * -> Nat
|
|
||||||
pub fn set_capacity<'ctx>(
|
|
||||||
builder: &Builder<'ctx>,
|
|
||||||
wrapper_struct: StructValue<'ctx>,
|
|
||||||
) -> IntValue<'ctx> {
|
|
||||||
builder
|
|
||||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_CAPACITY, "set_capacity")
|
|
||||||
.unwrap()
|
|
||||||
.into_int_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn set_from_list<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
list: BasicValueEnum<'ctx>,
|
|
||||||
key_layout: &Layout<'a>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let key_width = env
|
|
||||||
.ptr_int()
|
|
||||||
.const_int(key_layout.stack_size(env.target_info) as u64, false);
|
|
||||||
|
|
||||||
let value_width = env.ptr_int().const_zero();
|
|
||||||
|
|
||||||
let result_alloca = builder.build_alloca(zig_dict_type(env), "result_alloca");
|
|
||||||
|
|
||||||
let alignment = Alignment::from_key_value_layout(key_layout, &Layout::UNIT, env.target_info);
|
|
||||||
let alignment_iv = alignment.as_int_value(env.context);
|
|
||||||
|
|
||||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
|
||||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
let dec_key_fn = build_dec_wrapper(env, layout_ids, key_layout);
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
list_to_c_abi(env, list).into(),
|
|
||||||
alignment_iv.into(),
|
|
||||||
key_width.into(),
|
|
||||||
value_width.into(),
|
|
||||||
hash_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
eq_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
dec_key_fn.as_global_value().as_pointer_value().into(),
|
|
||||||
result_alloca.into(),
|
|
||||||
],
|
|
||||||
bitcode::SET_FROM_LIST,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.builder.build_load(result_alloca, "load_result")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_hash_wrapper<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
|
||||||
layout: &Layout<'a>,
|
|
||||||
) -> FunctionValue<'ctx> {
|
|
||||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
|
||||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
|
||||||
|
|
||||||
let symbol = Symbol::GENERIC_HASH_REF;
|
|
||||||
let fn_name = layout_ids
|
|
||||||
.get(symbol, layout)
|
|
||||||
.to_symbol_string(symbol, &env.interns);
|
|
||||||
|
|
||||||
let function_value = match env.module.get_function(fn_name.as_str()) {
|
|
||||||
Some(function_value) => function_value,
|
|
||||||
None => {
|
|
||||||
let seed_type = env.context.i64_type();
|
|
||||||
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let function_value = crate::llvm::refcounting::build_header_help(
|
|
||||||
env,
|
|
||||||
&fn_name,
|
|
||||||
seed_type.into(),
|
|
||||||
&[seed_type.into(), arg_type.into()],
|
|
||||||
);
|
|
||||||
|
|
||||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
|
||||||
debug_assert!(kind_id > 0);
|
|
||||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
|
||||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
|
||||||
|
|
||||||
let entry = env.context.append_basic_block(function_value, "entry");
|
|
||||||
env.builder.position_at_end(entry);
|
|
||||||
|
|
||||||
debug_info_init!(env, function_value);
|
|
||||||
|
|
||||||
let mut it = function_value.get_param_iter();
|
|
||||||
let seed_arg = it.next().unwrap().into_int_value();
|
|
||||||
let value_ptr = it.next().unwrap().into_pointer_value();
|
|
||||||
|
|
||||||
seed_arg.set_name(Symbol::ARG_1.as_str(&env.interns));
|
|
||||||
value_ptr.set_name(Symbol::ARG_2.as_str(&env.interns));
|
|
||||||
|
|
||||||
let value_type = basic_type_from_layout(env, layout).ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let value_cast = env
|
|
||||||
.builder
|
|
||||||
.build_bitcast(value_ptr, value_type, "cast_to_known_type")
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let val_arg = load_roc_value(env, *layout, value_cast, "load_opaque");
|
|
||||||
|
|
||||||
let result =
|
|
||||||
crate::llvm::build_hash::generic_hash(env, layout_ids, seed_arg, val_arg, layout);
|
|
||||||
|
|
||||||
env.builder.build_return(Some(&result));
|
|
||||||
|
|
||||||
function_value
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
env.builder.position_at_end(block);
|
|
||||||
env.builder
|
|
||||||
.set_current_debug_location(env.context, di_location);
|
|
||||||
|
|
||||||
function_value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dict_symbol_to_zig_dict<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
scope: &Scope<'a, 'ctx>,
|
|
||||||
symbol: Symbol,
|
|
||||||
) -> StructValue<'ctx> {
|
|
||||||
let dict = load_symbol(scope, &symbol);
|
|
||||||
|
|
||||||
complex_bitcast(
|
|
||||||
env.builder,
|
|
||||||
dict,
|
|
||||||
crate::llvm::convert::zig_dict_type(env).into(),
|
|
||||||
"dict_to_zig_dict",
|
|
||||||
)
|
|
||||||
.into_struct_value()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decref<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
wrapper_struct: StructValue<'ctx>,
|
|
||||||
alignment: u32,
|
|
||||||
) {
|
|
||||||
let pointer = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr")
|
|
||||||
.unwrap()
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
crate::llvm::refcounting::decref_pointer_check_null(env, pointer, alignment);
|
|
||||||
}
|
|
|
@ -70,11 +70,13 @@ fn pass_element_as_opaque<'a, 'ctx, 'env>(
|
||||||
.build_alloca(element_type, "element_to_pass_as_opaque");
|
.build_alloca(element_type, "element_to_pass_as_opaque");
|
||||||
store_roc_value(env, layout, element_ptr, element);
|
store_roc_value(env, layout, element_ptr, element);
|
||||||
|
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
element_ptr,
|
.build_pointer_cast(
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
element_ptr,
|
||||||
"pass_element_as_opaque",
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
)
|
"pass_element_as_opaque",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn layout_width<'a, 'ctx, 'env>(
|
pub(crate) fn layout_width<'a, 'ctx, 'env>(
|
||||||
|
@ -93,11 +95,13 @@ pub(crate) fn pass_as_opaque<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
ptr: PointerValue<'ctx>,
|
ptr: PointerValue<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
ptr,
|
.build_pointer_cast(
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
ptr,
|
||||||
"pass_as_opaque",
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
)
|
"pass_as_opaque",
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn list_with_capacity<'a, 'ctx, 'env>(
|
pub(crate) fn list_with_capacity<'a, 'ctx, 'env>(
|
||||||
|
|
|
@ -29,14 +29,11 @@ pub(crate) fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match env.target_info.ptr_width() {
|
match env.target_info.ptr_width() {
|
||||||
PtrWidth::Bytes4 | PtrWidth::Bytes8 => {
|
PtrWidth::Bytes4 | PtrWidth::Bytes8 => {
|
||||||
let result_ptr_cast = env
|
let result_ptr_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
pointer,
|
||||||
.build_bitcast(
|
record_type.ptr_type(AddressSpace::Generic),
|
||||||
pointer,
|
"to_unnamed",
|
||||||
record_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"to_unnamed",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.build_load(result_ptr_cast, "load_utf8_validate_bytes_result")
|
.build_load(result_ptr_cast, "load_utf8_validate_bytes_result")
|
||||||
|
|
|
@ -111,7 +111,6 @@ fn build_eq_builtin<'a, 'ctx, 'env>(
|
||||||
use FloatWidth::*;
|
use FloatWidth::*;
|
||||||
|
|
||||||
let name = match float_width {
|
let name = match float_width {
|
||||||
F128 => "eq_f128",
|
|
||||||
F64 => "eq_f64",
|
F64 => "eq_f64",
|
||||||
F32 => "eq_f32",
|
F32 => "eq_f32",
|
||||||
};
|
};
|
||||||
|
@ -199,15 +198,17 @@ fn build_eq<'a, 'ctx, 'env>(
|
||||||
let bt = basic_type_from_layout(env, &layout);
|
let bt = basic_type_from_layout(env, &layout);
|
||||||
|
|
||||||
// cast the i64 pointer to a pointer to block of memory
|
// cast the i64 pointer to a pointer to block of memory
|
||||||
let field1_cast = env
|
let field1_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
lhs_val.into_pointer_value(),
|
||||||
.build_bitcast(lhs_val, bt, "i64_to_opaque")
|
bt.into_pointer_type(),
|
||||||
.into_pointer_value();
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
let field2_cast = env
|
let field2_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
rhs_val.into_pointer_value(),
|
||||||
.build_bitcast(rhs_val, bt, "i64_to_opaque")
|
bt.into_pointer_type(),
|
||||||
.into_pointer_value();
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
build_tag_eq(
|
build_tag_eq(
|
||||||
env,
|
env,
|
||||||
|
@ -276,7 +277,6 @@ fn build_neq_builtin<'a, 'ctx, 'env>(
|
||||||
use FloatWidth::*;
|
use FloatWidth::*;
|
||||||
|
|
||||||
let name = match float_width {
|
let name = match float_width {
|
||||||
F128 => "neq_f128",
|
|
||||||
F64 => "neq_f64",
|
F64 => "neq_f64",
|
||||||
F32 => "neq_f32",
|
F32 => "neq_f32",
|
||||||
};
|
};
|
||||||
|
@ -724,15 +724,17 @@ fn build_struct_eq_help<'a, 'ctx, 'env>(
|
||||||
let bt = basic_type_from_layout(env, &field_layout);
|
let bt = basic_type_from_layout(env, &field_layout);
|
||||||
|
|
||||||
// cast the i64 pointer to a pointer to block of memory
|
// cast the i64 pointer to a pointer to block of memory
|
||||||
let field1_cast = env
|
let field1_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
field1.into_pointer_value(),
|
||||||
.build_bitcast(field1, bt, "i64_to_opaque")
|
bt.into_pointer_type(),
|
||||||
.into_pointer_value();
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
let field2_cast = env
|
let field2_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
field2.into_pointer_value(),
|
||||||
.build_bitcast(field2, bt, "i64_to_opaque")
|
bt.into_pointer_type(),
|
||||||
.into_pointer_value();
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
build_eq(
|
build_eq(
|
||||||
env,
|
env,
|
||||||
|
@ -1237,23 +1239,17 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
||||||
debug_assert!(wrapper_type.is_struct_type());
|
debug_assert!(wrapper_type.is_struct_type());
|
||||||
|
|
||||||
// cast the opaque pointer to a pointer of the correct shape
|
// cast the opaque pointer to a pointer of the correct shape
|
||||||
let struct1_ptr = env
|
let struct1_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
tag1,
|
||||||
.build_bitcast(
|
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||||
tag1,
|
"opaque_to_correct",
|
||||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"opaque_to_correct",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let struct2_ptr = env
|
let struct2_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
tag2,
|
||||||
.build_bitcast(
|
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||||
tag2,
|
"opaque_to_correct",
|
||||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"opaque_to_correct",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
let struct1 = env
|
let struct1 = env
|
||||||
.builder
|
.builder
|
||||||
|
|
|
@ -202,7 +202,6 @@ pub fn float_type_from_float_width<'a, 'ctx, 'env>(
|
||||||
use FloatWidth::*;
|
use FloatWidth::*;
|
||||||
|
|
||||||
match float_width {
|
match float_width {
|
||||||
F128 => todo!("F128 is not implemented"),
|
|
||||||
F64 => env.context.f64_type(),
|
F64 => env.context.f64_type(),
|
||||||
F32 => env.context.f32_type(),
|
F32 => env.context.f32_type(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@ use crate::llvm::build_list::{self, incrementing_elem_loop};
|
||||||
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
use crate::llvm::convert::{basic_type_from_layout, RocUnion};
|
||||||
use inkwell::builder::Builder;
|
use inkwell::builder::Builder;
|
||||||
use inkwell::module::Linkage;
|
use inkwell::module::Linkage;
|
||||||
use inkwell::types::{BasicMetadataTypeEnum, BasicType};
|
use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_mono::ir::LookupType;
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
|
|
||||||
|
@ -143,6 +144,21 @@ pub(crate) fn notify_parent_dbg(env: &Env, shared_memory: &SharedMemoryPointer)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shape of expect frame:
|
||||||
|
//
|
||||||
|
// ===
|
||||||
|
// Fixed-size header
|
||||||
|
// ===
|
||||||
|
// /-- ptr_lookup_1 (ptr_size)
|
||||||
|
// | var_lookup_1 (u32)
|
||||||
|
// | ..
|
||||||
|
// | ptr_lookup_n (ptr_size)
|
||||||
|
// | var_lookup_n (u32)
|
||||||
|
// \-> lookup_val_1 (varsize)
|
||||||
|
// ..
|
||||||
|
// lookup_val_n (varsize)
|
||||||
|
//
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
|
@ -151,6 +167,7 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||||
condition: Symbol,
|
condition: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
lookups: &[Symbol],
|
lookups: &[Symbol],
|
||||||
|
lookup_variables: &[LookupType],
|
||||||
) {
|
) {
|
||||||
let original_ptr = shared_memory.0;
|
let original_ptr = shared_memory.0;
|
||||||
|
|
||||||
|
@ -160,9 +177,11 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let after_header = offset;
|
let after_header = offset;
|
||||||
|
|
||||||
let space_for_offsets = env
|
let space_for_offsets = env.ptr_int().const_int(
|
||||||
.ptr_int()
|
(lookups.len() * env.target_info.ptr_size() + lookups.len() * std::mem::size_of::<u32>())
|
||||||
.const_int((lookups.len() * env.target_info.ptr_size()) as _, false);
|
as _,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let mut lookup_starts = bumpalo::collections::Vec::with_capacity_in(lookups.len(), env.arena);
|
let mut lookup_starts = bumpalo::collections::Vec::with_capacity_in(lookups.len(), env.arena);
|
||||||
|
|
||||||
|
@ -203,14 +222,43 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||||
{
|
{
|
||||||
let mut offset = after_header;
|
let mut offset = after_header;
|
||||||
|
|
||||||
for lookup_start in lookup_starts {
|
for (lookup_start, lookup_var) in lookup_starts.into_iter().zip(lookup_variables) {
|
||||||
build_copy(env, original_ptr, offset, lookup_start.into());
|
// Store the pointer to the value
|
||||||
|
{
|
||||||
|
build_copy(env, original_ptr, offset, lookup_start.into());
|
||||||
|
|
||||||
let ptr_width = env
|
let ptr_width = env
|
||||||
.ptr_int()
|
.ptr_int()
|
||||||
.const_int(env.target_info.ptr_size() as _, false);
|
.const_int(env.target_info.ptr_size() as _, false);
|
||||||
|
|
||||||
offset = env.builder.build_int_add(offset, ptr_width, "offset")
|
offset = env.builder.build_int_add(offset, ptr_width, "offset");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the specialized variable of the value
|
||||||
|
{
|
||||||
|
let ptr = unsafe {
|
||||||
|
env.builder
|
||||||
|
.build_in_bounds_gep(original_ptr, &[offset], "at_current_offset")
|
||||||
|
};
|
||||||
|
|
||||||
|
let u32_ptr = env.context.i32_type().ptr_type(AddressSpace::Generic);
|
||||||
|
let ptr = env
|
||||||
|
.builder
|
||||||
|
.build_pointer_cast(ptr, u32_ptr, "cast_ptr_type");
|
||||||
|
|
||||||
|
let var_value = env
|
||||||
|
.context
|
||||||
|
.i32_type()
|
||||||
|
.const_int(lookup_var.index() as _, false);
|
||||||
|
|
||||||
|
env.builder.build_store(ptr, var_value);
|
||||||
|
|
||||||
|
let var_size = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(std::mem::size_of::<u32>() as _, false);
|
||||||
|
|
||||||
|
offset = env.builder.build_int_add(offset, var_size, "offset");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,14 +380,18 @@ fn build_clone<'a, 'ctx, 'env>(
|
||||||
let bt = basic_type_from_layout(env, &layout);
|
let bt = basic_type_from_layout(env, &layout);
|
||||||
|
|
||||||
// cast the i64 pointer to a pointer to block of memory
|
// cast the i64 pointer to a pointer to block of memory
|
||||||
let field1_cast = env.builder.build_bitcast(value, bt, "i64_to_opaque");
|
let field1_cast = env.builder.build_pointer_cast(
|
||||||
|
value.into_pointer_value(),
|
||||||
|
bt.into_pointer_type(),
|
||||||
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
build_clone_tag(
|
build_clone_tag(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
ptr,
|
ptr,
|
||||||
cursors,
|
cursors,
|
||||||
field1_cast,
|
field1_cast.into(),
|
||||||
union_layout,
|
union_layout,
|
||||||
WhenRecursive::Loop(union_layout),
|
WhenRecursive::Loop(union_layout),
|
||||||
)
|
)
|
||||||
|
@ -477,6 +529,25 @@ fn build_clone_tag<'a, 'ctx, 'env>(
|
||||||
result.into_int_value()
|
result.into_int_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_tag_data<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
tag_value: PointerValue<'ctx>,
|
||||||
|
tag_type: BasicTypeEnum<'ctx>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let raw_data_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(tag_value, RocUnion::TAG_DATA_INDEX, "tag_data")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let data_ptr = env.builder.build_pointer_cast(
|
||||||
|
raw_data_ptr,
|
||||||
|
tag_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"data_ptr",
|
||||||
|
);
|
||||||
|
|
||||||
|
env.builder.build_load(data_ptr, "load_data")
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -536,28 +607,13 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
|
|
||||||
let raw_data_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_struct_gep(
|
|
||||||
tag_value.into_pointer_value(),
|
|
||||||
RocUnion::TAG_DATA_INDEX,
|
|
||||||
"tag_data",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let layout = Layout::struct_no_name_order(field_layouts);
|
let layout = Layout::struct_no_name_order(field_layouts);
|
||||||
let layout = Layout::struct_no_name_order(
|
let layout = Layout::struct_no_name_order(
|
||||||
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let basic_type = basic_type_from_layout(env, &layout);
|
let basic_type = basic_type_from_layout(env, &layout);
|
||||||
|
let data = load_tag_data(env, tag_value.into_pointer_value(), basic_type);
|
||||||
let data_ptr = env.builder.build_pointer_cast(
|
|
||||||
raw_data_ptr,
|
|
||||||
basic_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"data_ptr",
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = env.builder.build_load(data_ptr, "load_data");
|
|
||||||
|
|
||||||
let answer =
|
let answer =
|
||||||
build_clone(env, layout_ids, ptr, cursors, data, layout, when_recursive);
|
build_clone(env, layout_ids, ptr, cursors, data, layout, when_recursive);
|
||||||
|
@ -596,11 +652,6 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let tag_value = tag_pointer_clear_tag_id(env, tag_value.into_pointer_value());
|
let tag_value = tag_pointer_clear_tag_id(env, tag_value.into_pointer_value());
|
||||||
|
|
||||||
let raw_data_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_struct_gep(tag_value, RocUnion::TAG_DATA_INDEX, "tag_data")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let layout = Layout::struct_no_name_order(field_layouts);
|
let layout = Layout::struct_no_name_order(field_layouts);
|
||||||
let layout = if union_layout.stores_tag_id_in_pointer(env.target_info) {
|
let layout = if union_layout.stores_tag_id_in_pointer(env.target_info) {
|
||||||
layout
|
layout
|
||||||
|
@ -609,15 +660,9 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
env.arena.alloc([layout, union_layout.tag_id_layout()]),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let basic_type = basic_type_from_layout(env, &layout);
|
let basic_type = basic_type_from_layout(env, &layout);
|
||||||
|
let data = load_tag_data(env, tag_value, basic_type);
|
||||||
let data_ptr = env.builder.build_pointer_cast(
|
|
||||||
raw_data_ptr,
|
|
||||||
basic_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"data_ptr",
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = env.builder.build_load(data_ptr, "load_data");
|
|
||||||
|
|
||||||
let (width, _) =
|
let (width, _) =
|
||||||
union_layout.data_size_and_alignment(env.layout_interner, env.target_info);
|
union_layout.data_size_and_alignment(env.layout_interner, env.target_info);
|
||||||
|
@ -653,8 +698,6 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NonNullableUnwrapped(fields) => {
|
NonNullableUnwrapped(fields) => {
|
||||||
//
|
|
||||||
|
|
||||||
let tag_value = tag_value.into_pointer_value();
|
let tag_value = tag_value.into_pointer_value();
|
||||||
|
|
||||||
build_copy(env, ptr, offset, extra_offset.into());
|
build_copy(env, ptr, offset, extra_offset.into());
|
||||||
|
@ -674,18 +717,7 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let raw_data_ptr = env
|
let data = load_tag_data(env, tag_value, basic_type);
|
||||||
.builder
|
|
||||||
.build_struct_gep(tag_value, RocUnion::TAG_DATA_INDEX, "tag_data")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let data_ptr = env.builder.build_pointer_cast(
|
|
||||||
raw_data_ptr,
|
|
||||||
basic_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"data_ptr",
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = env.builder.build_load(data_ptr, "load_data");
|
|
||||||
|
|
||||||
let when_recursive = WhenRecursive::Loop(union_layout);
|
let when_recursive = WhenRecursive::Loop(union_layout);
|
||||||
let answer = build_clone(env, layout_ids, ptr, cursors, data, layout, when_recursive);
|
let answer = build_clone(env, layout_ids, ptr, cursors, data, layout, when_recursive);
|
||||||
|
@ -744,19 +776,7 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let tag_value = tag_pointer_clear_tag_id(env, tag_value.into_pointer_value());
|
let tag_value = tag_pointer_clear_tag_id(env, tag_value.into_pointer_value());
|
||||||
|
let data = load_tag_data(env, tag_value, basic_type);
|
||||||
let raw_data_ptr = env
|
|
||||||
.builder
|
|
||||||
.build_struct_gep(tag_value, RocUnion::TAG_DATA_INDEX, "tag_data")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let data_ptr = env.builder.build_pointer_cast(
|
|
||||||
raw_data_ptr,
|
|
||||||
basic_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"data_ptr",
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = env.builder.build_load(data_ptr, "load_data");
|
|
||||||
|
|
||||||
let when_recursive = WhenRecursive::Loop(union_layout);
|
let when_recursive = WhenRecursive::Loop(union_layout);
|
||||||
let answer =
|
let answer =
|
||||||
|
@ -830,22 +850,7 @@ fn build_clone_tag_help<'a, 'ctx, 'env>(
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let raw_data_ptr = env
|
let data = load_tag_data(env, tag_value.into_pointer_value(), basic_type);
|
||||||
.builder
|
|
||||||
.build_struct_gep(
|
|
||||||
tag_value.into_pointer_value(),
|
|
||||||
RocUnion::TAG_DATA_INDEX,
|
|
||||||
"tag_data",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let data_ptr = env.builder.build_pointer_cast(
|
|
||||||
raw_data_ptr,
|
|
||||||
basic_type.ptr_type(AddressSpace::Generic),
|
|
||||||
"data_ptr",
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = env.builder.build_load(data_ptr, "load_data");
|
|
||||||
|
|
||||||
let when_recursive = WhenRecursive::Loop(union_layout);
|
let when_recursive = WhenRecursive::Loop(union_layout);
|
||||||
let answer =
|
let answer =
|
||||||
|
|
|
@ -263,11 +263,11 @@ pub fn build_longjmp_call(env: &Env) {
|
||||||
call_void_bitcode_fn(env, &[jmp_buf.into(), tag.into()], bitcode::UTILS_LONGJMP);
|
call_void_bitcode_fn(env, &[jmp_buf.into(), tag.into()], bitcode::UTILS_LONGJMP);
|
||||||
} else {
|
} else {
|
||||||
// Call the LLVM-intrinsic longjmp: `void @llvm.eh.sjlj.longjmp(i8* %setjmp_buf)`
|
// Call the LLVM-intrinsic longjmp: `void @llvm.eh.sjlj.longjmp(i8* %setjmp_buf)`
|
||||||
let jmp_buf_i8p = env.builder.build_bitcast(
|
let jmp_buf_i8p = env.builder.build_pointer_cast(
|
||||||
jmp_buf,
|
jmp_buf,
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
"jmp_buf i8*",
|
"jmp_buf i8*",
|
||||||
);
|
);
|
||||||
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[jmp_buf_i8p]);
|
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[jmp_buf_i8p.into()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ fn add_float_intrinsic<'ctx, F>(
|
||||||
|
|
||||||
check!(FloatWidth::F32, ctx.f32_type());
|
check!(FloatWidth::F32, ctx.f32_type());
|
||||||
check!(FloatWidth::F64, ctx.f64_type());
|
check!(FloatWidth::F64, ctx.f64_type());
|
||||||
// check!(IntWidth::F128, ctx.i128_type());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_int_intrinsic<'ctx, F>(
|
fn add_int_intrinsic<'ctx, F>(
|
||||||
|
|
|
@ -11,7 +11,7 @@ use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::{low_level::LowLevel, symbol::Symbol};
|
use roc_module::{low_level::LowLevel, symbol::Symbol};
|
||||||
use roc_mono::{
|
use roc_mono::{
|
||||||
ir::HigherOrderLowLevel,
|
ir::{HigherOrderLowLevel, LookupType},
|
||||||
layout::{Builtin, LambdaSet, Layout, LayoutIds},
|
layout::{Builtin, LambdaSet, Layout, LayoutIds},
|
||||||
};
|
};
|
||||||
use roc_target::PtrWidth;
|
use roc_target::PtrWidth;
|
||||||
|
@ -1120,14 +1120,19 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Dbg => {
|
Dbg => {
|
||||||
// now what
|
assert_eq!(args.len(), 2);
|
||||||
arguments!(condition);
|
let condition = load_symbol(scope, &args[0]);
|
||||||
|
let dbg_spec_var_symbol = args[1];
|
||||||
|
|
||||||
if env.mode.runs_expects() {
|
if env.mode.runs_expects() {
|
||||||
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
|
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
|
||||||
|
|
||||||
let shared_memory = crate::llvm::expect::SharedMemoryPointer::get(env);
|
let shared_memory = crate::llvm::expect::SharedMemoryPointer::get(env);
|
||||||
|
|
||||||
|
// HACK(dbg-spec-var): the specialized type variable is passed along as a fake symbol
|
||||||
|
let specialized_var =
|
||||||
|
unsafe { LookupType::from_index(dbg_spec_var_symbol.ident_id().index() as _) };
|
||||||
|
|
||||||
crate::llvm::expect::clone_to_shared_memory(
|
crate::llvm::expect::clone_to_shared_memory(
|
||||||
env,
|
env,
|
||||||
scope,
|
scope,
|
||||||
|
@ -1136,6 +1141,7 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||||
args[0],
|
args[0],
|
||||||
region,
|
region,
|
||||||
&[args[0]],
|
&[args[0]],
|
||||||
|
&[specialized_var],
|
||||||
);
|
);
|
||||||
|
|
||||||
crate::llvm::expect::notify_parent_dbg(env, &shared_memory);
|
crate::llvm::expect::notify_parent_dbg(env, &shared_memory);
|
||||||
|
@ -2124,13 +2130,6 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
"f64_to_f32",
|
"f64_to_f32",
|
||||||
),
|
),
|
||||||
(FloatWidth::F64, FloatWidth::F64) => arg.into(),
|
(FloatWidth::F64, FloatWidth::F64) => arg.into(),
|
||||||
(FloatWidth::F128, FloatWidth::F128) => arg.into(),
|
|
||||||
(FloatWidth::F128, _) => {
|
|
||||||
unimplemented!("I cannot handle F128 with Num.toFrac yet")
|
|
||||||
}
|
|
||||||
(_, FloatWidth::F128) => {
|
|
||||||
unimplemented!("I cannot handle F128 with Num.toFrac yet")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumCeiling => {
|
NumCeiling => {
|
||||||
|
|
|
@ -35,14 +35,11 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||||
// must make sure it's a pointer to usize
|
// must make sure it's a pointer to usize
|
||||||
let refcount_type = env.ptr_int();
|
let refcount_type = env.ptr_int();
|
||||||
|
|
||||||
let value = env
|
let value = env.builder.build_pointer_cast(
|
||||||
.builder
|
ptr,
|
||||||
.build_bitcast(
|
refcount_type.ptr_type(AddressSpace::Generic),
|
||||||
ptr,
|
"to_refcount_ptr",
|
||||||
refcount_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"to_refcount_ptr",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
Self { value }
|
Self { value }
|
||||||
}
|
}
|
||||||
|
@ -56,9 +53,8 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||||
let refcount_type = env.ptr_int();
|
let refcount_type = env.ptr_int();
|
||||||
let refcount_ptr_type = refcount_type.ptr_type(AddressSpace::Generic);
|
let refcount_ptr_type = refcount_type.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
let ptr_as_usize_ptr = builder
|
let ptr_as_usize_ptr =
|
||||||
.build_bitcast(data_ptr, refcount_ptr_type, "as_usize_ptr")
|
builder.build_pointer_cast(data_ptr, refcount_ptr_type, "as_usize_ptr");
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// get a pointer to index -1
|
// get a pointer to index -1
|
||||||
let index_intvalue = refcount_type.const_int(-1_i64 as u64, false);
|
let index_intvalue = refcount_type.const_int(-1_i64 as u64, false);
|
||||||
|
@ -203,11 +199,13 @@ fn incref_pointer<'a, 'ctx, 'env>(
|
||||||
call_void_bitcode_fn(
|
call_void_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
pointer,
|
.build_pointer_cast(
|
||||||
env.ptr_int().ptr_type(AddressSpace::Generic),
|
pointer,
|
||||||
"to_isize_ptr",
|
env.ptr_int().ptr_type(AddressSpace::Generic),
|
||||||
),
|
"to_isize_ptr",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
amount.into(),
|
amount.into(),
|
||||||
],
|
],
|
||||||
roc_builtins::bitcode::UTILS_INCREF,
|
roc_builtins::bitcode::UTILS_INCREF,
|
||||||
|
@ -223,11 +221,13 @@ fn decref_pointer<'a, 'ctx, 'env>(
|
||||||
call_void_bitcode_fn(
|
call_void_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
pointer,
|
.build_pointer_cast(
|
||||||
env.ptr_int().ptr_type(AddressSpace::Generic),
|
pointer,
|
||||||
"to_isize_ptr",
|
env.ptr_int().ptr_type(AddressSpace::Generic),
|
||||||
),
|
"to_isize_ptr",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
alignment.into(),
|
alignment.into(),
|
||||||
],
|
],
|
||||||
roc_builtins::bitcode::UTILS_DECREF,
|
roc_builtins::bitcode::UTILS_DECREF,
|
||||||
|
@ -244,11 +244,13 @@ pub fn decref_pointer_check_null<'a, 'ctx, 'env>(
|
||||||
call_void_bitcode_fn(
|
call_void_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
env.builder.build_bitcast(
|
env.builder
|
||||||
pointer,
|
.build_pointer_cast(
|
||||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
pointer,
|
||||||
"to_i8_ptr",
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
),
|
"to_i8_ptr",
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
alignment.into(),
|
alignment.into(),
|
||||||
],
|
],
|
||||||
roc_builtins::bitcode::UTILS_DECREF_CHECK_NULL,
|
roc_builtins::bitcode::UTILS_DECREF_CHECK_NULL,
|
||||||
|
@ -466,10 +468,11 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>(
|
||||||
let bt = basic_type_from_layout(env, &layout);
|
let bt = basic_type_from_layout(env, &layout);
|
||||||
|
|
||||||
// cast the i64 pointer to a pointer to block of memory
|
// cast the i64 pointer to a pointer to block of memory
|
||||||
let field_cast = env
|
let field_cast = env.builder.build_pointer_cast(
|
||||||
.builder
|
value.into_pointer_value(),
|
||||||
.build_bitcast(value, bt, "i64_to_opaque")
|
bt.into_pointer_type(),
|
||||||
.into_pointer_value();
|
"i64_to_opaque",
|
||||||
|
);
|
||||||
|
|
||||||
call_help(env, function, call_mode, field_cast.into());
|
call_help(env, function, call_mode, field_cast.into());
|
||||||
}
|
}
|
||||||
|
@ -1207,14 +1210,11 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>(
|
||||||
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
|
basic_type_from_layout(env, &Layout::struct_no_name_order(field_layouts));
|
||||||
|
|
||||||
// cast the opaque pointer to a pointer of the correct shape
|
// cast the opaque pointer to a pointer of the correct shape
|
||||||
let struct_ptr = env
|
let struct_ptr = env.builder.build_pointer_cast(
|
||||||
.builder
|
value_ptr,
|
||||||
.build_bitcast(
|
wrapper_type.ptr_type(AddressSpace::Generic),
|
||||||
value_ptr,
|
"opaque_to_correct_recursive_decrement",
|
||||||
wrapper_type.ptr_type(AddressSpace::Generic),
|
);
|
||||||
"opaque_to_correct_recursive_decrement",
|
|
||||||
)
|
|
||||||
.into_pointer_value();
|
|
||||||
|
|
||||||
// defer actually performing the refcount modifications until after the current cell has
|
// defer actually performing the refcount modifications until after the current cell has
|
||||||
// been decremented, see below
|
// been decremented, see below
|
||||||
|
|
|
@ -21,7 +21,6 @@ pub enum StackMemoryFormat {
|
||||||
/// Record, Str, List, etc.
|
/// Record, Str, List, etc.
|
||||||
DataStructure,
|
DataStructure,
|
||||||
Int128,
|
Int128,
|
||||||
Float128,
|
|
||||||
Decimal,
|
Decimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,11 +70,6 @@ impl WasmLayout {
|
||||||
match float_width {
|
match float_width {
|
||||||
F32 => Self::Primitive(ValueType::F32, size),
|
F32 => Self::Primitive(ValueType::F32, size),
|
||||||
F64 => Self::Primitive(ValueType::F64, size),
|
F64 => Self::Primitive(ValueType::F64, size),
|
||||||
F128 => Self::StackMemory {
|
|
||||||
size,
|
|
||||||
alignment_bytes,
|
|
||||||
format: StackMemoryFormat::Float128,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +150,7 @@ impl CallConv {
|
||||||
use ValueType::*;
|
use ValueType::*;
|
||||||
|
|
||||||
match format {
|
match format {
|
||||||
Int128 | Float128 | Decimal => &[I64, I64],
|
Int128 | Decimal => &[I64, I64],
|
||||||
|
|
||||||
DataStructure => {
|
DataStructure => {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
@ -191,7 +185,7 @@ impl CallConv {
|
||||||
use StackMemoryFormat::*;
|
use StackMemoryFormat::*;
|
||||||
|
|
||||||
match format {
|
match format {
|
||||||
Int128 | Float128 | Decimal => WriteToPointerArg,
|
Int128 | Decimal => WriteToPointerArg,
|
||||||
|
|
||||||
DataStructure => {
|
DataStructure => {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
|
|
|
@ -251,7 +251,7 @@ pub const DEBUG_SETTINGS: WasmDebugSettings = WasmDebugSettings {
|
||||||
let_stmt_ir: false && cfg!(debug_assertions),
|
let_stmt_ir: false && cfg!(debug_assertions),
|
||||||
instructions: false && cfg!(debug_assertions),
|
instructions: false && cfg!(debug_assertions),
|
||||||
storage_map: false && cfg!(debug_assertions),
|
storage_map: false && cfg!(debug_assertions),
|
||||||
keep_test_binary: false && cfg!(debug_assertions),
|
keep_test_binary: false && cfg!(debug_assertions), // see also ROC_WRITE_FINAL_WASM
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -30,7 +30,6 @@ enum CodeGenNumType {
|
||||||
F32, // Supported in Wasm instruction set
|
F32, // Supported in Wasm instruction set
|
||||||
F64, // Supported in Wasm instruction set
|
F64, // Supported in Wasm instruction set
|
||||||
I128, // Bytes in memory, needs Zig builtins
|
I128, // Bytes in memory, needs Zig builtins
|
||||||
F128, // Bytes in memory, needs Zig builtins
|
|
||||||
Decimal, // Bytes in memory, needs Zig builtins
|
Decimal, // Bytes in memory, needs Zig builtins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +65,6 @@ impl From<Layout<'_>> for CodeGenNumType {
|
||||||
Builtin::Float(float_width) => match float_width {
|
Builtin::Float(float_width) => match float_width {
|
||||||
FloatWidth::F32 => F32,
|
FloatWidth::F32 => F32,
|
||||||
FloatWidth::F64 => F64,
|
FloatWidth::F64 => F64,
|
||||||
FloatWidth::F128 => F128,
|
|
||||||
},
|
},
|
||||||
Builtin::Decimal => Decimal,
|
Builtin::Decimal => Decimal,
|
||||||
_ => not_num_error(),
|
_ => not_num_error(),
|
||||||
|
@ -91,7 +89,6 @@ impl From<StackMemoryFormat> for CodeGenNumType {
|
||||||
fn from(format: StackMemoryFormat) -> CodeGenNumType {
|
fn from(format: StackMemoryFormat) -> CodeGenNumType {
|
||||||
match format {
|
match format {
|
||||||
StackMemoryFormat::Int128 => CodeGenNumType::I128,
|
StackMemoryFormat::Int128 => CodeGenNumType::I128,
|
||||||
StackMemoryFormat::Float128 => CodeGenNumType::F128,
|
|
||||||
StackMemoryFormat::Decimal => CodeGenNumType::Decimal,
|
StackMemoryFormat::Decimal => CodeGenNumType::Decimal,
|
||||||
StackMemoryFormat::DataStructure => {
|
StackMemoryFormat::DataStructure => {
|
||||||
internal_error!("Tried to perform a Num low-level operation on a data structure")
|
internal_error!("Tried to perform a Num low-level operation on a data structure")
|
||||||
|
@ -804,7 +801,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_add()
|
backend.code_builder.f64_add()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.add for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
self.load_args_and_call_zig(backend, bitcode::DEC_ADD_OR_PANIC)
|
self.load_args_and_call_zig(backend, bitcode::DEC_ADD_OR_PANIC)
|
||||||
|
@ -841,7 +837,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_add()
|
backend.code_builder.f64_add()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.add for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
// TODO: don't panic
|
// TODO: don't panic
|
||||||
|
@ -897,7 +892,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_sub()
|
backend.code_builder.f64_sub()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.sub for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
self.load_args_and_call_zig(backend, bitcode::DEC_SUB_OR_PANIC)
|
self.load_args_and_call_zig(backend, bitcode::DEC_SUB_OR_PANIC)
|
||||||
|
@ -934,7 +928,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_sub()
|
backend.code_builder.f64_sub()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.sub for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
// TODO: don't panic
|
// TODO: don't panic
|
||||||
|
@ -988,7 +981,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_mul()
|
backend.code_builder.f64_mul()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.mul for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
self.load_args_and_call_zig(backend, bitcode::DEC_MUL_OR_PANIC)
|
self.load_args_and_call_zig(backend, bitcode::DEC_MUL_OR_PANIC)
|
||||||
|
@ -1024,7 +1016,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.f64_mul()
|
backend.code_builder.f64_mul()
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("Num.mul for f128"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
// TODO: don't panic
|
// TODO: don't panic
|
||||||
|
@ -1466,9 +1457,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||||
backend.code_builder.f64_sqrt()
|
backend.code_builder.f64_sqrt()
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Float(FloatWidth::F128)) => {
|
|
||||||
todo!("sqrt for f128")
|
|
||||||
}
|
|
||||||
_ => panic_ret_type(),
|
_ => panic_ret_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1625,7 +1613,10 @@ impl<'a> LowLevelCall<'a> {
|
||||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||||
match CodeGenNumType::from(self.ret_layout) {
|
match CodeGenNumType::from(self.ret_layout) {
|
||||||
I32 => backend.code_builder.i32_shl(),
|
I32 => backend.code_builder.i32_shl(),
|
||||||
I64 => backend.code_builder.i64_shl(),
|
I64 => {
|
||||||
|
backend.code_builder.i64_extend_u_i32();
|
||||||
|
backend.code_builder.i64_shl();
|
||||||
|
}
|
||||||
I128 => todo!("{:?} for I128", self.lowlevel),
|
I128 => todo!("{:?} for I128", self.lowlevel),
|
||||||
_ => panic_ret_type(),
|
_ => panic_ret_type(),
|
||||||
}
|
}
|
||||||
|
@ -1672,6 +1663,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend
|
backend
|
||||||
.storage
|
.storage
|
||||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||||
|
backend.code_builder.i64_extend_u_i32();
|
||||||
backend.code_builder.i64_shr_s();
|
backend.code_builder.i64_shr_s();
|
||||||
}
|
}
|
||||||
I128 => todo!("{:?} for I128", self.lowlevel),
|
I128 => todo!("{:?} for I128", self.lowlevel),
|
||||||
|
@ -1714,6 +1706,7 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend
|
backend
|
||||||
.storage
|
.storage
|
||||||
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
.load_symbols(&mut backend.code_builder, &[num, bits]);
|
||||||
|
backend.code_builder.i64_extend_u_i32();
|
||||||
backend.code_builder.i64_shr_u();
|
backend.code_builder.i64_shr_u();
|
||||||
}
|
}
|
||||||
I128 => todo!("{:?} for I128", self.lowlevel),
|
I128 => todo!("{:?} for I128", self.lowlevel),
|
||||||
|
@ -2004,8 +1997,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
|
|
||||||
StackMemoryFormat::Int128 => Self::eq_num128_bytes(backend, locations),
|
StackMemoryFormat::Int128 => Self::eq_num128_bytes(backend, locations),
|
||||||
|
|
||||||
StackMemoryFormat::Float128 => todo!("equality for f128"),
|
|
||||||
|
|
||||||
StackMemoryFormat::DataStructure => {
|
StackMemoryFormat::DataStructure => {
|
||||||
internal_error!("Data structure equality is handled elsewhere")
|
internal_error!("Data structure equality is handled elsewhere")
|
||||||
}
|
}
|
||||||
|
@ -2052,7 +2043,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
FloatWidth::F64 => {
|
FloatWidth::F64 => {
|
||||||
self.load_args_and_call_zig(backend, &bitcode::STR_FROM_FLOAT[width]);
|
self.load_args_and_call_zig(backend, &bitcode::STR_FROM_FLOAT[width]);
|
||||||
}
|
}
|
||||||
FloatWidth::F128 => todo!("F128 to Str"),
|
|
||||||
},
|
},
|
||||||
Layout::Builtin(Builtin::Decimal) => {
|
Layout::Builtin(Builtin::Decimal) => {
|
||||||
self.load_args_and_call_zig(backend, bitcode::DEC_TO_STR)
|
self.load_args_and_call_zig(backend, bitcode::DEC_TO_STR)
|
||||||
|
@ -2090,27 +2080,13 @@ fn num_is_finite(backend: &mut WasmBackend<'_>, argument: Symbol) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StackMemory {
|
StackMemory { format, .. } => {
|
||||||
format, location, ..
|
|
||||||
} => {
|
|
||||||
let (local_id, offset) = location.local_and_offset(backend.storage.stack_frame_pointer);
|
|
||||||
|
|
||||||
match format {
|
match format {
|
||||||
// Integers and fixed-point numbers are always finite. Just return True.
|
// Integers and fixed-point numbers are always finite. Just return True.
|
||||||
StackMemoryFormat::Int128 | StackMemoryFormat::Decimal => {
|
StackMemoryFormat::Int128 | StackMemoryFormat::Decimal => {
|
||||||
backend.code_builder.i32_const(1)
|
backend.code_builder.i32_const(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// f128 is not supported anywhere else but it's easy to support it here, so why not...
|
|
||||||
StackMemoryFormat::Float128 => {
|
|
||||||
backend.code_builder.get_local(local_id);
|
|
||||||
backend.code_builder.i64_load(Align::Bytes4, offset + 8);
|
|
||||||
backend.code_builder.i64_const(0x7fff_0000_0000_0000);
|
|
||||||
backend.code_builder.i64_and();
|
|
||||||
backend.code_builder.i64_const(0x7fff_0000_0000_0000);
|
|
||||||
backend.code_builder.i64_ne();
|
|
||||||
}
|
|
||||||
|
|
||||||
StackMemoryFormat::DataStructure => {
|
StackMemoryFormat::DataStructure => {
|
||||||
internal_error!("Tried to perform NumIsFinite on a data structure")
|
internal_error!("Tried to perform NumIsFinite on a data structure")
|
||||||
}
|
}
|
||||||
|
|
|
@ -252,7 +252,7 @@ impl<'a> Storage<'a> {
|
||||||
.extend_from_slice(CallConv::C.stack_memory_arg_types(size, format));
|
.extend_from_slice(CallConv::C.stack_memory_arg_types(size, format));
|
||||||
|
|
||||||
let location = match format {
|
let location = match format {
|
||||||
Int128 | Float128 | Decimal => {
|
Int128 | Decimal => {
|
||||||
// passed as two i64's but stored in the stack frame
|
// passed as two i64's but stored in the stack frame
|
||||||
wide_number_args.push(local_index);
|
wide_number_args.push(local_index);
|
||||||
let loc =
|
let loc =
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -654,7 +654,8 @@ fn platform_does_not_exist() {
|
||||||
|
|
||||||
match multiple_modules("platform_does_not_exist", modules) {
|
match multiple_modules("platform_does_not_exist", modules) {
|
||||||
Err(report) => {
|
Err(report) => {
|
||||||
assert!(report.contains("FILE NOT FOUND"), "report=({})", report);
|
// TODO restore this assert once it can pass.
|
||||||
|
// assert!(report.contains("FILE NOT FOUND"), "report=({})", report);
|
||||||
assert!(
|
assert!(
|
||||||
report.contains("zzz-does-not-exist/main.roc"),
|
report.contains("zzz-does-not-exist/main.roc"),
|
||||||
"report=({})",
|
"report=({})",
|
||||||
|
|
|
@ -594,6 +594,13 @@ impl IdentId {
|
||||||
pub const fn index(self) -> usize {
|
pub const fn index(self) -> usize {
|
||||||
self.0 as usize
|
self.0 as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The index is not guaranteed to know to exist.
|
||||||
|
pub unsafe fn from_index(index: u32) -> Self {
|
||||||
|
Self(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stores a mapping between Ident and IdentId.
|
/// Stores a mapping between Ident and IdentId.
|
||||||
|
|
|
@ -162,9 +162,15 @@ impl<'a> DeclarationToIndex<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let similar = self
|
||||||
|
.elements
|
||||||
|
.iter()
|
||||||
|
.filter_map(|((s, lay), _)| if *s == needle_symbol { Some(lay) } else { None })
|
||||||
|
.collect::<std::vec::Vec<_>>();
|
||||||
unreachable!(
|
unreachable!(
|
||||||
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex",
|
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex\nHowever {} similar layouts were found:\n{:#?}",
|
||||||
needle_symbol, needle_layout
|
needle_symbol, needle_layout, similar.len(), similar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,7 +948,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
|
|
||||||
ListIsUnique => arena.alloc_slice_copy(&[borrowed]),
|
ListIsUnique => arena.alloc_slice_copy(&[borrowed]),
|
||||||
|
|
||||||
Dbg => arena.alloc_slice_copy(&[borrowed]),
|
Dbg => arena.alloc_slice_copy(&[borrowed, /* dbg-spec-var */ irrelevant]),
|
||||||
|
|
||||||
BoxExpr | UnboxExpr => {
|
BoxExpr | UnboxExpr => {
|
||||||
unreachable!("These lowlevel operations are turned into mono Expr's")
|
unreachable!("These lowlevel operations are turned into mono Expr's")
|
||||||
|
|
|
@ -309,14 +309,14 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||||
condition,
|
condition,
|
||||||
region: _,
|
region: _,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables: _,
|
||||||
remainder,
|
remainder,
|
||||||
}
|
}
|
||||||
| &Stmt::ExpectFx {
|
| &Stmt::ExpectFx {
|
||||||
condition,
|
condition,
|
||||||
region: _,
|
region: _,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables: _,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
self.check_sym_layout(
|
self.check_sym_layout(
|
||||||
|
@ -324,8 +324,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||||
Layout::Builtin(Builtin::Bool),
|
Layout::Builtin(Builtin::Bool),
|
||||||
UseKind::ExpectCond,
|
UseKind::ExpectCond,
|
||||||
);
|
);
|
||||||
for (sym, lay) in lookups.iter().zip(layouts) {
|
for sym in lookups.iter() {
|
||||||
self.check_sym_layout(*sym, *lay, UseKind::ExpectLookup);
|
self.check_sym_exists(*sym);
|
||||||
}
|
}
|
||||||
self.check_stmt(remainder);
|
self.check_stmt(remainder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,8 @@ where
|
||||||
.pretty(80)
|
.pretty(80)
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
|
eprintln!("Full source: {}", src);
|
||||||
|
|
||||||
let interpolated_docs = stack(
|
let interpolated_docs = stack(
|
||||||
f,
|
f,
|
||||||
docs.into_iter()
|
docs.into_iter()
|
||||||
|
|
|
@ -555,7 +555,13 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
match &call_type {
|
match &call_type {
|
||||||
LowLevel { op, .. } => {
|
LowLevel { op, .. } => {
|
||||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
let b = match op {
|
||||||
|
roc_module::low_level::LowLevel::Dbg => {
|
||||||
|
// NB(dbg-spec-var) second var is the Variable
|
||||||
|
self.add_dec_after_lowlevel(&arguments[..1], ps, b, b_live_vars)
|
||||||
|
}
|
||||||
|
_ => self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars),
|
||||||
|
};
|
||||||
|
|
||||||
let v = Expr::Call(crate::ir::Call {
|
let v = Expr::Call(crate::ir::Call {
|
||||||
call_type,
|
call_type,
|
||||||
|
@ -1199,7 +1205,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
} => {
|
} => {
|
||||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||||
|
|
||||||
|
@ -1207,7 +1213,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1223,7 +1229,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
} => {
|
} => {
|
||||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||||
|
|
||||||
|
@ -1231,7 +1237,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,14 +27,14 @@ use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapsh
|
||||||
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
||||||
use roc_problem::can::{RuntimeError, ShadowKind};
|
use roc_problem::can::{RuntimeError, ShadowKind};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
instantiate_rigids, Content, ExhaustiveMark, FlatType, RedundantMark, StorageSubs, Subs,
|
instantiate_rigids, storage_copy_var_to, Content, ExhaustiveMark, FlatType, RedundantMark,
|
||||||
Variable, VariableSubsSlice,
|
StorageSubs, Subs, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||||
|
@ -1465,6 +1465,9 @@ impl<'a> Specializations<'a> {
|
||||||
pub struct Env<'a, 'i> {
|
pub struct Env<'a, 'i> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub subs: &'i mut Subs,
|
pub subs: &'i mut Subs,
|
||||||
|
/// [Subs] to write specialized variables of lookups in expects.
|
||||||
|
/// [None] if this module doesn't produce any expects.
|
||||||
|
pub expectation_subs: Option<&'i mut Subs>,
|
||||||
pub home: ModuleId,
|
pub home: ModuleId,
|
||||||
pub ident_ids: &'i mut IdentIds,
|
pub ident_ids: &'i mut IdentIds,
|
||||||
pub target_info: TargetInfo,
|
pub target_info: TargetInfo,
|
||||||
|
@ -1601,6 +1604,9 @@ pub fn cond<'a>(
|
||||||
|
|
||||||
pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||||
|
|
||||||
|
/// The specialized type of a lookup. Represented as a type-variable.
|
||||||
|
pub type LookupType = Variable;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Stmt<'a> {
|
pub enum Stmt<'a> {
|
||||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||||
|
@ -1622,7 +1628,7 @@ pub enum Stmt<'a> {
|
||||||
condition: Symbol,
|
condition: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
lookups: &'a [Symbol],
|
lookups: &'a [Symbol],
|
||||||
layouts: &'a [Layout<'a>],
|
variables: &'a [LookupType],
|
||||||
/// what happens after the expect
|
/// what happens after the expect
|
||||||
remainder: &'a Stmt<'a>,
|
remainder: &'a Stmt<'a>,
|
||||||
},
|
},
|
||||||
|
@ -1630,7 +1636,7 @@ pub enum Stmt<'a> {
|
||||||
condition: Symbol,
|
condition: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
lookups: &'a [Symbol],
|
lookups: &'a [Symbol],
|
||||||
layouts: &'a [Layout<'a>],
|
variables: &'a [LookupType],
|
||||||
/// what happens after the expect
|
/// what happens after the expect
|
||||||
remainder: &'a Stmt<'a>,
|
remainder: &'a Stmt<'a>,
|
||||||
},
|
},
|
||||||
|
@ -6570,13 +6576,14 @@ pub fn from_can<'a>(
|
||||||
let cond_symbol = env.unique_symbol();
|
let cond_symbol = env.unique_symbol();
|
||||||
|
|
||||||
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
let mut lookup_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
|
let mut specialized_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
|
|
||||||
for ExpectLookup {
|
for ExpectLookup {
|
||||||
symbol,
|
symbol,
|
||||||
var,
|
var,
|
||||||
ability_info,
|
ability_info,
|
||||||
} in lookups_in_cond
|
} in lookups_in_cond.iter().copied()
|
||||||
{
|
{
|
||||||
let symbol = match ability_info {
|
let symbol = match ability_info {
|
||||||
Some(specialization_id) => late_resolve_ability_specialization(
|
Some(specialization_id) => late_resolve_ability_specialization(
|
||||||
|
@ -6587,20 +6594,28 @@ pub fn from_can<'a>(
|
||||||
),
|
),
|
||||||
None => symbol,
|
None => symbol,
|
||||||
};
|
};
|
||||||
let res_layout = layout_cache.from_var(env.arena, var, env.subs);
|
|
||||||
let layout = return_on_layout_error!(env, res_layout, "Expect");
|
let expectation_subs = env
|
||||||
if !matches!(layout, Layout::LambdaSet(..)) {
|
.expectation_subs
|
||||||
|
.as_deref_mut()
|
||||||
|
.expect("if expects are compiled, their subs should be available");
|
||||||
|
let spec_var = expectation_subs.fresh_unnamed_flex_var();
|
||||||
|
|
||||||
|
if !env.subs.is_function(var) {
|
||||||
// Exclude functions from lookups
|
// Exclude functions from lookups
|
||||||
lookups.push(symbol);
|
lookups.push(symbol);
|
||||||
layouts.push(layout);
|
lookup_variables.push(var);
|
||||||
|
specialized_variables.push(spec_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let specialized_variables = specialized_variables.into_bump_slice();
|
||||||
|
|
||||||
let mut stmt = Stmt::Expect {
|
let mut stmt = Stmt::Expect {
|
||||||
condition: cond_symbol,
|
condition: cond_symbol,
|
||||||
region: loc_condition.region,
|
region: loc_condition.region,
|
||||||
lookups: lookups.into_bump_slice(),
|
lookups: lookups.into_bump_slice(),
|
||||||
layouts: layouts.into_bump_slice(),
|
variables: specialized_variables,
|
||||||
remainder: env.arena.alloc(rest),
|
remainder: env.arena.alloc(rest),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6614,6 +6629,10 @@ pub fn from_can<'a>(
|
||||||
env.arena.alloc(stmt),
|
env.arena.alloc(stmt),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Now that the condition has been specialized, export the specialized types of our
|
||||||
|
// lookups into the expectation subs.
|
||||||
|
store_specialized_expectation_lookups(env, lookup_variables, specialized_variables);
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6626,13 +6645,14 @@ pub fn from_can<'a>(
|
||||||
let cond_symbol = env.unique_symbol();
|
let cond_symbol = env.unique_symbol();
|
||||||
|
|
||||||
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
let mut lookup_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
|
let mut specialized_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||||
|
|
||||||
for ExpectLookup {
|
for ExpectLookup {
|
||||||
symbol,
|
symbol,
|
||||||
var,
|
var,
|
||||||
ability_info,
|
ability_info,
|
||||||
} in lookups_in_cond
|
} in lookups_in_cond.iter().copied()
|
||||||
{
|
{
|
||||||
let symbol = match ability_info {
|
let symbol = match ability_info {
|
||||||
Some(specialization_id) => late_resolve_ability_specialization(
|
Some(specialization_id) => late_resolve_ability_specialization(
|
||||||
|
@ -6643,20 +6663,28 @@ pub fn from_can<'a>(
|
||||||
),
|
),
|
||||||
None => symbol,
|
None => symbol,
|
||||||
};
|
};
|
||||||
let res_layout = layout_cache.from_var(env.arena, var, env.subs);
|
|
||||||
let layout = return_on_layout_error!(env, res_layout, "Expect");
|
let expectation_subs = env
|
||||||
if !matches!(layout, Layout::LambdaSet(..)) {
|
.expectation_subs
|
||||||
|
.as_deref_mut()
|
||||||
|
.expect("if expects are compiled, their subs should be available");
|
||||||
|
let spec_var = expectation_subs.fresh_unnamed_flex_var();
|
||||||
|
|
||||||
|
if !env.subs.is_function(var) {
|
||||||
// Exclude functions from lookups
|
// Exclude functions from lookups
|
||||||
lookups.push(symbol);
|
lookups.push(symbol);
|
||||||
layouts.push(layout);
|
lookup_variables.push(var);
|
||||||
|
specialized_variables.push(spec_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let specialized_variables = specialized_variables.into_bump_slice();
|
||||||
|
|
||||||
let mut stmt = Stmt::ExpectFx {
|
let mut stmt = Stmt::ExpectFx {
|
||||||
condition: cond_symbol,
|
condition: cond_symbol,
|
||||||
region: loc_condition.region,
|
region: loc_condition.region,
|
||||||
lookups: lookups.into_bump_slice(),
|
lookups: lookups.into_bump_slice(),
|
||||||
layouts: layouts.into_bump_slice(),
|
variables: specialized_variables,
|
||||||
remainder: env.arena.alloc(rest),
|
remainder: env.arena.alloc(rest),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6670,6 +6698,8 @@ pub fn from_can<'a>(
|
||||||
env.arena.alloc(stmt),
|
env.arena.alloc(stmt),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
store_specialized_expectation_lookups(env, lookup_variables, specialized_variables);
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6681,12 +6711,23 @@ pub fn from_can<'a>(
|
||||||
} => {
|
} => {
|
||||||
let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache);
|
let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache);
|
||||||
|
|
||||||
|
let spec_var = env
|
||||||
|
.expectation_subs
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.fresh_unnamed_flex_var();
|
||||||
|
// HACK(dbg-spec-var): pass the specialized type variable along injected into a fake symbol
|
||||||
|
let dbg_spec_var_symbol = Symbol::new(ModuleId::ATTR, unsafe {
|
||||||
|
IdentId::from_index(spec_var.index())
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: need to store the specialized variable of this dbg in the expectation_subs
|
||||||
let call = crate::ir::Call {
|
let call = crate::ir::Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
op: LowLevel::Dbg,
|
op: LowLevel::Dbg,
|
||||||
update_mode: env.next_update_mode_id(),
|
update_mode: env.next_update_mode_id(),
|
||||||
},
|
},
|
||||||
arguments: env.arena.alloc([dbg_symbol]),
|
arguments: env.arena.alloc([dbg_symbol, dbg_spec_var_symbol]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dbg_layout = layout_cache
|
let dbg_layout = layout_cache
|
||||||
|
@ -6714,6 +6755,10 @@ pub fn from_can<'a>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the dbg value has been specialized, export its specialized type into the
|
||||||
|
// expectations subs.
|
||||||
|
store_specialized_expectation_lookups(env, [variable], &[spec_var]);
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6752,6 +6797,21 @@ pub fn from_can<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_specialized_expectation_lookups(
|
||||||
|
env: &mut Env,
|
||||||
|
lookup_variables: impl IntoIterator<Item = Variable>,
|
||||||
|
specialized_variables: &[Variable],
|
||||||
|
) {
|
||||||
|
let subs = &env.subs;
|
||||||
|
let expectation_subs = env.expectation_subs.as_deref_mut().unwrap();
|
||||||
|
for (lookup_var, stored_var) in lookup_variables.into_iter().zip(specialized_variables) {
|
||||||
|
let stored_specialized_var =
|
||||||
|
storage_copy_var_to(&mut Default::default(), subs, expectation_subs, lookup_var);
|
||||||
|
let stored_specialized_desc = expectation_subs.get(stored_specialized_var);
|
||||||
|
expectation_subs.union(*stored_var, stored_specialized_var, stored_specialized_desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_opt_branches<'a>(
|
fn to_opt_branches<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -7082,7 +7142,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let new_remainder =
|
let new_remainder =
|
||||||
|
@ -7097,7 +7157,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
condition: substitute(subs, *condition).unwrap_or(*condition),
|
condition: substitute(subs, *condition).unwrap_or(*condition),
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups: new_lookups.into_bump_slice(),
|
lookups: new_lookups.into_bump_slice(),
|
||||||
layouts,
|
variables,
|
||||||
remainder: new_remainder,
|
remainder: new_remainder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7108,7 +7168,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let new_remainder =
|
let new_remainder =
|
||||||
|
@ -7123,7 +7183,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
condition: substitute(subs, *condition).unwrap_or(*condition),
|
condition: substitute(subs, *condition).unwrap_or(*condition),
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups: new_lookups.into_bump_slice(),
|
lookups: new_lookups.into_bump_slice(),
|
||||||
layouts,
|
variables,
|
||||||
remainder: new_remainder,
|
remainder: new_remainder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8234,49 +8294,71 @@ fn specialize_symbol<'a>(
|
||||||
Err(e) => return_on_layout_error_help!(env, e, "specialize_symbol"),
|
Err(e) => return_on_layout_error_help!(env, e, "specialize_symbol"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if procs.is_imported_module_thunk(original) {
|
match raw {
|
||||||
let layout = match raw {
|
RawFunctionLayout::Function(_, lambda_set, _)
|
||||||
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
if !procs.is_imported_module_thunk(original) =>
|
||||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
{
|
||||||
Layout::LambdaSet(lambda_set)
|
let lambda_name =
|
||||||
}
|
find_lambda_name(env, layout_cache, lambda_set, original, &[]);
|
||||||
};
|
|
||||||
|
|
||||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
debug_assert!(
|
||||||
let top_level = ProcLayout::from_raw(
|
lambda_name.no_captures(),
|
||||||
env.arena,
|
"imported functions are top-level and should never capture"
|
||||||
&layout_cache.interner,
|
);
|
||||||
raw,
|
|
||||||
CapturesNiche::no_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.insert_passed_by_name(
|
let function_ptr_layout = ProcLayout::from_raw(
|
||||||
env,
|
env.arena,
|
||||||
arg_var,
|
&layout_cache.interner,
|
||||||
LambdaName::no_niche(original),
|
raw,
|
||||||
top_level,
|
lambda_name.captures_niche(),
|
||||||
layout_cache,
|
);
|
||||||
);
|
procs.insert_passed_by_name(
|
||||||
|
env,
|
||||||
|
arg_var,
|
||||||
|
lambda_name,
|
||||||
|
function_ptr_layout,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
|
|
||||||
force_thunk(env, original, layout, assign_to, env.arena.alloc(result))
|
construct_closure_data(
|
||||||
} else {
|
env,
|
||||||
// Imported symbol, so it must have no captures niche (since
|
procs,
|
||||||
// top-levels can't capture)
|
layout_cache,
|
||||||
let top_level = ProcLayout::from_raw(
|
lambda_set,
|
||||||
env.arena,
|
lambda_name,
|
||||||
&layout_cache.interner,
|
&[],
|
||||||
raw,
|
assign_to,
|
||||||
CapturesNiche::no_niche(),
|
env.arena.alloc(result),
|
||||||
);
|
)
|
||||||
procs.insert_passed_by_name(
|
}
|
||||||
env,
|
_ => {
|
||||||
arg_var,
|
// This is an imported ZAT that returns either a value, or the closure
|
||||||
LambdaName::no_niche(original),
|
// data for a lambda set.
|
||||||
top_level,
|
let layout = match raw {
|
||||||
layout_cache,
|
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||||
);
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
|
Layout::LambdaSet(lambda_set)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let_empty_struct(assign_to, env.arena.alloc(result))
|
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||||
|
let top_level = ProcLayout::from_raw(
|
||||||
|
env.arena,
|
||||||
|
&layout_cache.interner,
|
||||||
|
raw,
|
||||||
|
CapturesNiche::no_niche(),
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.insert_passed_by_name(
|
||||||
|
env,
|
||||||
|
arg_var,
|
||||||
|
LambdaName::no_niche(original),
|
||||||
|
top_level,
|
||||||
|
layout_cache,
|
||||||
|
);
|
||||||
|
|
||||||
|
force_thunk(env, original, layout, assign_to, env.arena.alloc(result))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2890,7 +2890,6 @@ impl<'a> Builtin<'a> {
|
||||||
use FloatWidth::*;
|
use FloatWidth::*;
|
||||||
|
|
||||||
match float_width {
|
match float_width {
|
||||||
F128 => alloc.text("Float128"),
|
|
||||||
F64 => alloc.text("Float64"),
|
F64 => alloc.text("Float64"),
|
||||||
F32 => alloc.text("Float32"),
|
F32 => alloc.text("Float32"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ fn function_s<'a, 'i>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let continuation: &Stmt = remainder;
|
let continuation: &Stmt = remainder;
|
||||||
|
@ -208,7 +208,7 @@ fn function_s<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: new_continuation,
|
remainder: new_continuation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ fn function_s<'a, 'i>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let continuation: &Stmt = remainder;
|
let continuation: &Stmt = remainder;
|
||||||
|
@ -233,7 +233,7 @@ fn function_s<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: new_continuation,
|
remainder: new_continuation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let (b, found) = function_d_main(env, x, c, remainder);
|
let (b, found) = function_d_main(env, x, c, remainder);
|
||||||
|
@ -452,7 +452,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -464,7 +464,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -475,7 +475,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let (b, found) = function_d_main(env, x, c, remainder);
|
let (b, found) = function_d_main(env, x, c, remainder);
|
||||||
|
@ -485,7 +485,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ fn function_d_main<'a, 'i>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -660,7 +660,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let b = function_r(env, remainder);
|
let b = function_r(env, remainder);
|
||||||
|
@ -669,7 +669,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -680,7 +680,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let b = function_r(env, remainder);
|
let b = function_r(env, remainder);
|
||||||
|
@ -689,7 +689,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: b,
|
remainder: b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ fn insert_jumps<'a>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => match insert_jumps(
|
} => match insert_jumps(
|
||||||
arena,
|
arena,
|
||||||
|
@ -267,7 +267,7 @@ fn insert_jumps<'a>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: cont,
|
remainder: cont,
|
||||||
})),
|
})),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -277,7 +277,7 @@ fn insert_jumps<'a>(
|
||||||
condition,
|
condition,
|
||||||
region,
|
region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder,
|
remainder,
|
||||||
} => match insert_jumps(
|
} => match insert_jumps(
|
||||||
arena,
|
arena,
|
||||||
|
@ -291,7 +291,7 @@ fn insert_jumps<'a>(
|
||||||
condition: *condition,
|
condition: *condition,
|
||||||
region: *region,
|
region: *region,
|
||||||
lookups,
|
lookups,
|
||||||
layouts,
|
variables,
|
||||||
remainder: cont,
|
remainder: cont,
|
||||||
})),
|
})),
|
||||||
None => None,
|
None => None,
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
"app"
|
"app"
|
||||||
"platform"
|
"platform"
|
||||||
|
"package"
|
||||||
"provides"
|
"provides"
|
||||||
"requires"
|
"requires"
|
||||||
"exposes"
|
"exposes"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PlatformHeader};
|
use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PackageHeader, PlatformHeader};
|
||||||
use crate::ident::Ident;
|
use crate::ident::Ident;
|
||||||
use bumpalo::collections::{String, Vec};
|
use bumpalo::collections::{String, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -90,6 +90,7 @@ pub struct Module<'a> {
|
||||||
pub enum Header<'a> {
|
pub enum Header<'a> {
|
||||||
Interface(InterfaceHeader<'a>),
|
Interface(InterfaceHeader<'a>),
|
||||||
App(AppHeader<'a>),
|
App(AppHeader<'a>),
|
||||||
|
Package(PackageHeader<'a>),
|
||||||
Platform(PlatformHeader<'a>),
|
Platform(PlatformHeader<'a>),
|
||||||
Hosted(HostedHeader<'a>),
|
Hosted(HostedHeader<'a>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,29 +2,50 @@ use crate::ast::{Collection, CommentOrNewline, Spaced, Spaces, StrLiteral, TypeA
|
||||||
use crate::blankspace::space0_e;
|
use crate::blankspace::space0_e;
|
||||||
use crate::ident::{lowercase_ident, UppercaseIdent};
|
use crate::ident::{lowercase_ident, UppercaseIdent};
|
||||||
use crate::parser::{optional, then};
|
use crate::parser::{optional, then};
|
||||||
use crate::parser::{specialize, word1, EPackageEntry, EPackagePath, Parser};
|
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
||||||
use crate::string_literal;
|
use crate::string_literal;
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
impl<'a> HeaderType<'a> {
|
||||||
|
pub fn exposed_or_provided_values(&'a self) -> &'a [Loc<ExposedName<'a>>] {
|
||||||
|
match self {
|
||||||
|
HeaderType::App {
|
||||||
|
provides: exposes, ..
|
||||||
|
}
|
||||||
|
| HeaderType::Hosted { exposes, .. }
|
||||||
|
| HeaderType::Builtin { exposes, .. }
|
||||||
|
| HeaderType::Interface { exposes, .. } => exposes,
|
||||||
|
HeaderType::Platform { .. } | HeaderType::Package { .. } => &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum HeaderType<'a> {
|
pub enum HeaderType<'a> {
|
||||||
App {
|
App {
|
||||||
output_name: StrLiteral<'a>,
|
output_name: StrLiteral<'a>,
|
||||||
|
provides: &'a [Loc<ExposedName<'a>>],
|
||||||
to_platform: To<'a>,
|
to_platform: To<'a>,
|
||||||
},
|
},
|
||||||
Hosted {
|
Hosted {
|
||||||
name: ModuleName<'a>,
|
name: ModuleName<'a>,
|
||||||
|
exposes: &'a [Loc<ExposedName<'a>>],
|
||||||
generates: UppercaseIdent<'a>,
|
generates: UppercaseIdent<'a>,
|
||||||
generates_with: &'a [Loc<ExposedName<'a>>],
|
generates_with: &'a [Loc<ExposedName<'a>>],
|
||||||
},
|
},
|
||||||
/// Only created during canonicalization, never actually parsed from source
|
/// Only created during canonicalization, never actually parsed from source
|
||||||
Builtin {
|
Builtin {
|
||||||
name: ModuleName<'a>,
|
name: ModuleName<'a>,
|
||||||
|
exposes: &'a [Loc<ExposedName<'a>>],
|
||||||
generates_with: &'a [Symbol],
|
generates_with: &'a [Symbol],
|
||||||
},
|
},
|
||||||
|
Package {
|
||||||
|
/// usually something other than `pf`
|
||||||
|
config_shorthand: &'a str,
|
||||||
|
exposes: &'a [Loc<ModuleName<'a>>],
|
||||||
|
},
|
||||||
Platform {
|
Platform {
|
||||||
opt_app_module_id: Option<ModuleId>,
|
opt_app_module_id: Option<ModuleId>,
|
||||||
/// the name and type scheme of the main function (required by the platform)
|
/// the name and type scheme of the main function (required by the platform)
|
||||||
|
@ -32,12 +53,14 @@ pub enum HeaderType<'a> {
|
||||||
provides: &'a [(Loc<ExposedName<'a>>, Loc<TypedIdent<'a>>)],
|
provides: &'a [(Loc<ExposedName<'a>>, Loc<TypedIdent<'a>>)],
|
||||||
requires: &'a [Loc<TypedIdent<'a>>],
|
requires: &'a [Loc<TypedIdent<'a>>],
|
||||||
requires_types: &'a [Loc<UppercaseIdent<'a>>],
|
requires_types: &'a [Loc<UppercaseIdent<'a>>],
|
||||||
|
exposes: &'a [Loc<ModuleName<'a>>],
|
||||||
|
|
||||||
/// usually `pf`
|
/// usually `pf`
|
||||||
config_shorthand: &'a str,
|
config_shorthand: &'a str,
|
||||||
},
|
},
|
||||||
Interface {
|
Interface {
|
||||||
name: ModuleName<'a>,
|
name: ModuleName<'a>,
|
||||||
|
exposes: &'a [Loc<ExposedName<'a>>],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +82,9 @@ pub enum VersionComparison {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct PackagePath<'a>(&'a str);
|
pub struct PackageName<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> PackagePath<'a> {
|
impl<'a> PackageName<'a> {
|
||||||
pub fn to_str(self) -> &'a str {
|
pub fn to_str(self) -> &'a str {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -71,13 +94,13 @@ impl<'a> PackagePath<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<PackagePath<'a>> for &'a str {
|
impl<'a> From<PackageName<'a>> for &'a str {
|
||||||
fn from(name: PackagePath<'a>) -> &'a str {
|
fn from(name: PackageName<'a>) -> &'a str {
|
||||||
name.0
|
name.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for PackagePath<'a> {
|
impl<'a> From<&'a str> for PackageName<'a> {
|
||||||
fn from(string: &'a str) -> Self {
|
fn from(string: &'a str) -> Self {
|
||||||
Self(string)
|
Self(string)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +204,7 @@ pub struct HostedHeader<'a> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum To<'a> {
|
pub enum To<'a> {
|
||||||
ExistingPackage(&'a str),
|
ExistingPackage(&'a str),
|
||||||
NewPackage(PackagePath<'a>),
|
NewPackage(PackageName<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -209,16 +232,11 @@ pub struct ProvidesTo<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PackageHeader<'a> {
|
pub struct PackageHeader<'a> {
|
||||||
pub before_name: &'a [CommentOrNewline<'a>],
|
pub before_name: &'a [CommentOrNewline<'a>],
|
||||||
pub name: Loc<PackagePath<'a>>,
|
pub name: Loc<PackageName<'a>>,
|
||||||
|
|
||||||
pub exposes_keyword: Spaces<'a, ExposesKeyword>,
|
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
pub packages:
|
||||||
|
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||||
pub packages_keyword: Spaces<'a, PackagesKeyword>,
|
|
||||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackagePath<'a>>)>,
|
|
||||||
|
|
||||||
pub imports_keyword: Spaces<'a, ImportsKeyword>,
|
|
||||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -230,7 +248,7 @@ pub struct PlatformRequires<'a> {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PlatformHeader<'a> {
|
pub struct PlatformHeader<'a> {
|
||||||
pub before_name: &'a [CommentOrNewline<'a>],
|
pub before_name: &'a [CommentOrNewline<'a>],
|
||||||
pub name: Loc<PackagePath<'a>>,
|
pub name: Loc<PackageName<'a>>,
|
||||||
|
|
||||||
pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
|
pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
|
||||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||||
|
@ -271,7 +289,7 @@ pub struct TypedIdent<'a> {
|
||||||
pub struct PackageEntry<'a> {
|
pub struct PackageEntry<'a> {
|
||||||
pub shorthand: &'a str,
|
pub shorthand: &'a str,
|
||||||
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
|
||||||
pub package_path: Loc<PackagePath<'a>>,
|
pub package_name: Loc<PackageName<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||||
|
@ -288,19 +306,19 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
||||||
),
|
),
|
||||||
space0_e(EPackageEntry::IndentPackage)
|
space0_e(EPackageEntry::IndentPackage)
|
||||||
)),
|
)),
|
||||||
loc!(specialize(EPackageEntry::BadPackage, package_path()))
|
loc!(specialize(EPackageEntry::BadPackage, package_name()))
|
||||||
),
|
),
|
||||||
move |(opt_shorthand, package_or_path)| {
|
move |(opt_shorthand, package_or_path)| {
|
||||||
let entry = match opt_shorthand {
|
let entry = match opt_shorthand {
|
||||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||||
shorthand,
|
shorthand,
|
||||||
spaces_after_shorthand,
|
spaces_after_shorthand,
|
||||||
package_path: package_or_path,
|
package_name: package_or_path,
|
||||||
},
|
},
|
||||||
None => PackageEntry {
|
None => PackageEntry {
|
||||||
shorthand: "",
|
shorthand: "",
|
||||||
spaces_after_shorthand: &[],
|
spaces_after_shorthand: &[],
|
||||||
package_path: package_or_path,
|
package_name: package_or_path,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -309,13 +327,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_path<'a>() -> impl Parser<'a, PackagePath<'a>, EPackagePath<'a>> {
|
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
|
||||||
then(
|
then(
|
||||||
loc!(specialize(EPackagePath::BadPath, string_literal::parse())),
|
loc!(specialize(EPackageName::BadPath, string_literal::parse())),
|
||||||
move |_arena, state, progress, text| match text.value {
|
move |_arena, state, progress, text| match text.value {
|
||||||
StrLiteral::PlainLine(text) => Ok((progress, PackagePath(text), state)),
|
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),
|
||||||
StrLiteral::Line(_) => Err((progress, EPackagePath::Escapes(text.region.start()))),
|
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(text.region.start()))),
|
||||||
StrLiteral::Block(_) => Err((progress, EPackagePath::Multiline(text.region.start()))),
|
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(text.region.start()))),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
||||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||||
use crate::header::{
|
use crate::header::{
|
||||||
package_entry, package_path, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||||
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
||||||
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||||
};
|
};
|
||||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
|
@ -67,6 +67,13 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
||||||
),
|
),
|
||||||
Header::App
|
Header::App
|
||||||
),
|
),
|
||||||
|
map!(
|
||||||
|
skip_first!(
|
||||||
|
keyword_e("package", EHeader::Start),
|
||||||
|
increment_min_indent(package_header())
|
||||||
|
),
|
||||||
|
Header::Package
|
||||||
|
),
|
||||||
map!(
|
map!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
keyword_e("platform", EHeader::Start),
|
keyword_e("platform", EHeader::Start),
|
||||||
|
@ -183,11 +190,22 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||||
.trace("app_header")
|
.trace("app_header")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
|
||||||
|
record!(PackageHeader {
|
||||||
|
before_name: space0_e(EHeader::IndentStart),
|
||||||
|
name: loc!(specialize(EHeader::PackageName, package_name())),
|
||||||
|
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
||||||
|
packages: specialize(EHeader::Packages, packages()),
|
||||||
|
})
|
||||||
|
.trace("package_header")
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||||
record!(PlatformHeader {
|
record!(PlatformHeader {
|
||||||
before_name: space0_e(EHeader::IndentStart),
|
before_name: space0_e(EHeader::IndentStart),
|
||||||
name: loc!(specialize(EHeader::PlatformName, package_path())),
|
name: loc!(specialize(EHeader::PlatformName, package_name())),
|
||||||
requires: specialize(EHeader::Requires, requires()),
|
requires: specialize(EHeader::Requires, requires()),
|
||||||
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
||||||
packages: specialize(EHeader::Packages, packages()),
|
packages: specialize(EHeader::Packages, packages()),
|
||||||
|
@ -203,7 +221,7 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
|
||||||
|_, pos| EProvides::Identifier(pos),
|
|_, pos| EProvides::Identifier(pos),
|
||||||
map!(lowercase_ident(), To::ExistingPackage)
|
map!(lowercase_ident(), To::ExistingPackage)
|
||||||
),
|
),
|
||||||
specialize(EProvides::Package, map!(package_path(), To::NewPackage))
|
specialize(EProvides::Package, map!(package_name(), To::NewPackage))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,8 @@ pub enum EHeader<'a> {
|
||||||
Start(Position),
|
Start(Position),
|
||||||
ModuleName(Position),
|
ModuleName(Position),
|
||||||
AppName(EString<'a>, Position),
|
AppName(EString<'a>, Position),
|
||||||
PlatformName(EPackagePath<'a>, Position),
|
PackageName(EPackageName<'a>, Position),
|
||||||
|
PlatformName(EPackageName<'a>, Position),
|
||||||
IndentStart(Position),
|
IndentStart(Position),
|
||||||
|
|
||||||
InconsistentModuleName(Region),
|
InconsistentModuleName(Region),
|
||||||
|
@ -146,7 +147,7 @@ pub enum EProvides<'a> {
|
||||||
ListStart(Position),
|
ListStart(Position),
|
||||||
ListEnd(Position),
|
ListEnd(Position),
|
||||||
Identifier(Position),
|
Identifier(Position),
|
||||||
Package(EPackagePath<'a>, Position),
|
Package(EPackageName<'a>, Position),
|
||||||
Space(BadInputError, Position),
|
Space(BadInputError, Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +203,7 @@ pub enum EPackages<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EPackagePath<'a> {
|
pub enum EPackageName<'a> {
|
||||||
BadPath(EString<'a>, Position),
|
BadPath(EString<'a>, Position),
|
||||||
Escapes(Position),
|
Escapes(Position),
|
||||||
Multiline(Position),
|
Multiline(Position),
|
||||||
|
@ -210,7 +211,7 @@ pub enum EPackagePath<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum EPackageEntry<'a> {
|
pub enum EPackageEntry<'a> {
|
||||||
BadPackage(EPackagePath<'a>, Position),
|
BadPackage(EPackageName<'a>, Position),
|
||||||
Shorthand(Position),
|
Shorthand(Position),
|
||||||
Colon(Position),
|
Colon(Position),
|
||||||
IndentPackage(Position),
|
IndentPackage(Position),
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
Module {
|
||||||
|
comments: [],
|
||||||
|
header: Package(
|
||||||
|
PackageHeader {
|
||||||
|
before_name: [],
|
||||||
|
name: @8-24 PackageName(
|
||||||
|
"rtfeldman/blah",
|
||||||
|
),
|
||||||
|
exposes: KeywordItem {
|
||||||
|
keyword: Spaces {
|
||||||
|
before: [],
|
||||||
|
item: ExposesKeyword,
|
||||||
|
after: [],
|
||||||
|
},
|
||||||
|
item: [],
|
||||||
|
},
|
||||||
|
packages: KeywordItem {
|
||||||
|
keyword: Spaces {
|
||||||
|
before: [],
|
||||||
|
item: PackagesKeyword,
|
||||||
|
after: [],
|
||||||
|
},
|
||||||
|
item: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package "rtfeldman/blah" exposes [] packages {}
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-25 PackagePath(
|
name: @9-25 PackageName(
|
||||||
"rtfeldman/blah",
|
"rtfeldman/blah",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@31-47 PackageEntry {
|
@31-47 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_path: @35-47 PackagePath(
|
package_name: @35-47 PackageName(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@31-47 PackageEntry {
|
@31-47 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_path: @35-47 PackagePath(
|
package_name: @35-47 PackageName(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-14 PackagePath(
|
name: @9-14 PackageName(
|
||||||
"cli",
|
"cli",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -22,7 +22,7 @@ Module {
|
||||||
after: [],
|
after: [],
|
||||||
},
|
},
|
||||||
to: @30-38 NewPackage(
|
to: @30-38 NewPackage(
|
||||||
PackagePath(
|
PackageName(
|
||||||
"./blah",
|
"./blah",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
Module {
|
||||||
|
comments: [],
|
||||||
|
header: Package(
|
||||||
|
PackageHeader {
|
||||||
|
before_name: [],
|
||||||
|
name: @8-20 PackageName(
|
||||||
|
"foo/barbaz",
|
||||||
|
),
|
||||||
|
exposes: KeywordItem {
|
||||||
|
keyword: Spaces {
|
||||||
|
before: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
item: ExposesKeyword,
|
||||||
|
after: [],
|
||||||
|
},
|
||||||
|
item: [
|
||||||
|
@34-37 ModuleName(
|
||||||
|
"Foo",
|
||||||
|
),
|
||||||
|
@39-42 ModuleName(
|
||||||
|
"Bar",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
packages: KeywordItem {
|
||||||
|
keyword: Spaces {
|
||||||
|
before: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
item: PackagesKeyword,
|
||||||
|
after: [],
|
||||||
|
},
|
||||||
|
item: [
|
||||||
|
@59-71 PackageEntry {
|
||||||
|
shorthand: "foo",
|
||||||
|
spaces_after_shorthand: [],
|
||||||
|
package_name: @64-71 PackageName(
|
||||||
|
"./foo",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package "foo/barbaz"
|
||||||
|
exposes [Foo, Bar]
|
||||||
|
packages { foo: "./foo" }
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-21 PackagePath(
|
name: @9-21 PackageName(
|
||||||
"foo/barbaz",
|
"foo/barbaz",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
@ -52,7 +52,7 @@ Module {
|
||||||
@87-99 PackageEntry {
|
@87-99 PackageEntry {
|
||||||
shorthand: "foo",
|
shorthand: "foo",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_path: @92-99 PackagePath(
|
package_name: @92-99 PackageName(
|
||||||
"./foo",
|
"./foo",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,7 +19,7 @@ Module {
|
||||||
@26-42 PackageEntry {
|
@26-42 PackageEntry {
|
||||||
shorthand: "pf",
|
shorthand: "pf",
|
||||||
spaces_after_shorthand: [],
|
spaces_after_shorthand: [],
|
||||||
package_path: @30-42 PackagePath(
|
package_name: @30-42 PackageName(
|
||||||
"./platform",
|
"./platform",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,7 +3,7 @@ Module {
|
||||||
header: Platform(
|
header: Platform(
|
||||||
PlatformHeader {
|
PlatformHeader {
|
||||||
before_name: [],
|
before_name: [],
|
||||||
name: @9-21 PackagePath(
|
name: @9-21 PackageName(
|
||||||
"test/types",
|
"test/types",
|
||||||
),
|
),
|
||||||
requires: KeywordItem {
|
requires: KeywordItem {
|
||||||
|
|
|
@ -224,6 +224,7 @@ mod test_parse {
|
||||||
pass/empty_hosted_header.header,
|
pass/empty_hosted_header.header,
|
||||||
pass/empty_interface_header.header,
|
pass/empty_interface_header.header,
|
||||||
pass/empty_list.expr,
|
pass/empty_list.expr,
|
||||||
|
pass/empty_package_header.header,
|
||||||
pass/empty_platform_header.header,
|
pass/empty_platform_header.header,
|
||||||
pass/empty_record.expr,
|
pass/empty_record.expr,
|
||||||
pass/empty_string.expr,
|
pass/empty_string.expr,
|
||||||
|
@ -280,6 +281,7 @@ mod test_parse {
|
||||||
pass/newline_inside_empty_list.expr,
|
pass/newline_inside_empty_list.expr,
|
||||||
pass/newline_singleton_list.expr,
|
pass/newline_singleton_list.expr,
|
||||||
pass/nonempty_hosted_header.header,
|
pass/nonempty_hosted_header.header,
|
||||||
|
pass/nonempty_package_header.header,
|
||||||
pass/nonempty_platform_header.header,
|
pass/nonempty_platform_header.header,
|
||||||
pass/not_docs.expr,
|
pass/not_docs.expr,
|
||||||
pass/number_literal_suffixes.expr,
|
pass/number_literal_suffixes.expr,
|
||||||
|
|
|
@ -262,6 +262,7 @@ mod solve_expr {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct InferOptions {
|
struct InferOptions {
|
||||||
|
print_can_decls: bool,
|
||||||
print_only_under_alias: bool,
|
print_only_under_alias: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
}
|
}
|
||||||
|
@ -302,7 +303,20 @@ mod solve_expr {
|
||||||
let queries = parse_queries(&src);
|
let queries = parse_queries(&src);
|
||||||
assert!(!queries.is_empty(), "No queries provided!");
|
assert!(!queries.is_empty(), "No queries provided!");
|
||||||
|
|
||||||
let mut solved_queries = Vec::with_capacity(queries.len());
|
let mut output_parts = Vec::with_capacity(queries.len() + 2);
|
||||||
|
|
||||||
|
if options.print_can_decls {
|
||||||
|
use roc_can::debug::{pretty_print_declarations, PPCtx};
|
||||||
|
let ctx = PPCtx {
|
||||||
|
home,
|
||||||
|
interns: &interns,
|
||||||
|
print_lambda_names: true,
|
||||||
|
};
|
||||||
|
let pretty_decls = pretty_print_declarations(&ctx, &decls);
|
||||||
|
output_parts.push(pretty_decls);
|
||||||
|
output_parts.push("\n".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
for TypeQuery(region) in queries.into_iter() {
|
for TypeQuery(region) in queries.into_iter() {
|
||||||
let start = region.start().offset;
|
let start = region.start().offset;
|
||||||
let end = region.end().offset;
|
let end = region.end().offset;
|
||||||
|
@ -340,12 +354,12 @@ mod solve_expr {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
solved_queries.push(elaborated);
|
output_parts.push(elaborated);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pretty_solved_queries = solved_queries.join("\n");
|
let pretty_output = output_parts.join("\n");
|
||||||
|
|
||||||
expected(&pretty_solved_queries);
|
expected(&pretty_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! infer_queries {
|
macro_rules! infer_queries {
|
||||||
|
@ -508,6 +522,41 @@ mod solve_expr {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn choose_correct_recursion_var_under_record() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Parser : [
|
||||||
|
Specialize Parser,
|
||||||
|
Record (List {parser: Parser}),
|
||||||
|
]
|
||||||
|
|
||||||
|
printCombinatorParser : Parser -> Str
|
||||||
|
printCombinatorParser = \parser ->
|
||||||
|
when parser is
|
||||||
|
# ^^^^^^
|
||||||
|
Specialize p ->
|
||||||
|
printed = printCombinatorParser p
|
||||||
|
if Bool.false then printed else "foo"
|
||||||
|
Record fields ->
|
||||||
|
fields
|
||||||
|
|> List.map \f ->
|
||||||
|
printed = printCombinatorParser f.parser
|
||||||
|
if Bool.false then printed else "foo"
|
||||||
|
|> List.first
|
||||||
|
|> Result.withDefault ("foo")
|
||||||
|
|
||||||
|
printCombinatorParser (Record [])
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
parser : [Record (List { parser : a }), Specialize a] as a
|
||||||
|
"###
|
||||||
|
print_only_under_alias: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn block_string_literal() {
|
// fn block_string_literal() {
|
||||||
// infer_eq(
|
// infer_eq(
|
||||||
|
@ -6720,9 +6769,9 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r#"
|
@r#"
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||||
Id#id(3) : a -[[] + a:id(3):1]-> ({} -[[] + a:id(3):2]-> a) | a has Id
|
Id#id(3) : a -[[] + a:id(3):1]-> ({} -[[] + a:id(3):2]-> a) | a has Id
|
||||||
alias : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
alias : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||||
"#
|
"#
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
)
|
)
|
||||||
|
@ -6751,8 +6800,8 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r#"
|
@r#"
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||||
it : {} -[[8(8)]]-> {}
|
it : {} -[[8]]-> {}
|
||||||
"#
|
"#
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
)
|
)
|
||||||
|
@ -6782,8 +6831,8 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r#"
|
@r#"
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||||
"#
|
"#
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
)
|
)
|
||||||
|
@ -6903,7 +6952,7 @@ mod solve_expr {
|
||||||
#^^^^^^^^^^^^^^^^^^^^^^{-1}
|
#^^^^^^^^^^^^^^^^^^^^^^{-1}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r#"[\{} -> {}, \{} -> {}] : List ({}* -[[1(1), 2(2)]]-> {})"#
|
@r###"[\{} -> {}, \{} -> {}] : List ({}* -[[1, 2]]-> {})"###
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7078,7 +7127,7 @@ mod solve_expr {
|
||||||
#^^^{-1}
|
#^^^{-1}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r#"fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str"#
|
@r#"fun : {} -[[thunk(9) (({} -[[15]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13 Str]]-> Str)), thunk(9) (({} -[[14]]-> Str)) (Str -[[f(3)]]-> ({} -[[11]]-> Str))]]-> Str"#
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7323,9 +7372,9 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r###"
|
@r###"
|
||||||
Fo#f(7) : Fo, b -[[f(7)]]-> ({} -[[13(13) b]]-> ({} -[[] + b:g(4):2]-> {})) | b has G
|
Fo#f(7) : Fo, b -[[f(7)]]-> ({} -[[13 b]]-> ({} -[[] + b:g(4):2]-> {})) | b has G
|
||||||
Go#g(8) : Go -[[g(8)]]-> ({} -[[14(14)]]-> {})
|
Go#g(8) : Go -[[g(8)]]-> ({} -[[14]]-> {})
|
||||||
Fo#f(7) : Fo, Go -[[f(7)]]-> ({} -[[13(13) Go]]-> ({} -[[14(14)]]-> {}))
|
Fo#f(7) : Fo, Go -[[f(7)]]-> ({} -[[13 Go]]-> ({} -[[14]]-> {}))
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7692,7 +7741,7 @@ mod solve_expr {
|
||||||
@r###"
|
@r###"
|
||||||
const : Str -[[const(2)]]-> (Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str)
|
const : Str -[[const(2)]]-> (Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str)
|
||||||
compose : (Str -a-> Str), (Str -[[]]-> Str) -[[compose(1)]]-> (Str -a-> Str)
|
compose : (Str -a-> Str), (Str -[[]]-> Str) -[[compose(1)]]-> (Str -a-> Str)
|
||||||
\c1, c2 -> compose c1 c2 : (Str -a-> Str), (Str -[[]]-> Str) -[[11(11)]]-> (Str -a-> Str)
|
\c1, c2 -> compose c1 c2 : (Str -a-> Str), (Str -[[]]-> Str) -[[11]]-> (Str -a-> Str)
|
||||||
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
||||||
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
||||||
"###
|
"###
|
||||||
|
@ -8077,7 +8126,7 @@ mod solve_expr {
|
||||||
# ^^^^^^^^^^^^^^
|
# ^^^^^^^^^^^^^^
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@"N#Decode.decoder(3) : List U8, fmt -[[7(7)]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting"
|
@"N#Decode.decoder(3) : List U8, fmt -[[7]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting"
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8360,7 +8409,7 @@ mod solve_expr {
|
||||||
),
|
),
|
||||||
@r###"
|
@r###"
|
||||||
isEqQ : ({} -[[]]-> Str), ({} -[[]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
isEqQ : ({} -[[]]-> Str), ({} -[[]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||||
isEqQ : ({} -[[6(6), 7(7)]]-> Str), ({} -[[6(6), 7(7)]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
isEqQ : ({} -[[6, 7]]-> Str), ({} -[[6, 7]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||||
"###
|
"###
|
||||||
print_only_under_alias: true
|
print_only_under_alias: true
|
||||||
);
|
);
|
||||||
|
@ -8456,4 +8505,82 @@ mod solve_expr {
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disjoint_nested_lambdas_result_in_disjoint_parents_issue_4712() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Parser a : {} -> a
|
||||||
|
|
||||||
|
v1 : {}
|
||||||
|
v1 = {}
|
||||||
|
|
||||||
|
v2 : Str
|
||||||
|
v2 = ""
|
||||||
|
|
||||||
|
apply : Parser (a -> Str), a -> Parser Str
|
||||||
|
apply = \fnParser, valParser ->
|
||||||
|
\{} ->
|
||||||
|
(fnParser {}) (valParser)
|
||||||
|
|
||||||
|
map : a, (a -> Str) -> Parser Str
|
||||||
|
map = \simpleParser, transform ->
|
||||||
|
apply (\{} -> transform) simpleParser
|
||||||
|
|
||||||
|
parseInput = \{} ->
|
||||||
|
when [ map v1 (\{} -> ""), map v2 (\s -> s) ] is
|
||||||
|
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
_ -> ""
|
||||||
|
|
||||||
|
main = parseInput {} == ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
v1 = {}
|
||||||
|
|
||||||
|
v2 = ""
|
||||||
|
|
||||||
|
apply = \fnParser, valParser-> \{} -[9]-> (fnParser {}) valParser
|
||||||
|
|
||||||
|
map = \simpleParser, transform-> apply \{} -[12]-> transform simpleParser
|
||||||
|
|
||||||
|
parseInput =
|
||||||
|
\{}->
|
||||||
|
when [
|
||||||
|
map v1 \{} -[13]-> "",
|
||||||
|
map v2 \s -[14]-> s,
|
||||||
|
] is
|
||||||
|
_ -> ""
|
||||||
|
|
||||||
|
main = Bool.isEq (parseInput {}) ""
|
||||||
|
|
||||||
|
|
||||||
|
[ map v1 (\{} -> ""), map v2 (\s -> s) ] : List (({} -[[9 (({} -[[12 (Str -[[14]]-> Str)]]-> (Str -[[14]]-> Str))) Str, 9 (({} -[[12 ({} -[[13]]-> Str)]]-> ({} -[[13]]-> Str))) {}]]-> Str))
|
||||||
|
"###
|
||||||
|
print_only_under_alias: true
|
||||||
|
print_can_decls: true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_dbg_flex_var() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
polyDbg = \x ->
|
||||||
|
#^^^^^^^{-1}
|
||||||
|
dbg x
|
||||||
|
x
|
||||||
|
|
||||||
|
main = polyDbg ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@"polyDbg : a -[[polyDbg(1)]]-> a"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,9 +103,9 @@ fn list() {
|
||||||
# Specialization lambda sets:
|
# Specialization lambda sets:
|
||||||
# @<1>: [[custom(3)]]
|
# @<1>: [[custom(3)]]
|
||||||
#Derived.decoder_list =
|
#Derived.decoder_list =
|
||||||
Decode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Decode.decodeWith #Derived.bytes (Decode.list Decode.decoder) #Derived.fmt
|
decodeWith #Derived.bytes (list decoder) #Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -121,21 +121,18 @@ fn record_2_fields() {
|
||||||
# Specialization lambda sets:
|
# Specialization lambda sets:
|
||||||
# @<1>: [[custom(22)]]
|
# @<1>: [[custom(22)]]
|
||||||
#Derived.decoder_{first,second} =
|
#Derived.decoder_{first,second} =
|
||||||
Decode.custom
|
custom
|
||||||
\#Derived.bytes3, #Derived.fmt3 ->
|
\#Derived.bytes3, #Derived.fmt3 ->
|
||||||
Decode.decodeWith
|
decodeWith
|
||||||
#Derived.bytes3
|
#Derived.bytes3
|
||||||
(Decode.record
|
(record
|
||||||
{ second: Err NoField, first: Err NoField }
|
{ second: Err NoField, first: Err NoField }
|
||||||
\#Derived.stateRecord2, #Derived.field ->
|
\#Derived.stateRecord2, #Derived.field ->
|
||||||
when #Derived.field is
|
when #Derived.field is
|
||||||
"first" ->
|
"first" ->
|
||||||
Keep (Decode.custom
|
Keep (custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
when Decode.decodeWith
|
when decodeWith #Derived.bytes decoder #Derived.fmt is
|
||||||
#Derived.bytes
|
|
||||||
Decode.decoder
|
|
||||||
#Derived.fmt is
|
|
||||||
#Derived.rec ->
|
#Derived.rec ->
|
||||||
{
|
{
|
||||||
result: when #Derived.rec.result is
|
result: when #Derived.rec.result is
|
||||||
|
@ -145,12 +142,9 @@ fn record_2_fields() {
|
||||||
rest: #Derived.rec.rest
|
rest: #Derived.rec.rest
|
||||||
})
|
})
|
||||||
"second" ->
|
"second" ->
|
||||||
Keep (Decode.custom
|
Keep (custom
|
||||||
\#Derived.bytes2, #Derived.fmt2 ->
|
\#Derived.bytes2, #Derived.fmt2 ->
|
||||||
when Decode.decodeWith
|
when decodeWith #Derived.bytes2 decoder #Derived.fmt2 is
|
||||||
#Derived.bytes2
|
|
||||||
Decode.decoder
|
|
||||||
#Derived.fmt2 is
|
|
||||||
#Derived.rec2 ->
|
#Derived.rec2 ->
|
||||||
{
|
{
|
||||||
result: when #Derived.rec2.result is
|
result: when #Derived.rec2.result is
|
||||||
|
|
|
@ -187,9 +187,9 @@ fn empty_record() {
|
||||||
# @<2>: [[custom(2) {}]]
|
# @<2>: [[custom(2) {}]]
|
||||||
#Derived.toEncoder_{} =
|
#Derived.toEncoder_{} =
|
||||||
\#Derived.rcd ->
|
\#Derived.rcd ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith #Derived.bytes (Encode.record []) #Derived.fmt
|
appendWith #Derived.bytes (record []) #Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -207,9 +207,9 @@ fn zero_field_record() {
|
||||||
# @<2>: [[custom(2) {}]]
|
# @<2>: [[custom(2) {}]]
|
||||||
#Derived.toEncoder_{} =
|
#Derived.toEncoder_{} =
|
||||||
\#Derived.rcd ->
|
\#Derived.rcd ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith #Derived.bytes (Encode.record []) #Derived.fmt
|
appendWith #Derived.bytes (record []) #Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -227,11 +227,11 @@ fn one_field_record() {
|
||||||
# @<2>: [[custom(2) { a : val }]] | val has Encoding
|
# @<2>: [[custom(2) { a : val }]] | val has Encoding
|
||||||
#Derived.toEncoder_{a} =
|
#Derived.toEncoder_{a} =
|
||||||
\#Derived.rcd ->
|
\#Derived.rcd ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(Encode.record [{ value: Encode.toEncoder #Derived.rcd.a, key: "a" }])
|
(record [{ value: toEncoder #Derived.rcd.a, key: "a" }])
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
|
@ -250,14 +250,14 @@ fn two_field_record() {
|
||||||
# @<2>: [[custom(2) { a : val, b : val1 }]] | val has Encoding, val1 has Encoding
|
# @<2>: [[custom(2) { a : val, b : val1 }]] | val has Encoding, val1 has Encoding
|
||||||
#Derived.toEncoder_{a,b} =
|
#Derived.toEncoder_{a,b} =
|
||||||
\#Derived.rcd ->
|
\#Derived.rcd ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(Encode.record
|
(record
|
||||||
[
|
[
|
||||||
{ value: Encode.toEncoder #Derived.rcd.a, key: "a" },
|
{ value: toEncoder #Derived.rcd.a, key: "a" },
|
||||||
{ value: Encode.toEncoder #Derived.rcd.b, key: "b" },
|
{ value: toEncoder #Derived.rcd.b, key: "b" },
|
||||||
])
|
])
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
|
@ -290,12 +290,12 @@ fn tag_one_label_zero_args() {
|
||||||
# @<2>: [[custom(2) [A]]]
|
# @<2>: [[custom(2) [A]]]
|
||||||
#Derived.toEncoder_[A 0] =
|
#Derived.toEncoder_[A 0] =
|
||||||
\#Derived.tag ->
|
\#Derived.tag ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(when #Derived.tag is
|
(when #Derived.tag is
|
||||||
A -> Encode.tag "A" [])
|
A -> tag "A" [])
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
|
@ -314,18 +314,13 @@ fn tag_one_label_two_args() {
|
||||||
# @<2>: [[custom(4) [A val val1]]] | val has Encoding, val1 has Encoding
|
# @<2>: [[custom(4) [A val val1]]] | val has Encoding, val1 has Encoding
|
||||||
#Derived.toEncoder_[A 2] =
|
#Derived.toEncoder_[A 2] =
|
||||||
\#Derived.tag ->
|
\#Derived.tag ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(when #Derived.tag is
|
(when #Derived.tag is
|
||||||
A #Derived.2 #Derived.3 ->
|
A #Derived.2 #Derived.3 ->
|
||||||
Encode.tag
|
tag "A" [toEncoder #Derived.2, toEncoder #Derived.3])
|
||||||
"A"
|
|
||||||
[
|
|
||||||
Encode.toEncoder #Derived.2,
|
|
||||||
Encode.toEncoder #Derived.3,
|
|
||||||
])
|
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
|
@ -339,30 +334,30 @@ fn tag_two_labels() {
|
||||||
v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]),
|
v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]),
|
||||||
|golden| {
|
|golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
# derived for [A U8 Str U16, B Str]
|
# derived for [A U8 Str U16, B Str]
|
||||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||||
# Specialization lambda sets:
|
# Specialization lambda sets:
|
||||||
# @<1>: [[toEncoder_[A 3,B 1](0)]]
|
# @<1>: [[toEncoder_[A 3,B 1](0)]]
|
||||||
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding
|
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding
|
||||||
#Derived.toEncoder_[A 3,B 1] =
|
#Derived.toEncoder_[A 3,B 1] =
|
||||||
\#Derived.tag ->
|
\#Derived.tag ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(when #Derived.tag is
|
(when #Derived.tag is
|
||||||
A #Derived.2 #Derived.3 #Derived.4 ->
|
A #Derived.2 #Derived.3 #Derived.4 ->
|
||||||
Encode.tag
|
tag
|
||||||
"A"
|
"A"
|
||||||
[
|
[
|
||||||
Encode.toEncoder #Derived.2,
|
toEncoder #Derived.2,
|
||||||
Encode.toEncoder #Derived.3,
|
toEncoder #Derived.3,
|
||||||
Encode.toEncoder #Derived.4,
|
toEncoder #Derived.4,
|
||||||
]
|
]
|
||||||
B #Derived.5 -> Encode.tag "B" [Encode.toEncoder #Derived.5])
|
B #Derived.5 -> tag "B" [toEncoder #Derived.5])
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -375,29 +370,24 @@ fn recursive_tag_union() {
|
||||||
v!([Nil, Cons v!(U8) v!(^lst) ] as lst),
|
v!([Nil, Cons v!(U8) v!(^lst) ] as lst),
|
||||||
|golden| {
|
|golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
# derived for [Cons U8 $rec, Nil] as $rec
|
# derived for [Cons U8 $rec, Nil] as $rec
|
||||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||||
# Specialization lambda sets:
|
# Specialization lambda sets:
|
||||||
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
|
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
|
||||||
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding
|
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding
|
||||||
#Derived.toEncoder_[Cons 2,Nil 0] =
|
#Derived.toEncoder_[Cons 2,Nil 0] =
|
||||||
\#Derived.tag ->
|
\#Derived.tag ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(when #Derived.tag is
|
(when #Derived.tag is
|
||||||
Cons #Derived.2 #Derived.3 ->
|
Cons #Derived.2 #Derived.3 ->
|
||||||
Encode.tag
|
tag "Cons" [toEncoder #Derived.2, toEncoder #Derived.3]
|
||||||
"Cons"
|
Nil -> tag "Nil" [])
|
||||||
[
|
#Derived.fmt
|
||||||
Encode.toEncoder #Derived.2,
|
"###
|
||||||
Encode.toEncoder #Derived.3,
|
|
||||||
]
|
|
||||||
Nil -> Encode.tag "Nil" [])
|
|
||||||
#Derived.fmt
|
|
||||||
"###
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -415,13 +405,11 @@ fn list() {
|
||||||
# @<2>: [[custom(4) (List val)]] | val has Encoding
|
# @<2>: [[custom(4) (List val)]] | val has Encoding
|
||||||
#Derived.toEncoder_list =
|
#Derived.toEncoder_list =
|
||||||
\#Derived.lst ->
|
\#Derived.lst ->
|
||||||
Encode.custom
|
custom
|
||||||
\#Derived.bytes, #Derived.fmt ->
|
\#Derived.bytes, #Derived.fmt ->
|
||||||
Encode.appendWith
|
appendWith
|
||||||
#Derived.bytes
|
#Derived.bytes
|
||||||
(Encode.list
|
(list #Derived.lst \#Derived.elem -> toEncoder #Derived.elem)
|
||||||
#Derived.lst
|
|
||||||
\#Derived.elem -> Encode.toEncoder #Derived.elem)
|
|
||||||
#Derived.fmt
|
#Derived.fmt
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
|
|
|
@ -178,7 +178,7 @@ fn one_field_record() {
|
||||||
# Specialization lambda sets:
|
# Specialization lambda sets:
|
||||||
# @<1>: [[hash_{a}(0)]]
|
# @<1>: [[hash_{a}(0)]]
|
||||||
#Derived.hash_{a} =
|
#Derived.hash_{a} =
|
||||||
\#Derived.hasher, #Derived.rcd -> Hash.hash #Derived.hasher #Derived.rcd.a
|
\#Derived.hasher, #Derived.rcd -> hash #Derived.hasher #Derived.rcd.a
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -195,7 +195,7 @@ fn two_field_record() {
|
||||||
# @<1>: [[hash_{a,b}(0)]]
|
# @<1>: [[hash_{a,b}(0)]]
|
||||||
#Derived.hash_{a,b} =
|
#Derived.hash_{a,b} =
|
||||||
\#Derived.hasher, #Derived.rcd ->
|
\#Derived.hasher, #Derived.rcd ->
|
||||||
Hash.hash (Hash.hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
|
hash (hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -227,7 +227,7 @@ fn tag_one_label_newtype() {
|
||||||
# @<1>: [[hash_[A 2](0)]]
|
# @<1>: [[hash_[A 2](0)]]
|
||||||
#Derived.hash_[A 2] =
|
#Derived.hash_[A 2] =
|
||||||
\#Derived.hasher, A #Derived.2 #Derived.3 ->
|
\#Derived.hasher, A #Derived.2 #Derived.3 ->
|
||||||
Hash.hash (Hash.hash #Derived.hasher #Derived.2) #Derived.3
|
hash (hash #Derived.hasher #Derived.2) #Derived.3
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -246,12 +246,10 @@ fn tag_two_labels() {
|
||||||
\#Derived.hasher, #Derived.union ->
|
\#Derived.hasher, #Derived.union ->
|
||||||
when #Derived.union is
|
when #Derived.union is
|
||||||
A #Derived.3 #Derived.4 #Derived.5 ->
|
A #Derived.3 #Derived.4 #Derived.5 ->
|
||||||
Hash.hash
|
hash
|
||||||
(Hash.hash
|
(hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4)
|
||||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
|
||||||
#Derived.4)
|
|
||||||
#Derived.5
|
#Derived.5
|
||||||
B #Derived.6 -> Hash.hash (Hash.addU8 #Derived.hasher 1) #Derived.6
|
B #Derived.6 -> hash (addU8 #Derived.hasher 1) #Derived.6
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -269,8 +267,8 @@ fn tag_two_labels_no_payloads() {
|
||||||
#Derived.hash_[A 0,B 0] =
|
#Derived.hash_[A 0,B 0] =
|
||||||
\#Derived.hasher, #Derived.union ->
|
\#Derived.hasher, #Derived.union ->
|
||||||
when #Derived.union is
|
when #Derived.union is
|
||||||
A -> Hash.addU8 #Derived.hasher 0
|
A -> addU8 #Derived.hasher 0
|
||||||
B -> Hash.addU8 #Derived.hasher 1
|
B -> addU8 #Derived.hasher 1
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -289,10 +287,8 @@ fn recursive_tag_union() {
|
||||||
\#Derived.hasher, #Derived.union ->
|
\#Derived.hasher, #Derived.union ->
|
||||||
when #Derived.union is
|
when #Derived.union is
|
||||||
Cons #Derived.3 #Derived.4 ->
|
Cons #Derived.3 #Derived.4 ->
|
||||||
Hash.hash
|
hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4
|
||||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
Nil -> addU8 #Derived.hasher 1
|
||||||
#Derived.4
|
|
||||||
Nil -> Hash.addU8 #Derived.hasher 1
|
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,5 +5,4 @@ mod encoding;
|
||||||
mod eq;
|
mod eq;
|
||||||
mod hash;
|
mod hash;
|
||||||
|
|
||||||
mod pretty_print;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
|
@ -5,10 +5,10 @@ use bumpalo::Bump;
|
||||||
use roc_packaging::cache::RocCacheDir;
|
use roc_packaging::cache::RocCacheDir;
|
||||||
use ven_pretty::DocAllocator;
|
use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
use crate::pretty_print::{pretty_print_def, Ctx};
|
|
||||||
use roc_can::{
|
use roc_can::{
|
||||||
abilities::{AbilitiesStore, SpecializationLambdaSets},
|
abilities::{AbilitiesStore, SpecializationLambdaSets},
|
||||||
constraint::Constraints,
|
constraint::Constraints,
|
||||||
|
debug::{pretty_print_def, PPCtx},
|
||||||
def::Def,
|
def::Def,
|
||||||
expr::Declarations,
|
expr::Declarations,
|
||||||
module::{
|
module::{
|
||||||
|
@ -529,8 +529,12 @@ where
|
||||||
interns.all_ident_ids.insert(DERIVED_MODULE, ident_ids);
|
interns.all_ident_ids.insert(DERIVED_MODULE, ident_ids);
|
||||||
DERIVED_MODULE.register_debug_idents(interns.all_ident_ids.get(&DERIVED_MODULE).unwrap());
|
DERIVED_MODULE.register_debug_idents(interns.all_ident_ids.get(&DERIVED_MODULE).unwrap());
|
||||||
|
|
||||||
let ctx = Ctx { interns: &interns };
|
let pp_ctx = PPCtx {
|
||||||
let derived_program = pretty_print_def(&ctx, &derived_def);
|
interns: &interns,
|
||||||
|
print_lambda_names: false,
|
||||||
|
home: builtin_module,
|
||||||
|
};
|
||||||
|
let derived_program = pretty_print_def(&pp_ctx, &derived_def);
|
||||||
|
|
||||||
check_derived_typechecks_and_golden(
|
check_derived_typechecks_and_golden(
|
||||||
derived_def,
|
derived_def,
|
||||||
|
|
|
@ -42,6 +42,7 @@ roc_error_macros = { path = "../../error_macros" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
roc_debug_flags = {path="../debug_flags"}
|
roc_debug_flags = {path="../debug_flags"}
|
||||||
roc_wasm_module = {path="../../wasm_module"}
|
roc_wasm_module = {path="../../wasm_module"}
|
||||||
|
roc_wasm_interp = {path="../../wasm_interp"}
|
||||||
|
|
||||||
bumpalo.workspace = true
|
bumpalo.workspace = true
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
|
@ -49,7 +50,6 @@ libloading.workspace = true
|
||||||
criterion.workspace = true
|
criterion.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
wasm3.workspace = true
|
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
inkwell.workspace = true
|
inkwell.workspace = true
|
||||||
target-lexicon.workspace = true
|
target-lexicon.workspace = true
|
||||||
|
|
|
@ -885,7 +885,7 @@ mod decode_immediate {
|
||||||
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
#[cfg(all(test, any(feature = "gen-llvm", feature = "gen-wasm")))]
|
#[cfg(all(test, any(feature = "gen-llvm")))]
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -339,7 +339,7 @@ fn eq_linked_list_false() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-wasm"))]
|
#[ignore] // breaks for LLVM (no tail recursion), takes a long time for Wasm
|
||||||
fn eq_linked_list_long() {
|
fn eq_linked_list_long() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -355,7 +355,7 @@ fn eq_linked_list_long() {
|
||||||
prependOnes (n-1) (Cons 1 tail)
|
prependOnes (n-1) (Cons 1 tail)
|
||||||
|
|
||||||
main =
|
main =
|
||||||
n = 100_000
|
n = 100_000 # be careful, can make a noticeble difference to test_gen total time!
|
||||||
|
|
||||||
x : LinkedList I64
|
x : LinkedList I64
|
||||||
x = prependOnes n (Cons 999 Nil)
|
x = prependOnes n (Cons 999 Nil)
|
||||||
|
|
|
@ -368,7 +368,7 @@ fn character_literal() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
65,
|
65,
|
||||||
u32
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ fn character_literal_back_slash() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
92,
|
92,
|
||||||
u32
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +398,7 @@ fn character_literal_single_quote() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
39,
|
39,
|
||||||
u32
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ fn character_literal_new_line() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
10,
|
10,
|
||||||
u32
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1363,7 +1363,7 @@ fn linked_list_singleton() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
0,
|
0,
|
||||||
i64,
|
usize,
|
||||||
|_| 0
|
|_| 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4107,3 +4107,41 @@ fn issue_4349() {
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn issue_4712() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Parser a : {} -> a
|
||||||
|
|
||||||
|
v1 : {}
|
||||||
|
v1 = {}
|
||||||
|
|
||||||
|
v2 : Str
|
||||||
|
v2 = "cd"
|
||||||
|
|
||||||
|
apply : Parser (a -> Str), a -> Parser Str
|
||||||
|
apply = \fnParser, valParser ->
|
||||||
|
\{} ->
|
||||||
|
(fnParser {}) (valParser)
|
||||||
|
|
||||||
|
map : a, (a -> Str) -> Parser Str
|
||||||
|
map = \simpleParser, transform ->
|
||||||
|
apply (\{} -> transform) simpleParser
|
||||||
|
|
||||||
|
gen = \{} ->
|
||||||
|
[ map v1 (\{} -> "ab"), map v2 (\s -> s) ]
|
||||||
|
|> List.map (\f -> f {})
|
||||||
|
|> Str.joinWith ","
|
||||||
|
|
||||||
|
main = gen {}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("ab,cd"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -2083,3 +2083,53 @@ fn unify_types_with_fixed_fixpoints_outside_fixing_region() {
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn lambda_set_with_imported_toplevels_issue_4733() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
fn = \s ->
|
||||||
|
instr = if s == "*" then (Op Num.mul) else (Op Num.add)
|
||||||
|
|
||||||
|
Op op = instr
|
||||||
|
|
||||||
|
\a -> op a a
|
||||||
|
|
||||||
|
main = ((fn "*") 3) * ((fn "+") 5)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
90,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn non_unary_union_with_lambda_set_with_imported_toplevels_issue_4733() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
fn = \s ->
|
||||||
|
instr =
|
||||||
|
if s == "*" then (Op Num.mul)
|
||||||
|
else if s == "+" then (Op Num.add)
|
||||||
|
else Noop
|
||||||
|
|
||||||
|
when instr is
|
||||||
|
Op op -> (\a -> op a a)
|
||||||
|
_ -> (\a -> a)
|
||||||
|
|
||||||
|
|
||||||
|
main = ((fn "*") 3) * ((fn "+") 5)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
90,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::RefCount;
|
use super::RefCount;
|
||||||
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
|
use crate::helpers::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
use roc_gen_wasm::wasm32_result::Wasm32Result;
|
use roc_gen_wasm::wasm32_result::Wasm32Result;
|
||||||
use roc_gen_wasm::DEBUG_SETTINGS;
|
use roc_gen_wasm::DEBUG_SETTINGS;
|
||||||
|
@ -7,12 +8,10 @@ use roc_load::{ExecutionMode, LoadConfig, Threading};
|
||||||
use roc_packaging::cache::RocCacheDir;
|
use roc_packaging::cache::RocCacheDir;
|
||||||
use roc_reporting::report::DEFAULT_PALETTE_HTML;
|
use roc_reporting::report::DEFAULT_PALETTE_HTML;
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
use roc_wasm_module::{Export, ExportType};
|
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
|
||||||
|
use roc_wasm_module::{Export, ExportType, Value, WasmModule};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use wasm3::{Environment, Module, Runtime};
|
|
||||||
|
|
||||||
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
||||||
const INIT_REFCOUNT_NAME: &str = "init_refcount_test";
|
const INIT_REFCOUNT_NAME: &str = "init_refcount_test";
|
||||||
|
@ -182,6 +181,39 @@ where
|
||||||
run_wasm_test_bytes::<T>(TEST_WRAPPER_NAME, wasm_bytes)
|
run_wasm_test_bytes::<T>(TEST_WRAPPER_NAME, wasm_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TestDispatcher<'a> {
|
||||||
|
wasi: WasiDispatcher<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ImportDispatcher for TestDispatcher<'a> {
|
||||||
|
fn dispatch(
|
||||||
|
&mut self,
|
||||||
|
module_name: &str,
|
||||||
|
function_name: &str,
|
||||||
|
arguments: &[Value],
|
||||||
|
memory: &mut [u8],
|
||||||
|
) -> Option<Value> {
|
||||||
|
if module_name == wasi::MODULE_NAME {
|
||||||
|
self.wasi.dispatch(function_name, arguments, memory)
|
||||||
|
} else if module_name == "env" && function_name == "send_panic_msg_to_rust" {
|
||||||
|
let msg_ptr = arguments[0].expect_i32().unwrap();
|
||||||
|
let tag = arguments[1].expect_i32().unwrap();
|
||||||
|
let roc_msg = RocStr::decode(memory, msg_ptr as _);
|
||||||
|
let msg = match tag {
|
||||||
|
0 => format!(r#"Roc failed with message: "{}""#, roc_msg),
|
||||||
|
1 => format!(r#"User crash with message: "{}""#, roc_msg),
|
||||||
|
tag => format!(r#"Got an invald panic tag: "{}""#, tag),
|
||||||
|
};
|
||||||
|
panic!("{}", msg)
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"TestDispatcher does not implement {}.{}",
|
||||||
|
module_name, function_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn run_wasm_test_bytes<T>(
|
pub(crate) fn run_wasm_test_bytes<T>(
|
||||||
test_wrapper_name: &str,
|
test_wrapper_name: &str,
|
||||||
wasm_bytes: Vec<u8>,
|
wasm_bytes: Vec<u8>,
|
||||||
|
@ -189,57 +221,31 @@ pub(crate) fn run_wasm_test_bytes<T>(
|
||||||
where
|
where
|
||||||
T: FromWasm32Memory + Wasm32Result,
|
T: FromWasm32Memory + Wasm32Result,
|
||||||
{
|
{
|
||||||
let env = Environment::new().expect("Unable to create environment");
|
let arena = Bump::new();
|
||||||
let rt = env
|
let require_relocatable = false;
|
||||||
.create_runtime(1024 * 60)
|
let module = WasmModule::preload(&arena, &wasm_bytes, require_relocatable)
|
||||||
.expect("Unable to create runtime");
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
run_wasm_test_module(&arena, test_wrapper_name, &module)
|
||||||
|
}
|
||||||
|
|
||||||
let parsed = Module::parse(&env, &wasm_bytes[..]).expect("Unable to parse module");
|
pub(crate) fn run_wasm_test_module<'a, T>(
|
||||||
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
arena: &'a Bump,
|
||||||
let panic_msg: Rc<Mutex<Option<(i32, u32)>>> = Default::default();
|
test_wrapper_name: &str,
|
||||||
link_module(&mut module, panic_msg.clone());
|
module: &WasmModule<'a>,
|
||||||
|
) -> Result<T, String>
|
||||||
let test_wrapper = module
|
where
|
||||||
.find_function::<(), i32>(test_wrapper_name)
|
T: FromWasm32Memory + Wasm32Result,
|
||||||
.expect("Unable to find test wrapper function");
|
{
|
||||||
|
let dispatcher = TestDispatcher {
|
||||||
match test_wrapper.call() {
|
wasi: wasi::WasiDispatcher::default(),
|
||||||
Err(e) => {
|
};
|
||||||
if let Some((msg_ptr, tag)) = *panic_msg.lock().unwrap() {
|
let is_debug_mode = roc_debug_flags::dbg_set!(roc_debug_flags::ROC_LOG_WASM_INTERP);
|
||||||
let memory: &[u8] = get_memory(&rt);
|
let mut inst = Instance::for_module(&arena, &module, dispatcher, is_debug_mode)?;
|
||||||
let msg = RocStr::decode(memory, msg_ptr as _);
|
let opt_value = inst.call_export(test_wrapper_name, [])?;
|
||||||
|
let addr_value = opt_value.ok_or("No return address from Wasm test")?;
|
||||||
dbg!(tag);
|
let addr = addr_value.expect_i32().map_err(|e| format!("{:?}", e))?;
|
||||||
let msg = match tag {
|
let output = <T as FromWasm32Memory>::decode(&inst.memory, addr as u32);
|
||||||
0 => format!(r#"Roc failed with message: "{}""#, msg),
|
Ok(output)
|
||||||
1 => format!(r#"User crash with message: "{}""#, msg),
|
|
||||||
tag => format!(r#"Got an invald panic tag: "{}""#, tag),
|
|
||||||
};
|
|
||||||
|
|
||||||
Err(msg)
|
|
||||||
} else {
|
|
||||||
Err(format!("{}", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(address) => {
|
|
||||||
let memory: &[u8] = get_memory(&rt);
|
|
||||||
|
|
||||||
if false {
|
|
||||||
println!("test_wrapper returned 0x{:x}", address);
|
|
||||||
println!("Stack:");
|
|
||||||
crate::helpers::wasm::debug_memory_hex(memory, address, std::mem::size_of::<T>());
|
|
||||||
}
|
|
||||||
if false {
|
|
||||||
println!("Heap:");
|
|
||||||
// Manually provide address and size based on printf in wasm_test_platform.c
|
|
||||||
crate::helpers::wasm::debug_memory_hex(memory, 0x11440, 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = <T as FromWasm32Memory>::decode(memory, address as u32);
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -255,36 +261,36 @@ where
|
||||||
|
|
||||||
let wasm_bytes = crate::helpers::wasm::compile_to_wasm_bytes(&arena, src, phantom);
|
let wasm_bytes = crate::helpers::wasm::compile_to_wasm_bytes(&arena, src, phantom);
|
||||||
|
|
||||||
let env = Environment::new().expect("Unable to create environment");
|
let require_relocatable = false;
|
||||||
let rt = env
|
let module = WasmModule::preload(&arena, &wasm_bytes, require_relocatable)
|
||||||
.create_runtime(1024 * 60)
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
.expect("Unable to create runtime");
|
|
||||||
let parsed = Module::parse(&env, wasm_bytes).expect("Unable to parse module");
|
|
||||||
let mut module = rt.load_module(parsed).expect("Unable to load module");
|
|
||||||
|
|
||||||
let panic_msg: Rc<Mutex<Option<(i32, u32)>>> = Default::default();
|
let dispatcher = TestDispatcher {
|
||||||
link_module(&mut module, panic_msg.clone());
|
wasi: wasi::WasiDispatcher::default(),
|
||||||
|
};
|
||||||
|
let is_debug_mode = roc_debug_flags::dbg_set!(roc_debug_flags::ROC_LOG_WASM_INTERP);
|
||||||
|
let mut inst = Instance::for_module(&arena, &module, dispatcher, is_debug_mode)?;
|
||||||
|
|
||||||
let expected_len = num_refcounts as i32;
|
// Allocate a vector in the test host that refcounts will be copied into
|
||||||
let init_refcount_test = module
|
let mut refcount_vector_addr: i32 = inst
|
||||||
.find_function::<i32, i32>(INIT_REFCOUNT_NAME)
|
.call_export(INIT_REFCOUNT_NAME, [Value::I32(num_refcounts as i32)])?
|
||||||
.expect("Unable to find refcount test init function");
|
.ok_or_else(|| format!("No return address from {}", INIT_REFCOUNT_NAME))?
|
||||||
let mut refcount_vector_offset = init_refcount_test.call(expected_len).unwrap() as usize;
|
.expect_i32()
|
||||||
|
.map_err(|type_err| format!("{:?}", type_err))?;
|
||||||
|
|
||||||
// Run the test
|
// Run the test, ignoring the result
|
||||||
let test_wrapper = module
|
let _result_addr: i32 = inst
|
||||||
.find_function::<(), i32>(TEST_WRAPPER_NAME)
|
.call_export(TEST_WRAPPER_NAME, [])?
|
||||||
.expect("Unable to find test wrapper function");
|
.ok_or_else(|| format!("No return address from {}", TEST_WRAPPER_NAME))?
|
||||||
test_wrapper.call().map_err(|e| format!("{:?}", e))?;
|
.expect_i32()
|
||||||
|
.map_err(|type_err| format!("{:?}", type_err))?;
|
||||||
let memory: &[u8] = get_memory(&rt);
|
|
||||||
|
|
||||||
// Read the length of the vector in the C host
|
// Read the length of the vector in the C host
|
||||||
let actual_len = read_i32(memory, refcount_vector_offset);
|
let actual_num_refcounts = read_i32(&inst.memory, refcount_vector_addr) as usize;
|
||||||
if actual_len != expected_len {
|
if actual_num_refcounts != num_refcounts {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Expected {} refcounts but got {}",
|
"Expected {} refcounts but got {}",
|
||||||
expected_len, actual_len
|
num_refcounts, actual_num_refcounts
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,13 +298,13 @@ where
|
||||||
let mut refcounts = Vec::with_capacity(num_refcounts);
|
let mut refcounts = Vec::with_capacity(num_refcounts);
|
||||||
for _ in 0..num_refcounts {
|
for _ in 0..num_refcounts {
|
||||||
// Get the next RC pointer from the host's vector
|
// Get the next RC pointer from the host's vector
|
||||||
refcount_vector_offset += 4;
|
refcount_vector_addr += 4;
|
||||||
let rc_ptr = read_i32(memory, refcount_vector_offset) as usize;
|
let rc_ptr = read_i32(&inst.memory, refcount_vector_addr);
|
||||||
let rc = if rc_ptr == 0 {
|
let rc = if rc_ptr == 0 {
|
||||||
RefCount::Deallocated
|
RefCount::Deallocated
|
||||||
} else {
|
} else {
|
||||||
// Dereference the RC pointer and decode its value from the negative number format
|
// Dereference the RC pointer and decode its value from the negative number format
|
||||||
let rc_encoded = read_i32(memory, rc_ptr);
|
let rc_encoded = read_i32(&inst.memory, rc_ptr);
|
||||||
if rc_encoded == 0 {
|
if rc_encoded == 0 {
|
||||||
RefCount::Constant
|
RefCount::Constant
|
||||||
} else {
|
} else {
|
||||||
|
@ -311,40 +317,16 @@ where
|
||||||
Ok(refcounts)
|
Ok(refcounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_memory(rt: &Runtime) -> &[u8] {
|
fn read_i32(memory: &[u8], addr: i32) -> i32 {
|
||||||
unsafe {
|
let index = addr as usize;
|
||||||
let memory_ptr: *const [u8] = rt.memory();
|
let mut bytes = [0; 4];
|
||||||
let (_, memory_size) = std::mem::transmute::<_, (usize, usize)>(memory_ptr);
|
bytes.copy_from_slice(&memory[index..][..4]);
|
||||||
std::slice::from_raw_parts(memory_ptr as _, memory_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_i32(memory: &[u8], ptr: usize) -> i32 {
|
|
||||||
let mut bytes = [0u8; 4];
|
|
||||||
bytes.copy_from_slice(&memory[ptr..][..4]);
|
|
||||||
i32::from_le_bytes(bytes)
|
i32::from_le_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_module(module: &mut Module, panic_msg: Rc<Mutex<Option<(i32, u32)>>>) {
|
|
||||||
let try_link_panic = module.link_closure(
|
|
||||||
"env",
|
|
||||||
"send_panic_msg_to_rust",
|
|
||||||
move |_call_context, (msg_ptr, tag): (i32, u32)| {
|
|
||||||
let mut w = panic_msg.lock().unwrap();
|
|
||||||
*w = Some((msg_ptr, tag));
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match try_link_panic {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(wasm3::error::Error::FunctionNotFound) => {}
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print out hex bytes of the test result, and a few words on either side
|
/// Print out hex bytes of the test result, and a few words on either side
|
||||||
/// Can be handy for debugging misalignment issues etc.
|
/// Can be handy for debugging misalignment issues etc.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn debug_memory_hex(memory_bytes: &[u8], address: i32, size: usize) {
|
pub fn debug_memory_hex(memory_bytes: &[u8], address: i32, size: usize) {
|
||||||
let memory_words: &[u32] =
|
let memory_words: &[u32] =
|
||||||
unsafe { std::slice::from_raw_parts(memory_bytes.as_ptr().cast(), memory_bytes.len() / 4) };
|
unsafe { std::slice::from_raw_parts(memory_bytes.as_ptr().cast(), memory_bytes.len() / 4) };
|
||||||
|
|
|
@ -6,7 +6,7 @@ extern fn js_unused() i32;
|
||||||
|
|
||||||
extern fn roc__app_proc_1_exposed() i32;
|
extern fn roc__app_proc_1_exposed() i32;
|
||||||
|
|
||||||
export fn host_called_indirectly_from_roc() i32 {
|
fn host_called_indirectly_from_roc() i32 {
|
||||||
return 0x40;
|
return 0x40;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,11 @@ export fn host_called_directly_from_roc() i32 {
|
||||||
return 0x80 | host_called_indirectly_from_roc() | js_called_indirectly_from_roc();
|
return 0x80 | host_called_indirectly_from_roc() | js_called_indirectly_from_roc();
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn host_called_indirectly_from_main() i32 {
|
fn host_called_indirectly_from_main() i32 {
|
||||||
return 0x100;
|
return 0x100;
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn host_called_directly_from_main() i32 {
|
fn host_called_directly_from_main() i32 {
|
||||||
return 0x200 | host_called_indirectly_from_main() | js_called_indirectly_from_main();
|
return 0x200 | host_called_indirectly_from_main() | js_called_indirectly_from_main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ use roc_mono::ir::{
|
||||||
UpdateModeId,
|
UpdateModeId,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Builtin, CapturesNiche, LambdaName, Layout, STLayoutInterner};
|
use roc_mono::layout::{Builtin, CapturesNiche, LambdaName, Layout, STLayoutInterner};
|
||||||
use roc_wasm_module::WasmModule;
|
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
|
||||||
use wasm3::{Environment, Module};
|
use roc_wasm_module::{Value, WasmModule};
|
||||||
|
|
||||||
const LINKING_TEST_HOST_WASM: &str = "build/wasm_linking_test_host.wasm";
|
const LINKING_TEST_HOST_WASM: &str = "build/wasm_linking_test_host.wasm";
|
||||||
const LINKING_TEST_HOST_NATIVE: &str = "build/wasm_linking_test_host";
|
const LINKING_TEST_HOST_NATIVE: &str = "build/wasm_linking_test_host";
|
||||||
|
@ -183,36 +183,67 @@ impl<'a> BackendInputs<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_wasm_bytes(wasm_bytes: &[u8]) -> i32 {
|
struct TestDispatcher<'a> {
|
||||||
let env = Environment::new().unwrap();
|
wasi: WasiDispatcher<'a>,
|
||||||
let rt = env.create_runtime(1024 * 60).unwrap();
|
}
|
||||||
|
|
||||||
let parsed_module = Module::parse(&env, &wasm_bytes[..]).unwrap();
|
impl ImportDispatcher for TestDispatcher<'_> {
|
||||||
let mut module = rt.load_module(parsed_module).unwrap();
|
fn dispatch(
|
||||||
module
|
&mut self,
|
||||||
.link_closure("env", "js_called_directly_from_roc", |_, ()| Ok(0x01i32))
|
module_name: &str,
|
||||||
.unwrap();
|
function_name: &str,
|
||||||
module
|
arguments: &[Value],
|
||||||
.link_closure("env", "js_called_indirectly_from_roc", |_, ()| Ok(0x02i32))
|
memory: &mut [u8],
|
||||||
.unwrap();
|
) -> Option<Value> {
|
||||||
module
|
if module_name == wasi::MODULE_NAME {
|
||||||
.link_closure("env", "js_called_directly_from_main", |_, ()| Ok(0x04i32))
|
self.wasi.dispatch(function_name, arguments, memory)
|
||||||
.unwrap();
|
} else if module_name == "env" {
|
||||||
module
|
match function_name {
|
||||||
.link_closure("env", "js_called_indirectly_from_main", |_, ()| Ok(0x08i32))
|
"js_called_directly_from_roc" => Some(Value::I32(0x01)),
|
||||||
.unwrap();
|
"js_called_indirectly_from_roc" => Some(Value::I32(0x02)),
|
||||||
module
|
"js_called_directly_from_main" => Some(Value::I32(0x04)),
|
||||||
.link_closure("env", "js_unused", |_, ()| Ok(0x10i32))
|
"js_called_indirectly_from_main" => Some(Value::I32(0x08)),
|
||||||
.unwrap_or_else(|_| {});
|
"js_unused" => Some(Value::I32(0x10)),
|
||||||
|
_ => panic!("Unknown import env.{}", function_name),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!(
|
||||||
|
"TestDispatcher does not implement {}.{}",
|
||||||
|
module_name, function_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_wasm_module<'a>(arena: &'a Bump, orig_module: WasmModule<'a>) -> Result<i32, String> {
|
||||||
|
// FIXME: see if we can skip serializing and re-parsing!
|
||||||
|
// Some metadata seems to be left over from the host file. e.g. CodeSection::section_start
|
||||||
|
let module = {
|
||||||
|
let mut buffer = Vec::with_capacity(orig_module.size());
|
||||||
|
orig_module.serialize(&mut buffer);
|
||||||
|
WasmModule::preload(arena, &buffer, false).map_err(|e| format!("{:?}", e))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let dispatcher = TestDispatcher {
|
||||||
|
wasi: wasi::WasiDispatcher::default(),
|
||||||
|
};
|
||||||
|
let is_debug_mode = false;
|
||||||
|
let mut inst = Instance::for_module(&arena, &module, dispatcher, is_debug_mode)?;
|
||||||
|
|
||||||
// In Zig, main can only return u8 or void, but our result is too wide for that.
|
// In Zig, main can only return u8 or void, but our result is too wide for that.
|
||||||
// But I want to use main so that I can test that _start is created for it!
|
// But I want to use main so that I can test that _start is created for it!
|
||||||
// So return void from main, and call another function to get the result.
|
// So return void from main, and call another function to get the result.
|
||||||
let start = module.find_function::<(), ()>("_start").unwrap();
|
inst.call_export("_start", [])?;
|
||||||
start.call().unwrap();
|
|
||||||
|
|
||||||
let read_host_result = module.find_function::<(), i32>("read_host_result").unwrap();
|
// FIXME: read_host_result does not actually appear as an export!
|
||||||
read_host_result.call().unwrap()
|
// The interpreter has to look it up in debug info! (Apparently Wasm3 did this!)
|
||||||
|
// If we change gen_wasm to export it, then it does the same for js_unused,
|
||||||
|
// so we can't test import elimination and function reordering.
|
||||||
|
// We should to come back to this and fix it.
|
||||||
|
inst.call_export("read_host_result", [])?
|
||||||
|
.ok_or(String::from("expected a return value"))?
|
||||||
|
.expect_i32()
|
||||||
|
.map_err(|type_err| format!("{:?}", type_err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_native_result() -> i32 {
|
fn get_native_result() -> i32 {
|
||||||
|
@ -224,63 +255,13 @@ fn get_native_result() -> i32 {
|
||||||
result_str.parse().unwrap()
|
result_str.parse().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn test_help(
|
||||||
fn test_linking_without_dce() {
|
eliminate_dead_code: bool,
|
||||||
let arena = Bump::new();
|
expected_host_import_names: &[&str],
|
||||||
let layout_interner = STLayoutInterner::with_capacity(4);
|
expected_final_import_names: &[&str],
|
||||||
|
expected_name_section_start: &[(u32, &str)],
|
||||||
let BackendInputs {
|
dump_filename: &str,
|
||||||
env,
|
) {
|
||||||
mut interns,
|
|
||||||
host_module,
|
|
||||||
procedures,
|
|
||||||
} = BackendInputs::new(&arena, &layout_interner);
|
|
||||||
|
|
||||||
let host_import_names = Vec::from_iter(host_module.import.imports.iter().map(|i| i.name));
|
|
||||||
assert_eq!(
|
|
||||||
&host_import_names,
|
|
||||||
&[
|
|
||||||
"__linear_memory",
|
|
||||||
"__stack_pointer",
|
|
||||||
"js_called_indirectly_from_roc",
|
|
||||||
"js_called_indirectly_from_main",
|
|
||||||
"js_unused",
|
|
||||||
"js_called_directly_from_roc",
|
|
||||||
"js_called_directly_from_main",
|
|
||||||
"roc__app_proc_1_exposed",
|
|
||||||
"__indirect_function_table",
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let (final_module, _called_fns, _roc_main_index) =
|
|
||||||
roc_gen_wasm::build_app_module(&env, &mut interns, host_module, procedures);
|
|
||||||
|
|
||||||
let mut buffer = Vec::with_capacity(final_module.size());
|
|
||||||
final_module.serialize(&mut buffer);
|
|
||||||
|
|
||||||
if std::env::var("DEBUG_WASM").is_ok() {
|
|
||||||
fs::write("build/without_dce.wasm", &buffer).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let final_import_names = Vec::from_iter(final_module.import.imports.iter().map(|i| i.name));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
&final_import_names,
|
|
||||||
&[
|
|
||||||
"js_called_indirectly_from_roc",
|
|
||||||
"js_called_indirectly_from_main",
|
|
||||||
"js_unused",
|
|
||||||
"js_called_directly_from_roc",
|
|
||||||
"js_called_directly_from_main",
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let wasm_result = execute_wasm_bytes(&buffer);
|
|
||||||
assert_eq!(wasm_result, get_native_result());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_linking_with_dce() {
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let layout_interner = STLayoutInterner::with_capacity(4);
|
let layout_interner = STLayoutInterner::with_capacity(4);
|
||||||
|
|
||||||
|
@ -292,57 +273,105 @@ fn test_linking_with_dce() {
|
||||||
} = BackendInputs::new(&arena, &layout_interner);
|
} = BackendInputs::new(&arena, &layout_interner);
|
||||||
|
|
||||||
let host_import_names = Vec::from_iter(host_module.import.imports.iter().map(|imp| imp.name));
|
let host_import_names = Vec::from_iter(host_module.import.imports.iter().map(|imp| imp.name));
|
||||||
assert_eq!(
|
assert_eq!(&host_import_names, expected_host_import_names);
|
||||||
&host_import_names,
|
|
||||||
&[
|
|
||||||
"__linear_memory",
|
|
||||||
"__stack_pointer",
|
|
||||||
"js_called_indirectly_from_roc",
|
|
||||||
"js_called_indirectly_from_main",
|
|
||||||
"js_unused",
|
|
||||||
"js_called_directly_from_roc",
|
|
||||||
"js_called_directly_from_main",
|
|
||||||
"roc__app_proc_1_exposed",
|
|
||||||
"__indirect_function_table",
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(&host_module.names.function_names.is_empty());
|
assert!(&host_module.names.function_names.is_empty());
|
||||||
|
|
||||||
let (mut final_module, called_fns, _roc_main_index) =
|
let (mut final_module, called_fns, _roc_main_index) =
|
||||||
roc_gen_wasm::build_app_module(&env, &mut interns, host_module, procedures);
|
roc_gen_wasm::build_app_module(&env, &mut interns, host_module, procedures);
|
||||||
|
|
||||||
final_module.eliminate_dead_code(env.arena, called_fns);
|
if eliminate_dead_code {
|
||||||
|
final_module.eliminate_dead_code(env.arena, called_fns);
|
||||||
|
}
|
||||||
|
|
||||||
let mut buffer = Vec::with_capacity(final_module.size());
|
|
||||||
final_module.serialize(&mut buffer);
|
|
||||||
if std::env::var("DEBUG_WASM").is_ok() {
|
if std::env::var("DEBUG_WASM").is_ok() {
|
||||||
fs::write("build/with_dce.wasm", &buffer).unwrap();
|
let mut buffer = Vec::with_capacity(final_module.size());
|
||||||
|
final_module.serialize(&mut buffer);
|
||||||
|
fs::write(dump_filename, &buffer).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_import_names = Vec::from_iter(final_module.import.imports.iter().map(|i| i.name));
|
let final_import_names = Vec::from_iter(final_module.import.imports.iter().map(|i| i.name));
|
||||||
|
|
||||||
|
assert_eq!(&final_import_names, expected_final_import_names);
|
||||||
|
|
||||||
|
let name_count = expected_name_section_start.len();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&final_import_names,
|
&final_module.names.function_names[0..name_count],
|
||||||
&[
|
expected_name_section_start
|
||||||
"js_called_indirectly_from_roc",
|
|
||||||
"js_called_indirectly_from_main",
|
|
||||||
"js_called_directly_from_roc",
|
|
||||||
"js_called_directly_from_main",
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
let wasm_result = execute_wasm_module(&arena, final_module).unwrap();
|
||||||
&final_module.names.function_names[0..5],
|
|
||||||
&[
|
|
||||||
(0, "js_called_indirectly_from_roc"),
|
|
||||||
(1, "js_called_indirectly_from_main"),
|
|
||||||
(2, "js_called_directly_from_roc"),
|
|
||||||
(3, "js_called_directly_from_main"),
|
|
||||||
(4, "js_unused"),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let wasm_result = execute_wasm_bytes(&buffer);
|
|
||||||
assert_eq!(wasm_result, get_native_result());
|
assert_eq!(wasm_result, get_native_result());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EXPECTED_HOST_IMPORT_NAMES: [&'static str; 9] = [
|
||||||
|
"__linear_memory",
|
||||||
|
"__stack_pointer",
|
||||||
|
"js_called_indirectly_from_roc",
|
||||||
|
"js_unused",
|
||||||
|
"js_called_directly_from_roc",
|
||||||
|
"js_called_directly_from_main",
|
||||||
|
"roc__app_proc_1_exposed",
|
||||||
|
"js_called_indirectly_from_main",
|
||||||
|
"__indirect_function_table",
|
||||||
|
];
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_linking_without_dce() {
|
||||||
|
let expected_final_import_names = &[
|
||||||
|
"js_called_indirectly_from_roc",
|
||||||
|
"js_unused", // not eliminated
|
||||||
|
"js_called_directly_from_roc",
|
||||||
|
"js_called_directly_from_main",
|
||||||
|
"js_called_indirectly_from_main",
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected_name_section_start = &[
|
||||||
|
(0, "js_called_indirectly_from_roc"),
|
||||||
|
(1, "js_unused"), // not eliminated
|
||||||
|
(2, "js_called_directly_from_roc"),
|
||||||
|
(3, "js_called_directly_from_main"),
|
||||||
|
(4, "js_called_indirectly_from_main"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let eliminate_dead_code = false;
|
||||||
|
let dump_filename = "build/without_dce.wasm";
|
||||||
|
|
||||||
|
test_help(
|
||||||
|
eliminate_dead_code,
|
||||||
|
&EXPECTED_HOST_IMPORT_NAMES,
|
||||||
|
expected_final_import_names,
|
||||||
|
expected_name_section_start,
|
||||||
|
dump_filename,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_linking_with_dce() {
|
||||||
|
let expected_final_import_names = &[
|
||||||
|
"js_called_indirectly_from_roc",
|
||||||
|
// "js_unused", // eliminated
|
||||||
|
"js_called_directly_from_roc",
|
||||||
|
"js_called_directly_from_main",
|
||||||
|
"js_called_indirectly_from_main",
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected_name_section_start = &[
|
||||||
|
(0, "js_called_indirectly_from_roc"),
|
||||||
|
(1, "js_called_directly_from_roc"), // index changed
|
||||||
|
(2, "js_called_directly_from_main"), // index changed
|
||||||
|
(3, "js_called_indirectly_from_main"), // index changed
|
||||||
|
(4, "js_unused"), // still exists, but now an internal dummy, with index changed
|
||||||
|
];
|
||||||
|
|
||||||
|
let eliminate_dead_code = true;
|
||||||
|
let dump_filename = "build/with_dce.wasm";
|
||||||
|
|
||||||
|
test_help(
|
||||||
|
eliminate_dead_code,
|
||||||
|
&EXPECTED_HOST_IMPORT_NAMES,
|
||||||
|
expected_final_import_names,
|
||||||
|
expected_name_section_start,
|
||||||
|
dump_filename,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
procedure Bool.1 ():
|
||||||
|
let Bool.24 : Int1 = false;
|
||||||
|
ret Bool.24;
|
||||||
|
|
||||||
|
procedure List.2 (List.95, List.96):
|
||||||
|
let List.492 : U64 = CallByName List.6 List.95;
|
||||||
|
let List.488 : Int1 = CallByName Num.22 List.96 List.492;
|
||||||
|
if List.488 then
|
||||||
|
let List.490 : Str = CallByName List.66 List.95 List.96;
|
||||||
|
let List.489 : [C {}, C Str] = TagId(1) List.490;
|
||||||
|
ret List.489;
|
||||||
|
else
|
||||||
|
let List.487 : {} = Struct {};
|
||||||
|
let List.486 : [C {}, C Str] = TagId(0) List.487;
|
||||||
|
ret List.486;
|
||||||
|
|
||||||
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
|
let List.494 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3;
|
||||||
|
ret List.494;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.493 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.493;
|
||||||
|
|
||||||
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
|
let List.491 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
|
ret List.491;
|
||||||
|
|
||||||
|
procedure List.9 (List.283):
|
||||||
|
let List.485 : U64 = 0i64;
|
||||||
|
let List.478 : [C {}, C Str] = CallByName List.2 List.283 List.485;
|
||||||
|
let List.482 : U8 = 1i64;
|
||||||
|
let List.483 : U8 = GetTagId List.478;
|
||||||
|
let List.484 : Int1 = lowlevel Eq List.482 List.483;
|
||||||
|
if List.484 then
|
||||||
|
let List.284 : Str = UnionAtIndex (Id 1) (Index 0) List.478;
|
||||||
|
inc List.284;
|
||||||
|
dec List.478;
|
||||||
|
let List.479 : [C {}, C Str] = TagId(1) List.284;
|
||||||
|
ret List.479;
|
||||||
|
else
|
||||||
|
dec List.478;
|
||||||
|
let List.481 : {} = Struct {};
|
||||||
|
let List.480 : [C {}, C Str] = TagId(0) List.481;
|
||||||
|
ret List.480;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.256;
|
||||||
|
|
||||||
|
procedure Result.5 (Result.12, Result.13):
|
||||||
|
let Result.39 : U8 = 1i64;
|
||||||
|
let Result.40 : U8 = GetTagId Result.12;
|
||||||
|
let Result.41 : Int1 = lowlevel Eq Result.39 Result.40;
|
||||||
|
if Result.41 then
|
||||||
|
let Result.14 : Str = UnionAtIndex (Id 1) (Index 0) Result.12;
|
||||||
|
inc Result.14;
|
||||||
|
dec Result.12;
|
||||||
|
ret Result.14;
|
||||||
|
else
|
||||||
|
dec Result.12;
|
||||||
|
inc Result.13;
|
||||||
|
ret Result.13;
|
||||||
|
|
||||||
|
procedure Test.10 (Test.11):
|
||||||
|
let Test.12 : Str = CallByName Test.2 Test.11;
|
||||||
|
let Test.26 : Int1 = CallByName Bool.1;
|
||||||
|
if Test.26 then
|
||||||
|
ret Test.12;
|
||||||
|
else
|
||||||
|
dec Test.12;
|
||||||
|
let Test.25 : Str = "foo";
|
||||||
|
ret Test.25;
|
||||||
|
|
||||||
|
procedure Test.2 (Test.6):
|
||||||
|
let Test.29 : U8 = 1i64;
|
||||||
|
let Test.30 : U8 = GetTagId Test.6;
|
||||||
|
let Test.31 : Int1 = lowlevel Eq Test.29 Test.30;
|
||||||
|
if Test.31 then
|
||||||
|
let Test.7 : [<r>C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6;
|
||||||
|
let Test.8 : Str = CallByName Test.2 Test.7;
|
||||||
|
let Test.18 : Int1 = CallByName Bool.1;
|
||||||
|
if Test.18 then
|
||||||
|
ret Test.8;
|
||||||
|
else
|
||||||
|
dec Test.8;
|
||||||
|
let Test.17 : Str = "foo";
|
||||||
|
ret Test.17;
|
||||||
|
else
|
||||||
|
let Test.9 : List [<r>C List *self, C *self] = UnionAtIndex (Id 0) (Index 0) Test.6;
|
||||||
|
let Test.24 : {} = Struct {};
|
||||||
|
let Test.23 : List Str = CallByName List.5 Test.9 Test.24;
|
||||||
|
let Test.21 : [C {}, C Str] = CallByName List.9 Test.23;
|
||||||
|
dec Test.23;
|
||||||
|
let Test.22 : Str = "foo";
|
||||||
|
let Test.20 : Str = CallByName Result.5 Test.21 Test.22;
|
||||||
|
dec Test.22;
|
||||||
|
ret Test.20;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.32 : List [<r>C List *self, C *self] = Array [];
|
||||||
|
let Test.15 : [<r>C List *self, C *self] = TagId(0) Test.32;
|
||||||
|
let Test.14 : Str = CallByName Test.2 Test.15;
|
||||||
|
dec Test.15;
|
||||||
|
ret Test.14;
|
316
crates/compiler/test_mono/generated/issue_4749.txt
Normal file
316
crates/compiler/test_mono/generated/issue_4749.txt
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
dec #Attr.3;
|
||||||
|
dec #Attr.2;
|
||||||
|
ret Bool.23;
|
||||||
|
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.31 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.31;
|
||||||
|
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.38 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.38;
|
||||||
|
|
||||||
|
procedure Bool.12 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.30 : Int1 = lowlevel NotEq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.30;
|
||||||
|
|
||||||
|
procedure Bool.7 (Bool.19, Bool.20):
|
||||||
|
let Bool.29 : Int1 = CallByName Bool.12 Bool.19 Bool.20;
|
||||||
|
ret Bool.29;
|
||||||
|
|
||||||
|
procedure Decode.23 (Decode.94):
|
||||||
|
ret Decode.94;
|
||||||
|
|
||||||
|
procedure Decode.24 (Decode.95, Decode.114, Decode.97):
|
||||||
|
let Decode.127 : {List U8, [C {}, C Str]} = CallByName Json.293 Decode.95 Decode.97;
|
||||||
|
ret Decode.127;
|
||||||
|
|
||||||
|
procedure Decode.25 (Decode.98, Decode.99):
|
||||||
|
let Decode.126 : {} = CallByName Json.41;
|
||||||
|
let Decode.125 : {List U8, [C {}, C Str]} = CallByName Decode.24 Decode.98 Decode.126 Decode.99;
|
||||||
|
ret Decode.125;
|
||||||
|
|
||||||
|
procedure Decode.26 (Decode.100, Decode.101):
|
||||||
|
let Decode.115 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.100 Decode.101;
|
||||||
|
let Decode.103 : List U8 = StructAtIndex 0 Decode.115;
|
||||||
|
inc Decode.103;
|
||||||
|
let Decode.102 : [C {}, C Str] = StructAtIndex 1 Decode.115;
|
||||||
|
inc Decode.102;
|
||||||
|
dec Decode.115;
|
||||||
|
let Decode.118 : Int1 = CallByName List.1 Decode.103;
|
||||||
|
if Decode.118 then
|
||||||
|
dec Decode.103;
|
||||||
|
let Decode.122 : U8 = 1i64;
|
||||||
|
let Decode.123 : U8 = GetTagId Decode.102;
|
||||||
|
let Decode.124 : Int1 = lowlevel Eq Decode.122 Decode.123;
|
||||||
|
if Decode.124 then
|
||||||
|
let Decode.104 : Str = UnionAtIndex (Id 1) (Index 0) Decode.102;
|
||||||
|
inc Decode.104;
|
||||||
|
dec Decode.102;
|
||||||
|
let Decode.119 : [C [C List U8, C ], C Str] = TagId(1) Decode.104;
|
||||||
|
ret Decode.119;
|
||||||
|
else
|
||||||
|
dec Decode.102;
|
||||||
|
let Decode.121 : [C List U8, C ] = TagId(1) ;
|
||||||
|
let Decode.120 : [C [C List U8, C ], C Str] = TagId(0) Decode.121;
|
||||||
|
ret Decode.120;
|
||||||
|
else
|
||||||
|
dec Decode.102;
|
||||||
|
let Decode.117 : [C List U8, C ] = TagId(0) Decode.103;
|
||||||
|
let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117;
|
||||||
|
ret Decode.116;
|
||||||
|
|
||||||
|
procedure Json.139 (Json.450, Json.451):
|
||||||
|
joinpoint Json.421 Json.418 Json.138:
|
||||||
|
let Json.141 : List U8 = StructAtIndex 0 Json.418;
|
||||||
|
inc Json.141;
|
||||||
|
let Json.140 : List U8 = StructAtIndex 1 Json.418;
|
||||||
|
inc Json.140;
|
||||||
|
dec Json.418;
|
||||||
|
let Json.422 : [C {}, C U8] = CallByName List.9 Json.141;
|
||||||
|
let Json.436 : U8 = 1i64;
|
||||||
|
let Json.437 : U8 = GetTagId Json.422;
|
||||||
|
let Json.438 : Int1 = lowlevel Eq Json.436 Json.437;
|
||||||
|
if Json.438 then
|
||||||
|
let Json.142 : U8 = UnionAtIndex (Id 1) (Index 0) Json.422;
|
||||||
|
let Json.424 : Int1 = CallByName Json.283 Json.142;
|
||||||
|
if Json.424 then
|
||||||
|
let Json.434 : U64 = 1i64;
|
||||||
|
let Json.430 : {List U8, List U8} = CallByName List.52 Json.141 Json.434;
|
||||||
|
let Json.431 : {} = Struct {};
|
||||||
|
let Json.428 : List U8 = CallByName Json.143 Json.430;
|
||||||
|
let Json.429 : List U8 = CallByName List.4 Json.140 Json.142;
|
||||||
|
let Json.426 : {List U8, List U8} = Struct {Json.428, Json.429};
|
||||||
|
jump Json.421 Json.426 Json.138;
|
||||||
|
else
|
||||||
|
let Json.423 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||||
|
ret Json.423;
|
||||||
|
else
|
||||||
|
let Json.435 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||||
|
ret Json.435;
|
||||||
|
in
|
||||||
|
jump Json.421 Json.450 Json.451;
|
||||||
|
|
||||||
|
procedure Json.143 (Json.432):
|
||||||
|
let Json.433 : List U8 = StructAtIndex 1 Json.432;
|
||||||
|
inc Json.433;
|
||||||
|
dec Json.432;
|
||||||
|
ret Json.433;
|
||||||
|
|
||||||
|
procedure Json.2 ():
|
||||||
|
let Json.396 : {} = Struct {};
|
||||||
|
ret Json.396;
|
||||||
|
|
||||||
|
procedure Json.22 (Json.137, Json.138):
|
||||||
|
let Json.440 : List U8 = Array [];
|
||||||
|
let Json.420 : {List U8, List U8} = Struct {Json.137, Json.440};
|
||||||
|
let Json.419 : {List U8, List U8} = CallByName Json.139 Json.420 Json.138;
|
||||||
|
ret Json.419;
|
||||||
|
|
||||||
|
procedure Json.283 (Json.284):
|
||||||
|
let Json.442 : U8 = 34i64;
|
||||||
|
let Json.441 : Int1 = CallByName Bool.7 Json.284 Json.442;
|
||||||
|
ret Json.441;
|
||||||
|
|
||||||
|
procedure Json.293 (Json.294, Json.399):
|
||||||
|
let Json.400 : {List U8, [C {}, C Str]} = CallByName Json.40 Json.294;
|
||||||
|
ret Json.400;
|
||||||
|
|
||||||
|
procedure Json.40 (Json.276):
|
||||||
|
let Json.446 : U64 = 1i64;
|
||||||
|
inc Json.276;
|
||||||
|
let Json.445 : {List U8, List U8} = CallByName List.52 Json.276 Json.446;
|
||||||
|
let Json.277 : List U8 = StructAtIndex 0 Json.445;
|
||||||
|
inc Json.277;
|
||||||
|
let Json.279 : List U8 = StructAtIndex 1 Json.445;
|
||||||
|
inc Json.279;
|
||||||
|
dec Json.445;
|
||||||
|
let Json.444 : U8 = 34i64;
|
||||||
|
let Json.443 : List U8 = Array [Json.444];
|
||||||
|
let Json.404 : Int1 = CallByName Bool.11 Json.277 Json.443;
|
||||||
|
dec Json.443;
|
||||||
|
dec Json.277;
|
||||||
|
if Json.404 then
|
||||||
|
dec Json.276;
|
||||||
|
let Json.417 : {} = Struct {};
|
||||||
|
let Json.416 : {List U8, List U8} = CallByName Json.22 Json.279 Json.417;
|
||||||
|
let Json.282 : List U8 = StructAtIndex 0 Json.416;
|
||||||
|
inc Json.282;
|
||||||
|
let Json.281 : List U8 = StructAtIndex 1 Json.416;
|
||||||
|
inc Json.281;
|
||||||
|
dec Json.416;
|
||||||
|
let Json.405 : [C {U64, U8}, C Str] = CallByName Str.9 Json.281;
|
||||||
|
let Json.413 : U8 = 1i64;
|
||||||
|
let Json.414 : U8 = GetTagId Json.405;
|
||||||
|
let Json.415 : Int1 = lowlevel Eq Json.413 Json.414;
|
||||||
|
if Json.415 then
|
||||||
|
let Json.285 : Str = UnionAtIndex (Id 1) (Index 0) Json.405;
|
||||||
|
inc Json.285;
|
||||||
|
dec Json.405;
|
||||||
|
let Json.409 : U64 = 1i64;
|
||||||
|
let Json.408 : {List U8, List U8} = CallByName List.52 Json.282 Json.409;
|
||||||
|
let Json.287 : List U8 = StructAtIndex 1 Json.408;
|
||||||
|
inc Json.287;
|
||||||
|
dec Json.408;
|
||||||
|
let Json.407 : [C {}, C Str] = TagId(1) Json.285;
|
||||||
|
let Json.406 : {List U8, [C {}, C Str]} = Struct {Json.287, Json.407};
|
||||||
|
ret Json.406;
|
||||||
|
else
|
||||||
|
dec Json.405;
|
||||||
|
let Json.412 : {} = Struct {};
|
||||||
|
let Json.411 : [C {}, C Str] = TagId(0) Json.412;
|
||||||
|
let Json.410 : {List U8, [C {}, C Str]} = Struct {Json.282, Json.411};
|
||||||
|
ret Json.410;
|
||||||
|
else
|
||||||
|
dec Json.279;
|
||||||
|
let Json.403 : {} = Struct {};
|
||||||
|
let Json.402 : [C {}, C Str] = TagId(0) Json.403;
|
||||||
|
let Json.401 : {List U8, [C {}, C Str]} = Struct {Json.276, Json.402};
|
||||||
|
ret Json.401;
|
||||||
|
|
||||||
|
procedure Json.41 ():
|
||||||
|
let Json.398 : {} = Struct {};
|
||||||
|
let Json.397 : {} = CallByName Decode.23 Json.398;
|
||||||
|
ret Json.397;
|
||||||
|
|
||||||
|
procedure List.1 (List.94):
|
||||||
|
let List.479 : U64 = CallByName List.6 List.94;
|
||||||
|
let List.480 : U64 = 0i64;
|
||||||
|
let List.478 : Int1 = CallByName Bool.11 List.479 List.480;
|
||||||
|
ret List.478;
|
||||||
|
|
||||||
|
procedure List.2 (List.95, List.96):
|
||||||
|
let List.536 : U64 = CallByName List.6 List.95;
|
||||||
|
let List.532 : Int1 = CallByName Num.22 List.96 List.536;
|
||||||
|
if List.532 then
|
||||||
|
let List.534 : U8 = CallByName List.66 List.95 List.96;
|
||||||
|
let List.533 : [C {}, C U8] = TagId(1) List.534;
|
||||||
|
ret List.533;
|
||||||
|
else
|
||||||
|
let List.531 : {} = Struct {};
|
||||||
|
let List.530 : [C {}, C U8] = TagId(0) List.531;
|
||||||
|
ret List.530;
|
||||||
|
|
||||||
|
procedure List.4 (List.106, List.107):
|
||||||
|
let List.520 : U64 = 1i64;
|
||||||
|
let List.518 : List U8 = CallByName List.70 List.106 List.520;
|
||||||
|
let List.517 : List U8 = CallByName List.71 List.518 List.107;
|
||||||
|
ret List.517;
|
||||||
|
|
||||||
|
procedure List.49 (List.366, List.367):
|
||||||
|
let List.492 : U64 = StructAtIndex 0 List.367;
|
||||||
|
let List.493 : U64 = 0i64;
|
||||||
|
let List.490 : Int1 = CallByName Bool.11 List.492 List.493;
|
||||||
|
if List.490 then
|
||||||
|
dec List.366;
|
||||||
|
let List.491 : List U8 = Array [];
|
||||||
|
ret List.491;
|
||||||
|
else
|
||||||
|
let List.487 : U64 = StructAtIndex 1 List.367;
|
||||||
|
let List.488 : U64 = StructAtIndex 0 List.367;
|
||||||
|
let List.486 : List U8 = CallByName List.72 List.366 List.487 List.488;
|
||||||
|
ret List.486;
|
||||||
|
|
||||||
|
procedure List.52 (List.381, List.382):
|
||||||
|
let List.383 : U64 = CallByName List.6 List.381;
|
||||||
|
joinpoint List.515 List.384:
|
||||||
|
let List.513 : U64 = 0i64;
|
||||||
|
let List.512 : {U64, U64} = Struct {List.384, List.513};
|
||||||
|
inc List.381;
|
||||||
|
let List.385 : List U8 = CallByName List.49 List.381 List.512;
|
||||||
|
let List.511 : U64 = CallByName Num.20 List.383 List.384;
|
||||||
|
let List.510 : {U64, U64} = Struct {List.511, List.384};
|
||||||
|
let List.386 : List U8 = CallByName List.49 List.381 List.510;
|
||||||
|
let List.509 : {List U8, List U8} = Struct {List.385, List.386};
|
||||||
|
ret List.509;
|
||||||
|
in
|
||||||
|
let List.516 : Int1 = CallByName Num.24 List.383 List.382;
|
||||||
|
if List.516 then
|
||||||
|
jump List.515 List.382;
|
||||||
|
else
|
||||||
|
jump List.515 List.383;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.556 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.556;
|
||||||
|
|
||||||
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
|
let List.535 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
|
ret List.535;
|
||||||
|
|
||||||
|
procedure List.70 (#Attr.2, #Attr.3):
|
||||||
|
let List.521 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
|
ret List.521;
|
||||||
|
|
||||||
|
procedure List.71 (#Attr.2, #Attr.3):
|
||||||
|
let List.519 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||||
|
ret List.519;
|
||||||
|
|
||||||
|
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
|
let List.489 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||||
|
ret List.489;
|
||||||
|
|
||||||
|
procedure List.9 (List.283):
|
||||||
|
let List.529 : U64 = 0i64;
|
||||||
|
let List.522 : [C {}, C U8] = CallByName List.2 List.283 List.529;
|
||||||
|
let List.526 : U8 = 1i64;
|
||||||
|
let List.527 : U8 = GetTagId List.522;
|
||||||
|
let List.528 : Int1 = lowlevel Eq List.526 List.527;
|
||||||
|
if List.528 then
|
||||||
|
let List.284 : U8 = UnionAtIndex (Id 1) (Index 0) List.522;
|
||||||
|
let List.523 : [C {}, C U8] = TagId(1) List.284;
|
||||||
|
ret List.523;
|
||||||
|
else
|
||||||
|
let List.525 : {} = Struct {};
|
||||||
|
let List.524 : [C {}, C U8] = TagId(0) List.525;
|
||||||
|
ret List.524;
|
||||||
|
|
||||||
|
procedure Num.20 (#Attr.2, #Attr.3):
|
||||||
|
let Num.258 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
|
ret Num.258;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.262 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.262;
|
||||||
|
|
||||||
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
|
let Num.261 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||||
|
ret Num.261;
|
||||||
|
|
||||||
|
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
|
let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||||
|
ret Str.274;
|
||||||
|
|
||||||
|
procedure Str.9 (Str.76):
|
||||||
|
let Str.272 : U64 = 0i64;
|
||||||
|
let Str.273 : U64 = CallByName List.6 Str.76;
|
||||||
|
let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273;
|
||||||
|
let Str.269 : Int1 = StructAtIndex 2 Str.77;
|
||||||
|
if Str.269 then
|
||||||
|
let Str.271 : Str = StructAtIndex 1 Str.77;
|
||||||
|
inc Str.271;
|
||||||
|
dec Str.77;
|
||||||
|
let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271;
|
||||||
|
ret Str.270;
|
||||||
|
else
|
||||||
|
let Str.267 : U8 = StructAtIndex 3 Str.77;
|
||||||
|
let Str.268 : U64 = StructAtIndex 0 Str.77;
|
||||||
|
dec Str.77;
|
||||||
|
let Str.266 : {U64, U8} = Struct {Str.268, Str.267};
|
||||||
|
let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266;
|
||||||
|
ret Str.265;
|
||||||
|
|
||||||
|
procedure Test.3 ():
|
||||||
|
let Test.0 : List U8 = Array [82i64, 111i64, 99i64];
|
||||||
|
let Test.8 : {} = CallByName Json.2;
|
||||||
|
inc Test.0;
|
||||||
|
let Test.1 : [C [C List U8, C ], C Str] = CallByName Decode.26 Test.0 Test.8;
|
||||||
|
let Test.7 : Str = "Roc";
|
||||||
|
let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7;
|
||||||
|
inc Test.1;
|
||||||
|
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
|
||||||
|
expect Test.5;
|
||||||
|
let Test.4 : {} = Struct {};
|
||||||
|
ret Test.4;
|
|
@ -0,0 +1,49 @@
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.23;
|
||||||
|
|
||||||
|
procedure Bool.2 ():
|
||||||
|
let Bool.24 : Int1 = true;
|
||||||
|
ret Bool.24;
|
||||||
|
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Num.256;
|
||||||
|
|
||||||
|
procedure Num.21 (#Attr.2, #Attr.3):
|
||||||
|
let Num.257 : U64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
|
ret Num.257;
|
||||||
|
|
||||||
|
procedure Test.0 (Test.8):
|
||||||
|
let Test.23 : Int1 = CallByName Bool.2;
|
||||||
|
if Test.23 then
|
||||||
|
let Test.24 : Int1 = true;
|
||||||
|
ret Test.24;
|
||||||
|
else
|
||||||
|
let Test.22 : Int1 = false;
|
||||||
|
ret Test.22;
|
||||||
|
|
||||||
|
procedure Test.5 (Test.6, Test.2):
|
||||||
|
joinpoint Test.19 Test.18:
|
||||||
|
ret Test.18;
|
||||||
|
in
|
||||||
|
switch Test.2:
|
||||||
|
case 0:
|
||||||
|
let Test.20 : U64 = CallByName Num.19 Test.6 Test.6;
|
||||||
|
jump Test.19 Test.20;
|
||||||
|
|
||||||
|
default:
|
||||||
|
let Test.21 : U64 = CallByName Num.21 Test.6 Test.6;
|
||||||
|
jump Test.19 Test.21;
|
||||||
|
|
||||||
|
|
||||||
|
procedure Test.7 ():
|
||||||
|
let Test.13 : U64 = 3i64;
|
||||||
|
let Test.15 : {} = Struct {};
|
||||||
|
let Test.14 : Int1 = CallByName Test.0 Test.15;
|
||||||
|
let Test.11 : U64 = CallByName Test.5 Test.13 Test.14;
|
||||||
|
let Test.12 : U64 = 9i64;
|
||||||
|
let Test.10 : Int1 = CallByName Bool.11 Test.11 Test.12;
|
||||||
|
expect Test.10;
|
||||||
|
let Test.9 : {} = Struct {};
|
||||||
|
ret Test.9;
|
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