mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge remote-tracking branch 'remote/main' into builtin-task
This commit is contained in:
commit
b489c44b19
262 changed files with 11354 additions and 5821 deletions
|
@ -1,5 +1,5 @@
|
|||
on:
|
||||
# pull_request:
|
||||
#pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
# this cancels workflows currently in progress if you start a new one
|
||||
|
@ -11,7 +11,7 @@ env:
|
|||
# use .tar.gz for quick testing
|
||||
ARCHIVE_FORMAT: .tar.br
|
||||
# Make a new basic-cli git tag and set it here before starting this workflow
|
||||
RELEASE_TAG: 0.9.1
|
||||
RELEASE_TAG: 0.12.0
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
|
@ -240,6 +240,8 @@ jobs:
|
|||
mkdir platform
|
||||
# move all files to platform dir
|
||||
find . -maxdepth 1 -type f -exec mv {} platform/ \;
|
||||
# move all dirs (except roc_nightly) to platform dir
|
||||
find . -maxdepth 1 -type d ! -name 'platform' ! -name 'roc_nightly' -exec mv {} platform/ \;
|
||||
|
||||
mkdir temp-basic-cli
|
||||
cd temp-basic-cli
|
||||
|
|
2
.github/workflows/ubuntu_x86_64.yml
vendored
2
.github/workflows/ubuntu_x86_64.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
run: cargo run --locked --release format --check crates/compiler/builtins/roc
|
||||
|
||||
- name: ensure there are no unused dependencies
|
||||
run: cargo +nightly-2023-12-21 udeps --all-targets
|
||||
run: cargo +nightly-2024-02-03 udeps --all-targets
|
||||
|
||||
- name: zig wasm tests
|
||||
run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh
|
||||
|
|
4
.github/workflows/windows_release_build.yml
vendored
4
.github/workflows/windows_release_build.yml
vendored
|
@ -29,8 +29,8 @@ jobs:
|
|||
- name: zig version
|
||||
run: zig version
|
||||
|
||||
- name: install rust nightly 1.76.0
|
||||
run: rustup install nightly-2023-12-21
|
||||
- name: install rust nightly 1.77.0
|
||||
run: rustup install nightly-2024-02-03
|
||||
|
||||
- name: set up llvm 16
|
||||
run: |
|
||||
|
|
4
.github/workflows/windows_tests.yml
vendored
4
.github/workflows/windows_tests.yml
vendored
|
@ -37,8 +37,8 @@ jobs:
|
|||
cd crates\compiler\builtins\bitcode\
|
||||
zig build test
|
||||
|
||||
- name: install rust nightly 1.76.0
|
||||
run: rustup install nightly-2023-12-21
|
||||
- name: install rust nightly 1.77.0
|
||||
run: rustup install nightly-2024-02-03
|
||||
|
||||
- name: set up llvm 16
|
||||
run: |
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -25,6 +25,7 @@
|
|||
target
|
||||
generated-docs
|
||||
zig-cache
|
||||
.zig-cache
|
||||
.direnv
|
||||
.envrc
|
||||
*.rs.bk
|
||||
|
|
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -941,6 +941,13 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_at"
|
||||
version = "0.1.10"
|
||||
|
@ -2335,6 +2342,7 @@ dependencies = [
|
|||
"bitvec",
|
||||
"bumpalo",
|
||||
"indoc",
|
||||
"insta",
|
||||
"pretty_assertions",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
|
@ -2344,7 +2352,6 @@ dependencies = [
|
|||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_serialize",
|
||||
"roc_test_utils",
|
||||
"roc_types",
|
||||
"static_assertions",
|
||||
"ven_pretty",
|
||||
|
@ -2828,6 +2835,7 @@ dependencies = [
|
|||
"bitvec",
|
||||
"bumpalo",
|
||||
"hashbrown",
|
||||
"indoc",
|
||||
"parking_lot",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
|
@ -4551,6 +4559,12 @@ version = "0.25.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
102
Cargo.toml
102
Cargo.toml
|
@ -1,40 +1,41 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"crates/compiler/*",
|
||||
"crates/vendor/*",
|
||||
"crates/glue",
|
||||
"crates/cli",
|
||||
"crates/cli_utils",
|
||||
"crates/highlight",
|
||||
"crates/error_macros",
|
||||
"crates/reporting",
|
||||
"crates/packaging",
|
||||
"crates/repl_cli",
|
||||
"crates/repl_eval",
|
||||
"crates/repl_test",
|
||||
"crates/repl_ui",
|
||||
"crates/repl_wasm",
|
||||
"crates/repl_expect",
|
||||
"crates/roc_std",
|
||||
"crates/test_utils",
|
||||
"crates/test_utils_dir",
|
||||
"crates/valgrind",
|
||||
"crates/tracing",
|
||||
"crates/utils/*",
|
||||
"crates/docs",
|
||||
"crates/docs_cli",
|
||||
"crates/linker",
|
||||
"crates/wasi-libc-sys",
|
||||
"crates/wasm_module",
|
||||
"crates/wasm_interp",
|
||||
"crates/language_server",
|
||||
"crates/compiler/*",
|
||||
"crates/vendor/*",
|
||||
"crates/fs",
|
||||
"crates/glue",
|
||||
"crates/cli",
|
||||
"crates/cli_utils",
|
||||
"crates/highlight",
|
||||
"crates/error_macros",
|
||||
"crates/reporting",
|
||||
"crates/packaging",
|
||||
"crates/repl_cli",
|
||||
"crates/repl_eval",
|
||||
"crates/repl_test",
|
||||
"crates/repl_ui",
|
||||
"crates/repl_wasm",
|
||||
"crates/repl_expect",
|
||||
"crates/roc_std",
|
||||
"crates/test_utils",
|
||||
"crates/test_utils_dir",
|
||||
"crates/valgrind",
|
||||
"crates/tracing",
|
||||
"crates/utils/*",
|
||||
"crates/docs",
|
||||
"crates/docs_cli",
|
||||
"crates/linker",
|
||||
"crates/wasi-libc-sys",
|
||||
"crates/wasm_module",
|
||||
"crates/wasm_interp",
|
||||
"crates/language_server",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"ci/benchmarks/bench-runner",
|
||||
"ci/repl_basic_test",
|
||||
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
|
||||
"examples",
|
||||
"ci/benchmarks/bench-runner",
|
||||
"ci/repl_basic_test",
|
||||
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
|
||||
"examples",
|
||||
]
|
||||
# Needed to be able to run `cargo run -p roc_cli --no-default-features` -
|
||||
# see www/build.sh for more.
|
||||
|
@ -69,7 +70,9 @@ version = "0.0.1"
|
|||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
||||
# This way, GitHub Actions works and nobody's builds get broken.
|
||||
# TODO: Switch this back to roc-lang/inkwell once it is updated
|
||||
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = ["llvm16-0"] }
|
||||
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [
|
||||
"llvm16-0",
|
||||
] }
|
||||
|
||||
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
|
||||
backtrace = "0.3.67"
|
||||
|
@ -78,18 +81,27 @@ bincode = "1.3.3"
|
|||
bitflags = "1.3.2"
|
||||
bitvec = "1.0.1"
|
||||
blake3 = "1.3.3"
|
||||
brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli
|
||||
brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||
capstone = { version = "0.11.0", default-features = false }
|
||||
cgmath = "0.18.0"
|
||||
chrono = "0.4.26"
|
||||
clap = { version = "4.2.7", default-features = false, features = ["std", "color", "suggestions", "help", "usage", "error-context"] }
|
||||
clap = { version = "4.2.7", default-features = false, features = [
|
||||
"std",
|
||||
"color",
|
||||
"suggestions",
|
||||
"help",
|
||||
"usage",
|
||||
"error-context",
|
||||
] }
|
||||
colored = "2.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
const_format = { version = "0.2.30", features = ["const_generics"] }
|
||||
copypasta = "0.8.2"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = ["html_reports"], rev = "30ea0c5" }
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [
|
||||
"html_reports",
|
||||
], rev = "30ea0c5" }
|
||||
criterion-perf-events = { git = "https://github.com/Anton-4/criterion-perf-events", rev = "0f38c3e" }
|
||||
crossbeam = "0.8.2"
|
||||
dircpy = "0.3.14"
|
||||
|
@ -102,7 +114,12 @@ fs_extra = "1.3.0"
|
|||
futures = "0.3.26"
|
||||
glyph_brush = "0.7.7"
|
||||
hashbrown = { version = "0.14.3" }
|
||||
iced-x86 = { version = "1.18.0", default-features = false, features = ["std", "decoder", "op_code_info", "instr_info"] }
|
||||
iced-x86 = { version = "1.18.0", default-features = false, features = [
|
||||
"std",
|
||||
"decoder",
|
||||
"op_code_info",
|
||||
"instr_info",
|
||||
] }
|
||||
im = "15.1.0"
|
||||
im-rc = "15.1.0"
|
||||
indexmap = "2.1.0"
|
||||
|
@ -138,12 +155,17 @@ quote = "1.0.23"
|
|||
rand = "0.8.5"
|
||||
regex = "1.7.1"
|
||||
remove_dir_all = "0.8.1"
|
||||
reqwest = { version = "0.11.23", default-features = false, features = ["blocking", "rustls-tls"] } # default-features=false removes libopenssl as a dependency on Linux, which might not be available!
|
||||
reqwest = { version = "0.11.23", default-features = false, features = [
|
||||
"blocking",
|
||||
"rustls-tls",
|
||||
] } # default-features=false removes libopenssl as a dependency on Linux, which might not be available!
|
||||
rlimit = "0.9.1"
|
||||
rustyline = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
||||
rustyline-derive = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
||||
schemars = "0.8.12"
|
||||
serde = { version = "1.0.153", features = ["derive"] } # update roc_std/Cargo.toml on change
|
||||
serde = { version = "1.0.153", features = [
|
||||
"derive",
|
||||
] } # update roc_std/Cargo.toml on change
|
||||
serde-xml-rs = "0.6.0"
|
||||
serde_json = "1.0.94" # update roc_std/Cargo.toml on change
|
||||
serial_test = "1.0.0"
|
||||
|
@ -191,7 +213,7 @@ debug = true
|
|||
|
||||
[profile.release-with-lto]
|
||||
inherits = "release"
|
||||
lto = "thin" # TODO: We could consider full here since this is only used for packaged release on github.
|
||||
lto = "thin" # TODO: We could consider full here since this is only used for packaged release on github.
|
||||
|
||||
[profile.debug-full]
|
||||
inherits = "dev"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
VERSION 0.6
|
||||
FROM rust:1.76.0-slim-buster # make sure to update rust-toolchain.toml too so that everything uses the same rust version
|
||||
FROM rust:1.77.2-slim-buster # make sure to update rust-toolchain.toml too so that everything uses the same rust version
|
||||
WORKDIR /earthbuild
|
||||
|
||||
prep-debian:
|
||||
|
|
|
@ -256,7 +256,7 @@ mod tests {
|
|||
use std::io::Write;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Task
|
||||
|
@ -264,7 +264,7 @@ import pf.Task
|
|||
main =
|
||||
Stdout.line! "I'm a Roc application!""#;
|
||||
|
||||
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
|
||||
import pf.Stdout
|
||||
|
|
|
@ -13,15 +13,18 @@ use roc_build::program::{
|
|||
handle_error_module, handle_loading_problem, standard_load_config, BuildFileError,
|
||||
BuildOrdering, BuiltFile, CodeGenBackend, CodeGenOptions, DEFAULT_ROC_FILENAME,
|
||||
};
|
||||
#[cfg(not(windows))]
|
||||
use roc_collections::MutMap;
|
||||
use roc_error_macros::{internal_error, user_error};
|
||||
use roc_gen_dev::AssemblyBackendMode;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{ExpectMetadata, Threading};
|
||||
#[cfg(not(windows))]
|
||||
use roc_module::symbol::ModuleId;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_packaging::tarball::Compression;
|
||||
#[cfg(not(windows))]
|
||||
use roc_reporting::report::ANSI_STYLE_CODES;
|
||||
use roc_target::{Architecture, Target};
|
||||
use std::env;
|
||||
|
@ -31,7 +34,9 @@ use std::mem::ManuallyDrop;
|
|||
use std::os::raw::{c_char, c_int};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(not(windows))]
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use strum::IntoEnumIterator;
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use tempfile::TempDir;
|
||||
|
@ -468,6 +473,7 @@ pub fn test(_matches: &ArgMatches, _target: Target) -> io::Result<i32> {
|
|||
todo!("running tests does not work on windows right now")
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
struct ModuleTestResults {
|
||||
module_id: ModuleId,
|
||||
failed_count: usize,
|
||||
|
@ -516,8 +522,7 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
}
|
||||
|
||||
let arena = &arena;
|
||||
// TODO may need to determine this dynamically based on dev builds.
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
|
@ -647,6 +652,7 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn print_test_results(
|
||||
module_test_results: ModuleTestResults,
|
||||
sources: &MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||
|
@ -666,6 +672,7 @@ fn print_test_results(
|
|||
println!("\n{module_name}:\n {test_summary_str}",);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn test_summary(failed_count: usize, passed_count: usize, tests_duration: Duration) -> String {
|
||||
let failed_color = if failed_count == 0 {
|
||||
ANSI_STYLE_CODES.green
|
||||
|
@ -1157,6 +1164,11 @@ unsafe fn roc_run_native_fast(
|
|||
enum ExecutableFile {
|
||||
#[cfg(target_os = "linux")]
|
||||
MemFd(libc::c_int, PathBuf),
|
||||
// We store the TempDir in the onDisk variant alongside the path to the executable,
|
||||
// so that the TempDir doesn't get dropped until after we're done with the path.
|
||||
// If we didn't do that, then the tempdir would potentially get deleted by the
|
||||
// TempDir's Drop impl before the file had been executed.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
OnDisk(TempDir, PathBuf),
|
||||
}
|
||||
|
@ -1322,6 +1334,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
|
|||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.mode(0o777) // create the file as executable
|
||||
.open(&app_path_buf)?;
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ fn main() -> io::Result<()> {
|
|||
.get_one::<String>(FLAG_TARGET)
|
||||
.and_then(|s| Target::from_str(s).ok())
|
||||
.unwrap_or_default();
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
roc_linker::generate_stub_lib(
|
||||
input_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Arg
|
||||
|
||||
main =
|
||||
args = Arg.list!
|
||||
parser =
|
||||
divCmd =
|
||||
Arg.succeed (\dividend -> \divisor -> Div (Num.toF64 dividend) (Num.toF64 divisor))
|
||||
|> Arg.withParser
|
||||
(
|
||||
Arg.i64Option {
|
||||
long: "dividend",
|
||||
short: "n",
|
||||
help: "the number to divide; corresponds to a numerator",
|
||||
}
|
||||
)
|
||||
|> Arg.withParser
|
||||
(
|
||||
Arg.i64Option {
|
||||
long: "divisor",
|
||||
short: "d",
|
||||
help: "the number to divide by; corresponds to a denominator",
|
||||
}
|
||||
)
|
||||
|> Arg.subCommand "div"
|
||||
|
||||
logCmd =
|
||||
Arg.succeed (\base -> \num -> Log (Num.toF64 base) (Num.toF64 num))
|
||||
|> Arg.withParser
|
||||
(
|
||||
Arg.i64Option {
|
||||
long: "base",
|
||||
short: "b",
|
||||
help: "base of the logarithm",
|
||||
}
|
||||
)
|
||||
|> Arg.withParser
|
||||
(
|
||||
Arg.i64Option {
|
||||
long: "num",
|
||||
help: "the number to take the logarithm of",
|
||||
}
|
||||
)
|
||||
|> Arg.subCommand "log"
|
||||
|
||||
Arg.choice [divCmd, logCmd]
|
||||
|> Arg.program { name: "args-example", help: "A calculator example of the CLI platform argument parser" }
|
||||
|
||||
when Arg.parseFormatted parser args is
|
||||
Ok cmd ->
|
||||
runCmd cmd
|
||||
|> Num.toStr
|
||||
|> Stdout.line
|
||||
|
||||
Err helpMenuErr ->
|
||||
Task.err (Exit 1 "unable to parse args: $(Inspect.toStr helpMenuErr)")
|
||||
|
||||
runCmd = \cmd ->
|
||||
when cmd is
|
||||
Div n d -> n / d
|
||||
Log b n ->
|
||||
# log_b(n) = log_x(n) / log_x(b) for all x
|
||||
runCmd (Div (Num.log n) (Num.log b))
|
22
crates/cli/tests/cli/combine-tasks.roc
Normal file
22
crates/cli/tests/cli/combine-tasks.roc
Normal file
|
@ -0,0 +1,22 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [Task]
|
||||
|
||||
main =
|
||||
multipleIn =
|
||||
{ sequential <-
|
||||
a: Task.ok 123,
|
||||
b: Task.ok "abc",
|
||||
c: Task.ok [123],
|
||||
d: Task.ok ["abc"],
|
||||
e: Task.ok (Dict.single "a" "b"),
|
||||
}!
|
||||
|
||||
Stdout.line! "For multiple tasks: $(Inspect.toStr multipleIn)"
|
||||
|
||||
sequential : Task a err, Task b err, (a, b -> c) -> Task c err
|
||||
sequential = \firstTask, secondTask, mapper ->
|
||||
first = firstTask!
|
||||
second = secondTask!
|
||||
Task.ok (mapper first second)
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.File
|
||||
import pf.Path
|
||||
import pf.Env
|
||||
import pf.Dir
|
||||
|
||||
main : Task {} [Exit I32 Str]_
|
||||
main =
|
||||
path = Path.fromStr "out.txt"
|
||||
pathStr = "out.txt"
|
||||
|
||||
task =
|
||||
cwd = Env.cwd!
|
||||
Stdout.line! "cwd: $(Path.display cwd)"
|
||||
dirEntries = Dir.list! cwd
|
||||
cwdPath = Env.cwd!
|
||||
cwdStr = Path.display cwdPath
|
||||
Stdout.line! "Current working directory: $(cwdStr)"
|
||||
|
||||
dirEntries = Path.listDir! cwdPath
|
||||
contentsStr = Str.joinWith (List.map dirEntries Path.display) "\n "
|
||||
Stdout.line! "Directory contents:\n $(contentsStr)\n"
|
||||
Stdout.line! "Writing a string to out.txt"
|
||||
File.writeUtf8! path "a string!"
|
||||
contents = File.readUtf8! path
|
||||
File.writeUtf8! pathStr "a string!"
|
||||
contents = File.readUtf8! pathStr
|
||||
Stdout.line! "I read the file back. Its contents: \"$(contents)\""
|
||||
|
||||
when Task.result! task is
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdin
|
||||
import pf.Stdout
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Http
|
||||
import pf.Stdout
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "ingested-file.roc" as license
|
||||
import "test-file.txt" as testFile
|
||||
|
||||
main =
|
||||
license
|
||||
# Due to the functions we apply on testFile, it will be inferred as a List U8.
|
||||
testFile
|
||||
|> List.map Num.toU64
|
||||
|> List.sum
|
||||
|> Num.toStr
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "ingested-file.roc" as license : _ # A type hole can also be used here.
|
||||
import "test-file.txt" as testFile : _ # the _ is optional
|
||||
|
||||
main =
|
||||
# Due to how license is used, it will be a List U8.
|
||||
license
|
||||
# Due to the functions we apply on testFile, it will be inferred as a List U8.
|
||||
testFile
|
||||
|> List.map Num.toU64
|
||||
|> List.sum
|
||||
|> Num.toStr
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
|
||||
import pf.Stdout
|
||||
import "ingested-file.roc" as ownCode : Str
|
||||
|
|
86
crates/cli/tests/cli/parse-args.roc
Normal file
86
crates/cli/tests/cli/parse-args.roc
Normal file
|
@ -0,0 +1,86 @@
|
|||
app [main] {
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br",
|
||||
}
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Task exposing [Task]
|
||||
|
||||
main =
|
||||
file = strParam { name: "file" }
|
||||
argParser =
|
||||
{ cliBuild <-
|
||||
file,
|
||||
count: numParam { name: "count" },
|
||||
doubled: numParam { name: "doubled" }
|
||||
|> cliMap \d -> d * 2,
|
||||
}
|
||||
|
||||
args = ["parse-args", "file.txt", "5", "7"]
|
||||
when argParser |> parseArgs args is
|
||||
Ok data -> Stdout.line "Success: $(Inspect.toStr data)"
|
||||
Err (FailedToParse message) -> Stdout.line "Failed: $(message)"
|
||||
|
||||
ArgParseErr : [NoMoreArgs, InvalidParam ParamConfig]
|
||||
|
||||
ParamConfig : {
|
||||
name : Str,
|
||||
type : [Num, Str],
|
||||
}
|
||||
|
||||
ArgParser out : {
|
||||
params : List ParamConfig,
|
||||
parser : List Str -> Result (out, List Str) ArgParseErr,
|
||||
}
|
||||
|
||||
strParam : { name : Str } -> ArgParser Str
|
||||
strParam = \{ name } ->
|
||||
parser = \args ->
|
||||
when args is
|
||||
[] -> Err NoMoreArgs
|
||||
[first, .. as rest] -> Ok (first, rest)
|
||||
|
||||
{ params: [{ name, type: Str }], parser }
|
||||
|
||||
numParam : { name : Str } -> ArgParser U64
|
||||
numParam = \{ name } ->
|
||||
param = { name, type: Num }
|
||||
parser = \args ->
|
||||
when args is
|
||||
[] -> Err NoMoreArgs
|
||||
[first, .. as rest] ->
|
||||
when Str.toU64 first is
|
||||
Ok num -> Ok (num, rest)
|
||||
Err InvalidNumStr -> Err (InvalidParam param)
|
||||
|
||||
{ params: [param], parser }
|
||||
|
||||
cliMap : ArgParser a, (a -> b) -> ArgParser b
|
||||
cliMap = \{ params, parser }, mapper -> {
|
||||
params,
|
||||
parser: \args ->
|
||||
(data, afterData) <- parser args
|
||||
|> Result.try
|
||||
|
||||
Ok (mapper data, afterData),
|
||||
}
|
||||
|
||||
cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c
|
||||
cliBuild = \firstWeaver, secondWeaver, combine ->
|
||||
allParams = List.concat firstWeaver.params secondWeaver.params
|
||||
combinedParser = \args ->
|
||||
(firstValue, afterFirst) <- firstWeaver.parser args
|
||||
|> Result.try
|
||||
(secondValue, afterSecond) <- secondWeaver.parser afterFirst
|
||||
|> Result.try
|
||||
|
||||
Ok (combine firstValue secondValue, afterSecond)
|
||||
|
||||
{ params: allParams, parser: combinedParser }
|
||||
|
||||
parseArgs : ArgParser a, List Str -> Result a [FailedToParse Str]
|
||||
parseArgs = \{ params: _, parser }, args ->
|
||||
when parser (List.dropFirst args 1) is
|
||||
Ok (data, []) -> Ok data
|
||||
Ok (_data, extraArgs) -> Err (FailedToParse "Got $(List.len extraArgs |> Inspect.toStr) extra args")
|
||||
Err NoMoreArgs -> Err (FailedToParse "I needed more args")
|
||||
Err (InvalidParam param) -> Err (FailedToParse "Parameter '$(param.name)' needed a $(Inspect.toStr param.type)")
|
|
@ -1,5 +1,5 @@
|
|||
app [main] {
|
||||
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
|
||||
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
app [main] {
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
|
||||
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br",
|
||||
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
|
||||
}
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stderr
|
||||
import parser.Core exposing [Parser, map, keep]
|
||||
import parser.Core exposing [map, keep]
|
||||
import parser.String exposing [strFromUtf8]
|
||||
import parser.CSV exposing [CSV]
|
||||
import parser.CSV
|
||||
|
||||
input : Str
|
||||
input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\""
|
||||
|
|
1
crates/cli/tests/cli/test-file.txt
Normal file
1
crates/cli/tests/cli/test-file.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Used by ingested-file-bytes.roc and ingested-file-bytes-no-ann.roc
|
|
@ -79,6 +79,8 @@ mod cli_run {
|
|||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Arg<'a> {
|
||||
ExamplePath(&'a str),
|
||||
// allow because we may need PlainText in the future
|
||||
#[allow(dead_code)]
|
||||
PlainText(&'a str),
|
||||
}
|
||||
|
||||
|
@ -799,39 +801,6 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "currently broken in basic-cli platform"]
|
||||
#[cfg_attr(windows, ignore = "missing __udivdi3 and some other symbols")]
|
||||
#[serial(cli_platform)]
|
||||
fn cli_args() {
|
||||
test_roc_app(
|
||||
"examples/cli",
|
||||
"argsBROKEN.roc",
|
||||
&[],
|
||||
&[
|
||||
Arg::PlainText("log"),
|
||||
Arg::PlainText("-b"),
|
||||
Arg::PlainText("3"),
|
||||
Arg::PlainText("--num"),
|
||||
Arg::PlainText("81"),
|
||||
],
|
||||
&[],
|
||||
"4\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove in favor of cli_args once mono bugs are resolved in investigation
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore = "missing __udivdi3 and some other symbols")]
|
||||
#[serial(cli_platform)]
|
||||
fn cli_args_check() {
|
||||
let path = file_path_from_root("crates/cli/tests/cli", "argsBROKEN.roc");
|
||||
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
// TODO: write a new test once mono bugs are resolved in investigation
|
||||
#[test]
|
||||
#[cfg(not(debug_assertions))] // https://github.com/roc-lang/roc/issues/4806
|
||||
|
@ -988,6 +957,38 @@ mod cli_run {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn combine_tasks_with_record_builder() {
|
||||
test_roc_app(
|
||||
"crates/cli/tests/cli",
|
||||
"combine-tasks.roc",
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"For multiple tasks: {a: 123, b: \"abc\", c: [123], d: [\"abc\"], e: {\"a\": \"b\"}}\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn parse_args_with_record_builder() {
|
||||
test_roc_app(
|
||||
"crates/cli/tests/cli",
|
||||
"parse-args.roc",
|
||||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"Success: {count: 5, doubled: 14, file: \"file.txt\"}\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(cli_platform)]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
|
@ -998,7 +999,7 @@ mod cli_run {
|
|||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"27101\n",
|
||||
"6239\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
|
@ -1013,7 +1014,7 @@ mod cli_run {
|
|||
&[],
|
||||
&[],
|
||||
&[],
|
||||
"27101\n",
|
||||
"6239\n",
|
||||
UseValgrind::No,
|
||||
TestCliCommands::Run,
|
||||
)
|
||||
|
|
|
@ -351,6 +351,7 @@ struct ValgrindOutput {
|
|||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[allow(dead_code)] // Some fields are unused but this allows for easy deserialization of the xml.
|
||||
enum ValgrindField {
|
||||
ProtocolVersion(isize),
|
||||
ProtocolTool(String),
|
||||
|
|
|
@ -863,7 +863,6 @@ fn call_spec<'a>(
|
|||
|
||||
let closure_env = env.symbols[&passed_function.captured_environment];
|
||||
|
||||
let return_layout = &passed_function.return_layout;
|
||||
let argument_layouts = passed_function.argument_layouts;
|
||||
|
||||
macro_rules! call_function {
|
||||
|
@ -879,30 +878,6 @@ fn call_spec<'a>(
|
|||
}
|
||||
|
||||
match op {
|
||||
ListMap { xs } => {
|
||||
let list = env.symbols[xs];
|
||||
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||
|
||||
let element = builder.add_bag_get(block, input_bag)?;
|
||||
|
||||
let new_element = call_function!(builder, block, [element]);
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListSortWith { xs } => {
|
||||
let list = env.symbols[xs];
|
||||
|
||||
|
@ -928,109 +903,6 @@ fn call_spec<'a>(
|
|||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMap2 { xs, ys } => {
|
||||
let list1 = env.symbols[xs];
|
||||
let list2 = env.symbols[ys];
|
||||
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag_1 =
|
||||
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let input_bag_2 =
|
||||
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
|
||||
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||
|
||||
let new_element = call_function!(builder, block, [element_1, element_2]);
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
|
||||
ListMap3 { xs, ys, zs } => {
|
||||
let list1 = env.symbols[xs];
|
||||
let list2 = env.symbols[ys];
|
||||
let list3 = env.symbols[zs];
|
||||
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag_1 =
|
||||
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let input_bag_2 =
|
||||
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
let input_bag_3 =
|
||||
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
||||
|
||||
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||
let element_3 = builder.add_bag_get(block, input_bag_3)?;
|
||||
|
||||
let new_element =
|
||||
call_function!(builder, block, [element_1, element_2, element_3]);
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
ListMap4 { xs, ys, zs, ws } => {
|
||||
let list1 = env.symbols[xs];
|
||||
let list2 = env.symbols[ys];
|
||||
let list3 = env.symbols[zs];
|
||||
let list4 = env.symbols[ws];
|
||||
|
||||
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||
let input_bag_1 =
|
||||
builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
||||
let input_bag_2 =
|
||||
builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
||||
let input_bag_3 =
|
||||
builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
||||
let input_bag_4 =
|
||||
builder.add_get_tuple_field(block, list4, LIST_BAG_INDEX)?;
|
||||
|
||||
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||
let element_3 = builder.add_bag_get(block, input_bag_3)?;
|
||||
let element_4 = builder.add_bag_get(block, input_bag_4)?;
|
||||
|
||||
let new_element = call_function!(
|
||||
builder,
|
||||
block,
|
||||
[element_1, element_2, element_3, element_4]
|
||||
);
|
||||
|
||||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
||||
add_loop(builder, block, state_type, init_state, loop_body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -540,7 +540,7 @@ pub fn rebuild_host(
|
|||
// on windows, we need the nightly toolchain so we can use `-Z export-executable-symbols`
|
||||
// using `+nightly` only works when running cargo through rustup
|
||||
let mut cmd = rustup();
|
||||
cmd.args(["run", "nightly-2023-12-21", "cargo"]);
|
||||
cmd.args(["run", "nightly-2024-02-03", "cargo"]);
|
||||
|
||||
cmd
|
||||
} else {
|
||||
|
@ -1105,6 +1105,10 @@ fn link_macos(
|
|||
.args(input_paths)
|
||||
.args(extra_link_flags());
|
||||
|
||||
if get_xcode_version() >= 15.0 {
|
||||
ld_command.arg("-ld_classic");
|
||||
}
|
||||
|
||||
let sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib";
|
||||
if Path::new(sdk_path).exists() {
|
||||
ld_command.arg(format!("-L{sdk_path}"));
|
||||
|
@ -1187,6 +1191,24 @@ fn get_macos_version() -> String {
|
|||
.join(".")
|
||||
}
|
||||
|
||||
fn get_xcode_version() -> f32 {
|
||||
let mut cmd = Command::new("xcodebuild");
|
||||
cmd.arg("-version");
|
||||
debug_print_command(&cmd);
|
||||
|
||||
cmd.output()
|
||||
.map_err(|_| ())
|
||||
.and_then(|out| String::from_utf8(out.stdout).map_err(|_| ()))
|
||||
.and_then(|str| {
|
||||
str.split_whitespace()
|
||||
.nth(1)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(())
|
||||
})
|
||||
.and_then(|version| version.parse::<f32>().map_err(|_| ()))
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
|
||||
fn link_wasm32(
|
||||
_target: Target,
|
||||
output_path: PathBuf,
|
||||
|
|
|
@ -22,7 +22,6 @@ use std::ffi::OsStr;
|
|||
use std::ops::Deref;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
thread::JoinHandle,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
|
@ -696,20 +695,9 @@ pub fn standard_load_config(
|
|||
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
|
||||
};
|
||||
|
||||
// UNSTABLE(lambda-erasure)
|
||||
let function_kind = if cfg!(debug_assertions) {
|
||||
if std::env::var("EXPERIMENTAL_ROC_ERASE").is_ok() {
|
||||
FunctionKind::Erased
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
}
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
};
|
||||
|
||||
LoadConfig {
|
||||
target,
|
||||
function_kind,
|
||||
function_kind: FunctionKind::from_env(),
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
|
@ -915,11 +903,6 @@ fn build_loaded_file<'a>(
|
|||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
let loaded = loaded;
|
||||
|
||||
enum HostRebuildTiming {
|
||||
BeforeApp(u128),
|
||||
ConcurrentWithApp(JoinHandle<u128>),
|
||||
}
|
||||
|
||||
let opt_rebuild_timing = if let Some(rebuild_thread) = rebuild_thread {
|
||||
if linking_strategy == LinkingStrategy::Additive {
|
||||
let rebuild_duration = rebuild_thread
|
||||
|
@ -930,9 +913,9 @@ fn build_loaded_file<'a>(
|
|||
println!("Finished rebuilding the platform in {rebuild_duration} ms\n");
|
||||
}
|
||||
|
||||
Some(HostRebuildTiming::BeforeApp(rebuild_duration))
|
||||
None
|
||||
} else {
|
||||
Some(HostRebuildTiming::ConcurrentWithApp(rebuild_thread))
|
||||
Some(rebuild_thread)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -977,7 +960,7 @@ fn build_loaded_file<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(HostRebuildTiming::ConcurrentWithApp(thread)) = opt_rebuild_timing {
|
||||
if let Some(thread) = opt_rebuild_timing {
|
||||
let rebuild_duration = thread.join().expect("Failed to (re)build platform.");
|
||||
|
||||
if emit_timings && !is_platform_prebuilt {
|
||||
|
@ -1208,8 +1191,7 @@ pub fn check_file<'a>(
|
|||
|
||||
let load_config = LoadConfig {
|
||||
target,
|
||||
// TODO: we may not want this for just checking.
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
function_kind: FunctionKind::from_env(),
|
||||
// TODO: expose this from CLI?
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
|
|
|
@ -98,7 +98,7 @@ fn generateObjectFile(
|
|||
|
||||
const obj_file = obj.getEmittedBin();
|
||||
|
||||
var suffix =
|
||||
const suffix =
|
||||
if (target.os_tag == .windows)
|
||||
"obj"
|
||||
else
|
||||
|
|
|
@ -84,12 +84,12 @@ pub const RocList = extern struct {
|
|||
return true;
|
||||
}
|
||||
|
||||
pub fn fromSlice(comptime T: type, slice: []const T) RocList {
|
||||
pub fn fromSlice(comptime T: type, slice: []const T, elements_refcounted: bool) RocList {
|
||||
if (slice.len == 0) {
|
||||
return RocList.empty();
|
||||
}
|
||||
|
||||
var list = allocate(@alignOf(T), slice.len, @sizeOf(T));
|
||||
const list = allocate(@alignOf(T), slice.len, @sizeOf(T), elements_refcounted);
|
||||
|
||||
if (slice.len > 0) {
|
||||
const dest = list.bytes orelse unreachable;
|
||||
|
@ -107,7 +107,7 @@ pub const RocList = extern struct {
|
|||
// The pointer is to just after the refcount.
|
||||
// For big lists, it just returns their bytes pointer.
|
||||
// For seamless slices, it returns the pointer stored in capacity_or_alloc_ptr.
|
||||
pub fn getAllocationPtr(self: RocList) ?[*]u8 {
|
||||
pub fn getAllocationDataPtr(self: RocList) ?[*]u8 {
|
||||
const list_alloc_ptr = @intFromPtr(self.bytes);
|
||||
const slice_alloc_ptr = self.capacity_or_alloc_ptr << 1;
|
||||
const slice_mask = self.seamlessSliceMask();
|
||||
|
@ -115,9 +115,60 @@ pub const RocList = extern struct {
|
|||
return @as(?[*]u8, @ptrFromInt(alloc_ptr));
|
||||
}
|
||||
|
||||
pub fn decref(self: RocList, alignment: u32) void {
|
||||
// This function is only valid if the list has refcounted elements.
|
||||
fn getAllocationElementCount(self: RocList) usize {
|
||||
if (self.isSeamlessSlice()) {
|
||||
// Seamless slices always refer to an underlying allocation.
|
||||
const alloc_ptr = self.getAllocationDataPtr() orelse unreachable;
|
||||
// - 1 is refcount.
|
||||
// - 2 is size on heap.
|
||||
const ptr = @as([*]usize, @ptrCast(@alignCast(alloc_ptr))) - 2;
|
||||
return ptr[0];
|
||||
} else {
|
||||
return self.length;
|
||||
}
|
||||
}
|
||||
|
||||
// This needs to be called when creating seamless slices from unique list.
|
||||
// It will put the allocation size on the heap to enable the seamless slice to free the underlying allocation.
|
||||
fn setAllocationElementCount(self: RocList, elements_refcounted: bool) void {
|
||||
if (elements_refcounted and !self.isSeamlessSlice()) {
|
||||
// - 1 is refcount.
|
||||
// - 2 is size on heap.
|
||||
const ptr = @as([*]usize, @alignCast(@ptrCast(self.getAllocationDataPtr()))) - 2;
|
||||
ptr[0] = self.length;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn incref(self: RocList, amount: isize, elements_refcounted: bool) void {
|
||||
// If the list is unique and not a seamless slice, the length needs to be store on the heap if the elements are refcounted.
|
||||
if (elements_refcounted and self.isUnique() and !self.isSeamlessSlice()) {
|
||||
if (self.getAllocationDataPtr()) |source| {
|
||||
// - 1 is refcount.
|
||||
// - 2 is size on heap.
|
||||
const ptr = @as([*]usize, @alignCast(@ptrCast(source))) - 2;
|
||||
ptr[0] = self.length;
|
||||
}
|
||||
}
|
||||
utils.increfDataPtrC(self.getAllocationDataPtr(), amount);
|
||||
}
|
||||
|
||||
pub fn decref(self: RocList, alignment: u32, element_width: usize, elements_refcounted: bool, dec: Dec) void {
|
||||
// If unique, decref will free the list. Before that happens, all elements must be decremented.
|
||||
if (elements_refcounted and self.isUnique()) {
|
||||
if (self.getAllocationDataPtr()) |source| {
|
||||
const count = self.getAllocationElementCount();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
const element = source + i * element_width;
|
||||
dec(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use the raw capacity to ensure we always decrement the refcount of seamless slices.
|
||||
utils.decref(self.getAllocationPtr(), self.capacity_or_alloc_ptr, alignment);
|
||||
utils.decref(self.getAllocationDataPtr(), self.capacity_or_alloc_ptr, alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
pub fn elements(self: RocList, comptime T: type) ?[*]T {
|
||||
|
@ -134,7 +185,7 @@ pub const RocList = extern struct {
|
|||
return utils.REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
const ptr: [*]usize = @as([*]usize, @ptrCast(@alignCast(self.bytes)));
|
||||
const ptr: [*]usize = @as([*]usize, @ptrCast(@alignCast(self.getAllocationDataPtr())));
|
||||
return (ptr - 1)[0];
|
||||
}
|
||||
|
||||
|
@ -142,15 +193,22 @@ pub const RocList = extern struct {
|
|||
return self.refcountMachine() - utils.REFCOUNT_ONE + 1;
|
||||
}
|
||||
|
||||
pub fn makeUniqueExtra(self: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) RocList {
|
||||
pub fn makeUniqueExtra(self: RocList, alignment: u32, element_width: usize, elements_refcounted: bool, dec: Dec, update_mode: UpdateMode) RocList {
|
||||
if (update_mode == .InPlace) {
|
||||
return self;
|
||||
} else {
|
||||
return self.makeUnique(alignment, element_width);
|
||||
return self.makeUnique(alignment, element_width, elements_refcounted, dec);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeUnique(self: RocList, alignment: u32, element_width: usize) RocList {
|
||||
pub fn makeUnique(
|
||||
self: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
) RocList {
|
||||
if (self.isUnique()) {
|
||||
return self;
|
||||
}
|
||||
|
@ -158,12 +216,12 @@ pub const RocList = extern struct {
|
|||
if (self.isEmpty()) {
|
||||
// Empty is not necessarily unique on it's own.
|
||||
// The list could have capacity and be shared.
|
||||
self.decref(alignment);
|
||||
self.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return RocList.empty();
|
||||
}
|
||||
|
||||
// unfortunately, we have to clone
|
||||
var new_list = RocList.allocate(alignment, self.length, element_width);
|
||||
const new_list = RocList.allocate(alignment, self.length, element_width, elements_refcounted);
|
||||
|
||||
var old_bytes: [*]u8 = @as([*]u8, @ptrCast(self.bytes));
|
||||
var new_bytes: [*]u8 = @as([*]u8, @ptrCast(new_list.bytes));
|
||||
|
@ -171,8 +229,15 @@ pub const RocList = extern struct {
|
|||
const number_of_bytes = self.len() * element_width;
|
||||
@memcpy(new_bytes[0..number_of_bytes], old_bytes[0..number_of_bytes]);
|
||||
|
||||
// NOTE we fuse an increment of all keys/values with a decrement of the input list.
|
||||
self.decref(alignment);
|
||||
// Increment refcount of all elements now in a new list.
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < self.len()) : (i += 1) {
|
||||
inc(new_bytes + i * element_width);
|
||||
}
|
||||
}
|
||||
|
||||
self.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return new_list;
|
||||
}
|
||||
|
@ -181,6 +246,7 @@ pub const RocList = extern struct {
|
|||
alignment: u32,
|
||||
length: usize,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
) RocList {
|
||||
if (length == 0) {
|
||||
return empty();
|
||||
|
@ -189,7 +255,7 @@ pub const RocList = extern struct {
|
|||
const capacity = utils.calculateCapacity(0, length, element_width);
|
||||
const data_bytes = capacity * element_width;
|
||||
return RocList{
|
||||
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
|
||||
.bytes = utils.allocateWithRefcount(data_bytes, alignment, elements_refcounted),
|
||||
.length = length,
|
||||
.capacity_or_alloc_ptr = capacity,
|
||||
};
|
||||
|
@ -199,6 +265,7 @@ pub const RocList = extern struct {
|
|||
alignment: u32,
|
||||
length: usize,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
) RocList {
|
||||
if (length == 0) {
|
||||
return empty();
|
||||
|
@ -206,7 +273,7 @@ pub const RocList = extern struct {
|
|||
|
||||
const data_bytes = length * element_width;
|
||||
return RocList{
|
||||
.bytes = utils.allocateWithRefcount(data_bytes, alignment),
|
||||
.bytes = utils.allocateWithRefcount(data_bytes, alignment, elements_refcounted),
|
||||
.length = length,
|
||||
.capacity_or_alloc_ptr = length,
|
||||
};
|
||||
|
@ -217,6 +284,8 @@ pub const RocList = extern struct {
|
|||
alignment: u32,
|
||||
new_length: usize,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
) RocList {
|
||||
if (self.bytes) |source_ptr| {
|
||||
if (self.isUnique() and !self.isSeamlessSlice()) {
|
||||
|
@ -225,13 +294,13 @@ pub const RocList = extern struct {
|
|||
return RocList{ .bytes = self.bytes, .length = new_length, .capacity_or_alloc_ptr = capacity };
|
||||
} else {
|
||||
const new_capacity = utils.calculateCapacity(capacity, new_length, element_width);
|
||||
const new_source = utils.unsafeReallocate(source_ptr, alignment, capacity, new_capacity, element_width);
|
||||
const new_source = utils.unsafeReallocate(source_ptr, alignment, capacity, new_capacity, element_width, elements_refcounted);
|
||||
return RocList{ .bytes = new_source, .length = new_length, .capacity_or_alloc_ptr = new_capacity };
|
||||
}
|
||||
}
|
||||
return self.reallocateFresh(alignment, new_length, element_width);
|
||||
return self.reallocateFresh(alignment, new_length, element_width, elements_refcounted, inc);
|
||||
}
|
||||
return RocList.allocate(alignment, new_length, element_width);
|
||||
return RocList.allocate(alignment, new_length, element_width, elements_refcounted);
|
||||
}
|
||||
|
||||
/// reallocate by explicitly making a new allocation and copying elements over
|
||||
|
@ -240,244 +309,52 @@ pub const RocList = extern struct {
|
|||
alignment: u32,
|
||||
new_length: usize,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
) RocList {
|
||||
const old_length = self.length;
|
||||
|
||||
const result = RocList.allocate(alignment, new_length, element_width);
|
||||
const result = RocList.allocate(alignment, new_length, element_width, elements_refcounted);
|
||||
|
||||
// transfer the memory
|
||||
if (self.bytes) |source_ptr| {
|
||||
// transfer the memory
|
||||
const dest_ptr = result.bytes orelse unreachable;
|
||||
|
||||
@memcpy(dest_ptr[0..(old_length * element_width)], source_ptr[0..(old_length * element_width)]);
|
||||
@memset(dest_ptr[(old_length * element_width)..(new_length * element_width)], 0);
|
||||
|
||||
// Increment refcount of all elements now in a new list.
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < old_length) : (i += 1) {
|
||||
inc(dest_ptr + i * element_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.decref(alignment);
|
||||
// Calls utils.decref directly to avoid decrementing the refcount of elements.
|
||||
utils.decref(self.getAllocationDataPtr(), self.capacity_or_alloc_ptr, alignment, elements_refcounted);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
const Caller0 = *const fn (?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
const Caller1 = *const fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
const Caller2 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
const Caller3 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
const Caller4 = *const fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||
|
||||
pub fn listMap(
|
||||
list: RocList,
|
||||
caller: Caller1,
|
||||
data: Opaque,
|
||||
inc_n_data: IncN,
|
||||
data_is_owned: bool,
|
||||
alignment: u32,
|
||||
old_element_width: usize,
|
||||
new_element_width: usize,
|
||||
) callconv(.C) RocList {
|
||||
if (list.bytes) |source_ptr| {
|
||||
const size = list.len();
|
||||
var i: usize = 0;
|
||||
const output = RocList.allocate(alignment, size, new_element_width);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, size);
|
||||
}
|
||||
|
||||
while (i < size) : (i += 1) {
|
||||
caller(data, source_ptr + (i * old_element_width), target_ptr + (i * new_element_width));
|
||||
}
|
||||
|
||||
return output;
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
pub fn listIncref(list: RocList, amount: isize, elements_refcounted: bool) callconv(.C) void {
|
||||
list.incref(amount, elements_refcounted);
|
||||
}
|
||||
|
||||
fn decrementTail(list: RocList, start_index: usize, element_width: usize, dec: Dec) void {
|
||||
if (list.bytes) |source| {
|
||||
var i = start_index;
|
||||
while (i < list.len()) : (i += 1) {
|
||||
const element = source + i * element_width;
|
||||
dec(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listMap2(
|
||||
list1: RocList,
|
||||
list2: RocList,
|
||||
caller: Caller2,
|
||||
data: Opaque,
|
||||
inc_n_data: IncN,
|
||||
data_is_owned: bool,
|
||||
alignment: u32,
|
||||
a_width: usize,
|
||||
b_width: usize,
|
||||
c_width: usize,
|
||||
dec_a: Dec,
|
||||
dec_b: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const output_length = @min(list1.len(), list2.len());
|
||||
|
||||
// if the lists don't have equal length, we must consume the remaining elements
|
||||
// In this case we consume by (recursively) decrementing the elements
|
||||
decrementTail(list1, output_length, a_width, dec_a);
|
||||
decrementTail(list2, output_length, b_width, dec_b);
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, output_length);
|
||||
}
|
||||
|
||||
if (list1.bytes) |source_a| {
|
||||
if (list2.bytes) |source_b| {
|
||||
const output = RocList.allocate(alignment, output_length, c_width);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < output_length) : (i += 1) {
|
||||
const element_a = source_a + i * a_width;
|
||||
const element_b = source_b + i * b_width;
|
||||
const target = target_ptr + i * c_width;
|
||||
caller(data, element_a, element_b, target);
|
||||
}
|
||||
|
||||
return output;
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listMap3(
|
||||
list1: RocList,
|
||||
list2: RocList,
|
||||
list3: RocList,
|
||||
caller: Caller3,
|
||||
data: Opaque,
|
||||
inc_n_data: IncN,
|
||||
data_is_owned: bool,
|
||||
alignment: u32,
|
||||
a_width: usize,
|
||||
b_width: usize,
|
||||
c_width: usize,
|
||||
d_width: usize,
|
||||
dec_a: Dec,
|
||||
dec_b: Dec,
|
||||
dec_c: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const smaller_length = @min(list1.len(), list2.len());
|
||||
const output_length = @min(smaller_length, list3.len());
|
||||
|
||||
decrementTail(list1, output_length, a_width, dec_a);
|
||||
decrementTail(list2, output_length, b_width, dec_b);
|
||||
decrementTail(list3, output_length, c_width, dec_c);
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, output_length);
|
||||
}
|
||||
|
||||
if (list1.bytes) |source_a| {
|
||||
if (list2.bytes) |source_b| {
|
||||
if (list3.bytes) |source_c| {
|
||||
const output = RocList.allocate(alignment, output_length, d_width);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < output_length) : (i += 1) {
|
||||
const element_a = source_a + i * a_width;
|
||||
const element_b = source_b + i * b_width;
|
||||
const element_c = source_c + i * c_width;
|
||||
const target = target_ptr + i * d_width;
|
||||
|
||||
caller(data, element_a, element_b, element_c, target);
|
||||
}
|
||||
|
||||
return output;
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listMap4(
|
||||
list1: RocList,
|
||||
list2: RocList,
|
||||
list3: RocList,
|
||||
list4: RocList,
|
||||
caller: Caller4,
|
||||
data: Opaque,
|
||||
inc_n_data: IncN,
|
||||
data_is_owned: bool,
|
||||
alignment: u32,
|
||||
a_width: usize,
|
||||
b_width: usize,
|
||||
c_width: usize,
|
||||
d_width: usize,
|
||||
e_width: usize,
|
||||
dec_a: Dec,
|
||||
dec_b: Dec,
|
||||
dec_c: Dec,
|
||||
dec_d: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const output_length = @min(@min(list1.len(), list2.len()), @min(list3.len(), list4.len()));
|
||||
|
||||
decrementTail(list1, output_length, a_width, dec_a);
|
||||
decrementTail(list2, output_length, b_width, dec_b);
|
||||
decrementTail(list3, output_length, c_width, dec_c);
|
||||
decrementTail(list4, output_length, d_width, dec_d);
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, output_length);
|
||||
}
|
||||
|
||||
if (list1.bytes) |source_a| {
|
||||
if (list2.bytes) |source_b| {
|
||||
if (list3.bytes) |source_c| {
|
||||
if (list4.bytes) |source_d| {
|
||||
const output = RocList.allocate(alignment, output_length, e_width);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < output_length) : (i += 1) {
|
||||
const element_a = source_a + i * a_width;
|
||||
const element_b = source_b + i * b_width;
|
||||
const element_c = source_c + i * c_width;
|
||||
const element_d = source_d + i * d_width;
|
||||
const target = target_ptr + i * e_width;
|
||||
|
||||
caller(data, element_a, element_b, element_c, element_d, target);
|
||||
}
|
||||
|
||||
return output;
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
} else {
|
||||
return RocList.empty();
|
||||
}
|
||||
pub fn listDecref(list: RocList, alignment: u32, element_width: usize, elements_refcounted: bool, dec: Dec) callconv(.C) void {
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
}
|
||||
|
||||
pub fn listWithCapacity(
|
||||
capacity: u64,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
) callconv(.C) RocList {
|
||||
return listReserve(RocList.empty(), alignment, capacity, element_width, .InPlace);
|
||||
return listReserve(RocList.empty(), alignment, capacity, element_width, elements_refcounted, inc, .InPlace);
|
||||
}
|
||||
|
||||
pub fn listReserve(
|
||||
|
@ -485,6 +362,8 @@ pub fn listReserve(
|
|||
alignment: u32,
|
||||
spare: u64,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
update_mode: UpdateMode,
|
||||
) callconv(.C) RocList {
|
||||
const original_len = list.len();
|
||||
|
@ -497,7 +376,7 @@ pub fn listReserve(
|
|||
// Make sure on 32-bit targets we don't accidentally wrap when we cast our U64 desired capacity to U32.
|
||||
const reserve_size: u64 = @min(desired_cap, @as(u64, @intCast(std.math.maxInt(usize))));
|
||||
|
||||
var output = list.reallocate(alignment, @as(usize, @intCast(reserve_size)), element_width);
|
||||
var output = list.reallocate(alignment, @as(usize, @intCast(reserve_size)), element_width, elements_refcounted, inc);
|
||||
output.length = original_len;
|
||||
return output;
|
||||
}
|
||||
|
@ -507,6 +386,9 @@ pub fn listReleaseExcessCapacity(
|
|||
list: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
update_mode: UpdateMode,
|
||||
) callconv(.C) RocList {
|
||||
const old_length = list.len();
|
||||
|
@ -514,16 +396,27 @@ pub fn listReleaseExcessCapacity(
|
|||
if ((update_mode == .InPlace or list.isUnique()) and list.capacity_or_alloc_ptr == old_length) {
|
||||
return list;
|
||||
} else if (old_length == 0) {
|
||||
list.decref(alignment);
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return RocList.empty();
|
||||
} else {
|
||||
var output = RocList.allocateExact(alignment, old_length, element_width);
|
||||
// TODO: This can be made more efficient, but has to work around the `decref`.
|
||||
// If the list is unique, we can avoid incrementing and decrementing the live items.
|
||||
// We can just decrement the dead elements and free the old list.
|
||||
// This pattern is also like true in other locations like listConcat and listDropAt.
|
||||
const output = RocList.allocateExact(alignment, old_length, element_width, elements_refcounted);
|
||||
if (list.bytes) |source_ptr| {
|
||||
const dest_ptr = output.bytes orelse unreachable;
|
||||
|
||||
@memcpy(dest_ptr[0..(old_length * element_width)], source_ptr[0..(old_length * element_width)]);
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < old_length) : (i += 1) {
|
||||
const element = source_ptr + i * element_width;
|
||||
inc(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
list.decref(alignment);
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
@ -547,15 +440,30 @@ pub fn listAppendUnsafe(
|
|||
return output;
|
||||
}
|
||||
|
||||
fn listAppend(list: RocList, alignment: u32, element: Opaque, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList {
|
||||
const with_capacity = listReserve(list, alignment, 1, element_width, update_mode);
|
||||
fn listAppend(
|
||||
list: RocList,
|
||||
alignment: u32,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
update_mode: UpdateMode,
|
||||
) callconv(.C) RocList {
|
||||
const with_capacity = listReserve(list, alignment, 1, element_width, elements_refcounted, inc, update_mode);
|
||||
return listAppendUnsafe(with_capacity, element, element_width);
|
||||
}
|
||||
|
||||
pub fn listPrepend(list: RocList, alignment: u32, element: Opaque, element_width: usize) callconv(.C) RocList {
|
||||
pub fn listPrepend(
|
||||
list: RocList,
|
||||
alignment: u32,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
) callconv(.C) RocList {
|
||||
const old_length = list.len();
|
||||
// TODO: properly wire in update mode.
|
||||
var with_capacity = listReserve(list, alignment, 1, element_width, .Immutable);
|
||||
var with_capacity = listReserve(list, alignment, 1, element_width, elements_refcounted, inc, .Immutable);
|
||||
with_capacity.length += 1;
|
||||
|
||||
// can't use one memcpy here because source and target overlap
|
||||
|
@ -586,8 +494,15 @@ pub fn listSwap(
|
|||
element_width: usize,
|
||||
index_1: u64,
|
||||
index_2: u64,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
update_mode: UpdateMode,
|
||||
) callconv(.C) RocList {
|
||||
// Early exit to avoid swapping the same element.
|
||||
if (index_1 == index_2)
|
||||
return list;
|
||||
|
||||
const size = @as(u64, @intCast(list.len()));
|
||||
if (index_1 == index_2 or index_1 >= size or index_2 >= size) {
|
||||
// Either one index was out of bounds, or both indices were the same; just return
|
||||
|
@ -598,7 +513,7 @@ pub fn listSwap(
|
|||
if (update_mode == .InPlace) {
|
||||
break :blk list;
|
||||
} else {
|
||||
break :blk list.makeUnique(alignment, element_width);
|
||||
break :blk list.makeUnique(alignment, element_width, elements_refcounted, inc, dec);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -616,26 +531,30 @@ pub fn listSublist(
|
|||
list: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
start_u64: u64,
|
||||
len_u64: u64,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const size = list.len();
|
||||
if (size == 0 or start_u64 >= @as(u64, @intCast(size))) {
|
||||
// Decrement the reference counts of all elements.
|
||||
if (list.bytes) |source_ptr| {
|
||||
var i: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
const element = source_ptr + i * element_width;
|
||||
dec(element);
|
||||
}
|
||||
}
|
||||
if (size == 0 or len_u64 == 0 or start_u64 >= @as(u64, @intCast(size))) {
|
||||
if (list.isUnique()) {
|
||||
// Decrement the reference counts of all elements.
|
||||
if (list.bytes) |source_ptr| {
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < size) : (i += 1) {
|
||||
const element = source_ptr + i * element_width;
|
||||
dec(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var output = list;
|
||||
output.length = 0;
|
||||
return output;
|
||||
}
|
||||
list.decref(alignment);
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return RocList.empty();
|
||||
}
|
||||
|
||||
|
@ -643,7 +562,6 @@ pub fn listSublist(
|
|||
// This cast is lossless because we would have early-returned already
|
||||
// if `start_u64` were greater than `size`, and `size` fits in usize.
|
||||
const start: usize = @intCast(start_u64);
|
||||
const drop_start_len = start;
|
||||
|
||||
// (size - start) can't overflow because we would have early-returned already
|
||||
// if `start` were greater than `size`.
|
||||
|
@ -654,32 +572,25 @@ pub fn listSublist(
|
|||
// than something that fit in usize.
|
||||
const keep_len = @as(usize, @intCast(@min(len_u64, @as(u64, @intCast(size_minus_start)))));
|
||||
|
||||
// This can't overflow because if len > size_minus_start,
|
||||
// then keep_len == size_minus_start and this will be 0.
|
||||
// Alternatively, if len <= size_minus_start, then keep_len will
|
||||
// be equal to len, meaning keep_len <= size_minus_start too,
|
||||
// which in turn means this won't overflow.
|
||||
const drop_end_len = size_minus_start - keep_len;
|
||||
|
||||
// Decrement the reference counts of elements before `start`.
|
||||
var i: usize = 0;
|
||||
while (i < drop_start_len) : (i += 1) {
|
||||
const element = source_ptr + i * element_width;
|
||||
dec(element);
|
||||
}
|
||||
|
||||
// Decrement the reference counts of elements after `start + keep_len`.
|
||||
i = 0;
|
||||
while (i < drop_end_len) : (i += 1) {
|
||||
const element = source_ptr + (start + keep_len + i) * element_width;
|
||||
dec(element);
|
||||
}
|
||||
|
||||
if (start == 0 and list.isUnique()) {
|
||||
// The list is unique, we actually have to decrement refcounts to elements we aren't keeping around.
|
||||
// Decrement the reference counts of elements after `start + keep_len`.
|
||||
if (elements_refcounted) {
|
||||
const drop_end_len = size_minus_start - keep_len;
|
||||
var i: usize = 0;
|
||||
while (i < drop_end_len) : (i += 1) {
|
||||
const element = source_ptr + (start + keep_len + i) * element_width;
|
||||
dec(element);
|
||||
}
|
||||
}
|
||||
|
||||
var output = list;
|
||||
output.length = keep_len;
|
||||
return output;
|
||||
} else {
|
||||
if (list.isUnique()) {
|
||||
list.setAllocationElementCount(elements_refcounted);
|
||||
}
|
||||
const list_alloc_ptr = (@intFromPtr(source_ptr) >> 1) | SEAMLESS_SLICE_BIT;
|
||||
const slice_alloc_ptr = list.capacity_or_alloc_ptr;
|
||||
const slice_mask = list.seamlessSliceMask();
|
||||
|
@ -699,7 +610,9 @@ pub fn listDropAt(
|
|||
list: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
drop_index_u64: u64,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const size = list.len();
|
||||
|
@ -708,11 +621,11 @@ pub fn listDropAt(
|
|||
// For simplicity, do this by calling listSublist.
|
||||
// In the future, we can test if it is faster to manually inline the important parts here.
|
||||
if (drop_index_u64 == 0) {
|
||||
return listSublist(list, alignment, element_width, 1, size -| 1, dec);
|
||||
return listSublist(list, alignment, element_width, elements_refcounted, 1, size -| 1, dec);
|
||||
} else if (drop_index_u64 == size_u64 - 1) { // It's fine if (size - 1) wraps on size == 0 here,
|
||||
// because if size is 0 then it's always fine for this branch to be taken; no
|
||||
// matter what drop_index was, we're size == 0, so empty list will always be returned.
|
||||
return listSublist(list, alignment, element_width, 0, size -| 1, dec);
|
||||
return listSublist(list, alignment, element_width, elements_refcounted, 0, size -| 1, dec);
|
||||
}
|
||||
|
||||
if (list.bytes) |source_ptr| {
|
||||
|
@ -724,15 +637,17 @@ pub fn listDropAt(
|
|||
// were >= than `size`, and we know `size` fits in usize.
|
||||
const drop_index: usize = @intCast(drop_index_u64);
|
||||
|
||||
const element = source_ptr + drop_index * element_width;
|
||||
dec(element);
|
||||
if (elements_refcounted) {
|
||||
const element = source_ptr + drop_index * element_width;
|
||||
dec(element);
|
||||
}
|
||||
|
||||
// NOTE
|
||||
// we need to return an empty list explicitly,
|
||||
// because we rely on the pointer field being null if the list is empty
|
||||
// which also requires duplicating the utils.decref call to spend the RC token
|
||||
if (size < 2) {
|
||||
list.decref(alignment);
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return RocList.empty();
|
||||
}
|
||||
|
||||
|
@ -751,7 +666,7 @@ pub fn listDropAt(
|
|||
return new_list;
|
||||
}
|
||||
|
||||
const output = RocList.allocate(alignment, size - 1, element_width);
|
||||
const output = RocList.allocate(alignment, size - 1, element_width, elements_refcounted);
|
||||
const target_ptr = output.bytes orelse unreachable;
|
||||
|
||||
const head_size = drop_index * element_width;
|
||||
|
@ -762,7 +677,15 @@ pub fn listDropAt(
|
|||
const tail_size = (size - drop_index - 1) * element_width;
|
||||
@memcpy(tail_target[0..tail_size], tail_source[0..tail_size]);
|
||||
|
||||
list.decref(alignment);
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < output.len()) : (i += 1) {
|
||||
const cloned_elem = target_ptr + i * element_width;
|
||||
inc(cloned_elem);
|
||||
}
|
||||
}
|
||||
|
||||
list.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return output;
|
||||
} else {
|
||||
|
@ -812,8 +735,11 @@ pub fn listSortWith(
|
|||
data_is_owned: bool,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
var list = input.makeUnique(alignment, element_width);
|
||||
var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec);
|
||||
|
||||
if (data_is_owned) {
|
||||
inc_n_data(data, list.len());
|
||||
|
@ -845,7 +771,7 @@ fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void {
|
|||
var ptr2 = p2;
|
||||
|
||||
var buffer_actual: [threshold]u8 = undefined;
|
||||
var buffer: [*]u8 = buffer_actual[0..];
|
||||
const buffer: [*]u8 = buffer_actual[0..];
|
||||
|
||||
while (true) {
|
||||
if (width < threshold) {
|
||||
|
@ -863,43 +789,60 @@ fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void {
|
|||
}
|
||||
|
||||
fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void {
|
||||
var element_at_i = source_ptr + (index_1 * element_width);
|
||||
var element_at_j = source_ptr + (index_2 * element_width);
|
||||
const element_at_i = source_ptr + (index_1 * element_width);
|
||||
const element_at_j = source_ptr + (index_2 * element_width);
|
||||
|
||||
return swap(element_width, element_at_i, element_at_j);
|
||||
}
|
||||
|
||||
pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_width: usize) callconv(.C) RocList {
|
||||
pub fn listConcat(
|
||||
list_a: RocList,
|
||||
list_b: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
// NOTE we always use list_a! because it is owned, we must consume it, and it may have unused capacity
|
||||
if (list_b.isEmpty()) {
|
||||
if (list_a.getCapacity() == 0) {
|
||||
// a could be a seamless slice, so we still need to decref.
|
||||
list_a.decref(alignment);
|
||||
list_a.decref(alignment, element_width, elements_refcounted, dec);
|
||||
return list_b;
|
||||
} else {
|
||||
// we must consume this list. Even though it has no elements, it could still have capacity
|
||||
list_b.decref(alignment);
|
||||
list_b.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return list_a;
|
||||
}
|
||||
} else if (list_a.isUnique()) {
|
||||
const total_length: usize = list_a.len() + list_b.len();
|
||||
|
||||
const resized_list_a = list_a.reallocate(alignment, total_length, element_width);
|
||||
const resized_list_a = list_a.reallocate(alignment, total_length, element_width, elements_refcounted, inc);
|
||||
|
||||
// These must exist, otherwise, the lists would have been empty.
|
||||
const source_a = resized_list_a.bytes orelse unreachable;
|
||||
const source_b = list_b.bytes orelse unreachable;
|
||||
@memcpy(source_a[(list_a.len() * element_width)..(total_length * element_width)], source_b[0..(list_b.len() * element_width)]);
|
||||
|
||||
// Increment refcount of all cloned elements.
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < list_b.len()) : (i += 1) {
|
||||
const cloned_elem = source_b + i * element_width;
|
||||
inc(cloned_elem);
|
||||
}
|
||||
}
|
||||
|
||||
// decrement list b.
|
||||
list_b.decref(alignment);
|
||||
list_b.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return resized_list_a;
|
||||
} else if (list_b.isUnique()) {
|
||||
const total_length: usize = list_a.len() + list_b.len();
|
||||
|
||||
const resized_list_b = list_b.reallocate(alignment, total_length, element_width);
|
||||
const resized_list_b = list_b.reallocate(alignment, total_length, element_width, elements_refcounted, inc);
|
||||
|
||||
// These must exist, otherwise, the lists would have been empty.
|
||||
const source_a = list_a.bytes orelse unreachable;
|
||||
|
@ -913,14 +856,23 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
|
|||
mem.copyBackwards(u8, source_b[byte_count_a .. byte_count_a + byte_count_b], source_b[0..byte_count_b]);
|
||||
@memcpy(source_b[0..byte_count_a], source_a[0..byte_count_a]);
|
||||
|
||||
// Increment refcount of all cloned elements.
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < list_a.len()) : (i += 1) {
|
||||
const cloned_elem = source_a + i * element_width;
|
||||
inc(cloned_elem);
|
||||
}
|
||||
}
|
||||
|
||||
// decrement list a.
|
||||
list_a.decref(alignment);
|
||||
list_a.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return resized_list_b;
|
||||
}
|
||||
const total_length: usize = list_a.len() + list_b.len();
|
||||
|
||||
const output = RocList.allocate(alignment, total_length, element_width);
|
||||
const output = RocList.allocate(alignment, total_length, element_width, elements_refcounted);
|
||||
|
||||
// These must exist, otherwise, the lists would have been empty.
|
||||
const target = output.bytes orelse unreachable;
|
||||
|
@ -930,9 +882,23 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
|
|||
@memcpy(target[0..(list_a.len() * element_width)], source_a[0..(list_a.len() * element_width)]);
|
||||
@memcpy(target[(list_a.len() * element_width)..(total_length * element_width)], source_b[0..(list_b.len() * element_width)]);
|
||||
|
||||
// Increment refcount of all cloned elements.
|
||||
if (elements_refcounted) {
|
||||
var i: usize = 0;
|
||||
while (i < list_a.len()) : (i += 1) {
|
||||
const cloned_elem = source_a + i * element_width;
|
||||
inc(cloned_elem);
|
||||
}
|
||||
i = 0;
|
||||
while (i < list_b.len()) : (i += 1) {
|
||||
const cloned_elem = source_b + i * element_width;
|
||||
inc(cloned_elem);
|
||||
}
|
||||
}
|
||||
|
||||
// decrement list a and b.
|
||||
list_a.decref(alignment);
|
||||
list_b.decref(alignment);
|
||||
list_a.decref(alignment, element_width, elements_refcounted, dec);
|
||||
list_b.decref(alignment, element_width, elements_refcounted, dec);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -960,6 +926,9 @@ pub fn listReplace(
|
|||
index: u64,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
out_element: ?[*]u8,
|
||||
) callconv(.C) RocList {
|
||||
// INVARIANT: bounds checking happens on the roc side
|
||||
|
@ -969,7 +938,8 @@ pub fn listReplace(
|
|||
// so we don't do a bounds check here. Hence, the list is also non-empty,
|
||||
// because inserting into an empty list is always out of bounds,
|
||||
// and it's always safe to cast index to usize.
|
||||
return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width), @as(usize, @intCast(index)), element, element_width, out_element);
|
||||
// because inserting into an empty list is always out of bounds
|
||||
return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width, elements_refcounted, inc, dec), @as(usize, @intCast(index)), element, element_width, out_element);
|
||||
}
|
||||
|
||||
inline fn listReplaceInPlaceHelp(
|
||||
|
@ -1001,8 +971,11 @@ pub fn listClone(
|
|||
list: RocList,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
inc: Inc,
|
||||
dec: Dec,
|
||||
) callconv(.C) RocList {
|
||||
return list.makeUnique(alignment, element_width);
|
||||
return list.makeUnique(alignment, element_width, elements_refcounted, inc, dec);
|
||||
}
|
||||
|
||||
pub fn listCapacity(
|
||||
|
@ -1014,23 +987,25 @@ pub fn listCapacity(
|
|||
pub fn listAllocationPtr(
|
||||
list: RocList,
|
||||
) callconv(.C) ?[*]u8 {
|
||||
return list.getAllocationPtr();
|
||||
return list.getAllocationDataPtr();
|
||||
}
|
||||
|
||||
fn rcNone(_: ?[*]u8) callconv(.C) void {}
|
||||
|
||||
test "listConcat: non-unique with unique overlapping" {
|
||||
var nonUnique = RocList.fromSlice(u8, ([_]u8{1})[0..]);
|
||||
var bytes: [*]u8 = @as([*]u8, @ptrCast(nonUnique.bytes));
|
||||
var nonUnique = RocList.fromSlice(u8, ([_]u8{1})[0..], false);
|
||||
const bytes: [*]u8 = @as([*]u8, @ptrCast(nonUnique.bytes));
|
||||
const ptr_width = @sizeOf(usize);
|
||||
const refcount_ptr = @as([*]isize, @ptrCast(@as([*]align(ptr_width) u8, @alignCast(bytes)) - ptr_width));
|
||||
utils.increfRcPtrC(&refcount_ptr[0], 1);
|
||||
defer nonUnique.decref(@sizeOf(u8)); // listConcat will dec the other refcount
|
||||
defer nonUnique.decref(@alignOf(u8), @sizeOf(u8), false, rcNone); // listConcat will dec the other refcount
|
||||
|
||||
var unique = RocList.fromSlice(u8, ([_]u8{ 2, 3, 4 })[0..]);
|
||||
defer unique.decref(@sizeOf(u8));
|
||||
var unique = RocList.fromSlice(u8, ([_]u8{ 2, 3, 4 })[0..], false);
|
||||
defer unique.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
|
||||
|
||||
var concatted = listConcat(nonUnique, unique, 1, 1);
|
||||
var wanted = RocList.fromSlice(u8, ([_]u8{ 1, 2, 3, 4 })[0..]);
|
||||
defer wanted.decref(@sizeOf(u8));
|
||||
var concatted = listConcat(nonUnique, unique, 1, 1, false, rcNone, rcNone);
|
||||
var wanted = RocList.fromSlice(u8, ([_]u8{ 1, 2, 3, 4 })[0..], false);
|
||||
defer wanted.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
|
||||
|
||||
try expect(concatted.eql(wanted));
|
||||
}
|
||||
|
@ -1045,7 +1020,7 @@ pub fn listConcatUtf8(
|
|||
const combined_length = list.len() + string.len();
|
||||
|
||||
// List U8 has alignment 1 and element_width 1
|
||||
var result = list.reallocate(1, combined_length, 1);
|
||||
const result = list.reallocate(1, combined_length, 1, false, &rcNone);
|
||||
// We just allocated combined_length, which is > 0 because string.len() > 0
|
||||
var bytes = result.bytes orelse unreachable;
|
||||
@memcpy(bytes[list.len()..combined_length], string.asU8ptr()[0..string.len()]);
|
||||
|
@ -1055,13 +1030,13 @@ pub fn listConcatUtf8(
|
|||
}
|
||||
|
||||
test "listConcatUtf8" {
|
||||
const list = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4 });
|
||||
defer list.decref(1);
|
||||
const list = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4 }, false);
|
||||
defer list.decref(1, 1, false, &rcNone);
|
||||
const string_bytes = "🐦";
|
||||
const string = str.RocStr.init(string_bytes, string_bytes.len);
|
||||
defer string.decref();
|
||||
const ret = listConcatUtf8(list, string);
|
||||
const expected = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4, 240, 159, 144, 166 });
|
||||
defer expected.decref(1);
|
||||
const expected = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4, 240, 159, 144, 166 }, false);
|
||||
defer expected.decref(1, 1, false, &rcNone);
|
||||
try expect(ret.eql(expected));
|
||||
}
|
||||
|
|
|
@ -65,10 +65,6 @@ comptime {
|
|||
const list = @import("list.zig");
|
||||
|
||||
comptime {
|
||||
exportListFn(list.listMap, "map");
|
||||
exportListFn(list.listMap2, "map2");
|
||||
exportListFn(list.listMap3, "map3");
|
||||
exportListFn(list.listMap4, "map4");
|
||||
exportListFn(list.listAppendUnsafe, "append_unsafe");
|
||||
exportListFn(list.listReserve, "reserve");
|
||||
exportListFn(list.listPrepend, "prepend");
|
||||
|
@ -86,6 +82,8 @@ comptime {
|
|||
exportListFn(list.listAllocationPtr, "allocation_ptr");
|
||||
exportListFn(list.listReleaseExcessCapacity, "release_excess_capacity");
|
||||
exportListFn(list.listConcatUtf8, "concat_utf8");
|
||||
exportListFn(list.listIncref, "incref");
|
||||
exportListFn(list.listDecref, "decref");
|
||||
}
|
||||
|
||||
// Num Module
|
||||
|
|
|
@ -96,7 +96,7 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
fn allocateBig(length: usize, capacity: usize) RocStr {
|
||||
const first_element = utils.allocateWithRefcount(capacity, @sizeOf(usize));
|
||||
const first_element = utils.allocateWithRefcount(capacity, @sizeOf(usize), false);
|
||||
|
||||
return RocStr{
|
||||
.bytes = first_element,
|
||||
|
@ -172,7 +172,7 @@ pub const RocStr = extern struct {
|
|||
|
||||
pub fn decref(self: RocStr) void {
|
||||
if (!self.isSmallStr()) {
|
||||
utils.decref(self.getAllocationPtr(), self.capacity_or_alloc_ptr, RocStr.alignment);
|
||||
utils.decref(self.getAllocationPtr(), self.capacity_or_alloc_ptr, RocStr.alignment, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,6 +247,7 @@ pub const RocStr = extern struct {
|
|||
old_capacity,
|
||||
new_capacity,
|
||||
element_width,
|
||||
false,
|
||||
);
|
||||
|
||||
return RocStr{ .bytes = new_source, .length = new_length, .capacity_or_alloc_ptr = new_capacity };
|
||||
|
@ -600,7 +601,7 @@ fn strFromFloatHelp(comptime T: type, float: T) RocStr {
|
|||
// Str.split
|
||||
pub fn strSplit(string: RocStr, delimiter: RocStr) callconv(.C) RocList {
|
||||
const segment_count = countSegments(string, delimiter);
|
||||
const list = RocList.allocate(@alignOf(RocStr), segment_count, @sizeOf(RocStr));
|
||||
const list = RocList.allocate(@alignOf(RocStr), segment_count, @sizeOf(RocStr), true);
|
||||
|
||||
if (list.bytes) |bytes| {
|
||||
const strings = @as([*]RocStr, @ptrCast(@alignCast(bytes)));
|
||||
|
@ -1427,7 +1428,7 @@ inline fn strToBytes(arg: RocStr) RocList {
|
|||
if (length == 0) {
|
||||
return RocList.empty();
|
||||
} else if (arg.isSmallStr()) {
|
||||
const ptr = utils.allocateWithRefcount(length, RocStr.alignment);
|
||||
const ptr = utils.allocateWithRefcount(length, RocStr.alignment, false);
|
||||
|
||||
@memcpy(ptr[0..length], arg.asU8ptr()[0..length]);
|
||||
|
||||
|
@ -1457,7 +1458,7 @@ pub fn fromUtf8(
|
|||
update_mode: UpdateMode,
|
||||
) FromUtf8Result {
|
||||
if (list.len() == 0) {
|
||||
list.decref(1); // Alignment 1 for List U8
|
||||
list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
|
||||
return FromUtf8Result{
|
||||
.is_ok = true,
|
||||
.string = RocStr.empty(),
|
||||
|
@ -1479,7 +1480,7 @@ pub fn fromUtf8(
|
|||
} else {
|
||||
const temp = errorToProblem(bytes);
|
||||
|
||||
list.decref(1); // Alignment 1 for List U8
|
||||
list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
|
||||
|
||||
return FromUtf8Result{
|
||||
.is_ok = false,
|
||||
|
@ -1603,7 +1604,7 @@ fn expectOk(result: FromUtf8Result) !void {
|
|||
}
|
||||
|
||||
fn sliceHelp(bytes: [*]const u8, length: usize) RocList {
|
||||
var list = RocList.allocate(RocStr.alignment, length, @sizeOf(u8));
|
||||
var list = RocList.allocate(RocStr.alignment, length, @sizeOf(u8), false);
|
||||
var list_bytes = list.bytes orelse unreachable;
|
||||
@memcpy(list_bytes[0..length], bytes[0..length]);
|
||||
list.length = length;
|
||||
|
@ -1971,6 +1972,13 @@ fn countTrailingWhitespaceBytes(string: RocStr) usize {
|
|||
return byte_count;
|
||||
}
|
||||
|
||||
fn rcNone(_: ?[*]u8) callconv(.C) void {}
|
||||
|
||||
fn decStr(ptr: ?[*]u8) callconv(.C) void {
|
||||
const str_ptr = @as(*RocStr, @ptrCast(@alignCast(ptr orelse unreachable)));
|
||||
str_ptr.decref();
|
||||
}
|
||||
|
||||
/// A backwards version of Utf8View from std.unicode
|
||||
const ReverseUtf8View = struct {
|
||||
bytes: []const u8,
|
||||
|
|
|
@ -219,6 +219,7 @@ pub fn increfRcPtrC(ptr_to_refcount: *isize, amount: isize) callconv(.C) void {
|
|||
pub fn decrefRcPtrC(
|
||||
bytes_or_null: ?[*]isize,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) void {
|
||||
// IMPORTANT: bytes_or_null is this case is expected to be a pointer to the refcount
|
||||
// (NOT the start of the data, or the start of the allocation)
|
||||
|
@ -226,22 +227,24 @@ pub fn decrefRcPtrC(
|
|||
// this is of course unsafe, but we trust what we get from the llvm side
|
||||
var bytes = @as([*]isize, @ptrCast(bytes_or_null));
|
||||
|
||||
return @call(.always_inline, decref_ptr_to_refcount, .{ bytes, alignment });
|
||||
return @call(.always_inline, decref_ptr_to_refcount, .{ bytes, alignment, elements_refcounted });
|
||||
}
|
||||
|
||||
pub fn decrefCheckNullC(
|
||||
bytes_or_null: ?[*]u8,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) void {
|
||||
if (bytes_or_null) |bytes| {
|
||||
const isizes: [*]isize = @as([*]isize, @ptrCast(@alignCast(bytes)));
|
||||
return @call(.always_inline, decref_ptr_to_refcount, .{ isizes - 1, alignment });
|
||||
return @call(.always_inline, decref_ptr_to_refcount, .{ isizes - 1, alignment, elements_refcounted });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrefDataPtrC(
|
||||
bytes_or_null: ?[*]u8,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
|
||||
|
@ -252,7 +255,7 @@ pub fn decrefDataPtrC(
|
|||
const isizes: [*]isize = @as([*]isize, @ptrFromInt(unmasked_ptr));
|
||||
const rc_ptr = isizes - 1;
|
||||
|
||||
return decrefRcPtrC(rc_ptr, alignment);
|
||||
return decrefRcPtrC(rc_ptr, alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
pub fn increfDataPtrC(
|
||||
|
@ -273,6 +276,7 @@ pub fn increfDataPtrC(
|
|||
pub fn freeDataPtrC(
|
||||
bytes_or_null: ?[*]u8,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
|
||||
|
@ -283,21 +287,23 @@ pub fn freeDataPtrC(
|
|||
const isizes: [*]isize = @as([*]isize, @ptrFromInt(masked_ptr));
|
||||
|
||||
// we always store the refcount right before the data
|
||||
return freeRcPtrC(isizes - 1, alignment);
|
||||
return freeRcPtrC(isizes - 1, alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
pub fn freeRcPtrC(
|
||||
bytes_or_null: ?[*]isize,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
return free_ptr_to_refcount(bytes, alignment);
|
||||
return free_ptr_to_refcount(bytes, alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
pub fn decref(
|
||||
bytes_or_null: ?[*]u8,
|
||||
data_bytes: usize,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) void {
|
||||
if (data_bytes == 0) {
|
||||
return;
|
||||
|
@ -307,15 +313,18 @@ pub fn decref(
|
|||
|
||||
const isizes: [*]isize = @as([*]isize, @ptrCast(@alignCast(bytes)));
|
||||
|
||||
decref_ptr_to_refcount(isizes - 1, alignment);
|
||||
decref_ptr_to_refcount(isizes - 1, alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
inline fn free_ptr_to_refcount(
|
||||
refcount_ptr: [*]isize,
|
||||
alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) void {
|
||||
if (RC_TYPE == Refcount.none) return;
|
||||
const extra_bytes = @max(alignment, @sizeOf(usize));
|
||||
const ptr_width = @sizeOf(usize);
|
||||
const required_space: usize = if (elements_refcounted) (2 * ptr_width) else ptr_width;
|
||||
const extra_bytes = @max(required_space, alignment);
|
||||
const allocation_ptr = @as([*]u8, @ptrCast(refcount_ptr)) - (extra_bytes - @sizeOf(usize));
|
||||
|
||||
// NOTE: we don't even check whether the refcount is "infinity" here!
|
||||
|
@ -328,7 +337,8 @@ inline fn free_ptr_to_refcount(
|
|||
|
||||
inline fn decref_ptr_to_refcount(
|
||||
refcount_ptr: [*]isize,
|
||||
alignment: u32,
|
||||
element_alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) void {
|
||||
if (RC_TYPE == Refcount.none) return;
|
||||
|
||||
|
@ -336,6 +346,10 @@ inline fn decref_ptr_to_refcount(
|
|||
std.debug.print("| decrement {*}: ", .{refcount_ptr});
|
||||
}
|
||||
|
||||
// Due to RC alignmen tmust take into acount pointer size.
|
||||
const ptr_width = @sizeOf(usize);
|
||||
const alignment = @max(ptr_width, element_alignment);
|
||||
|
||||
// Ensure that the refcount is not whole program lifetime.
|
||||
const refcount: isize = refcount_ptr[0];
|
||||
if (refcount != REFCOUNT_MAX_ISIZE) {
|
||||
|
@ -353,13 +367,13 @@ inline fn decref_ptr_to_refcount(
|
|||
}
|
||||
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
free_ptr_to_refcount(refcount_ptr, alignment);
|
||||
free_ptr_to_refcount(refcount_ptr, alignment, elements_refcounted);
|
||||
}
|
||||
},
|
||||
Refcount.atomic => {
|
||||
var last = @atomicRmw(isize, &refcount_ptr[0], std.builtin.AtomicRmwOp.Sub, 1, Monotonic);
|
||||
if (last == REFCOUNT_ONE_ISIZE) {
|
||||
free_ptr_to_refcount(refcount_ptr, alignment);
|
||||
free_ptr_to_refcount(refcount_ptr, alignment, elements_refcounted);
|
||||
}
|
||||
},
|
||||
Refcount.none => unreachable,
|
||||
|
@ -438,17 +452,23 @@ pub inline fn calculateCapacity(
|
|||
pub fn allocateWithRefcountC(
|
||||
data_bytes: usize,
|
||||
element_alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) callconv(.C) [*]u8 {
|
||||
return allocateWithRefcount(data_bytes, element_alignment);
|
||||
return allocateWithRefcount(data_bytes, element_alignment, elements_refcounted);
|
||||
}
|
||||
|
||||
pub fn allocateWithRefcount(
|
||||
data_bytes: usize,
|
||||
element_alignment: u32,
|
||||
elements_refcounted: bool,
|
||||
) [*]u8 {
|
||||
// If the element type is refcounted, we need to also allocate space to store the element count on the heap.
|
||||
// This is used so that a seamless slice can de-allocate the underlying list type.
|
||||
const ptr_width = @sizeOf(usize);
|
||||
const alignment = @max(ptr_width, element_alignment);
|
||||
const length = alignment + data_bytes;
|
||||
const required_space: usize = if (elements_refcounted) (2 * ptr_width) else ptr_width;
|
||||
const extra_bytes = @max(required_space, element_alignment);
|
||||
const length = extra_bytes + data_bytes;
|
||||
|
||||
var new_bytes: [*]u8 = alloc(length, alignment) orelse unreachable;
|
||||
|
||||
|
@ -456,7 +476,7 @@ pub fn allocateWithRefcount(
|
|||
std.debug.print("+ allocated {*} ({} bytes with alignment {})\n", .{ new_bytes, data_bytes, alignment });
|
||||
}
|
||||
|
||||
const data_ptr = new_bytes + alignment;
|
||||
const data_ptr = new_bytes + extra_bytes;
|
||||
const refcount_ptr = @as([*]usize, @ptrCast(@as([*]align(ptr_width) u8, @alignCast(data_ptr)) - ptr_width));
|
||||
refcount_ptr[0] = if (RC_TYPE == Refcount.none) REFCOUNT_MAX_ISIZE else REFCOUNT_ONE;
|
||||
|
||||
|
@ -474,11 +494,14 @@ pub fn unsafeReallocate(
|
|||
old_length: usize,
|
||||
new_length: usize,
|
||||
element_width: usize,
|
||||
elements_refcounted: bool,
|
||||
) [*]u8 {
|
||||
const align_width: usize = @max(alignment, @sizeOf(usize));
|
||||
const ptr_width: usize = @sizeOf(usize);
|
||||
const required_space: usize = if (elements_refcounted) (2 * ptr_width) else ptr_width;
|
||||
const extra_bytes = @max(required_space, alignment);
|
||||
|
||||
const old_width = align_width + old_length * element_width;
|
||||
const new_width = align_width + new_length * element_width;
|
||||
const old_width = extra_bytes + old_length * element_width;
|
||||
const new_width = extra_bytes + new_length * element_width;
|
||||
|
||||
if (old_width >= new_width) {
|
||||
return source_ptr;
|
||||
|
@ -486,10 +509,10 @@ pub fn unsafeReallocate(
|
|||
|
||||
// TODO handle out of memory
|
||||
// NOTE realloc will dealloc the original allocation
|
||||
const old_allocation = source_ptr - align_width;
|
||||
const old_allocation = source_ptr - extra_bytes;
|
||||
const new_allocation = realloc(old_allocation, new_width, old_width, alignment);
|
||||
|
||||
const new_source = @as([*]u8, @ptrCast(new_allocation)) + align_width;
|
||||
const new_source = @as([*]u8, @ptrCast(new_allocation)) + extra_bytes;
|
||||
return new_source;
|
||||
}
|
||||
|
||||
|
|
|
@ -746,6 +746,15 @@ keepErrs = \list, toResult ->
|
|||
## expect List.map ["", "a", "bc"] Str.isEmpty == [Bool.true, Bool.false, Bool.false]
|
||||
## ```
|
||||
map : List a, (a -> b) -> List b
|
||||
map = \list, mapper ->
|
||||
# TODO: allow checking the refcounting and running the map inplace.
|
||||
# Preferably allow it even if the types are different (must be same size with padding though).
|
||||
length = List.len list
|
||||
List.walk
|
||||
list
|
||||
(List.withCapacity length)
|
||||
\state, elem ->
|
||||
List.appendUnsafe state (mapper elem)
|
||||
|
||||
## Run a transformation function on the first element of each list,
|
||||
## and use that as the first element in the returned list.
|
||||
|
@ -757,16 +766,56 @@ map : List a, (a -> b) -> List b
|
|||
## zipped = List.map2 ["a", "b", "c"] [1, 2, 3] Pair
|
||||
## ```
|
||||
map2 : List a, List b, (a, b -> c) -> List c
|
||||
map2 = \listA, listB, mapper ->
|
||||
length = Num.min (List.len listA) (List.len listB)
|
||||
map2Help listA listB (List.withCapacity length) mapper 0 length
|
||||
|
||||
map2Help : List a, List b, List c, (a, b -> c), U64, U64 -> List c
|
||||
map2Help = \listA, listB, out, mapper, index, length ->
|
||||
if index < length then
|
||||
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index)
|
||||
|
||||
map2Help listA listB (List.appendUnsafe out mapped) mapper (Num.addWrap index 1) length
|
||||
else
|
||||
out
|
||||
|
||||
## Run a transformation function on the first element of each list,
|
||||
## and use that as the first element in the returned list.
|
||||
## Repeat until a list runs out of elements.
|
||||
map3 : List a, List b, List c, (a, b, c -> d) -> List d
|
||||
map3 = \listA, listB, listC, mapper ->
|
||||
length = Num.min
|
||||
(Num.min (List.len listA) (List.len listB))
|
||||
(List.len listC)
|
||||
map3Help listA listB listC (List.withCapacity length) mapper 0 length
|
||||
|
||||
map3Help : List a, List b, List c, List d, (a, b, c -> d), U64, U64 -> List d
|
||||
map3Help = \listA, listB, listC, out, mapper, index, length ->
|
||||
if index < length then
|
||||
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index) (List.getUnsafe listC index)
|
||||
|
||||
map3Help listA listB listC (List.appendUnsafe out mapped) mapper (Num.addWrap index 1) length
|
||||
else
|
||||
out
|
||||
|
||||
## Run a transformation function on the first element of each list,
|
||||
## and use that as the first element in the returned list.
|
||||
## Repeat until a list runs out of elements.
|
||||
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
|
||||
map4 = \listA, listB, listC, listD, mapper ->
|
||||
length = Num.min
|
||||
(Num.min (List.len listA) (List.len listB))
|
||||
(Num.min (List.len listC) (List.len listD))
|
||||
map4Help listA listB listC listD (List.withCapacity length) mapper 0 length
|
||||
|
||||
map4Help : List a, List b, List c, List d, List e, (a, b, c, d -> e), U64, U64 -> List e
|
||||
map4Help = \listA, listB, listC, listD, out, mapper, index, length ->
|
||||
if index < length then
|
||||
mapped = mapper (List.getUnsafe listA index) (List.getUnsafe listB index) (List.getUnsafe listC index) (List.getUnsafe listD index)
|
||||
|
||||
map4Help listA listB listC listD (List.append out mapped) mapper (Num.addWrap index 1) length
|
||||
else
|
||||
out
|
||||
|
||||
## This works like [List.map], except it also passes the index
|
||||
## of the element to the conversion function.
|
||||
|
|
|
@ -391,6 +391,8 @@ pub const LIST_CAPACITY: &str = "roc_builtins.list.capacity";
|
|||
pub const LIST_ALLOCATION_PTR: &str = "roc_builtins.list.allocation_ptr";
|
||||
pub const LIST_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.list.release_excess_capacity";
|
||||
pub const LIST_CONCAT_UTF8: &str = "roc_builtins.list.concat_utf8";
|
||||
pub const LIST_INCREF: &str = "roc_builtins.list.incref";
|
||||
pub const LIST_DECREF: &str = "roc_builtins.list.decref";
|
||||
|
||||
pub const DEC_ABS: &str = "roc_builtins.dec.abs";
|
||||
pub const DEC_ACOS: &str = "roc_builtins.dec.acos";
|
||||
|
|
|
@ -17,7 +17,6 @@ roc_problem = { path = "../problem" }
|
|||
roc_region = { path = "../region" }
|
||||
roc_serialize = { path = "../serialize" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_test_utils = { path = "../../test_utils" }
|
||||
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
|
||||
|
@ -27,4 +26,5 @@ static_assertions.workspace = true
|
|||
|
||||
[dev-dependencies]
|
||||
indoc.workspace = true
|
||||
insta.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
|
|
|
@ -968,19 +968,10 @@ fn can_annotation_help(
|
|||
);
|
||||
|
||||
if tags.is_empty() {
|
||||
match ext {
|
||||
Some(_) => {
|
||||
// just `a` does not mean the same as `[]`, so even
|
||||
// if there are no fields, still make this a `TagUnion`,
|
||||
// not an EmptyTagUnion
|
||||
Type::TagUnion(
|
||||
Default::default(),
|
||||
TypeExtension::from_type(ext_type, is_implicit_openness),
|
||||
)
|
||||
}
|
||||
|
||||
None => Type::EmptyTagUnion,
|
||||
}
|
||||
Type::TagUnion(
|
||||
Default::default(),
|
||||
TypeExtension::from_type(ext_type, is_implicit_openness),
|
||||
)
|
||||
} else {
|
||||
let mut tag_types = can_tags(
|
||||
env,
|
||||
|
@ -1173,6 +1164,7 @@ fn can_extension_type(
|
|||
local_aliases,
|
||||
references,
|
||||
);
|
||||
|
||||
if valid_extension_type(shallow_dealias_with_scope(scope, &ext_type)) {
|
||||
if matches!(loc_ann.extract_spaces().item, TypeAnnotation::Wildcard)
|
||||
&& matches!(ext_problem_kind, ExtensionTypeKind::TagUnion)
|
||||
|
|
|
@ -91,6 +91,8 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
|||
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
||||
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
||||
LowLevel::RefCountIsUnique => unimplemented!(),
|
||||
LowLevel::ListIncref => unimplemented!(),
|
||||
LowLevel::ListDecref => unimplemented!(),
|
||||
|
||||
LowLevel::SetJmp => unimplemented!(),
|
||||
LowLevel::LongJmp => unimplemented!(),
|
||||
|
@ -140,10 +142,6 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
ListGetUnsafe; LIST_GET_UNSAFE; 2,
|
||||
ListReplaceUnsafe; LIST_REPLACE_UNSAFE; 3,
|
||||
ListConcat; LIST_CONCAT; 2,
|
||||
ListMap; LIST_MAP; 2,
|
||||
ListMap2; LIST_MAP2; 3,
|
||||
ListMap3; LIST_MAP3; 4,
|
||||
ListMap4; LIST_MAP4; 5,
|
||||
ListSortWith; LIST_SORT_WITH; 2,
|
||||
ListSublist; LIST_SUBLIST_LOWLEVEL; 3,
|
||||
ListDropAt; LIST_DROP_AT; 2,
|
||||
|
|
|
@ -150,11 +150,7 @@ impl ExpectsOrDbgs {
|
|||
#[derive(Debug, Clone)]
|
||||
enum PendingValueDef<'a> {
|
||||
/// A standalone annotation with no body
|
||||
AnnotationOnly(
|
||||
&'a Loc<ast::Pattern<'a>>,
|
||||
Loc<Pattern>,
|
||||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
),
|
||||
AnnotationOnly(Loc<Pattern>, &'a Loc<ast::TypeAnnotation<'a>>),
|
||||
/// A body with no type annotation
|
||||
Body(Loc<Pattern>, &'a Loc<ast::Expr<'a>>),
|
||||
/// A body with a type annotation
|
||||
|
@ -175,7 +171,7 @@ enum PendingValueDef<'a> {
|
|||
impl PendingValueDef<'_> {
|
||||
fn loc_pattern(&self) -> &Loc<Pattern> {
|
||||
match self {
|
||||
PendingValueDef::AnnotationOnly(_, loc_pattern, _) => loc_pattern,
|
||||
PendingValueDef::AnnotationOnly(loc_pattern, _) => loc_pattern,
|
||||
PendingValueDef::Body(loc_pattern, _) => loc_pattern,
|
||||
PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern,
|
||||
PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern,
|
||||
|
@ -2208,7 +2204,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
let pending_abilities_in_scope = &Default::default();
|
||||
|
||||
let output = match pending_def {
|
||||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||
AnnotationOnly(loc_can_pattern, loc_ann) => {
|
||||
// Make types for the body expr, even if we won't end up having a body.
|
||||
let expr_var = var_store.fresh();
|
||||
let mut vars_by_symbol = SendMap::default();
|
||||
|
@ -2939,7 +2935,6 @@ fn to_pending_value_def<'a>(
|
|||
);
|
||||
|
||||
PendingValue::Def(PendingValueDef::AnnotationOnly(
|
||||
loc_pattern,
|
||||
loc_can_pattern,
|
||||
loc_ann,
|
||||
))
|
||||
|
|
|
@ -9,7 +9,7 @@ use roc_module::called_via::{BinOp, CalledVia};
|
|||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, ModuleImportParams, Pattern, RecordBuilderField, StrLiteral,
|
||||
AssignedField, Collection, ModuleImportParams, OldRecordBuilderField, Pattern, StrLiteral,
|
||||
StrSegment, ValueDef, WhenBranch,
|
||||
};
|
||||
use roc_region::all::{LineInfo, Loc, Region};
|
||||
|
@ -300,8 +300,11 @@ pub fn desugar_expr<'a>(
|
|||
| MalformedClosure
|
||||
| MalformedSuffixed(..)
|
||||
| PrecedenceConflict { .. }
|
||||
| MultipleRecordBuilders { .. }
|
||||
| UnappliedRecordBuilder { .. }
|
||||
| MultipleOldRecordBuilders(_)
|
||||
| UnappliedOldRecordBuilder(_)
|
||||
| EmptyRecordBuilder(_)
|
||||
| SingleFieldRecordBuilder(_)
|
||||
| OptionalFieldInRecordBuilder { .. }
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| Crash => loc_expr,
|
||||
|
@ -485,10 +488,236 @@ pub fn desugar_expr<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
RecordBuilder(_) => arena.alloc(Loc {
|
||||
value: UnappliedRecordBuilder(loc_expr),
|
||||
OldRecordBuilder(_) => arena.alloc(Loc {
|
||||
value: UnappliedOldRecordBuilder(loc_expr),
|
||||
region: loc_expr.region,
|
||||
}),
|
||||
RecordBuilder { mapper, fields } => {
|
||||
// NOTE the `mapper` is always a `Var { .. }`, we only desugar it to get rid of
|
||||
// any spaces before/after
|
||||
let new_mapper = desugar_expr(arena, mapper, src, line_info, module_path);
|
||||
|
||||
if fields.is_empty() {
|
||||
return arena.alloc(Loc {
|
||||
value: EmptyRecordBuilder(loc_expr),
|
||||
region: loc_expr.region,
|
||||
});
|
||||
} else if fields.len() == 1 {
|
||||
return arena.alloc(Loc {
|
||||
value: SingleFieldRecordBuilder(loc_expr),
|
||||
region: loc_expr.region,
|
||||
});
|
||||
}
|
||||
|
||||
let mut field_names = Vec::with_capacity_in(fields.len(), arena);
|
||||
let mut field_vals = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
||||
for field in fields.items {
|
||||
match desugar_field(arena, &field.value, src, line_info, module_path) {
|
||||
AssignedField::RequiredValue(loc_name, _, loc_val) => {
|
||||
field_names.push(loc_name);
|
||||
field_vals.push(loc_val);
|
||||
}
|
||||
AssignedField::LabelOnly(loc_name) => {
|
||||
field_names.push(loc_name);
|
||||
field_vals.push(arena.alloc(Loc {
|
||||
region: loc_name.region,
|
||||
value: Expr::Var {
|
||||
module_name: "",
|
||||
ident: loc_name.value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
AssignedField::OptionalValue(loc_name, _, loc_val) => {
|
||||
return arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: OptionalFieldInRecordBuilder(arena.alloc(loc_name), loc_val),
|
||||
});
|
||||
}
|
||||
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => {
|
||||
unreachable!("Should have been desugared in `desugar_field`")
|
||||
}
|
||||
AssignedField::Malformed(_name) => {}
|
||||
}
|
||||
}
|
||||
|
||||
let closure_arg_from_field = |field: Loc<&'a str>| Loc {
|
||||
region: field.region,
|
||||
value: Pattern::Identifier {
|
||||
ident: arena.alloc_str(&format!("#{}", field.value)),
|
||||
},
|
||||
};
|
||||
|
||||
let combiner_closure_in_region = |region| {
|
||||
let closure_body = Tuple(Collection::with_items(
|
||||
Vec::from_iter_in(
|
||||
[
|
||||
&*arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#record_builder_closure_arg_a",
|
||||
},
|
||||
)),
|
||||
&*arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: "#record_builder_closure_arg_b",
|
||||
},
|
||||
)),
|
||||
],
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
));
|
||||
|
||||
arena.alloc(Loc::at(
|
||||
region,
|
||||
Closure(
|
||||
arena.alloc_slice_copy(&[
|
||||
Loc::at(
|
||||
region,
|
||||
Pattern::Identifier {
|
||||
ident: "#record_builder_closure_arg_a",
|
||||
},
|
||||
),
|
||||
Loc::at(
|
||||
region,
|
||||
Pattern::Identifier {
|
||||
ident: "#record_builder_closure_arg_b",
|
||||
},
|
||||
),
|
||||
]),
|
||||
arena.alloc(Loc::at(region, closure_body)),
|
||||
),
|
||||
))
|
||||
};
|
||||
|
||||
let closure_args = {
|
||||
if field_names.len() == 2 {
|
||||
arena.alloc_slice_copy(&[
|
||||
closure_arg_from_field(field_names[0]),
|
||||
closure_arg_from_field(field_names[1]),
|
||||
])
|
||||
} else {
|
||||
let second_to_last_arg =
|
||||
closure_arg_from_field(field_names[field_names.len() - 2]);
|
||||
let last_arg = closure_arg_from_field(field_names[field_names.len() - 1]);
|
||||
|
||||
let mut second_arg = Pattern::Tuple(Collection::with_items(
|
||||
arena.alloc_slice_copy(&[second_to_last_arg, last_arg]),
|
||||
));
|
||||
let mut second_arg_region =
|
||||
Region::span_across(&second_to_last_arg.region, &last_arg.region);
|
||||
|
||||
for index in (1..(field_names.len() - 2)).rev() {
|
||||
second_arg =
|
||||
Pattern::Tuple(Collection::with_items(arena.alloc_slice_copy(&[
|
||||
closure_arg_from_field(field_names[index]),
|
||||
Loc::at(second_arg_region, second_arg),
|
||||
])));
|
||||
second_arg_region =
|
||||
Region::span_across(&field_names[index].region, &second_arg_region);
|
||||
}
|
||||
|
||||
arena.alloc_slice_copy(&[
|
||||
closure_arg_from_field(field_names[0]),
|
||||
Loc::at(second_arg_region, second_arg),
|
||||
])
|
||||
}
|
||||
};
|
||||
|
||||
let record_val = Record(Collection::with_items(
|
||||
Vec::from_iter_in(
|
||||
field_names.iter().map(|field_name| {
|
||||
Loc::at(
|
||||
field_name.region,
|
||||
AssignedField::RequiredValue(
|
||||
Loc::at(field_name.region, field_name.value),
|
||||
&[],
|
||||
arena.alloc(Loc::at(
|
||||
field_name.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: arena.alloc_str(&format!("#{}", field_name.value)),
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
}),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice(),
|
||||
));
|
||||
|
||||
let record_combiner_closure = arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Closure(
|
||||
closure_args,
|
||||
arena.alloc(Loc::at(loc_expr.region, record_val)),
|
||||
),
|
||||
});
|
||||
|
||||
if field_names.len() == 2 {
|
||||
return arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Apply(
|
||||
new_mapper,
|
||||
arena.alloc_slice_copy(&[
|
||||
field_vals[0],
|
||||
field_vals[1],
|
||||
record_combiner_closure,
|
||||
]),
|
||||
CalledVia::RecordBuilder,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
let mut inner_combined = arena.alloc(Loc {
|
||||
region: Region::span_across(
|
||||
&field_vals[field_names.len() - 2].region,
|
||||
&field_vals[field_names.len() - 1].region,
|
||||
),
|
||||
value: Apply(
|
||||
new_mapper,
|
||||
arena.alloc_slice_copy(&[
|
||||
field_vals[field_names.len() - 2],
|
||||
field_vals[field_names.len() - 1],
|
||||
combiner_closure_in_region(loc_expr.region),
|
||||
]),
|
||||
CalledVia::RecordBuilder,
|
||||
),
|
||||
});
|
||||
|
||||
for index in (1..(field_names.len() - 2)).rev() {
|
||||
inner_combined = arena.alloc(Loc {
|
||||
region: Region::span_across(&field_vals[index].region, &inner_combined.region),
|
||||
value: Apply(
|
||||
new_mapper,
|
||||
arena.alloc_slice_copy(&[
|
||||
field_vals[index],
|
||||
inner_combined,
|
||||
combiner_closure_in_region(loc_expr.region),
|
||||
]),
|
||||
CalledVia::RecordBuilder,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: Apply(
|
||||
new_mapper,
|
||||
arena.alloc_slice_copy(&[
|
||||
field_vals[0],
|
||||
inner_combined,
|
||||
record_combiner_closure,
|
||||
]),
|
||||
CalledVia::RecordBuilder,
|
||||
),
|
||||
})
|
||||
}
|
||||
BinOps(lefts, right) => desugar_bin_ops(
|
||||
arena,
|
||||
loc_expr.region,
|
||||
|
@ -513,15 +742,15 @@ pub fn desugar_expr<'a>(
|
|||
let mut current = loc_arg.value;
|
||||
let arg = loop {
|
||||
match current {
|
||||
RecordBuilder(fields) => {
|
||||
OldRecordBuilder(fields) => {
|
||||
if builder_apply_exprs.is_some() {
|
||||
return arena.alloc(Loc {
|
||||
value: MultipleRecordBuilders(loc_expr),
|
||||
value: MultipleOldRecordBuilders(loc_expr),
|
||||
region: loc_expr.region,
|
||||
});
|
||||
}
|
||||
|
||||
let builder_arg = record_builder_arg(arena, loc_arg.region, fields);
|
||||
let builder_arg = old_record_builder_arg(arena, loc_arg.region, fields);
|
||||
builder_apply_exprs = Some(builder_arg.apply_exprs);
|
||||
|
||||
break builder_arg.closure;
|
||||
|
@ -557,7 +786,7 @@ pub fn desugar_expr<'a>(
|
|||
let args = std::slice::from_ref(arena.alloc(apply));
|
||||
|
||||
apply = arena.alloc(Loc {
|
||||
value: Apply(desugared_expr, args, CalledVia::RecordBuilder),
|
||||
value: Apply(desugared_expr, args, CalledVia::OldRecordBuilder),
|
||||
region: loc_expr.region,
|
||||
});
|
||||
}
|
||||
|
@ -1007,16 +1236,16 @@ fn desugar_pattern<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
struct RecordBuilderArg<'a> {
|
||||
struct OldRecordBuilderArg<'a> {
|
||||
closure: &'a Loc<Expr<'a>>,
|
||||
apply_exprs: Vec<'a, &'a Loc<Expr<'a>>>,
|
||||
}
|
||||
|
||||
fn record_builder_arg<'a>(
|
||||
fn old_record_builder_arg<'a>(
|
||||
arena: &'a Bump,
|
||||
region: Region,
|
||||
fields: Collection<'a, Loc<RecordBuilderField<'a>>>,
|
||||
) -> RecordBuilderArg<'a> {
|
||||
fields: Collection<'a, Loc<OldRecordBuilderField<'a>>>,
|
||||
) -> OldRecordBuilderArg<'a> {
|
||||
let mut record_fields = Vec::with_capacity_in(fields.len(), arena);
|
||||
let mut apply_exprs = Vec::with_capacity_in(fields.len(), arena);
|
||||
let mut apply_field_names = Vec::with_capacity_in(fields.len(), arena);
|
||||
|
@ -1028,10 +1257,10 @@ fn record_builder_arg<'a>(
|
|||
|
||||
let new_field = loop {
|
||||
match current {
|
||||
RecordBuilderField::Value(label, spaces, expr) => {
|
||||
OldRecordBuilderField::Value(label, spaces, expr) => {
|
||||
break AssignedField::RequiredValue(label, spaces, expr)
|
||||
}
|
||||
RecordBuilderField::ApplyValue(label, _, _, expr) => {
|
||||
OldRecordBuilderField::ApplyValue(label, _, _, expr) => {
|
||||
apply_field_names.push(label);
|
||||
apply_exprs.push(expr);
|
||||
|
||||
|
@ -1045,14 +1274,14 @@ fn record_builder_arg<'a>(
|
|||
|
||||
break AssignedField::RequiredValue(label, &[], var);
|
||||
}
|
||||
RecordBuilderField::LabelOnly(label) => break AssignedField::LabelOnly(label),
|
||||
RecordBuilderField::SpaceBefore(sub_field, _) => {
|
||||
OldRecordBuilderField::LabelOnly(label) => break AssignedField::LabelOnly(label),
|
||||
OldRecordBuilderField::SpaceBefore(sub_field, _) => {
|
||||
current = *sub_field;
|
||||
}
|
||||
RecordBuilderField::SpaceAfter(sub_field, _) => {
|
||||
OldRecordBuilderField::SpaceAfter(sub_field, _) => {
|
||||
current = *sub_field;
|
||||
}
|
||||
RecordBuilderField::Malformed(malformed) => {
|
||||
OldRecordBuilderField::Malformed(malformed) => {
|
||||
break AssignedField::Malformed(malformed)
|
||||
}
|
||||
}
|
||||
|
@ -1092,7 +1321,7 @@ fn record_builder_arg<'a>(
|
|||
});
|
||||
}
|
||||
|
||||
RecordBuilderArg {
|
||||
OldRecordBuilderArg {
|
||||
closure: body,
|
||||
apply_exprs,
|
||||
}
|
||||
|
|
|
@ -1021,8 +1021,11 @@ pub fn canonicalize_expr<'a>(
|
|||
can_defs_with_return(env, var_store, inner_scope, env.arena.alloc(defs), loc_ret)
|
||||
})
|
||||
}
|
||||
ast::Expr::RecordBuilder(_) => {
|
||||
internal_error!("RecordBuilder should have been desugared by now")
|
||||
ast::Expr::OldRecordBuilder(_) => {
|
||||
internal_error!("Old record builder should have been desugared by now")
|
||||
}
|
||||
ast::Expr::RecordBuilder { .. } => {
|
||||
internal_error!("New record builder should have been desugared by now")
|
||||
}
|
||||
ast::Expr::Backpassing(_, _, _) => {
|
||||
internal_error!("Backpassing should have been desugared by now")
|
||||
|
@ -1340,18 +1343,43 @@ pub fn canonicalize_expr<'a>(
|
|||
use roc_problem::can::RuntimeError::*;
|
||||
(RuntimeError(MalformedSuffixed(region)), Output::default())
|
||||
}
|
||||
ast::Expr::MultipleRecordBuilders(sub_expr) => {
|
||||
ast::Expr::MultipleOldRecordBuilders(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
let problem = MultipleRecordBuilders(sub_expr.region);
|
||||
let problem = MultipleOldRecordBuilders(sub_expr.region);
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::UnappliedRecordBuilder(sub_expr) => {
|
||||
ast::Expr::UnappliedOldRecordBuilder(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
let problem = UnappliedRecordBuilder(sub_expr.region);
|
||||
let problem = UnappliedOldRecordBuilder(sub_expr.region);
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::EmptyRecordBuilder(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
let problem = EmptyRecordBuilder(sub_expr.region);
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::SingleFieldRecordBuilder(sub_expr) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
let problem = SingleFieldRecordBuilder(sub_expr.region);
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
}
|
||||
ast::Expr::OptionalFieldInRecordBuilder(loc_name, loc_value) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
let sub_region = Region::span_across(&loc_name.region, &loc_value.region);
|
||||
let problem = OptionalFieldInRecordBuilder {record: region, field: sub_region };
|
||||
env.problem(Problem::RuntimeError(problem.clone()));
|
||||
|
||||
(RuntimeError(problem), Output::default())
|
||||
|
@ -2414,9 +2442,12 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
ast::Expr::Tuple(fields) => fields
|
||||
.iter()
|
||||
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
|
||||
ast::Expr::MultipleRecordBuilders(loc_expr)
|
||||
| ast::Expr::MalformedSuffixed(loc_expr)
|
||||
| ast::Expr::UnappliedRecordBuilder(loc_expr)
|
||||
ast::Expr::MalformedSuffixed(loc_expr)
|
||||
| ast::Expr::MultipleOldRecordBuilders(loc_expr)
|
||||
| ast::Expr::UnappliedOldRecordBuilder(loc_expr)
|
||||
| ast::Expr::EmptyRecordBuilder(loc_expr)
|
||||
| ast::Expr::SingleFieldRecordBuilder(loc_expr)
|
||||
| ast::Expr::OptionalFieldInRecordBuilder(_, loc_expr)
|
||||
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
|
||||
| ast::Expr::UnaryOp(loc_expr, _)
|
||||
| ast::Expr::Closure(_, loc_expr) => is_valid_interpolation(&loc_expr.value),
|
||||
|
@ -2458,24 +2489,39 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
}
|
||||
ast::Expr::RecordBuilder(fields) => fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::RecordBuilderField::Value(_label, comments, loc_expr) => {
|
||||
comments.is_empty() && is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::RecordBuilderField::ApplyValue(
|
||||
_label,
|
||||
comments_before,
|
||||
comments_after,
|
||||
loc_expr,
|
||||
) => {
|
||||
comments_before.is_empty()
|
||||
&& comments_after.is_empty()
|
||||
&& is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::RecordBuilderField::Malformed(_) | ast::RecordBuilderField::LabelOnly(_) => true,
|
||||
ast::RecordBuilderField::SpaceBefore(_, _)
|
||||
| ast::RecordBuilderField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
ast::Expr::OldRecordBuilder(fields) => {
|
||||
fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::OldRecordBuilderField::Value(_label, comments, loc_expr) => {
|
||||
comments.is_empty() && is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::OldRecordBuilderField::ApplyValue(
|
||||
_label,
|
||||
comments_before,
|
||||
comments_after,
|
||||
loc_expr,
|
||||
) => {
|
||||
comments_before.is_empty()
|
||||
&& comments_after.is_empty()
|
||||
&& is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::OldRecordBuilderField::Malformed(_)
|
||||
| ast::OldRecordBuilderField::LabelOnly(_) => true,
|
||||
ast::OldRecordBuilderField::SpaceBefore(_, _)
|
||||
| ast::OldRecordBuilderField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
}
|
||||
ast::Expr::RecordBuilder { mapper, fields } => {
|
||||
is_valid_interpolation(&mapper.value)
|
||||
&& fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
|
||||
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,30 +12,18 @@ use std::cell::Cell;
|
|||
|
||||
thread_local! {
|
||||
// we use a thread_local here so that tests consistently give the same pattern
|
||||
static SUFFIXED_ANSWER_COUNTER: Cell<usize> = Cell::new(0);
|
||||
static SUFFIXED_ANSWER_COUNTER: Cell<usize> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
/// Provide an intermediate answer expression and pattern when unwrapping a
|
||||
/// (sub) expression
|
||||
///
|
||||
/// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0`
|
||||
fn next_suffixed_answer_pattern(arena: &Bump) -> (Expr, Pattern) {
|
||||
// Use the thread-local counter
|
||||
/// Generates a unique identifier, useful for intermediate items during desugaring.
|
||||
fn next_unique_suffixed_ident() -> String {
|
||||
SUFFIXED_ANSWER_COUNTER.with(|counter| {
|
||||
let count = counter.get();
|
||||
counter.set(count + 1);
|
||||
|
||||
let answer_ident = arena.alloc(format!("#!a{}", count));
|
||||
|
||||
(
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
},
|
||||
Pattern::Identifier {
|
||||
ident: answer_ident.as_str(),
|
||||
},
|
||||
)
|
||||
// # is used as prefix because it's impossible for code authors to define names like this.
|
||||
// This makes it easy to see this identifier was created by the compiler.
|
||||
format!("#!a{}", count)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -69,9 +57,23 @@ fn init_unwrapped_err<'a>(
|
|||
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr))
|
||||
}
|
||||
None => {
|
||||
let (answer_var, answer_pat) = next_suffixed_answer_pattern(arena);
|
||||
let sub_new = arena.alloc(Loc::at(unwrapped_expr.region, answer_var));
|
||||
let sub_pat = arena.alloc(Loc::at(unwrapped_expr.region, answer_pat));
|
||||
// Provide an intermediate answer expression and pattern when unwrapping a
|
||||
// (sub) expression.
|
||||
// e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0`
|
||||
let answer_ident = arena.alloc(next_unique_suffixed_ident());
|
||||
let sub_new = arena.alloc(Loc::at(
|
||||
unwrapped_expr.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: answer_ident,
|
||||
},
|
||||
));
|
||||
let sub_pat = arena.alloc(Loc::at(
|
||||
unwrapped_expr.region,
|
||||
Pattern::Identifier {
|
||||
ident: answer_ident,
|
||||
},
|
||||
));
|
||||
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: unwrapped_expr,
|
||||
|
@ -819,12 +821,6 @@ pub fn apply_task_await<'a>(
|
|||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
) -> &'a Loc<Expr<'a>> {
|
||||
// If the pattern and the new are the same then we don't need to unwrap anything
|
||||
// e.g. `Task.await foo \{} -> Task.ok {}` is the same as `foo`
|
||||
if is_matching_empty_record(loc_pat, loc_new) {
|
||||
return loc_arg;
|
||||
}
|
||||
|
||||
// If the pattern and the new are matching answers then we don't need to unwrap anything
|
||||
// e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo`
|
||||
if is_matching_intermediate_answer(loc_pat, loc_new) {
|
||||
|
@ -872,26 +868,6 @@ fn extract_wrapped_task_ok_value<'a>(loc_expr: &'a Loc<Expr<'a>>) -> Option<&'a
|
|||
}
|
||||
}
|
||||
|
||||
fn is_matching_empty_record<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_expr: &'a Loc<Expr<'a>>,
|
||||
) -> bool {
|
||||
let is_empty_record = match extract_wrapped_task_ok_value(loc_expr) {
|
||||
Some(task_expr) => match task_expr.value {
|
||||
Expr::Record(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
None => false,
|
||||
};
|
||||
|
||||
let is_pattern_empty_record = match loc_pat.value {
|
||||
Pattern::RecordDestructure(collection) => collection.is_empty(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_empty_record && is_pattern_empty_record
|
||||
}
|
||||
|
||||
pub fn is_matching_intermediate_answer<'a>(
|
||||
loc_pat: &'a Loc<Pattern<'a>>,
|
||||
loc_new: &'a Loc<Expr<'a>>,
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-22 Apply(
|
||||
@15-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@15-22 Closure(
|
||||
[
|
||||
@17-18 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-22 Apply(
|
||||
@15-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@20-21 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
@15-22 Closure(
|
||||
[
|
||||
@20-21 Identifier {
|
||||
ident: "#!a1",
|
||||
},
|
||||
],
|
||||
@15-22 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "c",
|
||||
},
|
||||
@15-22 Apply(
|
||||
@15-16 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
@20-21 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@27-28 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-25,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-19 Apply(
|
||||
@15-19 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@15-19 Closure(
|
||||
[
|
||||
@17-18 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-19 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-19,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "c",
|
||||
},
|
||||
@15-19 Apply(
|
||||
@15-16 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
@17-18 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@24-25 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-33 Apply(
|
||||
@15-33 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Apply(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@25-32 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-33 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-33 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
@15-33 Apply(
|
||||
@15-18 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
[
|
||||
@20-32 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@38-43 Apply(
|
||||
@38-41 Var {
|
||||
module_name: "",
|
||||
ident: "baz",
|
||||
},
|
||||
[
|
||||
@42-43 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-35 Apply(
|
||||
@15-35 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Apply(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@21-26 Str(
|
||||
PlainLine(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-35 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-35 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
@15-35 Apply(
|
||||
@16-26 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
[
|
||||
@28-35 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@40-45 Apply(
|
||||
@40-43 Var {
|
||||
module_name: "",
|
||||
ident: "baz",
|
||||
},
|
||||
[
|
||||
@44-45 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-22 Apply(
|
||||
@15-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@15-16 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@15-22 Closure(
|
||||
[
|
||||
@15-16 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-22 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "c",
|
||||
},
|
||||
@15-22 Apply(
|
||||
@21-22 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
@15-16 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@27-28 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-15 Apply(
|
||||
@11-15 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-15 Closure(
|
||||
[
|
||||
@11-15 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@21-26 Apply(
|
||||
@21-23 Var {
|
||||
module_name: "",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@24-26 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@16-35 Apply(
|
||||
@16-35 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "sayMultiple",
|
||||
},
|
||||
@16-35 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@16-35 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@16-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-13 Identifier {
|
||||
ident: "do",
|
||||
},
|
||||
@16-35 Apply(
|
||||
@17-29 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
[
|
||||
@31-35 Str(
|
||||
PlainLine(
|
||||
"hi",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@40-42 Var {
|
||||
module_name: "",
|
||||
ident: "do",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-69,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-69 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-57,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
@15-57 Closure(
|
||||
[
|
||||
@16-19 Identifier {
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
@31-42 Apply(
|
||||
@31-42 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@31-42 Apply(
|
||||
@31-42 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@31-34 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
@31-42 Closure(
|
||||
[
|
||||
@31-42 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@52-57 Apply(
|
||||
@52-54 Var {
|
||||
module_name: "",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@55-57 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@63-69 Apply(
|
||||
@63-64 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
[
|
||||
@65-69 Str(
|
||||
PlainLine(
|
||||
"hi",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-114,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-114 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@39-101,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
ann_type: @15-30 Function(
|
||||
[
|
||||
@15-18 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@22-30 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@27-28 Inferred,
|
||||
@29-30 Inferred,
|
||||
],
|
||||
),
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @35-36 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
body_expr: @39-101 Closure(
|
||||
[
|
||||
@40-43 Identifier {
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
@78-91 Apply(
|
||||
@78-91 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@78-91 Apply(
|
||||
@78-91 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@88-91 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@78-91 Closure(
|
||||
[
|
||||
@78-79 Identifier {
|
||||
ident: "y",
|
||||
},
|
||||
],
|
||||
@100-101 Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@107-114 Apply(
|
||||
@107-108 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
[
|
||||
@109-114 Str(
|
||||
PlainLine(
|
||||
"foo",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-143,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@12-143 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@56-119,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
AnnotatedBody {
|
||||
ann_pattern: @12-15 Identifier {
|
||||
ident: "foo",
|
||||
},
|
||||
ann_type: @18-45 Function(
|
||||
[
|
||||
@18-21 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
@23-25 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@27-30 Apply(
|
||||
"",
|
||||
"Str",
|
||||
[],
|
||||
),
|
||||
],
|
||||
@34-45 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@39-41 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@42-45 Apply(
|
||||
"",
|
||||
"I32",
|
||||
[],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @50-53 Identifier {
|
||||
ident: "foo",
|
||||
},
|
||||
body_expr: @56-119 Closure(
|
||||
[
|
||||
@57-58 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
@60-61 Underscore(
|
||||
"",
|
||||
),
|
||||
@63-64 Identifier {
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
@76-83 Apply(
|
||||
@76-83 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@76-83 Apply(
|
||||
@76-83 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@82-83 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@76-83 Closure(
|
||||
[
|
||||
@76-83 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@92-99 Apply(
|
||||
@92-99 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@92-99 Apply(
|
||||
@92-99 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@98-99 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@92-99 Closure(
|
||||
[
|
||||
@92-99 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@109-119 Apply(
|
||||
@109-116 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@117-119 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@125-143 Apply(
|
||||
@125-128 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@129-134 Str(
|
||||
PlainLine(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
@135-137 Record(
|
||||
[],
|
||||
),
|
||||
@138-143 Str(
|
||||
PlainLine(
|
||||
"baz",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@17-24 Apply(
|
||||
@17-24 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@17-24 Var {
|
||||
module_name: "",
|
||||
ident: "getFoo",
|
||||
},
|
||||
@17-24 Closure(
|
||||
[
|
||||
@11-14 Identifier {
|
||||
ident: "foo",
|
||||
},
|
||||
],
|
||||
@29-49 LowLevelDbg(
|
||||
(
|
||||
"test.roc:3",
|
||||
" ",
|
||||
),
|
||||
@33-36 Apply(
|
||||
@33-36 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@33-36 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@41-49 Apply(
|
||||
@41-49 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
[
|
||||
@46-49 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-24 Apply(
|
||||
@11-24 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@15-17 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@11-24 Closure(
|
||||
[
|
||||
@15-17 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@11-24 LowLevelDbg(
|
||||
(
|
||||
"test.roc:2",
|
||||
"in",
|
||||
),
|
||||
@15-17 Apply(
|
||||
@15-17 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@15-17 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@23-24 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-99 When(
|
||||
@16-17 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@29-30 NumLiteral(
|
||||
"0",
|
||||
),
|
||||
],
|
||||
value: @46-99 When(
|
||||
@51-52 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@72-73 NumLiteral(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
value: @97-99 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@0-54,
|
||||
@56-98,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 2),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 2, length = 0),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-54 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-20,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
@15-20 Str(
|
||||
PlainLine(
|
||||
"Foo",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@11-54 Apply(
|
||||
@11-54 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@25-39 Apply(
|
||||
@25-39 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@38-39 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@11-54 Closure(
|
||||
[
|
||||
@25-39 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@45-54 Var {
|
||||
module_name: "",
|
||||
ident: "printBar",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@56-64 Identifier {
|
||||
ident: "printBar",
|
||||
},
|
||||
@71-98 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@75-80,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@71-72 Identifier {
|
||||
ident: "b",
|
||||
},
|
||||
@75-80 Str(
|
||||
PlainLine(
|
||||
"Bar",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@85-98 Apply(
|
||||
@85-96 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@97-98 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-158,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-158 When(
|
||||
@16-17 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@29-30 Tag(
|
||||
"A",
|
||||
),
|
||||
],
|
||||
value: @46-130 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@50-52,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@46-47 Identifier {
|
||||
ident: "y",
|
||||
},
|
||||
@50-52 Num(
|
||||
"42",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@66-130 If(
|
||||
[
|
||||
(
|
||||
@69-70 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@92-94 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
),
|
||||
],
|
||||
@128-130 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
),
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@139-140 Tag(
|
||||
"B",
|
||||
),
|
||||
],
|
||||
value: @156-158 Var {
|
||||
module_name: "",
|
||||
ident: "d",
|
||||
},
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-31,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-31 Expect(
|
||||
@18-24 Apply(
|
||||
@20-22 Var {
|
||||
module_name: "Bool",
|
||||
ident: "isEq",
|
||||
},
|
||||
[
|
||||
@18-19 Num(
|
||||
"1",
|
||||
),
|
||||
@23-24 Num(
|
||||
"2",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Equals,
|
||||
),
|
||||
),
|
||||
@29-31 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-307,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-307 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
Index(2147483649),
|
||||
Index(2147483650),
|
||||
],
|
||||
regions: [
|
||||
@20-37,
|
||||
@53-68,
|
||||
@109-298,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
Slice(start = 1, length = 1),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 1, length = 0),
|
||||
Slice(start = 2, length = 0),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-17 Identifier {
|
||||
ident: "isTrue",
|
||||
},
|
||||
@20-37 Apply(
|
||||
@20-27 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@28-37 Var {
|
||||
module_name: "Bool",
|
||||
ident: "true",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@42-50 Identifier {
|
||||
ident: "isFalsey",
|
||||
},
|
||||
@53-68 Closure(
|
||||
[
|
||||
@54-55 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
@59-68 Apply(
|
||||
@59-66 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@67-68 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnnotatedBody {
|
||||
ann_pattern: @73-76 Identifier {
|
||||
ident: "msg",
|
||||
},
|
||||
ann_type: @79-90 Apply(
|
||||
"",
|
||||
"Task",
|
||||
[
|
||||
@84-86 Record {
|
||||
fields: [],
|
||||
ext: None,
|
||||
},
|
||||
@87-90 Apply(
|
||||
"",
|
||||
"I32",
|
||||
[],
|
||||
),
|
||||
],
|
||||
),
|
||||
comment: None,
|
||||
body_pattern: @95-98 Identifier {
|
||||
ident: "msg",
|
||||
},
|
||||
body_expr: Apply(
|
||||
Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "isTrue",
|
||||
},
|
||||
Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@109-298 If(
|
||||
[
|
||||
(
|
||||
@112-122 Apply(
|
||||
@112-113 Var {
|
||||
module_name: "Bool",
|
||||
ident: "not",
|
||||
},
|
||||
[
|
||||
@114-121 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
],
|
||||
UnaryOp(
|
||||
Not,
|
||||
),
|
||||
),
|
||||
@140-152 Apply(
|
||||
@140-152 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@140-152 Apply(
|
||||
@140-152 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@146-152 Str(
|
||||
PlainLine(
|
||||
"fail",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@140-152 Closure(
|
||||
[
|
||||
@140-152 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@165-170 Apply(
|
||||
@165-168 Var {
|
||||
module_name: "",
|
||||
ident: "err",
|
||||
},
|
||||
[
|
||||
@169-170 Num(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
Apply(
|
||||
Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Apply(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "isFalsey",
|
||||
},
|
||||
[
|
||||
@198-208 Var {
|
||||
module_name: "Bool",
|
||||
ident: "false",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a1",
|
||||
},
|
||||
],
|
||||
@109-298 If(
|
||||
[
|
||||
(
|
||||
@187-209 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
},
|
||||
),
|
||||
@227-239 Apply(
|
||||
@227-239 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@227-239 Apply(
|
||||
@227-239 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@233-239 Str(
|
||||
PlainLine(
|
||||
"nope",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@227-239 Closure(
|
||||
[
|
||||
@227-239 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@252-257 Apply(
|
||||
@252-254 Var {
|
||||
module_name: "",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@255-257 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
@283-298 Apply(
|
||||
@283-298 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@289-298 Str(
|
||||
PlainLine(
|
||||
"success",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@304-307 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-189,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-189 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
Index(2147483649),
|
||||
],
|
||||
regions: [
|
||||
@20-37,
|
||||
@52-70,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 0, length = 1),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
Slice(start = 1, length = 0),
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-17 Identifier {
|
||||
ident: "isTrue",
|
||||
},
|
||||
@20-37 Apply(
|
||||
@20-27 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@28-37 Var {
|
||||
module_name: "Bool",
|
||||
ident: "true",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
Body(
|
||||
@42-49 Identifier {
|
||||
ident: "isFalse",
|
||||
},
|
||||
@52-70 Apply(
|
||||
@52-59 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@60-70 Var {
|
||||
module_name: "Bool",
|
||||
ident: "false",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@79-87 Apply(
|
||||
@79-87 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@79-87 Var {
|
||||
module_name: "",
|
||||
ident: "isFalse",
|
||||
},
|
||||
@79-87 Closure(
|
||||
[
|
||||
@79-87 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@76-189 If(
|
||||
[
|
||||
(
|
||||
@79-87 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
@101-112 Apply(
|
||||
@101-105 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@106-112 Str(
|
||||
PlainLine(
|
||||
"fail",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
@125-132 Apply(
|
||||
@125-132 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@125-132 Var {
|
||||
module_name: "",
|
||||
ident: "isTrue",
|
||||
},
|
||||
@125-132 Closure(
|
||||
[
|
||||
@125-132 Identifier {
|
||||
ident: "#!a1",
|
||||
},
|
||||
],
|
||||
@76-189 If(
|
||||
[
|
||||
(
|
||||
@125-132 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
},
|
||||
@146-160 Apply(
|
||||
@146-150 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@151-160 Str(
|
||||
PlainLine(
|
||||
"success",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
@178-189 Apply(
|
||||
@178-182 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@183-189 Str(
|
||||
PlainLine(
|
||||
"fail",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-26 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-17,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
@15-17 Num(
|
||||
"42",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@11-26 Apply(
|
||||
@11-26 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-25 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
@11-26 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@22-26 Apply(
|
||||
@22-23 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
[
|
||||
@24-25 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-15 Apply(
|
||||
@11-15 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-15 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-15 Closure(
|
||||
[
|
||||
@11-15 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@20-24 Apply(
|
||||
@20-24 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@20-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
@20-24 Closure(
|
||||
[
|
||||
@20-24 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@29-33 Var {
|
||||
module_name: "",
|
||||
ident: "baz",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@0-26 Apply(
|
||||
@0-26 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@12-17 Str(
|
||||
PlainLine(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
@18-20 Record(
|
||||
[],
|
||||
),
|
||||
@21-26 Str(
|
||||
PlainLine(
|
||||
"baz",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-72,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-23 Apply(
|
||||
@11-23 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-23 Apply(
|
||||
@11-23 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@17-23 Str(
|
||||
PlainLine(
|
||||
"Ahoy",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@11-23 Closure(
|
||||
[
|
||||
@11-23 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@33-55 Apply(
|
||||
@33-55 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@33-55 Apply(
|
||||
@33-55 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@33-40 Str(
|
||||
PlainLine(
|
||||
"There",
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
@33-55 Closure(
|
||||
[
|
||||
@28-30 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@62-72 Apply(
|
||||
@62-69 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@70-72 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-51,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-51 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@17-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-14 Identifier {
|
||||
ident: "msg",
|
||||
},
|
||||
@17-24 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@11-51 Apply(
|
||||
@11-51 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@29-41 Apply(
|
||||
@29-41 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@38-41 Var {
|
||||
module_name: "",
|
||||
ident: "msg",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@11-51 Closure(
|
||||
[
|
||||
@29-30 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
@46-51 Apply(
|
||||
@46-49 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
[
|
||||
@50-51 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-24 Apply(
|
||||
@11-24 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-16 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@11-24 Closure(
|
||||
[
|
||||
@11-16 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@11-16 Apply(
|
||||
@11-16 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-16 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
@11-16 Closure(
|
||||
[
|
||||
@11-16 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@21-24 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-61,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-43 Apply(
|
||||
@15-43 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Apply(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
[
|
||||
@26-29 Var {
|
||||
module_name: "",
|
||||
ident: "baz",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-43 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@15-43 Apply(
|
||||
@15-43 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@15-43 Apply(
|
||||
@15-43 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
[
|
||||
@21-29 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
@32-42 ParensAround(
|
||||
Apply(
|
||||
@32-36 Var {
|
||||
module_name: "",
|
||||
ident: "blah",
|
||||
},
|
||||
[
|
||||
@37-42 Var {
|
||||
module_name: "",
|
||||
ident: "stuff",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-43 Closure(
|
||||
[
|
||||
@11-12 Identifier {
|
||||
ident: "z",
|
||||
},
|
||||
],
|
||||
@48-61 Apply(
|
||||
@48-59 Var {
|
||||
module_name: "",
|
||||
ident: "doSomething",
|
||||
},
|
||||
[
|
||||
@60-61 Var {
|
||||
module_name: "",
|
||||
ident: "z",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-49 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@23-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@11-12 Identifier {
|
||||
ident: "x",
|
||||
},
|
||||
@27-29 Apply(
|
||||
@27-29 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@27-29 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
@27-29 Closure(
|
||||
[
|
||||
@23-24 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
@38-42 Apply(
|
||||
@38-42 Var {
|
||||
module_name: "",
|
||||
ident: "c",
|
||||
},
|
||||
[
|
||||
@41-42 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@48-49 Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-3 Identifier {
|
||||
ident: "run",
|
||||
},
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "nextMsg",
|
||||
},
|
||||
@0-22 Closure(
|
||||
[
|
||||
Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@0-22 Apply(
|
||||
@0-22 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@13-21 ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-73,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-56 Apply(
|
||||
@11-56 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@11-56 Apply(
|
||||
@11-56 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@11-44 Apply(
|
||||
@26-36 Var {
|
||||
module_name: "Str",
|
||||
ident: "concat",
|
||||
},
|
||||
[
|
||||
@11-18 Str(
|
||||
PlainLine(
|
||||
"hello",
|
||||
),
|
||||
),
|
||||
@37-44 Str(
|
||||
PlainLine(
|
||||
"world",
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
@11-56 Closure(
|
||||
[
|
||||
@26-56 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@63-73 Apply(
|
||||
@63-70 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@71-73 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-67,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "copy",
|
||||
},
|
||||
@7-67 Closure(
|
||||
[
|
||||
@8-9 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
@10-11 Identifier {
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
@19-30 Apply(
|
||||
@19-30 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@19-30 Apply(
|
||||
@19-30 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@25-30 Str(
|
||||
PlainLine(
|
||||
"FOO",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@19-30 Closure(
|
||||
[
|
||||
@19-30 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@36-67 Apply(
|
||||
@36-67 Var {
|
||||
module_name: "",
|
||||
ident: "mapErr",
|
||||
},
|
||||
[
|
||||
@36-48 Apply(
|
||||
@36-43 Var {
|
||||
module_name: "CMD",
|
||||
ident: "new",
|
||||
},
|
||||
[
|
||||
@44-48 Str(
|
||||
PlainLine(
|
||||
"cp",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@64-67 Tag(
|
||||
"ERR",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Pizza,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-154,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@20-31 Apply(
|
||||
@20-31 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@20-31 Var {
|
||||
module_name: "Stdin",
|
||||
ident: "line",
|
||||
},
|
||||
@20-31 Closure(
|
||||
[
|
||||
@11-17 Identifier {
|
||||
ident: "result",
|
||||
},
|
||||
],
|
||||
@37-154 When(
|
||||
@42-48 Var {
|
||||
module_name: "",
|
||||
ident: "result",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@60-63 Tag(
|
||||
"End",
|
||||
),
|
||||
],
|
||||
value: @79-89 Apply(
|
||||
@79-86 Var {
|
||||
module_name: "Task",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@87-89 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@99-109 Apply(
|
||||
@99-104 Tag(
|
||||
"Input",
|
||||
),
|
||||
[
|
||||
@105-109 Identifier {
|
||||
ident: "name",
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
value: @125-154 Apply(
|
||||
@125-154 Var {
|
||||
module_name: "Stdout",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@138-154 Str(
|
||||
Line(
|
||||
[
|
||||
Plaintext(
|
||||
"Hello, ",
|
||||
),
|
||||
Interpolated(
|
||||
@148-152 Var {
|
||||
module_name: "",
|
||||
ident: "name",
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@15-19 Apply(
|
||||
@15-19 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@15-19 Var {
|
||||
module_name: "",
|
||||
ident: "foo",
|
||||
},
|
||||
@15-19 Closure(
|
||||
[
|
||||
@11-12 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
@15-19 Apply(
|
||||
@15-19 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
module_name: "",
|
||||
ident: "bar",
|
||||
},
|
||||
@15-19 Closure(
|
||||
[
|
||||
@24-33 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@24-33 Apply(
|
||||
@24-33 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@24-33 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
@24-33 Closure(
|
||||
[
|
||||
@24-25 Identifier {
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
@38-45 Apply(
|
||||
@38-41 Var {
|
||||
module_name: "",
|
||||
ident: "baz",
|
||||
},
|
||||
[
|
||||
@42-43 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
@44-45 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-120,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "list",
|
||||
},
|
||||
@11-120 Apply(
|
||||
@11-120 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "getList",
|
||||
},
|
||||
@11-120 Closure(
|
||||
[
|
||||
@16-24 Identifier {
|
||||
ident: "#!a1",
|
||||
},
|
||||
],
|
||||
@11-120 When(
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "#!a1",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@36-38 List(
|
||||
[],
|
||||
),
|
||||
],
|
||||
value: @54-65 Apply(
|
||||
@54-65 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@54-65 Apply(
|
||||
@54-65 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@60-65 Str(
|
||||
PlainLine(
|
||||
"foo",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@54-65 Closure(
|
||||
[
|
||||
@54-65 RecordDestructure(
|
||||
[],
|
||||
),
|
||||
],
|
||||
@78-89 Apply(
|
||||
@78-89 Var {
|
||||
module_name: "",
|
||||
ident: "line",
|
||||
},
|
||||
[
|
||||
@84-89 Str(
|
||||
PlainLine(
|
||||
"bar",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@98-99 Underscore(
|
||||
"",
|
||||
),
|
||||
],
|
||||
value: @115-120 Apply(
|
||||
@115-117 Var {
|
||||
module_name: "",
|
||||
ident: "ok",
|
||||
},
|
||||
[
|
||||
@118-120 Record(
|
||||
[],
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
Index(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-74,
|
||||
],
|
||||
space_before: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
space_after: [
|
||||
Slice(start = 0, length = 0),
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@0-4 Identifier {
|
||||
ident: "list",
|
||||
},
|
||||
@11-74 Apply(
|
||||
@11-74 Var {
|
||||
module_name: "Task",
|
||||
ident: "await",
|
||||
},
|
||||
[
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "getList",
|
||||
},
|
||||
@11-74 Closure(
|
||||
[
|
||||
@16-24 Identifier {
|
||||
ident: "#!a0",
|
||||
},
|
||||
],
|
||||
@11-74 When(
|
||||
@16-24 Var {
|
||||
module_name: "",
|
||||
ident: "#!a0",
|
||||
},
|
||||
[
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@36-38 List(
|
||||
[],
|
||||
),
|
||||
],
|
||||
value: @42-49 Str(
|
||||
PlainLine(
|
||||
"empty",
|
||||
),
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
WhenBranch {
|
||||
patterns: [
|
||||
@58-59 Underscore(
|
||||
"",
|
||||
),
|
||||
],
|
||||
value: @63-74 Str(
|
||||
PlainLine(
|
||||
"non-empty",
|
||||
),
|
||||
),
|
||||
guard: None,
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
BangSuffix,
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
|
@ -17,9 +17,12 @@ mod test_can {
|
|||
use core::panic;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{ClosureData, IntValue, Recursive};
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_module::called_via::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
||||
use roc_region::all::{Position, Region};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use std::{f64, i64};
|
||||
|
||||
fn assert_can_runtime_error(input: &str, expected: RuntimeError) {
|
||||
|
@ -651,7 +654,7 @@ mod test_can {
|
|||
|
||||
// RECORD BUILDERS
|
||||
#[test]
|
||||
fn record_builder_desugar() {
|
||||
fn old_record_builder_desugar() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
succeed = \_ -> crash "succeed"
|
||||
|
@ -766,7 +769,7 @@ mod test_can {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn record_builder_field_names_do_not_shadow() {
|
||||
fn old_record_builder_field_names_do_not_shadow() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
succeed = \_ -> crash "succeed"
|
||||
|
@ -806,7 +809,7 @@ mod test_can {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_record_builders_error() {
|
||||
fn multiple_old_record_builders_error() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
succeed
|
||||
|
@ -822,17 +825,17 @@ mod test_can {
|
|||
assert_eq!(problems.len(), 1);
|
||||
assert!(problems.iter().all(|problem| matches!(
|
||||
problem,
|
||||
Problem::RuntimeError(roc_problem::can::RuntimeError::MultipleRecordBuilders { .. })
|
||||
Problem::RuntimeError(roc_problem::can::RuntimeError::MultipleOldRecordBuilders { .. })
|
||||
)));
|
||||
|
||||
assert!(matches!(
|
||||
loc_expr.value,
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::MultipleRecordBuilders { .. })
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::MultipleOldRecordBuilders { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hanging_record_builder() {
|
||||
fn hanging_old_record_builder() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
{ a: <- apply "a" }
|
||||
|
@ -846,15 +849,189 @@ mod test_can {
|
|||
assert_eq!(problems.len(), 1);
|
||||
assert!(problems.iter().all(|problem| matches!(
|
||||
problem,
|
||||
Problem::RuntimeError(roc_problem::can::RuntimeError::UnappliedRecordBuilder { .. })
|
||||
Problem::RuntimeError(roc_problem::can::RuntimeError::UnappliedOldRecordBuilder { .. })
|
||||
)));
|
||||
|
||||
assert!(matches!(
|
||||
loc_expr.value,
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::UnappliedRecordBuilder { .. })
|
||||
Expr::RuntimeError(roc_problem::can::RuntimeError::UnappliedOldRecordBuilder { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_record_builder_desugar() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
map2 = \a, b, combine -> combine a b
|
||||
double = \n -> n * 2
|
||||
|
||||
c = 3
|
||||
|
||||
{ map2 <-
|
||||
a: 1,
|
||||
b: double 2,
|
||||
c
|
||||
}
|
||||
"#
|
||||
);
|
||||
let arena = Bump::new();
|
||||
let out = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(out.problems.len(), 0);
|
||||
|
||||
// Assert that we desugar to:
|
||||
//
|
||||
// map2
|
||||
// (1)
|
||||
// (map2
|
||||
// (double 2)
|
||||
// (c)
|
||||
// (\#a, #b -> (#a, #b))
|
||||
// )
|
||||
// (\a, (b, c) -> { a: #a, b: #b, c: #c })
|
||||
|
||||
let first_map2_args = assert_func_call(
|
||||
&out.loc_expr.value,
|
||||
"map2",
|
||||
CalledVia::RecordBuilder,
|
||||
&out.interns,
|
||||
);
|
||||
let (first_arg, second_arg, third_arg) = match &first_map2_args[..] {
|
||||
[first, second, third] => (&first.1.value, &second.1.value, &third.1.value),
|
||||
_ => panic!("map2 didn't receive three arguments"),
|
||||
};
|
||||
|
||||
assert_num_value(first_arg, 1);
|
||||
|
||||
let inner_map2_args =
|
||||
assert_func_call(second_arg, "map2", CalledVia::RecordBuilder, &out.interns);
|
||||
let (first_inner_arg, second_inner_arg, third_inner_arg) = match &inner_map2_args[..] {
|
||||
[first, second, third] => (&first.1.value, &second.1.value, &third.1.value),
|
||||
_ => panic!("inner map2 didn't receive three arguments"),
|
||||
};
|
||||
|
||||
let double_args =
|
||||
assert_func_call(first_inner_arg, "double", CalledVia::Space, &out.interns);
|
||||
assert_eq!(double_args.len(), 1);
|
||||
assert_num_value(&double_args[0].1.value, 2);
|
||||
|
||||
assert_var_usage(second_inner_arg, "c", &out.interns);
|
||||
|
||||
match third_inner_arg {
|
||||
Expr::Closure(ClosureData {
|
||||
arguments,
|
||||
loc_body,
|
||||
..
|
||||
}) => {
|
||||
assert_eq!(arguments.len(), 2);
|
||||
assert_pattern_name(
|
||||
&arguments[0].2.value,
|
||||
"#record_builder_closure_arg_a",
|
||||
&out.interns,
|
||||
);
|
||||
assert_pattern_name(
|
||||
&arguments[1].2.value,
|
||||
"#record_builder_closure_arg_b",
|
||||
&out.interns,
|
||||
);
|
||||
|
||||
match &loc_body.value {
|
||||
Expr::Tuple { elems, .. } => {
|
||||
assert_eq!(elems.len(), 2);
|
||||
assert_var_usage(
|
||||
&elems[0].1.value,
|
||||
"#record_builder_closure_arg_a",
|
||||
&out.interns,
|
||||
);
|
||||
assert_var_usage(
|
||||
&elems[1].1.value,
|
||||
"#record_builder_closure_arg_b",
|
||||
&out.interns,
|
||||
);
|
||||
}
|
||||
_ => panic!("Closure body was not a tuple"),
|
||||
}
|
||||
}
|
||||
_ => panic!("inner map2's combiner was not a closure"),
|
||||
}
|
||||
|
||||
match third_arg {
|
||||
Expr::Closure(ClosureData {
|
||||
arguments,
|
||||
loc_body,
|
||||
..
|
||||
}) => {
|
||||
assert_eq!(arguments.len(), 2);
|
||||
assert_pattern_name(&arguments[0].2.value, "#a", &out.interns);
|
||||
match &arguments[1].2.value {
|
||||
Pattern::TupleDestructure { destructs, .. } => {
|
||||
assert_eq!(destructs.len(), 2);
|
||||
assert_pattern_name(&destructs[0].value.typ.1.value, "#b", &out.interns);
|
||||
assert_pattern_name(&destructs[1].value.typ.1.value, "#c", &out.interns);
|
||||
}
|
||||
_ => panic!("Second arg to builder func was not a tuple destructure"),
|
||||
}
|
||||
|
||||
match &loc_body.value {
|
||||
Expr::Record { fields, .. } => {
|
||||
assert_eq!(fields.len(), 3);
|
||||
|
||||
assert_eq!(get_field_var_sym(fields, "a").as_str(&out.interns), "#a");
|
||||
assert_eq!(get_field_var_sym(fields, "b").as_str(&out.interns), "#b");
|
||||
assert_eq!(get_field_var_sym(fields, "c").as_str(&out.interns), "#c");
|
||||
}
|
||||
_ => panic!("Closure body was not a tuple"),
|
||||
}
|
||||
}
|
||||
_ => panic!("inner map2's combiner was not a closure"),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_num_value(expr: &Expr, num: usize) {
|
||||
match expr {
|
||||
Expr::Num(_, num_str, _, _) => {
|
||||
assert_eq!(&**num_str, &num.to_string())
|
||||
}
|
||||
_ => panic!("Expr wasn't a Num with value {num}: {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_var_usage(expr: &Expr, name: &str, interns: &roc_module::symbol::Interns) {
|
||||
match expr {
|
||||
Expr::Var(sym, _) => assert_eq!(sym.as_str(interns), name),
|
||||
_ => panic!("Expr was not a variable usage: {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_func_call(
|
||||
expr: &Expr,
|
||||
name: &str,
|
||||
called_via: CalledVia,
|
||||
interns: &roc_module::symbol::Interns,
|
||||
) -> Vec<(Variable, Loc<Expr>)> {
|
||||
match expr {
|
||||
Expr::LetNonRec(_, loc_expr) => {
|
||||
assert_func_call(&loc_expr.value, name, called_via, interns)
|
||||
}
|
||||
Expr::Call(fun, args, called) if called == &called_via => {
|
||||
match &fun.1.value {
|
||||
Expr::Var(sym, _) => assert_eq!(sym.as_str(interns), name),
|
||||
_ => panic!("Builder didn't desugar with mapper at front"),
|
||||
};
|
||||
|
||||
args.clone()
|
||||
}
|
||||
_ => panic!("Expr was not a RecordBuilder Call: {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_pattern_name(pattern: &Pattern, name: &str, interns: &roc_module::symbol::Interns) {
|
||||
match pattern {
|
||||
Pattern::Identifier(sym) => assert_eq!(sym.as_str(interns), name),
|
||||
_ => panic!("Pattern was not an identifier: {:?}", pattern),
|
||||
}
|
||||
}
|
||||
|
||||
// TAIL CALLS
|
||||
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
|
||||
match expr {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ use crate::{
|
|||
};
|
||||
use roc_parse::ast::{
|
||||
AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, ImplementsAbilities,
|
||||
ImplementsAbility, ImplementsClause, RecordBuilderField, Tag, TypeAnnotation, TypeHeader,
|
||||
ImplementsAbility, ImplementsClause, OldRecordBuilderField, Tag, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
|
@ -505,7 +505,7 @@ fn format_assigned_field_help<T>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for RecordBuilderField<'a> {
|
||||
impl<'a> Formattable for OldRecordBuilderField<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
is_multiline_record_builder_field_help(self)
|
||||
}
|
||||
|
@ -516,8 +516,8 @@ impl<'a> Formattable for RecordBuilderField<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_multiline_record_builder_field_help(afield: &RecordBuilderField<'_>) -> bool {
|
||||
use self::RecordBuilderField::*;
|
||||
fn is_multiline_record_builder_field_help(afield: &OldRecordBuilderField<'_>) -> bool {
|
||||
use self::OldRecordBuilderField::*;
|
||||
|
||||
match afield {
|
||||
Value(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(),
|
||||
|
@ -531,12 +531,12 @@ fn is_multiline_record_builder_field_help(afield: &RecordBuilderField<'_>) -> bo
|
|||
}
|
||||
|
||||
fn format_record_builder_field_help(
|
||||
zelf: &RecordBuilderField,
|
||||
zelf: &OldRecordBuilderField,
|
||||
buf: &mut Buf,
|
||||
indent: u16,
|
||||
is_multiline: bool,
|
||||
) {
|
||||
use self::RecordBuilderField::*;
|
||||
use self::OldRecordBuilderField::*;
|
||||
|
||||
match zelf {
|
||||
Value(name, spaces, ann) => {
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::Buf;
|
|||
use roc_module::called_via::{self, BinOp};
|
||||
use roc_parse::ast::{
|
||||
is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
|
||||
Pattern, RecordBuilderField, WhenBranch,
|
||||
OldRecordBuilderField, Pattern, WhenBranch,
|
||||
};
|
||||
use roc_parse::ast::{StrLiteral, StrSegment};
|
||||
use roc_parse::ident::Accessor;
|
||||
|
@ -85,8 +85,11 @@ impl<'a> Formattable for Expr<'a> {
|
|||
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
|
||||
expr: loc_subexpr, ..
|
||||
})
|
||||
| MultipleRecordBuilders(loc_subexpr)
|
||||
| UnappliedRecordBuilder(loc_subexpr) => loc_subexpr.is_multiline(),
|
||||
| MultipleOldRecordBuilders(loc_subexpr)
|
||||
| UnappliedOldRecordBuilder(loc_subexpr)
|
||||
| EmptyRecordBuilder(loc_subexpr)
|
||||
| SingleFieldRecordBuilder(loc_subexpr)
|
||||
| OptionalFieldInRecordBuilder(_, loc_subexpr) => loc_subexpr.is_multiline(),
|
||||
|
||||
ParensAround(subexpr) => subexpr.is_multiline(),
|
||||
|
||||
|
@ -109,7 +112,8 @@ impl<'a> Formattable for Expr<'a> {
|
|||
Record(fields) => is_collection_multiline(fields),
|
||||
Tuple(fields) => is_collection_multiline(fields),
|
||||
RecordUpdate { fields, .. } => is_collection_multiline(fields),
|
||||
RecordBuilder(fields) => is_collection_multiline(fields),
|
||||
OldRecordBuilder(fields) => is_collection_multiline(fields),
|
||||
RecordBuilder { fields, .. } => is_collection_multiline(fields),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +241,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
Expr::Tuple(_)
|
||||
| Expr::List(_)
|
||||
| Expr::Record(_)
|
||||
| Expr::RecordBuilder(_)
|
||||
| Expr::OldRecordBuilder(_)
|
||||
)
|
||||
&& a.extract_spaces().before == [CommentOrNewline::Newline]
|
||||
})
|
||||
|
@ -365,14 +369,24 @@ impl<'a> Formattable for Expr<'a> {
|
|||
RecordUpdate { update, fields } => {
|
||||
fmt_record_like(
|
||||
buf,
|
||||
Some(*update),
|
||||
Some(RecordPrefix::Update(update)),
|
||||
*fields,
|
||||
indent,
|
||||
format_assigned_field_multiline,
|
||||
assigned_field_to_space_before,
|
||||
);
|
||||
}
|
||||
RecordBuilder(fields) => {
|
||||
RecordBuilder { mapper, fields } => {
|
||||
fmt_record_like(
|
||||
buf,
|
||||
Some(RecordPrefix::Mapper(mapper)),
|
||||
*fields,
|
||||
indent,
|
||||
format_assigned_field_multiline,
|
||||
assigned_field_to_space_before,
|
||||
);
|
||||
}
|
||||
OldRecordBuilder(fields) => {
|
||||
fmt_record_like(
|
||||
buf,
|
||||
None,
|
||||
|
@ -520,8 +534,11 @@ impl<'a> Formattable for Expr<'a> {
|
|||
}
|
||||
MalformedClosure => {}
|
||||
PrecedenceConflict { .. } => {}
|
||||
MultipleRecordBuilders { .. } => {}
|
||||
UnappliedRecordBuilder { .. } => {}
|
||||
MultipleOldRecordBuilders { .. } => {}
|
||||
UnappliedOldRecordBuilder { .. } => {}
|
||||
EmptyRecordBuilder { .. } => {}
|
||||
SingleFieldRecordBuilder { .. } => {}
|
||||
OptionalFieldInRecordBuilder(_, _) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -582,7 +599,7 @@ fn is_outdentable(expr: &Expr) -> bool {
|
|||
Expr::Tuple(_)
|
||||
| Expr::List(_)
|
||||
| Expr::Record(_)
|
||||
| Expr::RecordBuilder(_)
|
||||
| Expr::OldRecordBuilder(_)
|
||||
| Expr::Closure(..)
|
||||
)
|
||||
}
|
||||
|
@ -1366,9 +1383,14 @@ fn pattern_needs_parens_when_backpassing(pat: &Pattern) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
enum RecordPrefix<'a> {
|
||||
Update(&'a Loc<Expr<'a>>),
|
||||
Mapper(&'a Loc<Expr<'a>>),
|
||||
}
|
||||
|
||||
fn fmt_record_like<'a, Field, Format, ToSpaceBefore>(
|
||||
buf: &mut Buf,
|
||||
update: Option<&'a Loc<Expr<'a>>>,
|
||||
prefix: Option<RecordPrefix<'a>>,
|
||||
fields: Collection<'a, Loc<Field>>,
|
||||
indent: u16,
|
||||
format_field_multiline: Format,
|
||||
|
@ -1381,22 +1403,27 @@ fn fmt_record_like<'a, Field, Format, ToSpaceBefore>(
|
|||
let loc_fields = fields.items;
|
||||
let final_comments = fields.final_comments();
|
||||
buf.indent(indent);
|
||||
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) && update.is_none() {
|
||||
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) && prefix.is_none() {
|
||||
buf.push_str("{}");
|
||||
} else {
|
||||
buf.push('{');
|
||||
|
||||
match update {
|
||||
match prefix {
|
||||
None => {}
|
||||
// We are presuming this to be a Var()
|
||||
// If it wasnt a Var() we would not have made
|
||||
// it this far. For example "{ 4 & hello = 9 }"
|
||||
// doesnt make sense.
|
||||
Some(record_var) => {
|
||||
Some(RecordPrefix::Update(record_var)) => {
|
||||
buf.spaces(1);
|
||||
record_var.format(buf, indent);
|
||||
buf.push_str(" &");
|
||||
}
|
||||
Some(RecordPrefix::Mapper(mapper_var)) => {
|
||||
buf.spaces(1);
|
||||
mapper_var.format(buf, indent);
|
||||
buf.push_str(" <-");
|
||||
}
|
||||
}
|
||||
|
||||
let is_multiline = loc_fields.iter().any(|loc_field| loc_field.is_multiline())
|
||||
|
@ -1554,11 +1581,11 @@ fn assigned_field_to_space_before<'a, T>(
|
|||
|
||||
fn format_record_builder_field_multiline(
|
||||
buf: &mut Buf,
|
||||
field: &RecordBuilderField,
|
||||
field: &OldRecordBuilderField,
|
||||
indent: u16,
|
||||
separator_prefix: &str,
|
||||
) {
|
||||
use self::RecordBuilderField::*;
|
||||
use self::OldRecordBuilderField::*;
|
||||
match field {
|
||||
Value(name, spaces, ann) => {
|
||||
buf.newline();
|
||||
|
@ -1651,10 +1678,10 @@ fn format_record_builder_field_multiline(
|
|||
}
|
||||
|
||||
fn record_builder_field_to_space_before<'a>(
|
||||
field: &'a RecordBuilderField<'a>,
|
||||
) -> Option<(&RecordBuilderField<'a>, &'a [CommentOrNewline<'a>])> {
|
||||
field: &'a OldRecordBuilderField<'a>,
|
||||
) -> Option<(&OldRecordBuilderField<'a>, &'a [CommentOrNewline<'a>])> {
|
||||
match field {
|
||||
RecordBuilderField::SpaceBefore(sub_field, spaces) => Some((sub_field, spaces)),
|
||||
OldRecordBuilderField::SpaceBefore(sub_field, spaces) => Some((sub_field, spaces)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use roc_parse::{
|
|||
AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias,
|
||||
ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation,
|
||||
IngestedFileImport, Module, ModuleImport, ModuleImportParams, Pattern, PatternAs,
|
||||
RecordBuilderField, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||
IngestedFileImport, Module, ModuleImport, ModuleImportParams, OldRecordBuilderField,
|
||||
Pattern, PatternAs, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
|
@ -715,26 +715,26 @@ impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for Assi
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for RecordBuilderField<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
RecordBuilderField::Value(a, _, c) => RecordBuilderField::Value(
|
||||
OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
RecordBuilderField::ApplyValue(a, _, _, c) => RecordBuilderField::ApplyValue(
|
||||
OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue(
|
||||
a.remove_spaces(arena),
|
||||
&[],
|
||||
&[],
|
||||
arena.alloc(c.remove_spaces(arena)),
|
||||
),
|
||||
RecordBuilderField::LabelOnly(a) => {
|
||||
RecordBuilderField::LabelOnly(a.remove_spaces(arena))
|
||||
OldRecordBuilderField::LabelOnly(a) => {
|
||||
OldRecordBuilderField::LabelOnly(a.remove_spaces(arena))
|
||||
}
|
||||
RecordBuilderField::Malformed(a) => RecordBuilderField::Malformed(a),
|
||||
RecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
RecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a),
|
||||
OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -788,7 +788,11 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
|||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||
Expr::RecordBuilder(a) => Expr::RecordBuilder(a.remove_spaces(arena)),
|
||||
Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)),
|
||||
Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder {
|
||||
mapper: arena.alloc(mapper.remove_spaces(arena)),
|
||||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
|
||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||
Expr::Underscore(a) => Expr::Underscore(a),
|
||||
|
@ -855,8 +859,13 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
|||
Expr::MalformedClosure => Expr::MalformedClosure,
|
||||
Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a),
|
||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||
Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
|
||||
Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),
|
||||
Expr::MultipleOldRecordBuilders(a) => Expr::MultipleOldRecordBuilders(a),
|
||||
Expr::UnappliedOldRecordBuilder(a) => Expr::UnappliedOldRecordBuilder(a),
|
||||
Expr::EmptyRecordBuilder(a) => Expr::EmptyRecordBuilder(a),
|
||||
Expr::SingleFieldRecordBuilder(a) => Expr::SingleFieldRecordBuilder(a),
|
||||
Expr::OptionalFieldInRecordBuilder(name, a) => {
|
||||
Expr::OptionalFieldInRecordBuilder(name, a)
|
||||
}
|
||||
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||
Expr::SingleQuote(a) => Expr::Num(a),
|
||||
|
|
|
@ -1082,6 +1082,8 @@ impl<
|
|||
arg_layouts: &[InLayout<'a>],
|
||||
ret_layout: &InLayout<'a>,
|
||||
) {
|
||||
debug_assert_eq!(args.len(), arg_layouts.len());
|
||||
|
||||
// Save used caller saved regs.
|
||||
self.storage_manager
|
||||
.push_used_caller_saved_regs_to_stack(&mut self.buf);
|
||||
|
@ -2278,6 +2280,25 @@ impl<
|
|||
refcount_proc_name
|
||||
}
|
||||
|
||||
fn build_indirect_inc_n(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
.all_ident_ids
|
||||
.get_mut(&self.env.module_id)
|
||||
.unwrap();
|
||||
|
||||
let (refcount_proc_name, linker_data) = self.helper_proc_gen.gen_refcount_proc(
|
||||
ident_ids,
|
||||
self.layout_interner,
|
||||
layout,
|
||||
HelperOp::IndirectIncN,
|
||||
);
|
||||
|
||||
self.helper_proc_symbols_mut().extend(linker_data);
|
||||
|
||||
refcount_proc_name
|
||||
}
|
||||
|
||||
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let ident_ids = self
|
||||
.interns
|
||||
|
@ -2310,17 +2331,6 @@ impl<
|
|||
.unwrap();
|
||||
|
||||
let caller_proc = match higher_order.op {
|
||||
HigherOrder::ListMap { .. }
|
||||
| HigherOrder::ListMap2 { .. }
|
||||
| HigherOrder::ListMap3 { .. }
|
||||
| HigherOrder::ListMap4 { .. } => CallerProc::new_list_map(
|
||||
self.env.arena,
|
||||
self.env.module_id,
|
||||
ident_ids,
|
||||
self.layout_interner,
|
||||
&higher_order.passed_function,
|
||||
higher_order.closure_env_layout,
|
||||
),
|
||||
HigherOrder::ListSortWith { .. } => CallerProc::new_compare(
|
||||
self.env.arena,
|
||||
self.env.module_id,
|
||||
|
@ -2348,7 +2358,7 @@ impl<
|
|||
|
||||
// function pointer to a function that takes a pointer, and increments
|
||||
let inc_n_data = if let Some(closure_env_layout) = higher_order.closure_env_layout {
|
||||
self.increment_fn_pointer(closure_env_layout)
|
||||
self.increment_n_fn_pointer(closure_env_layout)
|
||||
} else {
|
||||
// null pointer
|
||||
self.load_literal_i64(&Symbol::DEV_TMP, 0);
|
||||
|
@ -2374,394 +2384,6 @@ impl<
|
|||
let usize_ = Layout::U64;
|
||||
|
||||
match higher_order.op {
|
||||
HigherOrder::ListMap { xs } => {
|
||||
let old_element_layout = argument_layouts[0];
|
||||
let new_element_layout = higher_order.passed_function.return_layout;
|
||||
|
||||
let input_list_layout = LayoutRepr::Builtin(Builtin::List(old_element_layout));
|
||||
let input_list_in_layout = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout);
|
||||
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
let old_element_width = self.debug_symbol("old_element_width");
|
||||
let new_element_width = self.debug_symbol("new_element_width");
|
||||
|
||||
self.load_layout_alignment(new_element_layout, alignment);
|
||||
|
||||
self.load_layout_stack_size(old_element_layout, old_element_width);
|
||||
self.load_layout_stack_size(new_element_layout, new_element_width);
|
||||
|
||||
self.build_fn_pointer(&caller, caller_string);
|
||||
|
||||
// we pass a null pointer when the data is not owned. the zig code must not call this!
|
||||
let data_is_owned = higher_order.closure_env_layout.is_some()
|
||||
&& higher_order.passed_function.owns_captured_environment;
|
||||
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP2,
|
||||
&Layout::BOOL,
|
||||
&Literal::Bool(data_is_owned),
|
||||
);
|
||||
|
||||
// list: RocList,
|
||||
// caller: Caller1,
|
||||
// data: Opaque,
|
||||
// inc_n_data: IncN,
|
||||
// data_is_owned: bool,
|
||||
// alignment: u32,
|
||||
// old_element_width: usize,
|
||||
// new_element_width: usize,
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
Symbol::DEV_TMP2,
|
||||
alignment,
|
||||
old_element_width,
|
||||
new_element_width,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
input_list_in_layout,
|
||||
ptr,
|
||||
ptr,
|
||||
ptr,
|
||||
Layout::BOOL,
|
||||
Layout::U32,
|
||||
usize_,
|
||||
usize_,
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
bitcode::LIST_MAP.to_string(),
|
||||
&arguments,
|
||||
&layouts,
|
||||
ret_layout,
|
||||
*dst,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
}
|
||||
HigherOrder::ListMap2 { xs, ys } => {
|
||||
let old_element_layout1 = argument_layouts[0];
|
||||
let old_element_layout2 = argument_layouts[1];
|
||||
let new_element_layout = higher_order.passed_function.return_layout;
|
||||
|
||||
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
|
||||
let input_list_in_layout1 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout1);
|
||||
|
||||
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
|
||||
let input_list_in_layout2 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout2);
|
||||
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
let old_element_width1 = self.debug_symbol("old_element_width1");
|
||||
let old_element_width2 = self.debug_symbol("old_element_width2");
|
||||
let new_element_width = self.debug_symbol("new_element_width");
|
||||
|
||||
self.load_layout_alignment(new_element_layout, alignment);
|
||||
|
||||
self.load_layout_stack_size(old_element_layout1, old_element_width1);
|
||||
self.load_layout_stack_size(old_element_layout2, old_element_width2);
|
||||
|
||||
self.load_layout_stack_size(new_element_layout, new_element_width);
|
||||
|
||||
let dec1 = self.decrement_fn_pointer(old_element_layout1);
|
||||
let dec2 = self.decrement_fn_pointer(old_element_layout2);
|
||||
|
||||
self.build_fn_pointer(&caller, caller_string);
|
||||
|
||||
// we pass a null pointer when the data is not owned. the zig code must not call this!
|
||||
let data_is_owned = higher_order.closure_env_layout.is_some()
|
||||
&& higher_order.passed_function.owns_captured_environment;
|
||||
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP2,
|
||||
&Layout::BOOL,
|
||||
&Literal::Bool(data_is_owned),
|
||||
);
|
||||
|
||||
// list1: RocList,
|
||||
// list2: RocList,
|
||||
// caller: Caller1,
|
||||
// data: Opaque,
|
||||
// inc_n_data: IncN,
|
||||
// data_is_owned: bool,
|
||||
// alignment: u32,
|
||||
// old_element_width1: usize,
|
||||
// old_element_width2: usize,
|
||||
// new_element_width: usize,
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
ys,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
Symbol::DEV_TMP2,
|
||||
alignment,
|
||||
old_element_width1,
|
||||
old_element_width2,
|
||||
new_element_width,
|
||||
dec1,
|
||||
dec2,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
input_list_in_layout1,
|
||||
input_list_in_layout2,
|
||||
ptr,
|
||||
ptr,
|
||||
ptr,
|
||||
Layout::BOOL,
|
||||
Layout::U32,
|
||||
usize_,
|
||||
usize_,
|
||||
usize_,
|
||||
ptr, // dec1
|
||||
ptr, // dec2
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
bitcode::LIST_MAP2.to_string(),
|
||||
&arguments,
|
||||
&layouts,
|
||||
ret_layout,
|
||||
*dst,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
}
|
||||
HigherOrder::ListMap3 { xs, ys, zs } => {
|
||||
let old_element_layout1 = argument_layouts[0];
|
||||
let old_element_layout2 = argument_layouts[1];
|
||||
let old_element_layout3 = argument_layouts[2];
|
||||
let new_element_layout = higher_order.passed_function.return_layout;
|
||||
|
||||
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
|
||||
let input_list_in_layout1 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout1);
|
||||
|
||||
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
|
||||
let input_list_in_layout2 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout2);
|
||||
|
||||
let input_list_layout3 = LayoutRepr::Builtin(Builtin::List(old_element_layout3));
|
||||
let input_list_in_layout3 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout3);
|
||||
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
let old_element_width1 = self.debug_symbol("old_element_width1");
|
||||
let old_element_width2 = self.debug_symbol("old_element_width2");
|
||||
let old_element_width3 = self.debug_symbol("old_element_width3");
|
||||
let new_element_width = self.debug_symbol("new_element_width");
|
||||
|
||||
self.load_layout_alignment(new_element_layout, alignment);
|
||||
|
||||
self.load_layout_stack_size(old_element_layout1, old_element_width1);
|
||||
self.load_layout_stack_size(old_element_layout2, old_element_width2);
|
||||
self.load_layout_stack_size(old_element_layout3, old_element_width3);
|
||||
|
||||
self.load_layout_stack_size(new_element_layout, new_element_width);
|
||||
|
||||
let dec1 = self.decrement_fn_pointer(old_element_layout1);
|
||||
let dec2 = self.decrement_fn_pointer(old_element_layout2);
|
||||
let dec3 = self.decrement_fn_pointer(old_element_layout3);
|
||||
|
||||
self.build_fn_pointer(&caller, caller_string);
|
||||
|
||||
// we pass a null pointer when the data is not owned. the zig code must not call this!
|
||||
let data_is_owned = higher_order.closure_env_layout.is_some()
|
||||
&& higher_order.passed_function.owns_captured_environment;
|
||||
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP2,
|
||||
&Layout::BOOL,
|
||||
&Literal::Bool(data_is_owned),
|
||||
);
|
||||
|
||||
// list1: RocList,
|
||||
// list2: RocList,
|
||||
// caller: Caller1,
|
||||
// data: Opaque,
|
||||
// inc_n_data: IncN,
|
||||
// data_is_owned: bool,
|
||||
// alignment: u32,
|
||||
// old_element_width1: usize,
|
||||
// old_element_width2: usize,
|
||||
// new_element_width: usize,
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
ys,
|
||||
zs,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
Symbol::DEV_TMP2,
|
||||
alignment,
|
||||
old_element_width1,
|
||||
old_element_width2,
|
||||
old_element_width3,
|
||||
new_element_width,
|
||||
dec1,
|
||||
dec2,
|
||||
dec3,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
input_list_in_layout1,
|
||||
input_list_in_layout2,
|
||||
input_list_in_layout3,
|
||||
ptr,
|
||||
ptr,
|
||||
ptr,
|
||||
Layout::BOOL,
|
||||
Layout::U32,
|
||||
usize_, // old_element_width_1
|
||||
usize_, // old_element_width_2
|
||||
usize_, // old_element_width_3
|
||||
usize_, // new_element_width
|
||||
ptr, // dec1
|
||||
ptr, // dec2
|
||||
ptr, // dec3
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
bitcode::LIST_MAP3.to_string(),
|
||||
&arguments,
|
||||
&layouts,
|
||||
ret_layout,
|
||||
*dst,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
}
|
||||
HigherOrder::ListMap4 { xs, ys, zs, ws } => {
|
||||
let old_element_layout1 = argument_layouts[0];
|
||||
let old_element_layout2 = argument_layouts[1];
|
||||
let old_element_layout3 = argument_layouts[2];
|
||||
let old_element_layout4 = argument_layouts[3];
|
||||
let new_element_layout = higher_order.passed_function.return_layout;
|
||||
|
||||
let input_list_layout1 = LayoutRepr::Builtin(Builtin::List(old_element_layout1));
|
||||
let input_list_in_layout1 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout1);
|
||||
|
||||
let input_list_layout2 = LayoutRepr::Builtin(Builtin::List(old_element_layout2));
|
||||
let input_list_in_layout2 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout2);
|
||||
|
||||
let input_list_layout3 = LayoutRepr::Builtin(Builtin::List(old_element_layout3));
|
||||
let input_list_in_layout3 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout3);
|
||||
|
||||
let input_list_layout4 = LayoutRepr::Builtin(Builtin::List(old_element_layout4));
|
||||
let input_list_in_layout4 = self
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(input_list_layout4);
|
||||
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
let old_element_width1 = self.debug_symbol("old_element_width1");
|
||||
let old_element_width2 = self.debug_symbol("old_element_width2");
|
||||
let old_element_width3 = self.debug_symbol("old_element_width3");
|
||||
let old_element_width4 = self.debug_symbol("old_element_width4");
|
||||
let new_element_width = self.debug_symbol("new_element_width");
|
||||
|
||||
self.load_layout_alignment(new_element_layout, alignment);
|
||||
|
||||
self.load_layout_stack_size(old_element_layout1, old_element_width1);
|
||||
self.load_layout_stack_size(old_element_layout2, old_element_width2);
|
||||
self.load_layout_stack_size(old_element_layout3, old_element_width3);
|
||||
self.load_layout_stack_size(old_element_layout4, old_element_width4);
|
||||
|
||||
self.load_layout_stack_size(new_element_layout, new_element_width);
|
||||
|
||||
let dec1 = self.decrement_fn_pointer(old_element_layout1);
|
||||
let dec2 = self.decrement_fn_pointer(old_element_layout2);
|
||||
let dec3 = self.decrement_fn_pointer(old_element_layout3);
|
||||
let dec4 = self.decrement_fn_pointer(old_element_layout4);
|
||||
|
||||
self.build_fn_pointer(&caller, caller_string);
|
||||
|
||||
// we pass a null pointer when the data is not owned. the zig code must not call this!
|
||||
let data_is_owned = higher_order.closure_env_layout.is_some()
|
||||
&& higher_order.passed_function.owns_captured_environment;
|
||||
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP2,
|
||||
&Layout::BOOL,
|
||||
&Literal::Bool(data_is_owned),
|
||||
);
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
ys,
|
||||
zs,
|
||||
ws,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
Symbol::DEV_TMP2,
|
||||
alignment,
|
||||
old_element_width1,
|
||||
old_element_width2,
|
||||
old_element_width3,
|
||||
old_element_width4,
|
||||
new_element_width,
|
||||
dec1,
|
||||
dec2,
|
||||
dec3,
|
||||
dec4,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
input_list_in_layout1,
|
||||
input_list_in_layout2,
|
||||
input_list_in_layout3,
|
||||
input_list_in_layout4,
|
||||
ptr,
|
||||
ptr,
|
||||
ptr,
|
||||
Layout::BOOL,
|
||||
Layout::U32,
|
||||
usize_, // old_element_width_1
|
||||
usize_, // old_element_width_2
|
||||
usize_, // old_element_width_3
|
||||
usize_, // old_element_width_4
|
||||
usize_, // new_element_width
|
||||
ptr, // dec1
|
||||
ptr, // dec2
|
||||
ptr, // dec3
|
||||
ptr, // dec4
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
bitcode::LIST_MAP4.to_string(),
|
||||
&arguments,
|
||||
&layouts,
|
||||
ret_layout,
|
||||
*dst,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
}
|
||||
HigherOrder::ListSortWith { xs } => {
|
||||
let element_layout = argument_layouts[0];
|
||||
|
||||
|
@ -2788,6 +2410,12 @@ impl<
|
|||
&Literal::Bool(data_is_owned),
|
||||
);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(element_layout);
|
||||
|
||||
// input: RocList,
|
||||
// caller: CompareFn,
|
||||
// data: Opaque,
|
||||
|
@ -2795,6 +2423,9 @@ impl<
|
|||
// data_is_owned: bool,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// inc: Inc,
|
||||
// dec: Dec,
|
||||
|
||||
let arguments = [
|
||||
xs,
|
||||
|
@ -2804,6 +2435,9 @@ impl<
|
|||
Symbol::DEV_TMP2,
|
||||
alignment,
|
||||
element_width,
|
||||
Symbol::DEV_TMP3,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
];
|
||||
|
||||
let layouts = [
|
||||
|
@ -2814,6 +2448,9 @@ impl<
|
|||
Layout::BOOL,
|
||||
Layout::U32,
|
||||
usize_,
|
||||
Layout::BOOL,
|
||||
usize_,
|
||||
usize_,
|
||||
];
|
||||
|
||||
self.build_fn_call_stack_return(
|
||||
|
@ -2826,6 +2463,7 @@ impl<
|
|||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2852,6 +2490,12 @@ impl<
|
|||
// Load element_width argument (usize).
|
||||
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP2);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
self.storage_manager
|
||||
|
@ -2863,11 +2507,25 @@ impl<
|
|||
Symbol::DEV_TMP,
|
||||
// element_width
|
||||
Symbol::DEV_TMP2,
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
// inc
|
||||
inc_elem_fn,
|
||||
// dec
|
||||
dec_elem_fn,
|
||||
];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
ret_layout,
|
||||
Layout::U32,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
usize_layout,
|
||||
usize_layout,
|
||||
];
|
||||
let lowlevel_arg_layouts = [ret_layout, Layout::U32, Layout::U64];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
bitcode::LIST_CLONE.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -2875,17 +2533,18 @@ impl<
|
|||
);
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
|
||||
// Copy from list to the output record.
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
base_offset,
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
&ret_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
}
|
||||
|
||||
fn build_list_with_capacity(
|
||||
|
@ -2902,6 +2561,11 @@ impl<
|
|||
// Load element_width argument (usize).
|
||||
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP2);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
self.storage_manager
|
||||
|
@ -2913,11 +2577,22 @@ impl<
|
|||
Symbol::DEV_TMP,
|
||||
// element_width
|
||||
Symbol::DEV_TMP2,
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
// Inc element fn
|
||||
inc_elem_fn,
|
||||
];
|
||||
let layout_usize = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
capacity_layout,
|
||||
Layout::U32,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
layout_usize,
|
||||
];
|
||||
let lowlevel_arg_layouts = [capacity_layout, Layout::U32, Layout::U64];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
bitcode::LIST_WITH_CAPACITY.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -2925,17 +2600,18 @@ impl<
|
|||
);
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
|
||||
// Copy from list to the output record.
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
base_offset,
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
}
|
||||
|
||||
fn build_list_reserve(
|
||||
|
@ -2959,12 +2635,15 @@ impl<
|
|||
_ => unreachable!(),
|
||||
};
|
||||
self.load_layout_stack_size(element_layout, Symbol::DEV_TMP2);
|
||||
self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(element_layout);
|
||||
|
||||
// Load UpdateMode.Immutable argument (0u8)
|
||||
let u8_layout = Layout::U8;
|
||||
let update_mode = 0u8;
|
||||
self.load_literal(
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
&u8_layout,
|
||||
&Literal::Int((update_mode as i128).to_ne_bytes()),
|
||||
);
|
||||
|
@ -2982,20 +2661,26 @@ impl<
|
|||
spare,
|
||||
// element_width
|
||||
Symbol::DEV_TMP2,
|
||||
// update_mode
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
|
||||
// Inc element fn
|
||||
inc_elem_fn,
|
||||
// update_mode
|
||||
Symbol::DEV_TMP4,
|
||||
];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
spare_layout,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
usize_layout,
|
||||
u8_layout,
|
||||
];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP4,
|
||||
&Symbol::DEV_TMP5,
|
||||
bitcode::LIST_RESERVE.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -3004,17 +2689,18 @@ impl<
|
|||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
|
||||
// Return list value from fn call
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
base_offset,
|
||||
&Symbol::DEV_TMP4,
|
||||
&Symbol::DEV_TMP5,
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
self.free_symbol(&Symbol::DEV_TMP5);
|
||||
}
|
||||
|
||||
fn build_list_append_unsafe(
|
||||
|
@ -3155,6 +2841,12 @@ impl<
|
|||
// Load the elements size.
|
||||
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
self.storage_manager
|
||||
|
@ -3188,7 +2880,7 @@ impl<
|
|||
// Load address of output element into register.
|
||||
let reg = self
|
||||
.storage_manager
|
||||
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP4);
|
||||
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP5);
|
||||
ASM::add_reg64_reg64_imm32(&mut self.buf, reg, CC::BASE_PTR_REG, out_elem_offset);
|
||||
|
||||
let lowlevel_args = bumpalo::vec![
|
||||
|
@ -3199,6 +2891,9 @@ impl<
|
|||
Symbol::DEV_TMP2,
|
||||
Symbol::DEV_TMP3,
|
||||
Symbol::DEV_TMP4,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
Symbol::DEV_TMP5,
|
||||
];
|
||||
let lowlevel_arg_layouts = [
|
||||
list_layout,
|
||||
|
@ -3206,11 +2901,15 @@ impl<
|
|||
index_layout,
|
||||
u64_layout,
|
||||
u64_layout,
|
||||
Layout::BOOL,
|
||||
u64_layout,
|
||||
u64_layout,
|
||||
u64_layout,
|
||||
];
|
||||
|
||||
let out = self.debug_symbol("out");
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP5,
|
||||
&out,
|
||||
bitcode::LIST_REPLACE.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -3220,17 +2919,18 @@ impl<
|
|||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
self.free_symbol(&Symbol::DEV_TMP5);
|
||||
|
||||
// Copy from list to the output record.
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
out_list_offset,
|
||||
&Symbol::DEV_TMP5,
|
||||
&out,
|
||||
&list_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP5);
|
||||
self.free_symbol(&out);
|
||||
}
|
||||
|
||||
fn build_list_concat(
|
||||
|
@ -3252,6 +2952,12 @@ impl<
|
|||
// Load element_width argument (usize).
|
||||
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP2);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
self.storage_manager
|
||||
|
@ -3265,11 +2971,23 @@ impl<
|
|||
Symbol::DEV_TMP,
|
||||
// element_width
|
||||
Symbol::DEV_TMP2,
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP3,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
];
|
||||
let lowlevel_arg_layouts = [list_a_layout, list_b_layout, Layout::U32, Layout::U64];
|
||||
let lowlevel_arg_layouts = [
|
||||
list_a_layout,
|
||||
list_b_layout,
|
||||
Layout::U32,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
Layout::U64,
|
||||
Layout::U64,
|
||||
];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
bitcode::LIST_CONCAT.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -3278,17 +2996,18 @@ impl<
|
|||
|
||||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
|
||||
// Return list value from fn call
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
base_offset,
|
||||
&Symbol::DEV_TMP3,
|
||||
&Symbol::DEV_TMP4,
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
}
|
||||
|
||||
fn build_list_prepend(
|
||||
|
@ -3320,6 +3039,11 @@ impl<
|
|||
// Load element_witdh argument (usize).
|
||||
self.load_layout_stack_size(elem_layout, Symbol::DEV_TMP3);
|
||||
|
||||
// Load element_refcounted argument (bool).
|
||||
self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(elem_layout);
|
||||
|
||||
// Setup the return location.
|
||||
let base_offset =
|
||||
self.storage_manager
|
||||
|
@ -3333,11 +3057,23 @@ impl<
|
|||
Symbol::DEV_TMP2,
|
||||
// element_width
|
||||
Symbol::DEV_TMP3,
|
||||
// element_refcounted
|
||||
Symbol::DEV_TMP4,
|
||||
// inc
|
||||
inc_elem_fn,
|
||||
];
|
||||
let usize_layout = Layout::U64;
|
||||
let lowlevel_arg_layouts = [
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
Layout::U64,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
usize_layout,
|
||||
];
|
||||
let lowlevel_arg_layouts = [list_layout, Layout::U32, Layout::U64, Layout::U64];
|
||||
|
||||
self.build_fn_call(
|
||||
&Symbol::DEV_TMP4,
|
||||
&Symbol::DEV_TMP5,
|
||||
bitcode::LIST_PREPEND.to_string(),
|
||||
&lowlevel_args,
|
||||
&lowlevel_arg_layouts,
|
||||
|
@ -3346,17 +3082,18 @@ impl<
|
|||
self.free_symbol(&Symbol::DEV_TMP);
|
||||
self.free_symbol(&Symbol::DEV_TMP2);
|
||||
self.free_symbol(&Symbol::DEV_TMP3);
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
|
||||
// Return list value from fn call
|
||||
self.storage_manager.copy_symbol_to_stack_offset(
|
||||
self.layout_interner,
|
||||
&mut self.buf,
|
||||
base_offset,
|
||||
&Symbol::DEV_TMP4,
|
||||
&Symbol::DEV_TMP5,
|
||||
ret_layout,
|
||||
);
|
||||
|
||||
self.free_symbol(&Symbol::DEV_TMP4);
|
||||
self.free_symbol(&Symbol::DEV_TMP5);
|
||||
}
|
||||
|
||||
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
|
||||
|
@ -3403,15 +3140,20 @@ impl<
|
|||
let element_alignment_symbol = self.debug_symbol("element_alignment");
|
||||
self.load_layout_alignment(*element_in_layout, element_alignment_symbol);
|
||||
|
||||
let element_refcounted = self.debug_symbol("element_refcounted");
|
||||
self.load_layout_refcounted(*element_in_layout, element_refcounted);
|
||||
|
||||
let allocation_symbol = self.debug_symbol("list_allocation");
|
||||
self.allocate_with_refcount(
|
||||
allocation_symbol,
|
||||
data_bytes_symbol,
|
||||
element_alignment_symbol,
|
||||
element_refcounted,
|
||||
);
|
||||
|
||||
self.free_symbol(&data_bytes_symbol);
|
||||
self.free_symbol(&element_alignment_symbol);
|
||||
self.free_symbol(&element_refcounted);
|
||||
|
||||
enum Origin {
|
||||
S(Symbol),
|
||||
|
@ -3862,11 +3604,18 @@ impl<
|
|||
|
||||
match reuse {
|
||||
None => {
|
||||
// element_refcounted only applies to lists.
|
||||
let element_refcounted = self.debug_symbol("element_refcounted");
|
||||
self.load_literal(&element_refcounted, &Layout::BOOL, &Literal::Bool(false));
|
||||
|
||||
self.allocate_with_refcount(
|
||||
allocation,
|
||||
element_width_symbol,
|
||||
element_alignment_symbol,
|
||||
element_refcounted,
|
||||
);
|
||||
|
||||
self.free_symbol(&element_refcounted);
|
||||
}
|
||||
Some(reuse) => {
|
||||
self.allocate_with_refcount_if_null(allocation, reuse, element_layout);
|
||||
|
@ -5180,12 +4929,13 @@ impl<
|
|||
dst: Symbol,
|
||||
data_bytes: Symbol,
|
||||
element_alignment: Symbol,
|
||||
element_refcounted: Symbol,
|
||||
) {
|
||||
self.build_fn_call(
|
||||
&dst,
|
||||
bitcode::UTILS_ALLOCATE_WITH_REFCOUNT.to_string(),
|
||||
&[data_bytes, element_alignment],
|
||||
&[Layout::U64, Layout::U32],
|
||||
&[data_bytes, element_alignment, element_refcounted],
|
||||
&[Layout::U64, Layout::U32, Layout::BOOL],
|
||||
&Layout::U64,
|
||||
);
|
||||
}
|
||||
|
@ -5217,10 +4967,15 @@ impl<
|
|||
let element_alignment = self.debug_symbol("element_alignment");
|
||||
self.load_layout_alignment(layout, element_alignment);
|
||||
|
||||
self.allocate_with_refcount(dst, data_bytes, element_alignment);
|
||||
// element_refcounted only applies to lists.
|
||||
let element_refcounted = self.debug_symbol("element_refcounted");
|
||||
self.load_literal(&element_refcounted, &Layout::BOOL, &Literal::Bool(false));
|
||||
|
||||
self.allocate_with_refcount(dst, data_bytes, element_alignment, element_refcounted);
|
||||
|
||||
self.free_symbol(&data_bytes);
|
||||
self.free_symbol(&element_alignment);
|
||||
self.free_symbol(&element_refcounted);
|
||||
|
||||
let mut tmp = bumpalo::vec![in self.env.arena];
|
||||
|
||||
|
@ -5609,6 +5364,15 @@ impl<
|
|||
self.load_literal(&symbol, &u32_layout, &alignment_literal);
|
||||
}
|
||||
|
||||
/// Loads if the layout is refcounted (recursively checking) of `layout` into the given `symbol`
|
||||
fn load_layout_refcounted(&mut self, layout: InLayout<'_>, symbol: Symbol) {
|
||||
let u64_layout = Layout::BOOL;
|
||||
let refcounted = self.layout_interner.contains_refcounted(layout);
|
||||
let refcounted_literal = Literal::Bool(refcounted);
|
||||
|
||||
self.load_literal(&symbol, &u64_layout, &refcounted_literal);
|
||||
}
|
||||
|
||||
/// Loads the stack size of `layout` into the given `symbol`
|
||||
fn load_layout_stack_size(&mut self, layout: InLayout<'_>, symbol: Symbol) {
|
||||
let u64_layout = Layout::U64;
|
||||
|
|
|
@ -111,6 +111,7 @@ struct ListArgument<'a> {
|
|||
|
||||
alignment: Symbol,
|
||||
element_width: Symbol,
|
||||
element_refcounted: Symbol,
|
||||
}
|
||||
|
||||
// Track when a variable is last used (and hence when it can be disregarded). This is non-trivial
|
||||
|
@ -408,10 +409,19 @@ trait Backend<'a> {
|
|||
let element_width = self.debug_symbol("element_width");
|
||||
self.load_literal_i64(&element_width, element_width_int as i64);
|
||||
|
||||
let element_refcounted = self.debug_symbol("element_refcounted");
|
||||
let refcounted = self.interner().contains_refcounted(element_layout);
|
||||
self.load_literal(
|
||||
&element_refcounted,
|
||||
&Layout::BOOL,
|
||||
&Literal::Bool(refcounted),
|
||||
);
|
||||
|
||||
ListArgument {
|
||||
element_layout,
|
||||
alignment,
|
||||
element_width,
|
||||
element_refcounted,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -435,6 +445,26 @@ trait Backend<'a> {
|
|||
element_increment
|
||||
}
|
||||
|
||||
fn increment_n_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let box_layout = self
|
||||
.interner_mut()
|
||||
.insert_direct_no_semantic(LayoutRepr::Ptr(layout));
|
||||
|
||||
let element_increment = self.debug_symbol("element_increment_n");
|
||||
let element_increment_symbol = self.build_indirect_inc_n(layout);
|
||||
|
||||
let element_increment_string = self.lambda_name_to_string(
|
||||
LambdaName::no_niche(element_increment_symbol),
|
||||
[box_layout, Layout::I64].into_iter(),
|
||||
None,
|
||||
Layout::UNIT,
|
||||
);
|
||||
|
||||
self.build_fn_pointer(&element_increment, element_increment_string);
|
||||
|
||||
element_increment
|
||||
}
|
||||
|
||||
fn decrement_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol {
|
||||
let box_layout = self
|
||||
.interner_mut()
|
||||
|
@ -556,22 +586,29 @@ trait Backend<'a> {
|
|||
let dst = Symbol::DEV_TMP;
|
||||
|
||||
let layout = *self.layout_map().get(symbol).unwrap();
|
||||
debug_assert!(!matches!(self.interner().get_repr(layout), LayoutRepr::Builtin(Builtin::List(_))), "List are no longer safe to refcount through pointer alone. They must go through the zig bitcode functions");
|
||||
|
||||
let alignment_bytes = self.interner().allocation_alignment_bytes(layout);
|
||||
let alignment = self.debug_symbol("alignment");
|
||||
self.load_literal_i32(&alignment, alignment_bytes as i32);
|
||||
|
||||
// elems_refcounted (always false except for list which are refcounted differently)
|
||||
let elems_refcounted = self.debug_symbol("elems_refcounted");
|
||||
self.load_literal(&elems_refcounted, &Layout::BOOL, &Literal::Bool(false));
|
||||
|
||||
// NOTE: UTILS_FREE_DATA_PTR clears any tag id bits
|
||||
|
||||
self.build_fn_call(
|
||||
&dst,
|
||||
bitcode::UTILS_FREE_DATA_PTR.to_string(),
|
||||
&[*symbol, alignment],
|
||||
&[Layout::I64, Layout::I32],
|
||||
&[*symbol, alignment, elems_refcounted],
|
||||
&[Layout::I64, Layout::I32, Layout::BOOL],
|
||||
&Layout::UNIT,
|
||||
);
|
||||
|
||||
self.free_symbol(&dst);
|
||||
self.free_symbol(&alignment);
|
||||
self.free_symbol(&elems_refcounted);
|
||||
|
||||
self.build_stmt(layout_ids, following, ret_layout)
|
||||
}
|
||||
|
@ -784,6 +821,9 @@ trait Backend<'a> {
|
|||
for arg in *arguments {
|
||||
if let Some(layout) = layout_map.get(arg) {
|
||||
arg_layouts.push(*layout);
|
||||
} else if matches!(lowlevel, LowLevel::ListDecref) {
|
||||
// The last arg of ListDecref has no layout. It is a proc symbol.
|
||||
continue;
|
||||
} else {
|
||||
internal_error!("the argument, {:?}, has no know layout", arg);
|
||||
}
|
||||
|
@ -1883,6 +1923,7 @@ trait Backend<'a> {
|
|||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
start,
|
||||
len,
|
||||
self.decrement_fn_pointer(element_layout),
|
||||
|
@ -1894,6 +1935,7 @@ trait Backend<'a> {
|
|||
arg_layouts[0],
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
Layout::BOOL,
|
||||
Layout::U64,
|
||||
Layout::U64,
|
||||
layout_usize,
|
||||
|
@ -1913,6 +1955,9 @@ trait Backend<'a> {
|
|||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
// list: RocList,
|
||||
|
@ -1920,6 +1965,9 @@ trait Backend<'a> {
|
|||
// element_width: usize,
|
||||
// index_1: u64,
|
||||
// index_2: u64,
|
||||
// element_refcounted: bool,
|
||||
// inc: Inc
|
||||
// dec: Dec
|
||||
// update_mode: UpdateMode,
|
||||
|
||||
self.build_fn_call(
|
||||
|
@ -1931,6 +1979,9 @@ trait Backend<'a> {
|
|||
list_argument.element_width,
|
||||
i,
|
||||
j,
|
||||
list_argument.element_refcounted,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
update_mode,
|
||||
],
|
||||
&[
|
||||
|
@ -1939,6 +1990,9 @@ trait Backend<'a> {
|
|||
layout_usize,
|
||||
Layout::U64,
|
||||
Layout::U64,
|
||||
Layout::BOOL,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
Layout::U8,
|
||||
],
|
||||
ret_layout,
|
||||
|
@ -1953,11 +2007,17 @@ trait Backend<'a> {
|
|||
let update_mode = self.debug_symbol("update_mode");
|
||||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout);
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// inc_elem_fn: Inc,
|
||||
// dec_elem_fn: Dec,
|
||||
// update_mode: UpdateMode,
|
||||
|
||||
self.build_fn_call(
|
||||
|
@ -1967,9 +2027,88 @@ trait Backend<'a> {
|
|||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
inc_elem_fn,
|
||||
dec_elem_fn,
|
||||
update_mode,
|
||||
],
|
||||
&[list_layout, Layout::U32, layout_usize, Layout::U8],
|
||||
&[
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
Layout::BOOL,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
Layout::U8,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
|
||||
LowLevel::ListIncref => {
|
||||
let list = args[0];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
|
||||
let layout_isize = Layout::I64;
|
||||
|
||||
let amount = if args.len() == 2 {
|
||||
// amount explicitly specified,
|
||||
args[1]
|
||||
} else {
|
||||
// amount implicit 1.
|
||||
let sym = self.debug_symbol("amount");
|
||||
self.load_literal_i64(&sym, 1);
|
||||
sym
|
||||
};
|
||||
|
||||
// list: RocList,
|
||||
// amount: isize,
|
||||
// element_refcounted: bool,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
bitcode::LIST_INCREF.to_string(),
|
||||
&[list, amount, list_argument.element_refcounted],
|
||||
&[list_layout, layout_isize, Layout::BOOL],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
|
||||
LowLevel::ListDecref => {
|
||||
let list = args[0];
|
||||
|
||||
let list_layout = arg_layouts[0];
|
||||
let list_argument = self.list_argument(list_layout);
|
||||
|
||||
let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// dec_elem_fn: Dec,
|
||||
|
||||
self.build_fn_call(
|
||||
sym,
|
||||
bitcode::LIST_DECREF.to_string(),
|
||||
&[
|
||||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
dec_elem_fn,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
Layout::BOOL,
|
||||
layout_usize,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
}
|
||||
|
@ -1986,12 +2125,15 @@ trait Backend<'a> {
|
|||
self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8);
|
||||
|
||||
let layout_usize = Layout::U64;
|
||||
let element_increment = self.increment_fn_pointer(element_layout);
|
||||
let element_decrement = self.decrement_fn_pointer(element_layout);
|
||||
|
||||
// list: RocList,
|
||||
// alignment: u32,
|
||||
// element_width: usize,
|
||||
// element_refcounted: bool,
|
||||
// drop_index: u64,
|
||||
// inc: Inc,
|
||||
// dec: Dec,
|
||||
|
||||
self.build_fn_call(
|
||||
|
@ -2001,15 +2143,19 @@ trait Backend<'a> {
|
|||
list,
|
||||
list_argument.alignment,
|
||||
list_argument.element_width,
|
||||
list_argument.element_refcounted,
|
||||
drop_index,
|
||||
element_increment,
|
||||
element_decrement,
|
||||
],
|
||||
&[
|
||||
list_layout,
|
||||
Layout::U32,
|
||||
layout_usize,
|
||||
Layout::BOOL,
|
||||
Layout::U64,
|
||||
layout_usize,
|
||||
layout_usize,
|
||||
],
|
||||
ret_layout,
|
||||
);
|
||||
|
@ -2434,6 +2580,7 @@ trait Backend<'a> {
|
|||
);
|
||||
|
||||
fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_inc_n(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol;
|
||||
|
||||
fn build_list_clone(
|
||||
|
|
|
@ -1113,6 +1113,57 @@ pub(crate) fn call_str_bitcode_fn<'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn call_void_list_bitcode_fn<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
lists: &[StructValue<'ctx>],
|
||||
other_arguments: &[BasicValueEnum<'ctx>],
|
||||
fn_name: &str,
|
||||
) {
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_target::Architecture::*;
|
||||
|
||||
match env.target.architecture() {
|
||||
Aarch32 | X86_32 => {
|
||||
let mut arguments: Vec<BasicValueEnum> =
|
||||
Vec::with_capacity_in(other_arguments.len() + 2 * lists.len(), env.arena);
|
||||
|
||||
for list in lists {
|
||||
let (a, b) = pass_list_or_string_to_zig_32bit(env, *list);
|
||||
arguments.push(a.into());
|
||||
arguments.push(b.into());
|
||||
}
|
||||
|
||||
arguments.extend(other_arguments);
|
||||
|
||||
call_void_bitcode_fn(env, &arguments, fn_name);
|
||||
}
|
||||
X86_64 | Aarch64 => {
|
||||
let capacity = other_arguments.len() + lists.len();
|
||||
let mut arguments: Vec<BasicValueEnum> = Vec::with_capacity_in(capacity, env.arena);
|
||||
|
||||
for list in lists {
|
||||
arguments.push(pass_list_to_zig_64bit(env, (*list).into()).into());
|
||||
}
|
||||
|
||||
arguments.extend(other_arguments);
|
||||
|
||||
call_void_bitcode_fn(env, &arguments, fn_name);
|
||||
}
|
||||
Wasm32 => {
|
||||
let capacity = other_arguments.len() + lists.len();
|
||||
let mut arguments: Vec<BasicValueEnum> = Vec::with_capacity_in(capacity, env.arena);
|
||||
|
||||
for list in lists {
|
||||
arguments.push(pass_list_to_zig_wasm(env, (*list).into()).into());
|
||||
}
|
||||
|
||||
arguments.extend(other_arguments);
|
||||
|
||||
call_void_bitcode_fn(env, &arguments, fn_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn call_list_bitcode_fn<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
lists: &[StructValue<'ctx>],
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build_list::{self, allocate_list, empty_polymorphic_list};
|
||||
use crate::llvm::bitcode::{build_dec_wrapper, call_bitcode_fn, call_void_list_bitcode_fn};
|
||||
use crate::llvm::build_list::{
|
||||
allocate_list, empty_polymorphic_list, layout_refcounted, layout_width,
|
||||
};
|
||||
use crate::llvm::convert::{
|
||||
argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
|
||||
};
|
||||
|
@ -2860,19 +2862,6 @@ fn union_field_ptr_at_index<'a, 'ctx>(
|
|||
.into_pointer_value()
|
||||
}
|
||||
|
||||
pub fn reserve_with_refcount<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let stack_size = layout_interner.stack_size(layout);
|
||||
let alignment_bytes = layout_interner.alignment_bytes(layout);
|
||||
|
||||
let basic_type = basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout));
|
||||
|
||||
reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes)
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
|
@ -2887,7 +2876,7 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx>(
|
|||
RocUnion::untagged_from_slices(layout_interner, env.context, fields)
|
||||
};
|
||||
|
||||
reserve_with_refcount_help(
|
||||
reserve_union_with_refcount_help(
|
||||
env,
|
||||
roc_union.struct_type(),
|
||||
roc_union.tag_width(),
|
||||
|
@ -2895,7 +2884,7 @@ fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx>(
|
|||
)
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_help<'a, 'ctx, 'env>(
|
||||
fn reserve_union_with_refcount_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
basic_type: impl BasicType<'ctx>,
|
||||
stack_size: u32,
|
||||
|
@ -2905,21 +2894,15 @@ fn reserve_with_refcount_help<'a, 'ctx, 'env>(
|
|||
|
||||
let value_bytes_intvalue = len_type.const_int(stack_size as u64, false);
|
||||
|
||||
allocate_with_refcount_help(env, basic_type, alignment_bytes, value_bytes_intvalue)
|
||||
}
|
||||
|
||||
pub fn allocate_with_refcount<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let data_ptr = reserve_with_refcount(env, layout_interner, layout);
|
||||
|
||||
// store the value in the pointer
|
||||
env.builder.new_build_store(data_ptr, value);
|
||||
|
||||
data_ptr
|
||||
// elem_refcounted does not apply to unions, only lists.
|
||||
let elem_refcounted = env.context.bool_type().const_zero().into();
|
||||
allocate_with_refcount_help(
|
||||
env,
|
||||
basic_type,
|
||||
alignment_bytes,
|
||||
value_bytes_intvalue,
|
||||
elem_refcounted,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||
|
@ -2927,12 +2910,14 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
|||
value_type: impl BasicType<'ctx>,
|
||||
alignment_bytes: u32,
|
||||
number_of_data_bytes: IntValue<'ctx>,
|
||||
elem_refcounted: BasicValueEnum<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let ptr = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
number_of_data_bytes.into(),
|
||||
env.alignment_const(alignment_bytes).into(),
|
||||
elem_refcounted,
|
||||
],
|
||||
roc_builtins::bitcode::UTILS_ALLOCATE_WITH_REFCOUNT,
|
||||
)
|
||||
|
@ -3473,10 +3458,19 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
LayoutRepr::Builtin(Builtin::Str) => todo!(),
|
||||
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
||||
debug_assert!(value.is_struct_value());
|
||||
let element_layout = layout_interner.get_repr(element_layout);
|
||||
let alignment = element_layout.alignment_bytes(layout_interner);
|
||||
|
||||
build_list::decref(env, value.into_struct_value(), alignment);
|
||||
let dec_element_fn =
|
||||
build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_void_list_bitcode_fn(
|
||||
env,
|
||||
&[value.into_struct_value()],
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_DECREF,
|
||||
)
|
||||
}
|
||||
|
||||
other_layout if other_layout.is_refcounted(layout_interner) => {
|
||||
|
@ -3546,7 +3540,8 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
debug_assert!(value.is_pointer_value());
|
||||
let value = value.into_pointer_value();
|
||||
|
||||
let clear_tag_id = match layout_interner.runtime_representation(layout) {
|
||||
let runtime_layout = layout_interner.runtime_representation(layout);
|
||||
let clear_tag_id = match runtime_layout {
|
||||
LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target),
|
||||
_ => false,
|
||||
};
|
||||
|
@ -3558,7 +3553,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
};
|
||||
|
||||
let rc_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
|
||||
rc_ptr.deallocate(env, alignment);
|
||||
rc_ptr.deallocate(env, alignment, runtime_layout);
|
||||
|
||||
build_exp_stmt(
|
||||
env,
|
||||
|
|
|
@ -12,7 +12,7 @@ use roc_mono::layout::{
|
|||
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
|
||||
};
|
||||
|
||||
use super::bitcode::{call_list_bitcode_fn, BitcodeReturns};
|
||||
use super::bitcode::{build_inc_wrapper, call_list_bitcode_fn, BitcodeReturns};
|
||||
use super::build::{
|
||||
create_entry_block_alloca, load_roc_value, store_roc_value, use_roc_value, BuilderExt,
|
||||
};
|
||||
|
@ -97,6 +97,20 @@ pub(crate) fn layout_width<'a, 'ctx>(
|
|||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn layout_refcounted<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let is_refcounted = layout_interner
|
||||
.get_repr(layout)
|
||||
.contains_refcounted(layout_interner);
|
||||
env.context
|
||||
.bool_type()
|
||||
.const_int(is_refcounted as u64, false)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn pass_as_opaque<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
|
@ -113,9 +127,11 @@ pub(crate) fn pass_as_opaque<'ctx>(
|
|||
pub(crate) fn list_with_capacity<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
capacity: IntValue<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[],
|
||||
|
@ -123,6 +139,8 @@ pub(crate) fn list_with_capacity<'a, 'ctx>(
|
|||
capacity.into(),
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_WITH_CAPACITY,
|
||||
|
@ -173,11 +191,13 @@ pub(crate) fn list_get_unsafe<'a, 'ctx>(
|
|||
pub(crate) fn list_reserve<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
spare: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
update_mode: UpdateMode,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
|
@ -185,6 +205,8 @@ pub(crate) fn list_reserve<'a, 'ctx>(
|
|||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
spare,
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
],
|
||||
bitcode::LIST_RESERVE,
|
||||
|
@ -195,16 +217,22 @@ pub(crate) fn list_reserve<'a, 'ctx>(
|
|||
pub(crate) fn list_release_excess_capacity<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
update_mode: UpdateMode,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
],
|
||||
bitcode::LIST_RELEASE_EXCESS_CAPACITY,
|
||||
|
@ -234,10 +262,12 @@ pub(crate) fn list_append_unsafe<'a, 'ctx>(
|
|||
pub(crate) fn list_prepend<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
original_wrapper: StructValue<'ctx>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
original_wrapper,
|
||||
|
@ -245,21 +275,50 @@ pub(crate) fn list_prepend<'a, 'ctx>(
|
|||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
pass_element_as_opaque(env, layout_interner, element, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_PREPEND,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn list_clone<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list: StructValue<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list,
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_CLONE,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.swap : List elem, U64, U64 -> List elem
|
||||
pub(crate) fn list_swap<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
original_wrapper: StructValue<'ctx>,
|
||||
index_1: IntValue<'ctx>,
|
||||
index_2: IntValue<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
update_mode: UpdateMode,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
original_wrapper,
|
||||
|
@ -268,6 +327,9 @@ pub(crate) fn list_swap<'a, 'ctx>(
|
|||
layout_width(env, layout_interner, element_layout),
|
||||
index_1.into(),
|
||||
index_2.into(),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_update_mode(env, update_mode),
|
||||
],
|
||||
bitcode::LIST_SWAP,
|
||||
|
@ -291,6 +353,7 @@ pub(crate) fn list_sublist<'a, 'ctx>(
|
|||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
start.into(),
|
||||
len.into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
|
@ -308,6 +371,7 @@ pub(crate) fn list_drop_at<'a, 'ctx>(
|
|||
count: IntValue<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
|
@ -315,7 +379,9 @@ pub(crate) fn list_drop_at<'a, 'ctx>(
|
|||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
count.into(),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_DROP_AT,
|
||||
|
@ -326,7 +392,7 @@ pub(crate) fn list_drop_at<'a, 'ctx>(
|
|||
pub(crate) fn list_replace_unsafe<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
_layout_ids: &mut LayoutIds<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
index: IntValue<'ctx>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
|
@ -356,18 +422,27 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>(
|
|||
],
|
||||
bitcode::LIST_REPLACE_IN_PLACE,
|
||||
),
|
||||
UpdateMode::Immutable => call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, layout_interner, element, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
pass_as_opaque(env, element_ptr),
|
||||
],
|
||||
bitcode::LIST_REPLACE,
|
||||
),
|
||||
UpdateMode::Immutable => {
|
||||
let inc_element_fn =
|
||||
build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn =
|
||||
build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
index.into(),
|
||||
pass_element_as_opaque(env, layout_interner, element, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_as_opaque(env, element_ptr),
|
||||
],
|
||||
bitcode::LIST_REPLACE,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// Load the element and returned list into a struct.
|
||||
|
@ -436,36 +511,6 @@ pub(crate) fn list_len_usize<'ctx>(
|
|||
.into_int_value()
|
||||
}
|
||||
|
||||
pub(crate) fn list_capacity_or_ref_ptr<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
builder
|
||||
.build_extract_value(
|
||||
wrapper_struct,
|
||||
Builtin::WRAPPER_CAPACITY,
|
||||
"list_capacity_or_ref_ptr",
|
||||
)
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
// Gets a pointer to just after the refcount for a list or seamless slice.
|
||||
// The value is just after the refcount so that normal lists and seamless slices can share code paths easily.
|
||||
pub(crate) fn list_allocation_ptr<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[wrapper_struct],
|
||||
&[],
|
||||
BitcodeReturns::Basic,
|
||||
bitcode::LIST_ALLOCATION_PTR,
|
||||
)
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
pub(crate) fn destructure<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
|
@ -493,11 +538,14 @@ pub(crate) fn destructure<'ctx>(
|
|||
pub(crate) fn list_sort_with<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
compare_wrapper: PointerValue<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
|
@ -508,176 +556,34 @@ pub(crate) fn list_sort_with<'a, 'ctx>(
|
|||
roc_function_call.data_is_owned.into(),
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_SORT_WITH,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.map : List before, (before -> after) -> List after
|
||||
pub(crate) fn list_map<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
return_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
call_list_bitcode_fn_1(
|
||||
env,
|
||||
list.into_struct_value(),
|
||||
&[
|
||||
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.alignment_intvalue(layout_interner, return_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, return_layout),
|
||||
],
|
||||
bitcode::LIST_MAP,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn list_map2<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list1: BasicValueEnum<'ctx>,
|
||||
list2: BasicValueEnum<'ctx>,
|
||||
element1_layout: InLayout<'a>,
|
||||
element2_layout: InLayout<'a>,
|
||||
return_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_a = build_dec_wrapper(env, layout_interner, layout_ids, element1_layout);
|
||||
let dec_b = build_dec_wrapper(env, layout_interner, layout_ids, element2_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[list1.into_struct_value(), list2.into_struct_value()],
|
||||
&[
|
||||
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.alignment_intvalue(layout_interner, return_layout),
|
||||
layout_width(env, layout_interner, element1_layout),
|
||||
layout_width(env, layout_interner, element2_layout),
|
||||
layout_width(env, layout_interner, return_layout),
|
||||
dec_a.as_global_value().as_pointer_value().into(),
|
||||
dec_b.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_MAP2,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn list_map3<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list1: BasicValueEnum<'ctx>,
|
||||
list2: BasicValueEnum<'ctx>,
|
||||
list3: BasicValueEnum<'ctx>,
|
||||
element1_layout: InLayout<'a>,
|
||||
element2_layout: InLayout<'a>,
|
||||
element3_layout: InLayout<'a>,
|
||||
result_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_a = build_dec_wrapper(env, layout_interner, layout_ids, element1_layout);
|
||||
let dec_b = build_dec_wrapper(env, layout_interner, layout_ids, element2_layout);
|
||||
let dec_c = build_dec_wrapper(env, layout_interner, layout_ids, element3_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list1.into_struct_value(),
|
||||
list2.into_struct_value(),
|
||||
list3.into_struct_value(),
|
||||
],
|
||||
&[
|
||||
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.alignment_intvalue(layout_interner, result_layout),
|
||||
layout_width(env, layout_interner, element1_layout),
|
||||
layout_width(env, layout_interner, element2_layout),
|
||||
layout_width(env, layout_interner, element3_layout),
|
||||
layout_width(env, layout_interner, result_layout),
|
||||
dec_a.as_global_value().as_pointer_value().into(),
|
||||
dec_b.as_global_value().as_pointer_value().into(),
|
||||
dec_c.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_MAP3,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn list_map4<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
list1: BasicValueEnum<'ctx>,
|
||||
list2: BasicValueEnum<'ctx>,
|
||||
list3: BasicValueEnum<'ctx>,
|
||||
list4: BasicValueEnum<'ctx>,
|
||||
element1_layout: InLayout<'a>,
|
||||
element2_layout: InLayout<'a>,
|
||||
element3_layout: InLayout<'a>,
|
||||
element4_layout: InLayout<'a>,
|
||||
result_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_a = build_dec_wrapper(env, layout_interner, layout_ids, element1_layout);
|
||||
let dec_b = build_dec_wrapper(env, layout_interner, layout_ids, element2_layout);
|
||||
let dec_c = build_dec_wrapper(env, layout_interner, layout_ids, element3_layout);
|
||||
let dec_d = build_dec_wrapper(env, layout_interner, layout_ids, element4_layout);
|
||||
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
list1.into_struct_value(),
|
||||
list2.into_struct_value(),
|
||||
list3.into_struct_value(),
|
||||
list4.into_struct_value(),
|
||||
],
|
||||
&[
|
||||
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.alignment_intvalue(layout_interner, result_layout),
|
||||
layout_width(env, layout_interner, element1_layout),
|
||||
layout_width(env, layout_interner, element2_layout),
|
||||
layout_width(env, layout_interner, element3_layout),
|
||||
layout_width(env, layout_interner, element4_layout),
|
||||
layout_width(env, layout_interner, result_layout),
|
||||
dec_a.as_global_value().as_pointer_value().into(),
|
||||
dec_b.as_global_value().as_pointer_value().into(),
|
||||
dec_c.as_global_value().as_pointer_value().into(),
|
||||
dec_d.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_MAP4,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.concat : List elem, List elem -> List elem
|
||||
pub(crate) fn list_concat<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
list1: BasicValueEnum<'ctx>,
|
||||
list2: BasicValueEnum<'ctx>,
|
||||
element_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[list1.into_struct_value(), list2.into_struct_value()],
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_CONCAT,
|
||||
|
@ -791,21 +697,6 @@ pub(crate) fn empty_polymorphic_list<'ctx>(env: &Env<'_, 'ctx, '_>) -> BasicValu
|
|||
BasicValueEnum::StructValue(struct_type.const_zero())
|
||||
}
|
||||
|
||||
pub(crate) fn load_list<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
ptr_type: PointerType<'ctx>,
|
||||
) -> (IntValue<'ctx>, PointerValue<'ctx>) {
|
||||
let ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
||||
|
||||
let length = builder
|
||||
.build_extract_value(wrapper_struct, Builtin::WRAPPER_LEN, "list_len")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
(length, ptr)
|
||||
}
|
||||
|
||||
pub(crate) fn load_list_ptr<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
|
@ -838,7 +729,14 @@ pub(crate) fn allocate_list<'a, 'ctx>(
|
|||
let basic_type =
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(elem_layout));
|
||||
let alignment_bytes = layout_interner.alignment_bytes(elem_layout);
|
||||
allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes)
|
||||
let elem_refcounted = layout_refcounted(env, layout_interner, elem_layout);
|
||||
allocate_with_refcount_help(
|
||||
env,
|
||||
basic_type,
|
||||
alignment_bytes,
|
||||
number_of_data_bytes,
|
||||
elem_refcounted,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn store_list<'ctx>(
|
||||
|
@ -860,13 +758,3 @@ pub(crate) fn store_list<'ctx>(
|
|||
.into_iter(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn decref<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
wrapper_struct: StructValue<'ctx>,
|
||||
alignment: u32,
|
||||
) {
|
||||
let refcount_ptr = list_allocation_ptr(env, wrapper_struct);
|
||||
|
||||
crate::llvm::refcounting::decref_pointer_check_null(env, refcount_ptr, alignment);
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@ use crate::llvm::{
|
|||
BuilderExt, FuncBorrowSpec, RocReturn,
|
||||
},
|
||||
build_list::{
|
||||
layout_width, list_append_unsafe, list_concat, list_drop_at, list_get_unsafe,
|
||||
list_len_usize, list_map, list_map2, list_map3, list_map4, list_prepend,
|
||||
list_release_excess_capacity, list_replace_unsafe, list_reserve, list_sort_with,
|
||||
list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity, pass_update_mode,
|
||||
list_append_unsafe, list_clone, list_concat, list_drop_at, list_get_unsafe, list_len_usize,
|
||||
list_prepend, list_release_excess_capacity, list_replace_unsafe, list_reserve,
|
||||
list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity,
|
||||
pass_update_mode,
|
||||
},
|
||||
compare::{generic_eq, generic_neq},
|
||||
convert::{
|
||||
|
@ -645,6 +645,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
list_with_capacity(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
list_len.into_int_value(),
|
||||
list_element_layout!(layout_interner, result_layout),
|
||||
)
|
||||
|
@ -661,6 +662,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
list_concat(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
first_list,
|
||||
second_list,
|
||||
element_layout,
|
||||
|
@ -682,7 +684,14 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
let original_wrapper = scope.load_symbol(&args[0]).into_struct_value();
|
||||
let (elem, elem_layout) = scope.load_symbol_and_layout(&args[1]);
|
||||
|
||||
list_prepend(env, layout_interner, original_wrapper, elem, elem_layout)
|
||||
list_prepend(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
original_wrapper,
|
||||
elem,
|
||||
elem_layout,
|
||||
)
|
||||
}
|
||||
ListReserve => {
|
||||
// List.reserve : List elem, U64 -> List elem
|
||||
|
@ -695,6 +704,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
list_reserve(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
list,
|
||||
spare,
|
||||
element_layout,
|
||||
|
@ -708,7 +718,14 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
let (list, list_layout) = scope.load_symbol_and_layout(&args[0]);
|
||||
let element_layout = list_element_layout!(layout_interner, list_layout);
|
||||
|
||||
list_release_excess_capacity(env, layout_interner, list, element_layout, update_mode)
|
||||
list_release_excess_capacity(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
list,
|
||||
element_layout,
|
||||
update_mode,
|
||||
)
|
||||
}
|
||||
ListSwap => {
|
||||
// List.swap : List elem, U64, U64 -> List elem
|
||||
|
@ -724,6 +741,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
list_swap(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
original_wrapper,
|
||||
index_1.into_int_value(),
|
||||
index_2.into_int_value(),
|
||||
|
@ -826,19 +844,13 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
let element_layout = list_element_layout!(layout_interner, list_layout);
|
||||
|
||||
match update_mode {
|
||||
UpdateMode::Immutable => {
|
||||
//
|
||||
call_list_bitcode_fn(
|
||||
env,
|
||||
&[list.into_struct_value()],
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
],
|
||||
BitcodeReturns::List,
|
||||
bitcode::LIST_CLONE,
|
||||
)
|
||||
}
|
||||
UpdateMode::Immutable => list_clone(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
list.into_struct_value(),
|
||||
element_layout,
|
||||
),
|
||||
UpdateMode::InPlace => {
|
||||
// we statically know the list is unique
|
||||
list
|
||||
|
@ -1294,7 +1306,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => {
|
||||
ListSortWith => {
|
||||
unreachable!("these are higher order, and are handled elsewhere")
|
||||
}
|
||||
|
||||
|
@ -1399,7 +1411,9 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
call_bitcode_fn(env, &[], bitcode::UTILS_DICT_PSEUDO_SEED)
|
||||
}
|
||||
|
||||
SetJmp | LongJmp | SetLongJmpBuffer => unreachable!("only inserted in dev backend codegen"),
|
||||
ListIncref | ListDecref | SetJmp | LongJmp | SetLongJmpBuffer => {
|
||||
unreachable!("only inserted in dev backend codegen")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2758,7 +2772,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
return_layout: InLayout<'a>,
|
||||
_return_layout: InLayout<'a>,
|
||||
func_spec: FuncSpec,
|
||||
higher_order: &HigherOrderLowLevel<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
|
@ -2797,201 +2811,6 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
|||
}
|
||||
|
||||
match op {
|
||||
ListMap { xs } => {
|
||||
// List.map : List before, (before -> after) -> List after
|
||||
let (list, list_layout) = scope.load_symbol_and_layout(xs);
|
||||
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match (
|
||||
layout_interner.get_repr(list_layout),
|
||||
layout_interner.get_repr(return_layout),
|
||||
) {
|
||||
(
|
||||
LayoutRepr::Builtin(Builtin::List(element_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(result_layout)),
|
||||
) => {
|
||||
let argument_layouts = &[element_layout];
|
||||
|
||||
let roc_function_call = roc_function_call(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
function,
|
||||
closure,
|
||||
closure_layout,
|
||||
function_owns_closure_data,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
);
|
||||
|
||||
list_map(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function_call,
|
||||
list,
|
||||
element_layout,
|
||||
result_layout,
|
||||
)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListMap2 { xs, ys } => {
|
||||
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
|
||||
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
|
||||
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match (
|
||||
layout_interner.get_repr(list1_layout),
|
||||
layout_interner.get_repr(list2_layout),
|
||||
layout_interner.get_repr(return_layout),
|
||||
) {
|
||||
(
|
||||
LayoutRepr::Builtin(Builtin::List(element1_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element2_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(result_layout)),
|
||||
) => {
|
||||
let argument_layouts = &[element1_layout, element2_layout];
|
||||
|
||||
let roc_function_call = roc_function_call(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
function,
|
||||
closure,
|
||||
closure_layout,
|
||||
function_owns_closure_data,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
);
|
||||
|
||||
list_map2(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
list1,
|
||||
list2,
|
||||
element1_layout,
|
||||
element2_layout,
|
||||
result_layout,
|
||||
)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListMap3 { xs, ys, zs } => {
|
||||
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
|
||||
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
|
||||
let (list3, list3_layout) = scope.load_symbol_and_layout(zs);
|
||||
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match (
|
||||
layout_interner.get_repr(list1_layout),
|
||||
layout_interner.get_repr(list2_layout),
|
||||
layout_interner.get_repr(list3_layout),
|
||||
layout_interner.get_repr(return_layout),
|
||||
) {
|
||||
(
|
||||
LayoutRepr::Builtin(Builtin::List(element1_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element2_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element3_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(result_layout)),
|
||||
) => {
|
||||
let argument_layouts = &[element1_layout, element2_layout, element3_layout];
|
||||
|
||||
let roc_function_call = roc_function_call(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
function,
|
||||
closure,
|
||||
closure_layout,
|
||||
function_owns_closure_data,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
);
|
||||
|
||||
list_map3(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
list1,
|
||||
list2,
|
||||
list3,
|
||||
element1_layout,
|
||||
element2_layout,
|
||||
element3_layout,
|
||||
result_layout,
|
||||
)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListMap4 { xs, ys, zs, ws } => {
|
||||
let (list1, list1_layout) = scope.load_symbol_and_layout(xs);
|
||||
let (list2, list2_layout) = scope.load_symbol_and_layout(ys);
|
||||
let (list3, list3_layout) = scope.load_symbol_and_layout(zs);
|
||||
let (list4, list4_layout) = scope.load_symbol_and_layout(ws);
|
||||
|
||||
let (function, closure, closure_layout) = function_details!();
|
||||
|
||||
match (
|
||||
layout_interner.get_repr(list1_layout),
|
||||
layout_interner.get_repr(list2_layout),
|
||||
layout_interner.get_repr(list3_layout),
|
||||
layout_interner.get_repr(list4_layout),
|
||||
layout_interner.get_repr(return_layout),
|
||||
) {
|
||||
(
|
||||
LayoutRepr::Builtin(Builtin::List(element1_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element2_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element3_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(element4_layout)),
|
||||
LayoutRepr::Builtin(Builtin::List(result_layout)),
|
||||
) => {
|
||||
let argument_layouts = &[
|
||||
element1_layout,
|
||||
element2_layout,
|
||||
element3_layout,
|
||||
element4_layout,
|
||||
];
|
||||
|
||||
let roc_function_call = roc_function_call(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
function,
|
||||
closure,
|
||||
closure_layout,
|
||||
function_owns_closure_data,
|
||||
argument_layouts,
|
||||
result_layout,
|
||||
);
|
||||
|
||||
list_map4(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
list1,
|
||||
list2,
|
||||
list3,
|
||||
list4,
|
||||
element1_layout,
|
||||
element2_layout,
|
||||
element3_layout,
|
||||
element4_layout,
|
||||
result_layout,
|
||||
)
|
||||
}
|
||||
_ => unreachable!("invalid list layout"),
|
||||
}
|
||||
}
|
||||
ListSortWith { xs } => {
|
||||
// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||
let (list, list_layout) = scope.load_symbol_and_layout(xs);
|
||||
|
@ -3030,6 +2849,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
|||
list_sort_with(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
compare_wrapper,
|
||||
list,
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
use crate::llvm::bitcode::{build_dec_wrapper, call_void_bitcode_fn, call_void_list_bitcode_fn};
|
||||
use crate::llvm::build::BuilderExt;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV,
|
||||
};
|
||||
use crate::llvm::build_list::{
|
||||
incrementing_elem_loop, list_allocation_ptr, list_capacity_or_ref_ptr, load_list,
|
||||
};
|
||||
use crate::llvm::build_list::{layout_refcounted, layout_width};
|
||||
use crate::llvm::build_str::str_allocation_ptr;
|
||||
use crate::llvm::convert::{basic_type_from_layout, zig_str_type, RocUnion};
|
||||
use crate::llvm::struct_::RocStruct;
|
||||
|
@ -16,6 +14,7 @@ use inkwell::module::Linkage;
|
|||
use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, InstructionValue, IntValue, PointerValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::ErasedField;
|
||||
|
@ -111,13 +110,18 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
) {
|
||||
match mode {
|
||||
CallMode::Inc(inc_amount) => self.increment(inc_amount, env),
|
||||
CallMode::Inc(inc_amount) => self.increment(inc_amount, env, layout),
|
||||
CallMode::Dec => self.decrement(env, layout_interner, layout),
|
||||
}
|
||||
}
|
||||
|
||||
fn increment<'a, 'env>(&self, amount: IntValue<'ctx>, env: &Env<'a, 'ctx, 'env>) {
|
||||
incref_pointer(env, self.value, amount);
|
||||
fn increment<'a, 'env>(
|
||||
&self,
|
||||
amount: IntValue<'ctx>,
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: LayoutRepr<'a>,
|
||||
) {
|
||||
incref_pointer(env, self.value, amount, layout);
|
||||
}
|
||||
|
||||
pub fn decrement<'a, 'env>(
|
||||
|
@ -158,7 +162,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
Self::build_decrement_function_body(env, function_value, alignment);
|
||||
Self::build_decrement_function_body(env, function_value, alignment, layout);
|
||||
|
||||
function_value
|
||||
}
|
||||
|
@ -180,6 +184,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
alignment: u32,
|
||||
layout: LayoutRepr<'a>,
|
||||
) {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
@ -193,6 +198,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
env,
|
||||
parent.get_nth_param(0).unwrap().into_pointer_value(),
|
||||
alignment,
|
||||
layout,
|
||||
);
|
||||
|
||||
builder.new_build_return(None);
|
||||
|
@ -202,16 +208,23 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
&self,
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
alignment: u32,
|
||||
layout: LayoutRepr<'a>,
|
||||
) -> InstructionValue<'ctx> {
|
||||
free_pointer(env, self.value, alignment)
|
||||
free_pointer(env, self.value, alignment, layout)
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_assert_not_list(layout: LayoutRepr<'_>) {
|
||||
debug_assert!(!matches!(layout, LayoutRepr::Builtin(Builtin::List(_))), "List are no longer safe to refcount through pointer alone. They must go through the zig bitcode functions");
|
||||
}
|
||||
|
||||
fn incref_pointer<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
amount: IntValue<'ctx>,
|
||||
layout: LayoutRepr<'_>,
|
||||
) {
|
||||
debug_assert_not_list(layout);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
|
@ -232,7 +245,9 @@ fn free_pointer<'ctx>(
|
|||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
alignment: u32,
|
||||
layout: LayoutRepr<'_>,
|
||||
) -> InstructionValue<'ctx> {
|
||||
debug_assert_not_list(layout);
|
||||
let alignment = env.context.i32_type().const_int(alignment as _, false);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -245,12 +260,19 @@ fn free_pointer<'ctx>(
|
|||
)
|
||||
.into(),
|
||||
alignment.into(),
|
||||
env.context.bool_type().const_int(0, false).into(),
|
||||
],
|
||||
roc_builtins::bitcode::UTILS_FREE_RC_PTR,
|
||||
)
|
||||
}
|
||||
|
||||
fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, alignment: u32) {
|
||||
fn decref_pointer<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
alignment: u32,
|
||||
layout: LayoutRepr<'_>,
|
||||
) {
|
||||
debug_assert_not_list(layout);
|
||||
let alignment = env.context.i32_type().const_int(alignment as _, false);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -263,6 +285,7 @@ fn decref_pointer<'ctx>(env: &Env<'_, 'ctx, '_>, pointer: PointerValue<'ctx>, al
|
|||
)
|
||||
.into(),
|
||||
alignment.into(),
|
||||
env.context.bool_type().const_int(0, false).into(),
|
||||
],
|
||||
roc_builtins::bitcode::UTILS_DECREF_RC_PTR,
|
||||
);
|
||||
|
@ -273,7 +296,9 @@ pub fn decref_pointer_check_null<'ctx>(
|
|||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
alignment: u32,
|
||||
layout: LayoutRepr<'_>,
|
||||
) {
|
||||
debug_assert_not_list(layout);
|
||||
let alignment = env.context.i32_type().const_int(alignment as _, false);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -286,6 +311,7 @@ pub fn decref_pointer_check_null<'ctx>(
|
|||
)
|
||||
.into(),
|
||||
alignment.into(),
|
||||
env.context.bool_type().const_int(0, false).into(),
|
||||
],
|
||||
roc_builtins::bitcode::UTILS_DECREF_CHECK_NULL,
|
||||
);
|
||||
|
@ -764,7 +790,6 @@ fn modify_refcount_list<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_ids,
|
||||
mode,
|
||||
list_layout,
|
||||
element_layout,
|
||||
function_value,
|
||||
);
|
||||
|
@ -791,7 +816,6 @@ fn modify_refcount_list_help<'a, 'ctx>(
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
mode: Mode,
|
||||
layout: LayoutRepr<'a>,
|
||||
element_layout: InLayout<'a>,
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
|
@ -807,73 +831,49 @@ fn modify_refcount_list_help<'a, 'ctx>(
|
|||
|
||||
// Add args to scope
|
||||
let arg_symbol = Symbol::ARG_1;
|
||||
let arg_val = fn_val.get_param_iter().next().unwrap();
|
||||
let mut param_iter = fn_val.get_param_iter();
|
||||
let arg_val = param_iter.next().unwrap();
|
||||
|
||||
arg_val.set_name(arg_symbol.as_str(&env.interns));
|
||||
|
||||
let parent = fn_val;
|
||||
let original_wrapper = arg_val.into_struct_value();
|
||||
|
||||
// We use the raw capacity to ensure we always decrement the refcount of seamless slices.
|
||||
let capacity = list_capacity_or_ref_ptr(builder, original_wrapper);
|
||||
|
||||
let is_non_empty = builder.new_build_int_compare(
|
||||
IntPredicate::UGT,
|
||||
capacity,
|
||||
env.ptr_int().const_zero(),
|
||||
"cap > 0",
|
||||
);
|
||||
|
||||
// build blocks
|
||||
let modification_list_block = ctx.append_basic_block(parent, "modification_list_block");
|
||||
let cont_block = ctx.append_basic_block(parent, "modify_rc_list_cont");
|
||||
|
||||
builder.new_build_conditional_branch(is_non_empty, modification_list_block, cont_block);
|
||||
|
||||
builder.position_at_end(modification_list_block);
|
||||
|
||||
if layout_interner.contains_refcounted(element_layout) {
|
||||
let ptr_type = basic_type_from_layout(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_interner.get_repr(element_layout),
|
||||
)
|
||||
.ptr_type(AddressSpace::default());
|
||||
|
||||
let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type);
|
||||
|
||||
let loop_fn = |layout_interner, _index, element| {
|
||||
modify_refcount_layout_help(
|
||||
// List incrementing and decrementing is more complex now.
|
||||
// Always go through zig.
|
||||
match mode {
|
||||
Mode::Dec => {
|
||||
let dec_element_fn =
|
||||
build_dec_wrapper(env, layout_interner, layout_ids, element_layout);
|
||||
call_void_list_bitcode_fn(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
mode.to_call_mode(fn_val),
|
||||
element,
|
||||
element_layout,
|
||||
);
|
||||
};
|
||||
&[original_wrapper],
|
||||
&[
|
||||
env.alignment_intvalue(layout_interner, element_layout),
|
||||
layout_width(env, layout_interner, element_layout),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_DECREF,
|
||||
)
|
||||
}
|
||||
Mode::Inc => {
|
||||
let inc_amount_symbol = Symbol::ARG_2;
|
||||
let inc_amount_val = param_iter.next().unwrap();
|
||||
|
||||
incrementing_elem_loop(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
element_layout,
|
||||
ptr,
|
||||
len,
|
||||
"modify_rc_index",
|
||||
loop_fn,
|
||||
);
|
||||
inc_amount_val.set_name(inc_amount_symbol.as_str(&env.interns));
|
||||
|
||||
call_void_list_bitcode_fn(
|
||||
env,
|
||||
&[original_wrapper],
|
||||
&[
|
||||
inc_amount_val.into_int_value().into(),
|
||||
layout_refcounted(env, layout_interner, element_layout),
|
||||
],
|
||||
bitcode::LIST_INCREF,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let refcount_ptr =
|
||||
PointerToRefcount::from_ptr_to_data(env, list_allocation_ptr(env, original_wrapper));
|
||||
let call_mode = mode_to_call_mode(fn_val, mode);
|
||||
refcount_ptr.modify(call_mode, layout, env, layout_interner);
|
||||
|
||||
builder.new_build_unconditional_branch(cont_block);
|
||||
|
||||
builder.position_at_end(cont_block);
|
||||
|
||||
// this function returns void
|
||||
builder.new_build_return(None);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ pub enum ProcSource {
|
|||
Roc,
|
||||
Helper,
|
||||
/// Wrapper function for higher-order calls from Zig to Roc
|
||||
HigherOrderMapper(usize),
|
||||
HigherOrderCompare(usize),
|
||||
}
|
||||
|
||||
|
@ -491,126 +490,6 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
self.module.names.append_function(wasm_fn_index, name);
|
||||
}
|
||||
|
||||
/// Build a wrapper around a Roc procedure so that it can be called from Zig builtins List.map*
|
||||
///
|
||||
/// The generic Zig code passes *pointers* to all of the argument values (e.g. on the heap in a List).
|
||||
/// Numbers up to 64 bits are passed by value, so we need to load them from the provided pointer.
|
||||
/// Everything else is passed by reference, so we can just pass the pointer through.
|
||||
///
|
||||
/// NOTE: If the builtins expected the return pointer first and closure data last, we could eliminate the wrapper
|
||||
/// when all args are pass-by-reference and non-zero size. But currently we need it to swap those around.
|
||||
pub fn build_higher_order_mapper(
|
||||
&mut self,
|
||||
wrapper_lookup_idx: usize,
|
||||
inner_lookup_idx: usize,
|
||||
) {
|
||||
use Align::*;
|
||||
use ValueType::*;
|
||||
|
||||
let ProcLookupData {
|
||||
name: wrapper_name,
|
||||
layout: wrapper_proc_layout,
|
||||
..
|
||||
} = self.proc_lookup[wrapper_lookup_idx];
|
||||
let wrapper_arg_layouts = wrapper_proc_layout.arguments;
|
||||
|
||||
// Our convention is that the last arg of the wrapper is the heap return pointer
|
||||
let heap_return_ptr_id = LocalId(wrapper_arg_layouts.len() as u32 - 1);
|
||||
let inner_ret_layout = match wrapper_arg_layouts
|
||||
.last()
|
||||
.map(|l| self.layout_interner.get_repr(*l))
|
||||
{
|
||||
Some(LayoutRepr::Ptr(inner)) => WasmLayout::new(self.layout_interner, inner),
|
||||
x => internal_error!("Higher-order wrapper: invalid return layout {:?}", x),
|
||||
};
|
||||
|
||||
let ret_type_and_size = match inner_ret_layout.return_method() {
|
||||
ReturnMethod::NoReturnValue => None,
|
||||
ReturnMethod::Primitive(ty, size) => {
|
||||
// If the inner function returns a primitive, load the address to store it at
|
||||
// After the call, it will be under the call result in the value stack
|
||||
self.code_builder.get_local(heap_return_ptr_id);
|
||||
Some((ty, size))
|
||||
}
|
||||
ReturnMethod::WriteToPointerArg => {
|
||||
// If the inner function writes to a return pointer, load its address
|
||||
self.code_builder.get_local(heap_return_ptr_id);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Load all the arguments for the inner function
|
||||
for (i, wrapper_arg) in wrapper_arg_layouts.iter().enumerate() {
|
||||
let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner). We'll handle it below.
|
||||
let is_return_pointer = i == wrapper_arg_layouts.len() - 1; // Skip return pointer (may not be an arg for inner. And if it is, swaps from end to start)
|
||||
if is_closure_data || is_return_pointer {
|
||||
continue;
|
||||
}
|
||||
|
||||
let inner_layout = match self.layout_interner.get_repr(*wrapper_arg) {
|
||||
LayoutRepr::Ptr(inner) => inner,
|
||||
x => internal_error!("Expected a Ptr layout, got {:?}", x),
|
||||
};
|
||||
if self.layout_interner.stack_size(inner_layout) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load the argument pointer. If it's a primitive value, dereference it too.
|
||||
self.code_builder.get_local(LocalId(i as u32));
|
||||
self.dereference_boxed_value(inner_layout);
|
||||
}
|
||||
|
||||
// If the inner function has closure data, it's the last arg of the inner fn
|
||||
let closure_data_layout = wrapper_arg_layouts[0];
|
||||
if self.layout_interner.stack_size(closure_data_layout) > 0 {
|
||||
// The closure data exists, and will have been passed in to the wrapper as a
|
||||
// one-element struct.
|
||||
let inner_closure_data_layout = match self.layout_interner.get_repr(closure_data_layout)
|
||||
{
|
||||
LayoutRepr::Struct([inner]) => inner,
|
||||
other => internal_error!(
|
||||
"Expected a boxed layout for wrapped closure data, got {:?}",
|
||||
other
|
||||
),
|
||||
};
|
||||
self.code_builder.get_local(LocalId(0));
|
||||
// Since the closure data is wrapped in a one-element struct, we've been passed in the
|
||||
// pointer to that struct in the stack memory. To get the closure data we just need to
|
||||
// dereference the pointer.
|
||||
self.dereference_boxed_value(*inner_closure_data_layout);
|
||||
}
|
||||
|
||||
// Call the wrapped inner function
|
||||
let inner_wasm_fn_index = self.fn_index_offset + inner_lookup_idx as u32;
|
||||
self.code_builder.call(inner_wasm_fn_index);
|
||||
|
||||
// If the inner function returns a primitive, store it to the address we loaded at the very beginning
|
||||
if let Some((ty, size)) = ret_type_and_size {
|
||||
match (ty, size) {
|
||||
(I64, 8) => self.code_builder.i64_store(Bytes8, 0),
|
||||
(I32, 4) => self.code_builder.i32_store(Bytes4, 0),
|
||||
(I32, 2) => self.code_builder.i32_store16(Bytes2, 0),
|
||||
(I32, 1) => self.code_builder.i32_store8(Bytes1, 0),
|
||||
(F32, 4) => self.code_builder.f32_store(Bytes4, 0),
|
||||
(F64, 8) => self.code_builder.f64_store(Bytes8, 0),
|
||||
_ => {
|
||||
internal_error!("Cannot store {:?} with alignment of {:?}", ty, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write empty function header (local variables array with zero length)
|
||||
self.code_builder.build_fn_header_and_footer(&[], 0, None);
|
||||
|
||||
self.module.add_function_signature(Signature {
|
||||
param_types: bumpalo::vec![in self.env.arena; I32; wrapper_arg_layouts.len()],
|
||||
ret_type: None,
|
||||
});
|
||||
|
||||
self.append_proc_debug_name(wrapper_name);
|
||||
self.reset();
|
||||
}
|
||||
|
||||
/// Build a wrapper around a Roc comparison proc so that it can be called from higher-order Zig builtins.
|
||||
/// Comparison procedure signature is: closure_data, a, b -> Order (u8)
|
||||
///
|
||||
|
@ -986,6 +865,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
|
||||
fn stmt_refcounting_free(&mut self, value: Symbol, following: &'a Stmt<'a>) {
|
||||
let layout = self.storage.symbol_layouts[&value];
|
||||
debug_assert!(!matches!(self.layout_interner.get_repr(layout), LayoutRepr::Builtin(Builtin::List(_))), "List are no longer safe to refcount through pointer alone. They must go through the zig bitcode functions");
|
||||
|
||||
let alignment = self.layout_interner.allocation_alignment_bytes(layout);
|
||||
|
||||
// Get pointer and offset
|
||||
|
@ -1010,6 +891,9 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
// push the allocation's alignment
|
||||
self.code_builder.i32_const(alignment as i32);
|
||||
|
||||
// elems_refcounted (always false except for list which are refcounted differently)
|
||||
self.code_builder.i32_const(false as i32);
|
||||
|
||||
self.call_host_fn_after_loading_args(bitcode::UTILS_FREE_DATA_PTR);
|
||||
|
||||
self.stmt(following);
|
||||
|
@ -1557,7 +1441,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
// Allocate heap space and store its address in a local variable
|
||||
let heap_local_id = self.storage.create_anonymous_local(PTR_TYPE);
|
||||
let heap_alignment = self.layout_interner.alignment_bytes(elem_layout);
|
||||
self.allocate_with_refcount(Some(size), heap_alignment, 1);
|
||||
let elems_refcounted = self.layout_interner.contains_refcounted(elem_layout);
|
||||
self.allocate_with_refcount(size, heap_alignment, elems_refcounted);
|
||||
self.code_builder.set_local(heap_local_id);
|
||||
|
||||
let (stack_local_id, stack_offset) =
|
||||
|
@ -1671,13 +1556,13 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
}
|
||||
self.code_builder.else_();
|
||||
{
|
||||
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||
self.allocate_with_refcount(data_size, data_alignment, false);
|
||||
self.code_builder.set_local(*local_id);
|
||||
}
|
||||
self.code_builder.end();
|
||||
} else {
|
||||
// Call the allocator to get a memory address.
|
||||
self.allocate_with_refcount(Some(data_size), data_alignment, 1);
|
||||
self.allocate_with_refcount(data_size, data_alignment, false);
|
||||
self.code_builder.set_local(*local_id);
|
||||
}
|
||||
(*local_id, 0)
|
||||
|
@ -1960,53 +1845,31 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
* Refcounting & Heap allocation
|
||||
*******************************************************************/
|
||||
|
||||
/// Allocate heap space and write an initial refcount
|
||||
/// If the data size is known at compile time, pass it in comptime_data_size.
|
||||
/// If size is only known at runtime, push *data* size to the VM stack first.
|
||||
/// Allocates heap space and write an initial refcount of 1.
|
||||
/// Leaves the *data* address on the VM stack
|
||||
///
|
||||
/// elements_refcounted should only ever be set for lists.
|
||||
fn allocate_with_refcount(
|
||||
&mut self,
|
||||
comptime_data_size: Option<u32>,
|
||||
data_size: u32,
|
||||
alignment_bytes: u32,
|
||||
initial_refcount: u32,
|
||||
elements_refcounted: bool,
|
||||
) {
|
||||
if !self.can_relocate_heap {
|
||||
// This will probably only happen for test hosts.
|
||||
panic!("The app tries to allocate heap memory but the host doesn't support that. It needs to export symbols __heap_base and __heap_end");
|
||||
}
|
||||
// Add extra bytes for the refcount
|
||||
let extra_bytes = alignment_bytes.max(PTR_SIZE);
|
||||
|
||||
if let Some(data_size) = comptime_data_size {
|
||||
// Data size known at compile time and passed as an argument
|
||||
self.code_builder
|
||||
.i32_const((data_size + extra_bytes) as i32);
|
||||
} else {
|
||||
// Data size known only at runtime and is on top of VM stack
|
||||
self.code_builder.i32_const(extra_bytes as i32);
|
||||
self.code_builder.i32_add();
|
||||
}
|
||||
// Zig arguments Wasm types
|
||||
// data_bytes: usize i32
|
||||
// element_alignment: u32 i32
|
||||
// element_refcounted: bool i32
|
||||
|
||||
// Provide a constant for the alignment argument
|
||||
self.code_builder.i32_const(data_size as i32);
|
||||
self.code_builder.i32_const(alignment_bytes as i32);
|
||||
self.code_builder.i32_const(elements_refcounted as i32);
|
||||
|
||||
// Call the foreign function. (Zig and C calling conventions are the same for this signature)
|
||||
self.call_host_fn_after_loading_args("roc_alloc");
|
||||
|
||||
// Save the allocation address to a temporary local variable
|
||||
let local_id = self.storage.create_anonymous_local(ValueType::I32);
|
||||
self.code_builder.tee_local(local_id);
|
||||
|
||||
// Write the initial refcount
|
||||
let refcount_offset = extra_bytes - PTR_SIZE;
|
||||
let encoded_refcount = (initial_refcount as i32) - 1 + i32::MIN;
|
||||
self.code_builder.i32_const(encoded_refcount);
|
||||
self.code_builder.i32_store(Align::Bytes4, refcount_offset);
|
||||
|
||||
// Put the data address on the VM stack
|
||||
self.code_builder.get_local(local_id);
|
||||
self.code_builder.i32_const(extra_bytes as i32);
|
||||
self.code_builder.i32_add();
|
||||
self.call_host_fn_after_loading_args(bitcode::UTILS_ALLOCATE_WITH_REFCOUNT);
|
||||
}
|
||||
|
||||
fn expr_reset(&mut self, argument: Symbol, ret_symbol: Symbol, ret_storage: &StoredValue) {
|
||||
|
@ -2118,9 +1981,30 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper);
|
||||
}
|
||||
|
||||
self.get_existing_refcount_fn_index(proc_symbol, layout, op)
|
||||
}
|
||||
|
||||
/// return a pointer (table index) to a refcount helper procedure.
|
||||
/// This allows it to be indirectly called from Zig code
|
||||
pub fn get_existing_refcount_fn_index(
|
||||
&mut self,
|
||||
proc_symbol: Symbol,
|
||||
layout: InLayout<'a>,
|
||||
op: HelperOp,
|
||||
) -> u32 {
|
||||
let layout_repr = self.layout_interner.runtime_representation(layout);
|
||||
let same_layout =
|
||||
|layout| self.layout_interner.runtime_representation(layout) == layout_repr;
|
||||
let same_layout = |layout| {
|
||||
if op.is_indirect() {
|
||||
if let LayoutRepr::Ptr(inner) = self.layout_interner.runtime_representation(layout)
|
||||
{
|
||||
self.layout_interner.runtime_representation(inner) == layout_repr
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
self.layout_interner.runtime_representation(layout) == layout_repr
|
||||
}
|
||||
};
|
||||
let proc_index = self
|
||||
.proc_lookup
|
||||
.iter()
|
||||
|
|
|
@ -178,7 +178,6 @@ pub fn build_app_module<'a, 'r>(
|
|||
match source {
|
||||
Roc => { /* already generated */ }
|
||||
Helper => backend.build_proc(helper_iter.next().unwrap()),
|
||||
HigherOrderMapper(inner_idx) => backend.build_higher_order_mapper(idx, *inner_idx),
|
||||
HigherOrderCompare(inner_idx) => backend.build_higher_order_compare(idx, *inner_idx),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,27 +275,109 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
ListClone => {
|
||||
let input_list: Symbol = self.arguments[0];
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// input_list: &RocList i32
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[self.ret_symbol, input_list]);
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_CLONE);
|
||||
}
|
||||
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => {
|
||||
ListIncref => {
|
||||
let input_list: Symbol = self.arguments[0];
|
||||
let list_layout = backend
|
||||
.layout_interner
|
||||
.get_repr(backend.storage.symbol_layouts[&input_list]);
|
||||
let elem_in_layout = unwrap_list_elem_layout(list_layout);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// input_list: &RocList i32
|
||||
// amount: isize i32
|
||||
// element_refcounted: bool i32
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[input_list]);
|
||||
if self.arguments.len() == 2 {
|
||||
// amount explicitly specified.
|
||||
let amount = self.arguments[1];
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[amount]);
|
||||
} else {
|
||||
// implicit 1 amount
|
||||
backend.code_builder.i32_const(1);
|
||||
}
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_INCREF);
|
||||
}
|
||||
|
||||
ListDecref => {
|
||||
let input_list: Symbol = self.arguments[0];
|
||||
let dec_fn_sym = self.arguments[1];
|
||||
|
||||
let list_layout = backend
|
||||
.layout_interner
|
||||
.get_repr(backend.storage.symbol_layouts[&input_list]);
|
||||
let elem_in_layout = unwrap_list_elem_layout(list_layout);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let dec_fn = backend.get_existing_refcount_fn_index(
|
||||
dec_fn_sym,
|
||||
elem_in_layout,
|
||||
HelperOp::IndirectDec,
|
||||
);
|
||||
let dec_fn_ptr = backend.get_fn_ptr(dec_fn);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// input_list: &RocList i32
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// dec: Dec i32
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[input_list]);
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_DECREF);
|
||||
}
|
||||
|
||||
ListSortWith => {
|
||||
internal_error!("HigherOrder lowlevels should not be handled here")
|
||||
}
|
||||
|
||||
|
@ -334,14 +416,6 @@ impl<'a> LowLevelCall<'a> {
|
|||
AddressValue::NotLoaded(elem_local),
|
||||
0,
|
||||
);
|
||||
|
||||
// Increment refcount
|
||||
if self.ret_layout_raw.is_refcounted(backend.layout_interner) {
|
||||
let inc_fn = backend.get_refcount_fn_index(self.ret_layout, HelperOp::Inc);
|
||||
backend.code_builder.get_local(elem_local);
|
||||
backend.code_builder.i32_const(1);
|
||||
backend.code_builder.call(inc_fn);
|
||||
}
|
||||
}
|
||||
ListReplaceUnsafe => {
|
||||
// List.replace_unsafe : List elem, U64, elem -> { list: List elem, value: elem }
|
||||
|
@ -359,7 +433,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
};
|
||||
|
||||
// Byte offsets of each field in the return struct
|
||||
let (ret_list_offset, ret_elem_offset, elem_layout) = match self.ret_layout_raw {
|
||||
let (ret_list_offset, ret_elem_offset, elem_in_layout) = match self.ret_layout_raw {
|
||||
LayoutRepr::Struct(&[f1, f2]) => {
|
||||
let l1 = backend.layout_interner.get_repr(f1);
|
||||
let l2 = backend.layout_interner.get_repr(f2);
|
||||
|
@ -387,20 +461,33 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
let (elem_width, elem_alignment) = backend
|
||||
.layout_interner
|
||||
.stack_size_and_alignment(elem_layout);
|
||||
.stack_size_and_alignment(elem_in_layout);
|
||||
|
||||
// Ensure the new element is stored in memory so we can pass a pointer to Zig
|
||||
let (new_elem_local, new_elem_offset, _) =
|
||||
ensure_symbol_is_in_memory(backend, new_elem, elem_layout, backend.env.arena);
|
||||
let (new_elem_local, new_elem_offset, _) = ensure_symbol_is_in_memory(
|
||||
backend,
|
||||
new_elem,
|
||||
elem_in_layout,
|
||||
backend.env.arena,
|
||||
);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Load all the arguments for Zig
|
||||
// (List return pointer) i32
|
||||
// list: RocList, i32
|
||||
// alignment: u32, i32
|
||||
// index: usize, i32
|
||||
// element: Opaque, i32
|
||||
// element_width: usize, i32
|
||||
// out_element: ?[*]u8, i32
|
||||
// (List return pointer) i32
|
||||
// list: RocList, i32
|
||||
// alignment: u32, i32
|
||||
// index: usize, i32
|
||||
// element: Opaque, i32
|
||||
// element_width: usize, i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// out_element: ?[*]u8, i32
|
||||
|
||||
let code_builder = &mut backend.code_builder;
|
||||
|
||||
|
@ -421,6 +508,9 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
|
||||
code_builder.i32_const(elem_width as i32);
|
||||
code_builder.i32_const(elem_refcounted as i32);
|
||||
code_builder.i32_const(inc_fn_ptr);
|
||||
code_builder.i32_const(dec_fn_ptr);
|
||||
|
||||
code_builder.get_local(ret_local);
|
||||
if (ret_offset + ret_elem_offset) > 0 {
|
||||
|
@ -435,33 +525,34 @@ impl<'a> LowLevelCall<'a> {
|
|||
// List.withCapacity : U64 -> List elem
|
||||
|
||||
let capacity: Symbol = self.arguments[0];
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// capacity: u64 i64
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[self.ret_symbol, capacity]);
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr as i32);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_WITH_CAPACITY);
|
||||
}
|
||||
ListConcat => {
|
||||
// List.concat : List elem, List elem -> List elem
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list_a: RocList i32
|
||||
// list_b: RocList i32
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
|
||||
// Load the arguments that have symbols
|
||||
backend.storage.load_symbols_for_call(
|
||||
|
@ -472,12 +563,31 @@ impl<'a> LowLevelCall<'a> {
|
|||
);
|
||||
|
||||
// Load monomorphization constants
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list_a: RocList i32
|
||||
// list_b: RocList i32
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_CONCAT);
|
||||
}
|
||||
|
@ -489,10 +599,13 @@ impl<'a> LowLevelCall<'a> {
|
|||
let list: Symbol = self.arguments[0];
|
||||
let spare: Symbol = self.arguments[1];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
|
@ -500,6 +613,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
// alignment: u32 i32
|
||||
// spare: u64 i64
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// update_mode: UpdateMode i32
|
||||
|
||||
// return pointer and list
|
||||
|
@ -517,7 +632,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
.load_symbols(&mut backend.code_builder, &[spare]);
|
||||
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_RESERVE);
|
||||
|
@ -528,16 +644,25 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
let list: Symbol = self.arguments[0];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (elem_width, elem_align) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list: RocList i32
|
||||
// alignment: u32 i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// update_mode: UpdateMode i32
|
||||
|
||||
// return pointer and list
|
||||
|
@ -549,9 +674,10 @@ impl<'a> LowLevelCall<'a> {
|
|||
);
|
||||
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_RELEASE_EXCESS_CAPACITY);
|
||||
|
@ -598,12 +724,16 @@ impl<'a> LowLevelCall<'a> {
|
|||
let list: Symbol = self.arguments[0];
|
||||
let elem: Symbol = self.arguments[1];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let (elem_width, elem_align) = backend
|
||||
.layout_interner
|
||||
.stack_size_and_alignment(elem_layout);
|
||||
.stack_size_and_alignment(elem_in_layout);
|
||||
let (elem_local, elem_offset, _) =
|
||||
ensure_symbol_is_in_memory(backend, elem, elem_layout, backend.env.arena);
|
||||
ensure_symbol_is_in_memory(backend, elem, elem_in_layout, backend.env.arena);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
|
@ -611,6 +741,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
// alignment: u32 i32
|
||||
// element: Opaque i32
|
||||
// element_width: usize i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
|
||||
// return pointer and list
|
||||
backend.storage.load_symbols_for_call(
|
||||
|
@ -628,6 +760,8 @@ impl<'a> LowLevelCall<'a> {
|
|||
backend.code_builder.i32_add();
|
||||
}
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_PREPEND);
|
||||
}
|
||||
|
@ -639,27 +773,21 @@ impl<'a> LowLevelCall<'a> {
|
|||
let start: Symbol = self.arguments[1];
|
||||
let len: Symbol = self.arguments[2];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let (elem_width, elem_align) = backend
|
||||
.layout_interner
|
||||
.stack_size_and_alignment(elem_layout);
|
||||
.stack_size_and_alignment(elem_in_layout);
|
||||
|
||||
// The refcount function receives a pointer to an element in the list
|
||||
// This is the same as a Struct containing the element
|
||||
let in_memory_layout =
|
||||
backend
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(LayoutRepr::Struct(
|
||||
backend.env.arena.alloc([elem_layout]),
|
||||
));
|
||||
let dec_fn = backend.get_refcount_fn_index(in_memory_layout, HelperOp::Dec);
|
||||
let dec_fn_ptr = backend.get_fn_ptr(dec_fn);
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list: RocList, i32
|
||||
// alignment: u32, i32
|
||||
// element_width: usize, i32
|
||||
// element_recounted: bool, i32
|
||||
// start: u64, i64
|
||||
// len: u64, i64
|
||||
// dec: Dec, i32
|
||||
|
@ -673,6 +801,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[start, len]);
|
||||
|
@ -685,28 +814,25 @@ impl<'a> LowLevelCall<'a> {
|
|||
let list: Symbol = self.arguments[0];
|
||||
let drop_index: Symbol = self.arguments[1];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let (elem_width, elem_align) = backend
|
||||
.layout_interner
|
||||
.stack_size_and_alignment(elem_layout);
|
||||
.stack_size_and_alignment(elem_in_layout);
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
|
||||
// The refcount function receives a pointer to an element in the list
|
||||
// This is the same as a Struct containing the element
|
||||
let in_memory_layout =
|
||||
backend
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(LayoutRepr::Struct(
|
||||
backend.env.arena.alloc([elem_layout]),
|
||||
));
|
||||
let dec_fn = backend.get_refcount_fn_index(in_memory_layout, HelperOp::Dec);
|
||||
let dec_fn_ptr = backend.get_fn_ptr(dec_fn);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
// list: RocList, i32
|
||||
// element_width: usize, i32
|
||||
// alignment: u32, i32
|
||||
// element_width: usize, i32
|
||||
// elements_refcounted: bool, i32
|
||||
// drop_index: u64, i64
|
||||
// inc: Inc, i32
|
||||
// dec: Dec, i32
|
||||
|
||||
// Load the return pointer and the list
|
||||
|
@ -717,11 +843,13 @@ impl<'a> LowLevelCall<'a> {
|
|||
&WasmLayout::new(backend.layout_interner, self.ret_layout),
|
||||
);
|
||||
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_align as i32);
|
||||
backend.code_builder.i32_const(elem_width as i32);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[drop_index]);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_DROP_AT);
|
||||
|
@ -732,10 +860,16 @@ impl<'a> LowLevelCall<'a> {
|
|||
let index_1: Symbol = self.arguments[1];
|
||||
let index_2: Symbol = self.arguments[2];
|
||||
|
||||
let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw);
|
||||
let (elem_width, elem_align) = backend
|
||||
.layout_interner
|
||||
.stack_size_and_alignment(elem_layout);
|
||||
.stack_size_and_alignment(elem_in_layout);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
// Zig arguments Wasm types
|
||||
// (return pointer) i32
|
||||
|
@ -744,6 +878,9 @@ impl<'a> LowLevelCall<'a> {
|
|||
// element_width: usize, i32
|
||||
// index_1: u64, i64
|
||||
// index_2: u64, i64
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
// update_mode: UpdateMode, i32
|
||||
|
||||
// Load the return pointer and the list
|
||||
|
@ -759,6 +896,9 @@ impl<'a> LowLevelCall<'a> {
|
|||
backend
|
||||
.storage
|
||||
.load_symbols(&mut backend.code_builder, &[index_1, index_2]);
|
||||
backend.code_builder.i32_const(elem_refcounted as i32);
|
||||
backend.code_builder.i32_const(inc_fn_ptr);
|
||||
backend.code_builder.i32_const(dec_fn_ptr);
|
||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_SWAP);
|
||||
|
@ -2467,7 +2607,7 @@ fn num_is_finite(backend: &mut WasmBackend<'_, '_>, argument: Symbol) {
|
|||
pub fn call_higher_order_lowlevel<'a>(
|
||||
backend: &mut WasmBackend<'a, '_>,
|
||||
return_sym: Symbol,
|
||||
return_layout: &InLayout<'a>,
|
||||
_return_layout: &InLayout<'a>,
|
||||
higher_order: &'a HigherOrderLowLevel<'a>,
|
||||
) {
|
||||
use HigherOrder::*;
|
||||
|
@ -2579,9 +2719,6 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
.unwrap();
|
||||
match op {
|
||||
ListSortWith { .. } => ProcSource::HigherOrderCompare(passed_proc_index),
|
||||
ListMap { .. } | ListMap2 { .. } | ListMap3 { .. } | ListMap4 { .. } => {
|
||||
ProcSource::HigherOrderMapper(passed_proc_index)
|
||||
}
|
||||
}
|
||||
};
|
||||
let wrapper_sym = backend.create_symbol(&format!("#wrap#{fn_name:?}"));
|
||||
|
@ -2606,19 +2743,6 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
wrapper_arg_layouts.extend(boxed_closure_arg_layouts);
|
||||
|
||||
match helper_proc_source {
|
||||
ProcSource::HigherOrderMapper(_) => {
|
||||
// Our convention for mappers is that they write to the heap via the last argument
|
||||
wrapper_arg_layouts.push(
|
||||
backend
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(LayoutRepr::Ptr(*result_layout)),
|
||||
);
|
||||
ProcLayout {
|
||||
arguments: wrapper_arg_layouts.into_bump_slice(),
|
||||
result: Layout::UNIT,
|
||||
niche: fn_name.niche(),
|
||||
}
|
||||
}
|
||||
ProcSource::HigherOrderCompare(_) => ProcLayout {
|
||||
arguments: wrapper_arg_layouts.into_bump_slice(),
|
||||
result: *result_layout,
|
||||
|
@ -2633,90 +2757,47 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
let wrapper_fn_idx =
|
||||
backend.register_helper_proc(wrapper_sym, wrapper_layout, helper_proc_source);
|
||||
let wrapper_fn_ptr = backend.get_fn_ptr(wrapper_fn_idx);
|
||||
let inc_fn_ptr = if !closure_data_exists {
|
||||
let inc_n_fn_ptr = if !closure_data_exists {
|
||||
// Our code gen would ignore the Unit arg, but the Zig builtin passes a pointer for it!
|
||||
// That results in an exception (type signature mismatch in indirect call).
|
||||
// The workaround is to use I32 layout, treating the (ignored) pointer as an integer.
|
||||
let inc_fn = backend.get_refcount_fn_index(Layout::I32, HelperOp::Inc);
|
||||
let inc_fn = backend.get_refcount_fn_index(Layout::I32, HelperOp::IncN);
|
||||
backend.get_fn_ptr(inc_fn)
|
||||
} else {
|
||||
let inc_fn = backend.get_refcount_fn_index(wrapped_captures_layout, HelperOp::Inc);
|
||||
let inc_fn = backend.get_refcount_fn_index(wrapped_captures_layout, HelperOp::IncN);
|
||||
backend.get_fn_ptr(inc_fn)
|
||||
};
|
||||
|
||||
match op {
|
||||
ListMap { xs } => list_map_n(
|
||||
bitcode::LIST_MAP,
|
||||
backend,
|
||||
&[*xs],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
ListMap2 { xs, ys } => list_map_n(
|
||||
bitcode::LIST_MAP2,
|
||||
backend,
|
||||
&[*xs, *ys],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
ListMap3 { xs, ys, zs } => list_map_n(
|
||||
bitcode::LIST_MAP3,
|
||||
backend,
|
||||
&[*xs, *ys, *zs],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
ListMap4 { xs, ys, zs, ws } => list_map_n(
|
||||
bitcode::LIST_MAP4,
|
||||
backend,
|
||||
&[*xs, *ys, *zs, *ws],
|
||||
return_sym,
|
||||
*return_layout,
|
||||
wrapper_fn_ptr,
|
||||
inc_fn_ptr,
|
||||
closure_data_exists,
|
||||
wrapped_captured_environment,
|
||||
*owns_captured_environment,
|
||||
),
|
||||
|
||||
ListSortWith { xs } => {
|
||||
let elem_layout = unwrap_list_elem_layout(
|
||||
let elem_in_layout = unwrap_list_elem_layout(
|
||||
backend
|
||||
.layout_interner
|
||||
.get_repr(backend.storage.symbol_layouts[xs]),
|
||||
);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_layout);
|
||||
let elem_layout = backend.layout_interner.get_repr(elem_in_layout);
|
||||
let (element_width, alignment) =
|
||||
elem_layout.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout);
|
||||
let inc_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc);
|
||||
let dec_fn_ptr =
|
||||
build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec);
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
|
||||
// (return pointer) i32
|
||||
// input: RocList, i32
|
||||
// caller: CompareFn, i32
|
||||
// data: Opaque, i32
|
||||
// inc_n_data: IncN, i32
|
||||
// data_is_owned: bool, i32
|
||||
// alignment: u32, i32
|
||||
// element_width: usize, i32
|
||||
// (return pointer) i32
|
||||
// input: RocList, i32
|
||||
// caller: CompareFn, i32
|
||||
// data: Opaque, i32
|
||||
// inc_n_data: IncN, i32
|
||||
// data_is_owned: bool, i32
|
||||
// alignment: u32, i32
|
||||
// element_width: usize, i32
|
||||
// element_refcounted: bool i32
|
||||
// inc: Inc i32
|
||||
// dec: Dec i32
|
||||
|
||||
backend.storage.load_symbols(cb, &[return_sym, *xs]);
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
|
@ -2729,10 +2810,13 @@ pub fn call_higher_order_lowlevel<'a>(
|
|||
// but that's a specialization that our Zig code doesn't have! Pass a null pointer.
|
||||
cb.i32_const(0);
|
||||
}
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(inc_n_fn_ptr);
|
||||
cb.i32_const(*owns_captured_environment as i32);
|
||||
cb.i32_const(alignment as i32);
|
||||
cb.i32_const(element_width as i32);
|
||||
cb.i32_const(elem_refcounted as i32);
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(dec_fn_ptr);
|
||||
|
||||
backend.call_host_fn_after_loading_args(bitcode::LIST_SORT_WITH);
|
||||
}
|
||||
|
@ -2746,75 +2830,6 @@ fn unwrap_list_elem_layout(list_layout: LayoutRepr) -> InLayout {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn list_map_n<'a>(
|
||||
zig_fn_name: &'static str,
|
||||
backend: &mut WasmBackend<'a, '_>,
|
||||
arg_symbols: &[Symbol],
|
||||
return_sym: Symbol,
|
||||
return_layout: InLayout<'a>,
|
||||
wrapper_fn_ptr: i32,
|
||||
inc_fn_ptr: i32,
|
||||
closure_data_exists: bool,
|
||||
captured_environment: Symbol,
|
||||
owns_captured_environment: bool,
|
||||
) {
|
||||
let arg_elem_layouts = Vec::from_iter_in(
|
||||
arg_symbols.iter().map(|sym| {
|
||||
unwrap_list_elem_layout(
|
||||
backend
|
||||
.layout_interner
|
||||
.get_repr(backend.storage.symbol_layouts[sym]),
|
||||
)
|
||||
}),
|
||||
backend.env.arena,
|
||||
);
|
||||
|
||||
let elem_ret = unwrap_list_elem_layout(backend.layout_interner.get_repr(return_layout));
|
||||
let elem_ret = backend.layout_interner.get_repr(elem_ret);
|
||||
let (elem_ret_size, elem_ret_align) =
|
||||
elem_ret.stack_size_and_alignment(backend.layout_interner);
|
||||
|
||||
let cb = &mut backend.code_builder;
|
||||
|
||||
let mut args_vec = Vec::with_capacity_in(arg_symbols.len() + 1, backend.env.arena);
|
||||
args_vec.push(return_sym);
|
||||
args_vec.extend_from_slice(arg_symbols);
|
||||
backend.storage.load_symbols(cb, &args_vec);
|
||||
|
||||
cb.i32_const(wrapper_fn_ptr);
|
||||
if closure_data_exists {
|
||||
backend.storage.load_symbols(cb, &[captured_environment]);
|
||||
} else {
|
||||
// load_symbols assumes that a zero-size arg should be eliminated in code gen,
|
||||
// but that's a specialization that our Zig code doesn't have! Pass a null pointer.
|
||||
cb.i32_const(0);
|
||||
}
|
||||
cb.i32_const(inc_fn_ptr);
|
||||
cb.i32_const(owns_captured_environment as i32);
|
||||
cb.i32_const(elem_ret_align as i32);
|
||||
for el in arg_elem_layouts.iter() {
|
||||
cb.i32_const(backend.layout_interner.stack_size(*el) as i32);
|
||||
}
|
||||
cb.i32_const(elem_ret_size as i32);
|
||||
|
||||
// If we have lists of different lengths, we may need to decrement
|
||||
if arg_elem_layouts.len() > 1 {
|
||||
for el in arg_elem_layouts.iter() {
|
||||
// The dec function will be passed a pointer to the element within the list, not the element itself!
|
||||
// Here we wrap the layout in a Struct to ensure we get the right code gen
|
||||
let el_ptr = backend
|
||||
.layout_interner
|
||||
.insert_direct_no_semantic(LayoutRepr::Struct(backend.env.arena.alloc([*el])));
|
||||
let idx = backend.get_refcount_fn_index(el_ptr, HelperOp::Dec);
|
||||
let ptr = backend.get_fn_ptr(idx);
|
||||
backend.code_builder.i32_const(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
backend.call_host_fn_after_loading_args(zig_fn_name);
|
||||
}
|
||||
|
||||
fn ensure_symbol_is_in_memory<'a>(
|
||||
backend: &mut WasmBackend<'a, '_>,
|
||||
symbol: Symbol,
|
||||
|
@ -2845,3 +2860,12 @@ fn ensure_symbol_is_in_memory<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_refcount_element_fn<'a>(
|
||||
backend: &mut WasmBackend<'a, '_>,
|
||||
elem_layout: InLayout<'a>,
|
||||
rc_op: HelperOp,
|
||||
) -> i32 {
|
||||
let rc_fn = backend.get_refcount_fn_index(elem_layout, rc_op);
|
||||
backend.get_fn_ptr(rc_fn)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use bumpalo::{collections::Vec, Bump};
|
|||
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{Builtin, InLayout, LayoutInterner, LayoutRepr, UnionLayout};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocRefcounted, RocResult, RocStr, I128, U128};
|
||||
use roc_wasm_module::{
|
||||
linking::SymInfo, linking::WasmObjectSymbol, Align, Export, ExportType, LocalId, Signature,
|
||||
ValueType, WasmModule,
|
||||
|
@ -197,13 +197,19 @@ impl Wasm32Result for RocStr {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Wasm32Result> Wasm32Result for RocList<T> {
|
||||
impl<T: Wasm32Result> Wasm32Result for RocList<T>
|
||||
where
|
||||
T: RocRefcounted,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
build_wrapper_body_stack_memory(code_builder, main_function_index, 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Wasm32Result> Wasm32Result for RocBox<T> {
|
||||
impl<T: Wasm32Result> Wasm32Result for RocBox<T>
|
||||
where
|
||||
T: RocRefcounted,
|
||||
{
|
||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) {
|
||||
// treat box as if it's just a isize value
|
||||
<i32 as Wasm32Result>::build_wrapper_body(code_builder, main_function_index)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocResult, RocStr, I128, U128};
|
||||
use roc_std::{RocBox, RocDec, RocList, RocOrder, RocRefcounted, RocResult, RocStr, I128, U128};
|
||||
|
||||
pub trait Wasm32Sized: Sized {
|
||||
const SIZE_OF_WASM: usize;
|
||||
|
@ -53,12 +53,18 @@ impl Wasm32Sized for RocStr {
|
|||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized> Wasm32Sized for RocList<T> {
|
||||
impl<T: Wasm32Sized> Wasm32Sized for RocList<T>
|
||||
where
|
||||
T: RocRefcounted,
|
||||
{
|
||||
const SIZE_OF_WASM: usize = 12;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
||||
impl<T: Wasm32Sized> Wasm32Sized for RocBox<T> {
|
||||
impl<T: Wasm32Sized> Wasm32Sized for RocBox<T>
|
||||
where
|
||||
T: RocRefcounted,
|
||||
{
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
}
|
||||
|
|
|
@ -4972,7 +4972,7 @@ mod test_reporting {
|
|||
}
|
||||
"
|
||||
),@r###"
|
||||
── RECORD BUILDER IN MODULE PARAMS in ...ord_builder_in_module_params/Test.roc ─
|
||||
── OLD-STYLE RECORD BUILDER IN MODULE PARAMS in ...r_in_module_params/Test.roc ─
|
||||
|
||||
I was partway through parsing module params, but I got stuck here:
|
||||
|
||||
|
@ -4981,8 +4981,8 @@ mod test_reporting {
|
|||
6│ name: <- applyName
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This looks like a record builder field, but those are not allowed in
|
||||
module params.
|
||||
This looks like an old-style record builder field, but those are not
|
||||
allowed in module params.
|
||||
"###
|
||||
);
|
||||
|
||||
|
@ -10700,7 +10700,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
// Record Builders
|
||||
|
||||
test_report!(
|
||||
optional_field_in_record_builder,
|
||||
optional_field_in_old_record_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{
|
||||
|
@ -10711,7 +10711,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
"#
|
||||
),
|
||||
@r#"
|
||||
── BAD RECORD BUILDER in tmp/optional_field_in_record_builder/Test.roc ─────────
|
||||
── BAD OLD-STYLE RECORD BUILDER in ...nal_field_in_old_record_builder/Test.roc ─
|
||||
|
||||
I am partway through parsing a record builder, and I found an optional
|
||||
field:
|
||||
|
@ -10730,7 +10730,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
);
|
||||
|
||||
test_report!(
|
||||
record_update_builder,
|
||||
record_update_old_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{ rec &
|
||||
|
@ -10740,10 +10740,10 @@ In roc, functions are always written as a lambda, like{}
|
|||
"#
|
||||
),
|
||||
@r#"
|
||||
── BAD RECORD UPDATE in tmp/record_update_builder/Test.roc ─────────────────────
|
||||
── BAD RECORD UPDATE in tmp/record_update_old_builder/Test.roc ─────────────────
|
||||
|
||||
I am partway through parsing a record update, and I found a record
|
||||
builder field:
|
||||
I am partway through parsing a record update, and I found an old-style
|
||||
record builder field:
|
||||
|
||||
1│ app "test" provides [main] to "./platform"
|
||||
2│
|
||||
|
@ -10752,12 +10752,12 @@ In roc, functions are always written as a lambda, like{}
|
|||
5│ a: <- apply "a",
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Record builders cannot be updated like records.
|
||||
Old-style record builders cannot be updated like records.
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
multiple_record_builders,
|
||||
multiple_old_record_builders,
|
||||
indoc!(
|
||||
r#"
|
||||
succeed
|
||||
|
@ -10766,32 +10766,31 @@ In roc, functions are always written as a lambda, like{}
|
|||
"#
|
||||
),
|
||||
@r#"
|
||||
── MULTIPLE RECORD BUILDERS in /code/proj/Main.roc ─────────────────────────────
|
||||
── MULTIPLE OLD-STYLE RECORD BUILDERS in /code/proj/Main.roc ───────────────────
|
||||
|
||||
This function is applied to multiple record builders:
|
||||
This function is applied to multiple old-style record builders:
|
||||
|
||||
4│> succeed
|
||||
5│> { a: <- apply "a" }
|
||||
6│> { b: <- apply "b" }
|
||||
|
||||
Note: Functions can only take at most one record builder!
|
||||
Note: Functions can only take at most one old-style record builder!
|
||||
|
||||
Tip: You can combine them or apply them separately.
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
unapplied_record_builder,
|
||||
unapplied_old_record_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{ a: <- apply "a" }
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
── UNAPPLIED RECORD BUILDER in /code/proj/Main.roc ─────────────────────────────
|
||||
── UNAPPLIED OLD-STYLE RECORD BUILDER in /code/proj/Main.roc ───────────────────
|
||||
|
||||
This record builder was not applied to a function:
|
||||
This old-style record builder was not applied to a function:
|
||||
|
||||
4│ { a: <- apply "a" }
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -10803,7 +10802,7 @@ In roc, functions are always written as a lambda, like{}
|
|||
);
|
||||
|
||||
test_report!(
|
||||
record_builder_apply_non_function,
|
||||
old_record_builder_apply_non_function,
|
||||
indoc!(
|
||||
r#"
|
||||
succeed = \_ -> crash ""
|
||||
|
@ -10857,6 +10856,107 @@ In roc, functions are always written as a lambda, like{}
|
|||
// "#
|
||||
// );
|
||||
|
||||
test_report!(
|
||||
empty_record_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{ a <- }
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
── EMPTY RECORD BUILDER in /code/proj/Main.roc ─────────────────────────────────
|
||||
|
||||
This record builder has no fields:
|
||||
|
||||
4│ { a <- }
|
||||
^^^^^^^^
|
||||
|
||||
I need at least two fields to combine their values into a record.
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
single_field_record_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{ a <-
|
||||
b: 123
|
||||
}
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
── NOT ENOUGH FIELDS IN RECORD BUILDER in /code/proj/Main.roc ──────────────────
|
||||
|
||||
This record builder only has one field:
|
||||
|
||||
4│> { a <-
|
||||
5│> b: 123
|
||||
6│> }
|
||||
|
||||
I need at least two fields to combine their values into a record.
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
optional_field_in_record_builder,
|
||||
indoc!(
|
||||
r#"
|
||||
{ a <-
|
||||
b: 123,
|
||||
c? 456
|
||||
}
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
── OPTIONAL FIELD IN RECORD BUILDER in /code/proj/Main.roc ─────────────────────
|
||||
|
||||
Optional fields are not allowed to be used in record builders.
|
||||
|
||||
4│ { a <-
|
||||
5│ b: 123,
|
||||
6│> c? 456
|
||||
7│ }
|
||||
|
||||
Record builders can only have required values for their fields.
|
||||
"#
|
||||
);
|
||||
|
||||
// CalledVia::RecordBuilder => {
|
||||
// alloc.concat([
|
||||
// alloc.note(""),
|
||||
// alloc.reflow("Record builders need a mapper function before the "),
|
||||
// alloc.keyword("<-"),
|
||||
// alloc.reflow(" to combine fields together with.")
|
||||
// ])
|
||||
// }
|
||||
// _ => {
|
||||
// alloc.reflow("Are there any missing commas? Or missing parentheses?")
|
||||
|
||||
test_report!(
|
||||
record_builder_with_non_function_mapper,
|
||||
indoc!(
|
||||
r#"
|
||||
xyz = "abc"
|
||||
|
||||
{ xyz <-
|
||||
b: 123,
|
||||
c: 456
|
||||
}
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
── TOO MANY ARGS in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
The `xyz` value is not a function, but it was given 3 arguments:
|
||||
|
||||
6│ { xyz <-
|
||||
^^^
|
||||
|
||||
Note: Record builders need a mapper function before the `<-` to combine
|
||||
fields together with.
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
destructure_assignment_introduces_no_variables_nested,
|
||||
indoc!(
|
||||
|
|
|
@ -2987,8 +2987,10 @@ fn update<'a>(
|
|||
fn register_package_shorthands<'a>(
|
||||
shorthands: &mut MutMap<&'a str, ShorthandPath>,
|
||||
package_entries: &MutMap<&'a str, header::PackageName<'a>>,
|
||||
#[allow(unused_variables)] // for wasm
|
||||
module_path: &Path,
|
||||
src_dir: &Path,
|
||||
#[allow(unused_variables)] // for wasm
|
||||
cache_dir: &Path,
|
||||
) -> Result<(), LoadingProblem<'a>> {
|
||||
for (shorthand, package_name) in package_entries.iter() {
|
||||
|
@ -3651,6 +3653,8 @@ fn load_module<'a>(
|
|||
#[derive(Debug)]
|
||||
enum ShorthandPath {
|
||||
/// e.g. "/home/rtfeldman/.cache/roc/0.1.0/oUkxSOI9zFGtSoIaMB40QPdrXphr1p1780eiui2iO9Mz"
|
||||
#[allow(dead_code)]
|
||||
// wasm warns FromHttpsUrl is unused, but errors if it is removed ¯\_(ツ)_/¯
|
||||
FromHttpsUrl {
|
||||
/// e.g. "/home/rtfeldman/.cache/roc/0.1.0/oUkxSOI9zFGtSoIaMB40QPdrXphr1p1780eiui2iO9Mz"
|
||||
root_module_dir: PathBuf,
|
||||
|
@ -4059,6 +4063,7 @@ fn load_packages<'a>(
|
|||
app_module_id: Option<ModuleId>,
|
||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
ident_ids_by_module: SharedIdentIdsByModule,
|
||||
#[allow(unused_variables)] // for wasm
|
||||
filename: PathBuf,
|
||||
) {
|
||||
// Load all the packages
|
||||
|
|
|
@ -1783,7 +1783,7 @@ fn roc_file_no_extension() {
|
|||
indoc!(
|
||||
r#"
|
||||
app "helloWorld"
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
|
||||
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
|
||||
imports [pf.Stdout]
|
||||
provides [main] to pf
|
||||
|
||||
|
|
|
@ -89,8 +89,21 @@ pub enum CalledVia {
|
|||
/// e.g. "$(first) $(last)" is transformed into Str.concat (Str.concat first " ") last.
|
||||
StringInterpolation,
|
||||
|
||||
/// This call is the result of desugaring a Record Builder field.
|
||||
/// This call is the result of desugaring an old style Record Builder field.
|
||||
/// e.g. succeed { a <- get "a" } is transformed into (get "a") (succeed \a -> { a })
|
||||
OldRecordBuilder,
|
||||
|
||||
/// This call is the result of desugaring a map2-based Record Builder field. e.g.
|
||||
/// ```roc
|
||||
/// { Task.parallel <-
|
||||
/// foo: get "a",
|
||||
/// bar: get "b",
|
||||
/// }
|
||||
/// ```
|
||||
/// is transformed into
|
||||
/// ```roc
|
||||
/// Task.parallel (get "a") (get "b") \foo, bar -> { foo, bar }
|
||||
/// ```
|
||||
RecordBuilder,
|
||||
|
||||
/// This call is the result of desugaring a Task.await from `!` syntax
|
||||
|
|
|
@ -36,10 +36,6 @@ pub enum LowLevel {
|
|||
ListReplaceUnsafe,
|
||||
ListConcat,
|
||||
ListPrepend,
|
||||
ListMap,
|
||||
ListMap2,
|
||||
ListMap3,
|
||||
ListMap4,
|
||||
ListSortWith,
|
||||
ListSublist,
|
||||
ListDropAt,
|
||||
|
@ -48,6 +44,8 @@ pub enum LowLevel {
|
|||
ListIsUnique,
|
||||
ListClone,
|
||||
ListConcatUtf8,
|
||||
ListIncref,
|
||||
ListDecref,
|
||||
NumAdd,
|
||||
NumAddWrap,
|
||||
NumAddChecked,
|
||||
|
@ -135,7 +133,7 @@ pub enum LowLevel {
|
|||
|
||||
macro_rules! higher_order {
|
||||
() => {
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith
|
||||
ListSortWith
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,10 +150,6 @@ impl LowLevel {
|
|||
use LowLevel::*;
|
||||
|
||||
match self {
|
||||
ListMap => 1,
|
||||
ListMap2 => 2,
|
||||
ListMap3 => 3,
|
||||
ListMap4 => 4,
|
||||
ListSortWith => 1,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -211,10 +205,6 @@ macro_rules! map_symbol_to_lowlevel {
|
|||
|
||||
// these are higher-order lowlevels. these need the surrounding
|
||||
// function to provide enough type information for code generation
|
||||
LowLevel::ListMap => unreachable!(),
|
||||
LowLevel::ListMap2 => unreachable!(),
|
||||
LowLevel::ListMap3 => unreachable!(),
|
||||
LowLevel::ListMap4 => unreachable!(),
|
||||
LowLevel::ListSortWith => unreachable!(),
|
||||
|
||||
// (un)boxing is handled in a custom way
|
||||
|
@ -239,6 +229,8 @@ macro_rules! map_symbol_to_lowlevel {
|
|||
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
||||
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
||||
LowLevel::RefCountIsUnique => unimplemented!(),
|
||||
LowLevel::ListIncref => unimplemented!(),
|
||||
LowLevel::ListDecref => unimplemented!(),
|
||||
|
||||
LowLevel::SetJmp => unimplemented!(),
|
||||
LowLevel::LongJmp => unimplemented!(),
|
||||
|
|
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