mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
Merge remote-tracking branch 'origin/trunk' into partialproc-by-reference
This commit is contained in:
commit
c5005d3dd1
77 changed files with 4144 additions and 3003 deletions
1
AUTHORS
1
AUTHORS
|
@ -43,3 +43,4 @@ Locria Cyber <locriacyber@noreply.users.github.com>
|
||||||
Matthias Beyer <mail@beyermatthias.de>
|
Matthias Beyer <mail@beyermatthias.de>
|
||||||
Tim Whiting <tim@whitings.org>
|
Tim Whiting <tim@whitings.org>
|
||||||
Logan Lowder <logan.lowder@logikcull.com>
|
Logan Lowder <logan.lowder@logikcull.com>
|
||||||
|
Joshua Warner <joshuawarner32@gmail.com>
|
||||||
|
|
|
@ -82,7 +82,7 @@ There are also alternative installation options at http://releases.llvm.org/down
|
||||||
|
|
||||||
## Using Nix
|
## Using Nix
|
||||||
|
|
||||||
:exclamation: **Our Nix setup is currently broken, you'll have to install manually for now** :exclamation:
|
:exclamation: **Our Nix setup is not yet working on MacOS, you'll have to install manually for now** :exclamation:
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ First, install nix:
|
||||||
|
|
||||||
`curl -L https://nixos.org/nix/install | sh`
|
`curl -L https://nixos.org/nix/install | sh`
|
||||||
|
|
||||||
If MacOS and using a version >= 10.15:
|
If you're on MacOS and using a OS version >= 10.15:
|
||||||
|
|
||||||
`sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume`
|
`sh <(curl -L https://nixos.org/nix/install) --darwin-use-unencrypted-nix-store-volume`
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ You may prefer to setup up the volume manually by following nix documentation.
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
Now with nix installed you just need to run one command:
|
Now with nix installed, you just need to run one command:
|
||||||
|
|
||||||
`nix-shell`
|
`nix-shell`
|
||||||
|
|
||||||
|
@ -120,33 +120,49 @@ You should be in a repl now. Have fun!
|
||||||
|
|
||||||
### Extra tips
|
### Extra tips
|
||||||
|
|
||||||
If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/target/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependecies into your current shell, so you never have to run nix-shell directly!
|
If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.net/) and [lorri](https://github.com/nix-community/lorri). Whenever you `cd` into `roc/`, they will automatically load the Nix dependecies into your current shell, so you never have to run nix-shell directly!
|
||||||
|
|
||||||
### Editor
|
### Editor
|
||||||
|
|
||||||
When you want to run the editor from Ubuntu inside nix you need to install [nixGL](https://github.com/guibou/nixGL) as well:
|
`cargo run edit` should work from NixOS, if you use a nix-shell from inside another OS, follow the instructions below.
|
||||||
|
|
||||||
|
#### Nvidia GPU
|
||||||
|
|
||||||
|
Outside of a nix shell, execute the following:
|
||||||
|
```
|
||||||
|
nix-channel --add https://github.com/guibou/nixGL/archive/main.tar.gz nixgl && nix-channel --update
|
||||||
|
nix-env -iA nixgl.auto.nixVulkanNvidia
|
||||||
|
```
|
||||||
|
Running the editor does not work with `nix-shell --pure`.
|
||||||
|
```
|
||||||
|
nix-shell
|
||||||
|
```
|
||||||
|
460.91.03 may be different for you, type nixVulkanNvidia and press tab to autocomplete for your version.
|
||||||
|
```
|
||||||
|
nixVulkanNvidia-460.91.03 cargo run edit
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Integrated Intel Graphics
|
||||||
|
|
||||||
|
:exclamation: ** Our Nix setup currently cannot run the editor with integrated intel graphics, see #1856 ** :exclamation:
|
||||||
|
|
||||||
|
Outside of a nix shell, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nix-shell
|
|
||||||
git clone https://github.com/guibou/nixGL
|
git clone https://github.com/guibou/nixGL
|
||||||
cd nixGL
|
cd nixGL
|
||||||
```
|
|
||||||
|
|
||||||
If you have an Nvidia graphics card, run:
|
|
||||||
```
|
|
||||||
nix-env -f ./ -iA nixVulkanNvidia
|
|
||||||
```
|
|
||||||
If you have integrated Intel graphics, run:
|
|
||||||
```
|
|
||||||
nix-env -f ./ -iA nixVulkanIntel
|
nix-env -f ./ -iA nixVulkanIntel
|
||||||
```
|
```
|
||||||
Check the [nixGL repo](https://github.com/guibou/nixGL) for other configurations.
|
|
||||||
|
|
||||||
Now you should be able to run the editor:
|
cd to the roc repo, and run (without --pure):
|
||||||
```bash
|
|
||||||
cd roc
|
|
||||||
nixVulkanNvidia cargo run edit `# replace Nvidia with the config you chose in the previous step`
|
|
||||||
```
|
```
|
||||||
|
nix-shell
|
||||||
|
nixVulkanIntel cargo run edit
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Other configs
|
||||||
|
|
||||||
|
Check the [nixGL repo](https://github.com/guibou/nixGL) for other graphics configurations.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
@ -197,6 +213,11 @@ on Windows. After lots of help from [**@IanMacKenzie**](https://github.com/IanMa
|
||||||
|
|
||||||
Once all that was done, `cargo` ran successfully for Roc!
|
Once all that was done, `cargo` ran successfully for Roc!
|
||||||
|
|
||||||
|
### Build speed on WSL/WSL2
|
||||||
|
|
||||||
|
If your Roc project folder is in the Windows filesystem but you're compiling from Linux, rebuilds may be as much as 20x slower than they should be!
|
||||||
|
Disk access during linking seems to be the bottleneck. It's recommended to move your folder to the Linux filesystem.
|
||||||
|
|
||||||
## Use LLD for the linker
|
## Use LLD for the linker
|
||||||
|
|
||||||
Using [`lld` for Rust's linker](https://github.com/rust-lang/rust/issues/39915#issuecomment-538049306)
|
Using [`lld` for Rust's linker](https://github.com/rust-lang/rust/issues/39915#issuecomment-538049306)
|
||||||
|
|
115
Cargo.lock
generated
115
Cargo.lock
generated
|
@ -498,7 +498,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"criterion",
|
"criterion",
|
||||||
"inlinable_string",
|
|
||||||
"rlimit",
|
"rlimit",
|
||||||
"roc_cli",
|
"roc_cli",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -1851,12 +1850,6 @@ dependencies = [
|
||||||
"syn 1.0.76",
|
"syn 1.0.76",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inlinable_string"
|
|
||||||
version = "0.1.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inplace_it"
|
name = "inplace_it"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -3579,9 +3572,7 @@ dependencies = [
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"inkwell 0.1.0",
|
"inkwell 0.1.0",
|
||||||
"inlinable_string",
|
|
||||||
"libloading 0.6.7",
|
"libloading 0.6.7",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -3613,7 +3604,6 @@ name = "roc_builtins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -3631,7 +3621,6 @@ dependencies = [
|
||||||
"im 14.3.0",
|
"im 14.3.0",
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -3658,9 +3647,7 @@ dependencies = [
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"inkwell 0.1.0",
|
"inkwell 0.1.0",
|
||||||
"libc",
|
|
||||||
"libloading 0.6.7",
|
"libloading 0.6.7",
|
||||||
"maplit",
|
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
|
@ -3724,7 +3711,6 @@ name = "roc_constrain"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -3742,7 +3728,6 @@ name = "roc_docs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"roc_ast",
|
"roc_ast",
|
||||||
|
@ -3750,7 +3735,6 @@ dependencies = [
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_code_markup",
|
"roc_code_markup",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_fmt",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
@ -3781,7 +3765,6 @@ dependencies = [
|
||||||
"indoc 1.0.3",
|
"indoc 1.0.3",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"maplit",
|
|
||||||
"nonempty",
|
"nonempty",
|
||||||
"page_size",
|
"page_size",
|
||||||
"palette",
|
"palette",
|
||||||
|
@ -3796,7 +3779,6 @@ dependencies = [
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_code_markup",
|
"roc_code_markup",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_fmt",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
@ -3807,7 +3789,6 @@ dependencies = [
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"rodio",
|
"rodio",
|
||||||
"ropey",
|
|
||||||
"serde",
|
"serde",
|
||||||
"snafu",
|
"snafu",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -3817,7 +3798,6 @@ dependencies = [
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"wgpu_glyph",
|
"wgpu_glyph",
|
||||||
"winit",
|
"winit",
|
||||||
"zerocopy",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3828,7 +3808,6 @@ dependencies = [
|
||||||
"im 14.3.0",
|
"im 14.3.0",
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -3847,9 +3826,7 @@ dependencies = [
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"itertools 0.9.0",
|
"itertools 0.9.0",
|
||||||
"libc",
|
|
||||||
"libloading 0.6.7",
|
"libloading 0.6.7",
|
||||||
"maplit",
|
|
||||||
"object 0.24.0",
|
"object 0.24.0",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
|
@ -3884,8 +3861,6 @@ dependencies = [
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"inkwell 0.1.0",
|
"inkwell 0.1.0",
|
||||||
"libc",
|
|
||||||
"maplit",
|
|
||||||
"morphic_lib",
|
"morphic_lib",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
|
@ -3915,7 +3890,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"libc",
|
|
||||||
"parity-wasm",
|
"parity-wasm",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
|
@ -3992,7 +3966,6 @@ dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_ident",
|
"roc_ident",
|
||||||
|
@ -4009,7 +3982,6 @@ dependencies = [
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
"maplit",
|
|
||||||
"morphic_lib",
|
"morphic_lib",
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
|
@ -4051,7 +4023,6 @@ name = "roc_problem"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -4074,7 +4045,6 @@ dependencies = [
|
||||||
"im 14.3.0",
|
"im 14.3.0",
|
||||||
"im-rc 14.3.0",
|
"im-rc 14.3.0",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -4098,7 +4068,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -4125,7 +4094,6 @@ name = "roc_types"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -4141,7 +4109,6 @@ name = "roc_unify"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"maplit",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
|
@ -4170,15 +4137,6 @@ dependencies = [
|
||||||
"minimp3",
|
"minimp3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ropey"
|
|
||||||
version = "1.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9150aff6deb25b20ed110889f070a678bcd1033e46e5e9d6fb1abeab17947f28"
|
|
||||||
dependencies = [
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
|
@ -4624,18 +4582,6 @@ dependencies = [
|
||||||
"unicode-xid 0.2.2",
|
"unicode-xid 0.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "synstructure"
|
|
||||||
version = "0.12.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2 1.0.29",
|
|
||||||
"quote 1.0.9",
|
|
||||||
"syn 1.0.76",
|
|
||||||
"unicode-xid 0.2.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
|
@ -4677,7 +4623,6 @@ dependencies = [
|
||||||
"inkwell 0.1.0",
|
"inkwell 0.1.0",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading 0.6.7",
|
"libloading 0.6.7",
|
||||||
"maplit",
|
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
"roc_build",
|
"roc_build",
|
||||||
|
@ -4710,31 +4655,16 @@ name = "test_mono"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"either",
|
|
||||||
"im 14.3.0",
|
|
||||||
"im-rc 14.3.0",
|
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"libc",
|
|
||||||
"libloading 0.6.7",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"quickcheck 0.8.5",
|
"quickcheck 0.8.5",
|
||||||
"quickcheck_macros 0.8.0",
|
"quickcheck_macros 0.8.0",
|
||||||
"roc_build",
|
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
"roc_parse",
|
|
||||||
"roc_problem",
|
|
||||||
"roc_region",
|
|
||||||
"roc_reporting",
|
|
||||||
"roc_solve",
|
|
||||||
"roc_types",
|
|
||||||
"roc_unify",
|
|
||||||
"target-lexicon",
|
|
||||||
"test_mono_macros",
|
"test_mono_macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4742,12 +4672,34 @@ dependencies = [
|
||||||
name = "test_mono_macros"
|
name = "test_mono_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling 0.10.2",
|
|
||||||
"proc-macro2 1.0.29",
|
"proc-macro2 1.0.29",
|
||||||
"quote 1.0.9",
|
"quote 1.0.9",
|
||||||
"syn 1.0.76",
|
"syn 1.0.76",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"indoc 0.3.6",
|
||||||
|
"libc",
|
||||||
|
"parity-wasm",
|
||||||
|
"pretty_assertions 0.5.1",
|
||||||
|
"roc_builtins",
|
||||||
|
"roc_can",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_gen_wasm",
|
||||||
|
"roc_load",
|
||||||
|
"roc_module",
|
||||||
|
"roc_std",
|
||||||
|
"roc_types",
|
||||||
|
"target-lexicon",
|
||||||
|
"tempfile",
|
||||||
|
"wasmer",
|
||||||
|
"wasmer-wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -5749,24 +5701,3 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2 1.0.29",
|
|
||||||
"syn 1.0.76",
|
|
||||||
"synstructure",
|
|
||||||
]
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ members = [
|
||||||
"compiler/build",
|
"compiler/build",
|
||||||
"compiler/arena_pool",
|
"compiler/arena_pool",
|
||||||
"compiler/test_gen",
|
"compiler/test_gen",
|
||||||
|
"compiler/test_wasm",
|
||||||
"vendor/ena",
|
"vendor/ena",
|
||||||
"vendor/inkwell",
|
"vendor/inkwell",
|
||||||
"vendor/pathfinding",
|
"vendor/pathfinding",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM rust:1.54-slim-bullseye
|
FROM rust:1.56.1-slim-bullseye
|
||||||
WORKDIR /earthbuild
|
WORKDIR /earthbuild
|
||||||
|
|
||||||
prep-debian:
|
prep-debian:
|
||||||
|
|
|
@ -23,7 +23,7 @@ libc = "0.2"
|
||||||
page_size = "0.4"
|
page_size = "0.4"
|
||||||
snafu = { version = "0.6", features = ["backtraces"] }
|
snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
ven_graph = { path = "../vendor/pathfinding" }
|
ven_graph = { path = "../vendor/pathfinding" }
|
||||||
indoc = "1.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.6"
|
pretty_assertions = "0.6"
|
||||||
|
indoc = "1.0"
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
///
|
///
|
||||||
/// Pages also use the node value 0 (all 0 bits) to mark nodes as unoccupied.
|
/// Pages also use the node value 0 (all 0 bits) to mark nodes as unoccupied.
|
||||||
/// This is important for performance.
|
/// This is important for performance.
|
||||||
use libc::{c_void, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ptr::null;
|
use std::ptr::null;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::pool::{NodeId, Pool, NODE_BYTES};
|
use super::pool::{NodeId, Pool, NODE_BYTES};
|
||||||
use super::shallow_clone::ShallowClone;
|
use super::shallow_clone::ShallowClone;
|
||||||
use libc::c_void;
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use super::pool::{NodeId, Pool, NODE_BYTES};
|
use super::pool::{NodeId, Pool, NODE_BYTES};
|
||||||
use super::shallow_clone::ShallowClone;
|
use super::shallow_clone::ShallowClone;
|
||||||
use libc::c_void;
|
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,6 @@ rustyline-derive = { git = "https://github.com/rtfeldman/rustyline", tag = "prom
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
libc = "0.2"
|
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
mimalloc = { version = "0.1.26", default-features = false }
|
mimalloc = { version = "0.1.26", default-features = false }
|
||||||
|
|
||||||
|
@ -78,7 +77,6 @@ wasmer-wasi = "2.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
@ -90,4 +88,3 @@ cli_utils = { path = "cli_utils" }
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "time_bench"
|
name = "time_bench"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ roc_collections = { path = "../../compiler/collections" }
|
||||||
roc_load = { path = "../../compiler/load" }
|
roc_load = { path = "../../compiler/load" }
|
||||||
roc_module = { path = "../../compiler/module" }
|
roc_module = { path = "../../compiler/module" }
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||||
inlinable_string = "0.1"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde-xml-rs = "0.4"
|
serde-xml-rs = "0.4"
|
||||||
strip-ansi-escapes = "0.1"
|
strip-ansi-escapes = "0.1"
|
||||||
|
|
|
@ -119,7 +119,7 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
App::new(CMD_DOCS)
|
App::new(CMD_DOCS)
|
||||||
.about("Generate documentation for Roc modules")
|
.about("Generate documentation for Roc modules (Work In Progress)")
|
||||||
.arg(Arg::with_name(DIRECTORY_OR_FILES)
|
.arg(Arg::with_name(DIRECTORY_OR_FILES)
|
||||||
.index(1)
|
.index(1)
|
||||||
.multiple(true)
|
.multiple(true)
|
||||||
|
|
|
@ -362,7 +362,7 @@ mod cli_run {
|
||||||
stdin: &[],
|
stdin: &[],
|
||||||
input_file: Some("examples/hello.false"),
|
input_file: Some("examples/hello.false"),
|
||||||
expected_ending:"Hello, World!\n",
|
expected_ending:"Hello, World!\n",
|
||||||
use_valgrind: false,
|
use_valgrind: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ roc_std = { path = "../../roc_std" }
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
inlinable_string = "0.1.0"
|
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -36,7 +35,6 @@ target-lexicon = "0.12.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::target::arch_str;
|
use crate::target::{arch_str, target_triple_str};
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
use libloading::{Error, Library};
|
use libloading::{Error, Library};
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
|
@ -372,6 +372,20 @@ pub fn rebuild_host(
|
||||||
shared_lib_path,
|
shared_lib_path,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Architecture::Aarch64(_) => {
|
||||||
|
let emit_bin = format!("-femit-bin={}", host_dest_native.to_str().unwrap());
|
||||||
|
build_zig_host_native(
|
||||||
|
&env_path,
|
||||||
|
&env_home,
|
||||||
|
&emit_bin,
|
||||||
|
zig_host_src.to_str().unwrap(),
|
||||||
|
zig_str_path.to_str().unwrap(),
|
||||||
|
target_triple_str(target),
|
||||||
|
opt_level,
|
||||||
|
shared_lib_path,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -140,6 +140,7 @@ const Caller0 = fn (?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
const Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
const Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
|
const Caller4 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
|
|
||||||
pub fn listReverse(list: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList {
|
pub fn listReverse(list: RocList, alignment: u32, element_width: usize, update_mode: UpdateMode) callconv(.C) RocList {
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
|
@ -352,6 +353,70 @@ pub fn listMap3(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = std.math.min(std.math.min(list1.len(), list2.len()), std.math.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 listKeepIf(
|
pub fn listKeepIf(
|
||||||
list: RocList,
|
list: RocList,
|
||||||
caller: Caller1,
|
caller: Caller1,
|
||||||
|
|
|
@ -26,6 +26,7 @@ comptime {
|
||||||
exportListFn(list.listMap, "map");
|
exportListFn(list.listMap, "map");
|
||||||
exportListFn(list.listMap2, "map2");
|
exportListFn(list.listMap2, "map2");
|
||||||
exportListFn(list.listMap3, "map3");
|
exportListFn(list.listMap3, "map3");
|
||||||
|
exportListFn(list.listMap4, "map4");
|
||||||
exportListFn(list.listMapWithIndex, "map_with_index");
|
exportListFn(list.listMapWithIndex, "map_with_index");
|
||||||
exportListFn(list.listKeepIf, "keep_if");
|
exportListFn(list.listKeepIf, "keep_if");
|
||||||
exportListFn(list.listWalk, "walk");
|
exportListFn(list.listWalk, "walk");
|
||||||
|
|
|
@ -25,6 +25,7 @@ interface List
|
||||||
map,
|
map,
|
||||||
map2,
|
map2,
|
||||||
map3,
|
map3,
|
||||||
|
map4,
|
||||||
mapWithIndex,
|
mapWithIndex,
|
||||||
mapOrDrop,
|
mapOrDrop,
|
||||||
mapJoin,
|
mapJoin,
|
||||||
|
@ -269,6 +270,11 @@ map2 : List a, List b, (a, b -> c) -> List c
|
||||||
## Repeat until a list runs out of elements.
|
## Repeat until a list runs out of elements.
|
||||||
map3 : List a, List b, List c, (a, b, c -> d) -> List d
|
map3 : List a, List b, List c, (a, b, c -> d) -> List d
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
## This works like [List.map], except it also passes the index
|
## This works like [List.map], except it also passes the index
|
||||||
## of the element to the conversion function.
|
## of the element to the conversion function.
|
||||||
mapWithIndex : List before, (before, Nat -> after) -> List after
|
mapWithIndex : List before, (before, Nat -> after) -> List after
|
||||||
|
|
|
@ -165,6 +165,7 @@ pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list";
|
||||||
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
||||||
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
|
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
|
||||||
pub const LIST_MAP3: &str = "roc_builtins.list.map3";
|
pub const LIST_MAP3: &str = "roc_builtins.list.map3";
|
||||||
|
pub const LIST_MAP4: &str = "roc_builtins.list.map4";
|
||||||
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
|
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
|
||||||
pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if";
|
pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if";
|
||||||
pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks";
|
pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks";
|
||||||
|
|
|
@ -64,7 +64,7 @@ const TVAR1: VarId = VarId::from_u32(1);
|
||||||
const TVAR2: VarId = VarId::from_u32(2);
|
const TVAR2: VarId = VarId::from_u32(2);
|
||||||
const TVAR3: VarId = VarId::from_u32(3);
|
const TVAR3: VarId = VarId::from_u32(3);
|
||||||
const TVAR4: VarId = VarId::from_u32(4);
|
const TVAR4: VarId = VarId::from_u32(4);
|
||||||
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(5);
|
const TOP_LEVEL_CLOSURE_VAR: VarId = VarId::from_u32(10);
|
||||||
|
|
||||||
pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
let mut types = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
let mut types = HashMap::with_capacity_and_hasher(NUM_BUILTIN_IMPORTS, default_hasher());
|
||||||
|
@ -930,6 +930,27 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let_tvars! {a, b, c, d, e, cvar};
|
||||||
|
|
||||||
|
// map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::LIST_MAP4,
|
||||||
|
vec![
|
||||||
|
list_type(flex(a)),
|
||||||
|
list_type(flex(b)),
|
||||||
|
list_type(flex(c)),
|
||||||
|
list_type(flex(d)),
|
||||||
|
closure(
|
||||||
|
vec![flex(a), flex(b), flex(c), flex(d)],
|
||||||
|
cvar,
|
||||||
|
Box::new(flex(e))
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Box::new(list_type(flex(e))),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// append : List elem, elem -> List elem
|
// append : List elem, elem -> List elem
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::LIST_APPEND,
|
Symbol::LIST_APPEND,
|
||||||
|
|
|
@ -20,7 +20,6 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -89,6 +89,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_MAP => list_map,
|
LIST_MAP => list_map,
|
||||||
LIST_MAP2 => list_map2,
|
LIST_MAP2 => list_map2,
|
||||||
LIST_MAP3 => list_map3,
|
LIST_MAP3 => list_map3,
|
||||||
|
LIST_MAP4 => list_map4,
|
||||||
LIST_DROP => list_drop,
|
LIST_DROP => list_drop,
|
||||||
LIST_DROP_AT => list_drop_at,
|
LIST_DROP_AT => list_drop_at,
|
||||||
LIST_DROP_LAST => list_drop_last,
|
LIST_DROP_LAST => list_drop_last,
|
||||||
|
@ -290,6 +291,41 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lowlevel_5(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
|
||||||
|
let arg1_var = var_store.fresh();
|
||||||
|
let arg2_var = var_store.fresh();
|
||||||
|
let arg3_var = var_store.fresh();
|
||||||
|
let arg4_var = var_store.fresh();
|
||||||
|
let arg5_var = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op,
|
||||||
|
args: vec![
|
||||||
|
(arg1_var, Var(Symbol::ARG_1)),
|
||||||
|
(arg2_var, Var(Symbol::ARG_2)),
|
||||||
|
(arg3_var, Var(Symbol::ARG_3)),
|
||||||
|
(arg4_var, Var(Symbol::ARG_4)),
|
||||||
|
(arg5_var, Var(Symbol::ARG_5)),
|
||||||
|
],
|
||||||
|
ret_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![
|
||||||
|
(arg1_var, Symbol::ARG_1),
|
||||||
|
(arg2_var, Symbol::ARG_2),
|
||||||
|
(arg3_var, Symbol::ARG_3),
|
||||||
|
(arg4_var, Symbol::ARG_4),
|
||||||
|
(arg5_var, Symbol::ARG_5),
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Num.maxInt : Int
|
/// Num.maxInt : Int
|
||||||
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let int_var = var_store.fresh();
|
let int_var = var_store.fresh();
|
||||||
|
@ -2541,11 +2577,16 @@ fn list_map2(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_3(symbol, LowLevel::ListMap2, var_store)
|
lowlevel_3(symbol, LowLevel::ListMap2, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.map3 : List a, List b, (a, b -> c) -> List c
|
/// List.map3 : List a, List b, List c, (a, b, c -> d) -> List d
|
||||||
fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_4(symbol, LowLevel::ListMap3, var_store)
|
lowlevel_4(symbol, LowLevel::ListMap3, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
|
||||||
|
fn list_map4(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
lowlevel_5(symbol, LowLevel::ListMap4, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
/// List.sortWith : List a, (a, a -> Ordering) -> List a
|
||||||
fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
|
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
|
||||||
|
|
|
@ -16,7 +16,6 @@ roc_builtins = { path = "../builtins" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -16,7 +16,6 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -9,12 +9,10 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_load = { path = "../load" }
|
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
|
@ -22,7 +20,6 @@ im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.2"
|
||||||
libloading = "0.6"
|
|
||||||
object = { version = "0.24", features = ["write"] }
|
object = { version = "0.24", features = ["write"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -30,17 +27,18 @@ roc_can = { path = "../can" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
roc_build = { path = "../build" }
|
roc_build = { path = "../build" }
|
||||||
|
roc_load = { path = "../load" }
|
||||||
|
roc_constrain = { path = "../constrain" }
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
libc = "0.2"
|
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
itertools = "0.9"
|
itertools = "0.9"
|
||||||
|
libloading = "0.6"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
target-aarch64 = ["roc_build/target-aarch64"]
|
target-aarch64 = ["roc_build/target-aarch64"]
|
||||||
|
|
|
@ -830,8 +830,9 @@ impl<
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
fields: &'a [Symbol],
|
fields: &'a [Symbol],
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
|
|
||||||
if let Layout::Struct(field_layouts) = layout {
|
if let Layout::Struct(field_layouts) = layout {
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
|
||||||
if struct_size > 0 {
|
if struct_size > 0 {
|
||||||
let offset = self.claim_stack_size(struct_size)?;
|
let offset = self.claim_stack_size(struct_size)?;
|
||||||
self.symbol_storage_map.insert(
|
self.symbol_storage_map.insert(
|
||||||
|
@ -862,7 +863,6 @@ impl<
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// This is a single element struct. Just copy the single field to the stack.
|
// This is a single element struct. Just copy the single field to the stack.
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
|
||||||
let offset = self.claim_stack_size(struct_size)?;
|
let offset = self.claim_stack_size(struct_size)?;
|
||||||
self.symbol_storage_map.insert(
|
self.symbol_storage_map.insert(
|
||||||
*sym,
|
*sym,
|
||||||
|
|
|
@ -31,10 +31,8 @@ roc_load = { path = "../load" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
roc_build = { path = "../build" }
|
roc_build = { path = "../build" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
libc = "0.2"
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ use crate::llvm::build_hash::generic_hash;
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
|
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
|
||||||
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
|
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
|
||||||
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map_with_index,
|
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4,
|
||||||
list_prepend, list_range, list_repeat, list_reverse, list_set, list_single, list_sort_with,
|
list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set,
|
||||||
list_swap,
|
list_single, list_sort_with, list_swap,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||||
|
@ -1453,7 +1453,33 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
||||||
UnionLayout::NonRecursive(tags) => {
|
UnionLayout::NonRecursive(tags) => {
|
||||||
debug_assert!(union_size > 1);
|
debug_assert!(union_size > 1);
|
||||||
|
|
||||||
let ctx = env.context;
|
let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
||||||
|
|
||||||
|
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
||||||
|
let wrapper_type = env
|
||||||
|
.context
|
||||||
|
.struct_type(&[internal_type, tag_id_type.into()], false);
|
||||||
|
let result_alloca = env.builder.build_alloca(wrapper_type, "tag_opaque");
|
||||||
|
|
||||||
|
// Initialize all memory of the alloca. This _should_ not be required, but currently
|
||||||
|
// LLVM can access uninitialized memory after applying some optimizations. Hopefully
|
||||||
|
// we can in the future adjust code gen so this is no longer an issue.
|
||||||
|
//
|
||||||
|
// An example is
|
||||||
|
//
|
||||||
|
// main : Task.Task {} []
|
||||||
|
// main =
|
||||||
|
// when List.len [ Ok "foo", Err 42, Ok "spam" ] is
|
||||||
|
// n -> Task.putLine (Str.fromInt n)
|
||||||
|
//
|
||||||
|
// Here the decrement function of result must first check it's an Ok tag,
|
||||||
|
// then defers to string decrement, which must check is the string is small or large
|
||||||
|
//
|
||||||
|
// After inlining, those checks are combined. That means that even if the tag is Err,
|
||||||
|
// a check is done on the "string" to see if it is big or small, which will touch the
|
||||||
|
// uninitialized memory.
|
||||||
|
let all_zeros = wrapper_type.const_zero();
|
||||||
|
env.builder.build_store(result_alloca, all_zeros);
|
||||||
|
|
||||||
// Determine types
|
// Determine types
|
||||||
let num_fields = arguments.len() + 1;
|
let num_fields = arguments.len() + 1;
|
||||||
|
@ -1484,54 +1510,46 @@ pub fn build_tag<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// store the tag id
|
||||||
// Create the struct_type
|
let tag_id_ptr = env
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
.builder
|
||||||
|
.build_struct_gep(result_alloca, TAG_ID_INDEX, "get_opaque_data")
|
||||||
// Insert field exprs into struct_val
|
.unwrap();
|
||||||
let struct_val =
|
|
||||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
|
||||||
|
|
||||||
// How we create tag values
|
|
||||||
//
|
|
||||||
// The memory layout of tags can be different. e.g. in
|
|
||||||
//
|
|
||||||
// [ Ok Int, Err Str ]
|
|
||||||
//
|
|
||||||
// the `Ok` tag stores a 64-bit integer, the `Err` tag stores a struct.
|
|
||||||
// All tags of a union must have the same length, for easy addressing (e.g. array lookups).
|
|
||||||
// So we need to ask for the maximum of all tag's sizes, even if most tags won't use
|
|
||||||
// all that memory, and certainly won't use it in the same way (the tags have fields of
|
|
||||||
// different types/sizes)
|
|
||||||
//
|
|
||||||
// In llvm, we must be explicit about the type of value we're creating: we can't just
|
|
||||||
// make a unspecified block of memory. So what we do is create a byte array of the
|
|
||||||
// desired size. Then when we know which tag we have (which is here, in this function),
|
|
||||||
// we need to cast that down to the array of bytes that llvm expects
|
|
||||||
//
|
|
||||||
// There is the bitcast instruction, but it doesn't work for arrays. So we need to jump
|
|
||||||
// through some hoops using store and load to get this to work: the array is put into a
|
|
||||||
// one-element struct, which can be cast to the desired type.
|
|
||||||
//
|
|
||||||
// This tricks comes from
|
|
||||||
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
|
||||||
|
|
||||||
let internal_type = block_of_memory_slices(env.context, tags, env.ptr_bytes);
|
|
||||||
|
|
||||||
let data = cast_tag_to_block_of_memory(env, struct_val, internal_type);
|
|
||||||
let tag_id_type = basic_type_from_layout(env, &tag_id_layout).into_int_type();
|
|
||||||
let wrapper_type = env
|
|
||||||
.context
|
|
||||||
.struct_type(&[data.get_type(), tag_id_type.into()], false);
|
|
||||||
|
|
||||||
let tag_id_intval = tag_id_type.const_int(tag_id as u64, false);
|
let tag_id_intval = tag_id_type.const_int(tag_id as u64, false);
|
||||||
|
env.builder.build_store(tag_id_ptr, tag_id_intval);
|
||||||
|
|
||||||
let field_vals = [
|
// Create the struct_type
|
||||||
(TAG_DATA_INDEX as usize, data),
|
let struct_type = env
|
||||||
(TAG_ID_INDEX as usize, tag_id_intval.into()),
|
.context
|
||||||
];
|
.struct_type(field_types.into_bump_slice(), false);
|
||||||
|
|
||||||
struct_from_fields(env, wrapper_type, field_vals.iter().copied()).into()
|
let struct_opaque_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(result_alloca, TAG_DATA_INDEX, "get_opaque_data")
|
||||||
|
.unwrap();
|
||||||
|
let struct_ptr = env.builder.build_pointer_cast(
|
||||||
|
struct_opaque_ptr,
|
||||||
|
struct_type.ptr_type(AddressSpace::Generic),
|
||||||
|
"to_specific",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
//let struct_val =
|
||||||
|
//struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
||||||
|
|
||||||
|
// Insert field exprs into struct_val
|
||||||
|
for (index, field_val) in field_vals.iter().copied().enumerate() {
|
||||||
|
let index: u32 = index as u32;
|
||||||
|
|
||||||
|
let ptr = env
|
||||||
|
.builder
|
||||||
|
.build_struct_gep(struct_ptr, index, "get_tag_field_ptr")
|
||||||
|
.unwrap();
|
||||||
|
env.builder.build_store(ptr, field_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
env.builder.build_load(result_alloca, "load_result")
|
||||||
}
|
}
|
||||||
UnionLayout::Recursive(tags) => {
|
UnionLayout::Recursive(tags) => {
|
||||||
debug_assert!(union_size > 1);
|
debug_assert!(union_size > 1);
|
||||||
|
@ -2653,14 +2671,6 @@ pub fn complex_bitcast_struct_struct<'ctx>(
|
||||||
complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value()
|
complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_tag_to_block_of_memory<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
from_value: StructValue<'ctx>,
|
|
||||||
to_type: BasicTypeEnum<'ctx>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
complex_bitcast_check_size(env, from_value.into(), to_type, "tag_to_block_of_memory")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cast_block_of_memory_to_tag<'ctx>(
|
pub fn cast_block_of_memory_to_tag<'ctx>(
|
||||||
builder: &Builder<'ctx>,
|
builder: &Builder<'ctx>,
|
||||||
from_value: StructValue<'ctx>,
|
from_value: StructValue<'ctx>,
|
||||||
|
@ -4609,6 +4619,67 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
_ => unreachable!("invalid list layout"),
|
_ => unreachable!("invalid list layout"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListMap4 { xs, ys, zs, ws } => {
|
||||||
|
let (list1, list1_layout) = load_symbol_and_layout(scope, &xs);
|
||||||
|
let (list2, list2_layout) = load_symbol_and_layout(scope, &ys);
|
||||||
|
let (list3, list3_layout) = load_symbol_and_layout(scope, &zs);
|
||||||
|
let (list4, list4_layout) = load_symbol_and_layout(scope, &ws);
|
||||||
|
|
||||||
|
let (function, closure, closure_layout) = function_details!();
|
||||||
|
|
||||||
|
match (
|
||||||
|
list1_layout,
|
||||||
|
list2_layout,
|
||||||
|
list3_layout,
|
||||||
|
list4_layout,
|
||||||
|
return_layout,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
Layout::Builtin(Builtin::List(element1_layout)),
|
||||||
|
Layout::Builtin(Builtin::List(element2_layout)),
|
||||||
|
Layout::Builtin(Builtin::List(element3_layout)),
|
||||||
|
Layout::Builtin(Builtin::List(element4_layout)),
|
||||||
|
Layout::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_ids,
|
||||||
|
function,
|
||||||
|
closure,
|
||||||
|
closure_layout,
|
||||||
|
function_owns_closure_data,
|
||||||
|
argument_layouts,
|
||||||
|
);
|
||||||
|
|
||||||
|
list_map4(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
roc_function_call,
|
||||||
|
list1,
|
||||||
|
list2,
|
||||||
|
list3,
|
||||||
|
list4,
|
||||||
|
element1_layout,
|
||||||
|
element2_layout,
|
||||||
|
element3_layout,
|
||||||
|
element4_layout,
|
||||||
|
result_layout,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Layout::Builtin(Builtin::EmptyList), _, _, _, _)
|
||||||
|
| (_, Layout::Builtin(Builtin::EmptyList), _, _, _)
|
||||||
|
| (_, _, Layout::Builtin(Builtin::EmptyList), _, _)
|
||||||
|
| (_, _, _, Layout::Builtin(Builtin::EmptyList), _) => empty_list(env),
|
||||||
|
_ => unreachable!("invalid list layout"),
|
||||||
|
}
|
||||||
|
}
|
||||||
ListMapWithIndex { xs } => {
|
ListMapWithIndex { xs } => {
|
||||||
// List.mapWithIndex : List before, (Nat, before -> after) -> List after
|
// List.mapWithIndex : List before, (Nat, before -> after) -> List after
|
||||||
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
|
let (list, list_layout) = load_symbol_and_layout(scope, &xs);
|
||||||
|
@ -5640,7 +5711,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
cond
|
cond
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||||
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
|
| DictWalk => unreachable!("these are higher order, and are handled elsewhere"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -821,6 +821,51 @@ pub fn list_map3<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list_map4<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
|
list1: BasicValueEnum<'ctx>,
|
||||||
|
list2: BasicValueEnum<'ctx>,
|
||||||
|
list3: BasicValueEnum<'ctx>,
|
||||||
|
list4: BasicValueEnum<'ctx>,
|
||||||
|
element1_layout: &Layout<'a>,
|
||||||
|
element2_layout: &Layout<'a>,
|
||||||
|
element3_layout: &Layout<'a>,
|
||||||
|
element4_layout: &Layout<'a>,
|
||||||
|
result_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let dec_a = build_dec_wrapper(env, layout_ids, element1_layout);
|
||||||
|
let dec_b = build_dec_wrapper(env, layout_ids, element2_layout);
|
||||||
|
let dec_c = build_dec_wrapper(env, layout_ids, element3_layout);
|
||||||
|
let dec_d = build_dec_wrapper(env, layout_ids, element4_layout);
|
||||||
|
|
||||||
|
call_bitcode_fn_returns_list(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
pass_list_cc(env, list1),
|
||||||
|
pass_list_cc(env, list2),
|
||||||
|
pass_list_cc(env, list3),
|
||||||
|
pass_list_cc(env, list4),
|
||||||
|
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(result_layout),
|
||||||
|
layout_width(env, element1_layout),
|
||||||
|
layout_width(env, element2_layout),
|
||||||
|
layout_width(env, element3_layout),
|
||||||
|
layout_width(env, element4_layout),
|
||||||
|
layout_width(env, 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(),
|
||||||
|
],
|
||||||
|
bitcode::LIST_MAP4,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.concat : List elem, List elem -> List elem
|
/// List.concat : List elem, List elem -> List elem
|
||||||
pub fn list_concat<'a, 'ctx, 'env>(
|
pub fn list_concat<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -965,6 +1010,8 @@ where
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let entry = env.builder.get_insert_block().unwrap();
|
||||||
|
|
||||||
// constant 1i64
|
// constant 1i64
|
||||||
let one = env.ptr_int().const_int(1, false);
|
let one = env.ptr_int().const_int(1, false);
|
||||||
|
|
||||||
|
@ -976,15 +1023,15 @@ where
|
||||||
builder.build_unconditional_branch(loop_bb);
|
builder.build_unconditional_branch(loop_bb);
|
||||||
builder.position_at_end(loop_bb);
|
builder.position_at_end(loop_bb);
|
||||||
|
|
||||||
let curr_index = builder
|
let current_index_phi = env.builder.build_phi(env.ptr_int(), "current_index");
|
||||||
.build_load(index_alloca, index_name)
|
let current_index = current_index_phi.as_basic_value().into_int_value();
|
||||||
.into_int_value();
|
|
||||||
let next_index = builder.build_int_add(curr_index, one, "nextindex");
|
|
||||||
|
|
||||||
builder.build_store(index_alloca, next_index);
|
let next_index = builder.build_int_add(current_index, one, "next_index");
|
||||||
|
|
||||||
|
current_index_phi.add_incoming(&[(&next_index, loop_bb), (&env.ptr_int().const_zero(), entry)]);
|
||||||
|
|
||||||
// The body of the loop
|
// The body of the loop
|
||||||
loop_fn(curr_index);
|
loop_fn(current_index);
|
||||||
|
|
||||||
// #index < end
|
// #index < end
|
||||||
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
||||||
|
|
|
@ -15,7 +15,6 @@ parity-wasm = { git = "https://github.com/brian-carroll/parity-wasm", branch = "
|
||||||
|
|
||||||
roc_std = { path = "../../roc_std" }
|
roc_std = { path = "../../roc_std" }
|
||||||
wasmer = "2.0.0"
|
wasmer = "2.0.0"
|
||||||
wasmer-wasi = "2.0.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
|
@ -25,6 +24,6 @@ roc_types = { path = "../types" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
libc = "0.2"
|
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.2"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
wasmer-wasi = "2.0.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use parity_wasm::builder;
|
use parity_wasm::builder;
|
||||||
use parity_wasm::builder::{CodeLocation, FunctionDefinition, ModuleBuilder};
|
use parity_wasm::builder::{FunctionDefinition, ModuleBuilder};
|
||||||
|
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -10,8 +10,10 @@ use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
use crate::code_builder::{BlockType, CodeBuilder, ValueType};
|
use crate::code_builder::{BlockType, CodeBuilder, ValueType};
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::WasmLayout;
|
||||||
|
use crate::module_builder::RelocationEntry;
|
||||||
|
use crate::serialize::SerialBuffer;
|
||||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||||
use crate::{copy_memory, overwrite_padded_u32, CopyMemoryConfig, Env, LocalId, PTR_TYPE};
|
use crate::{copy_memory, CopyMemoryConfig, Env, LocalId, PTR_TYPE};
|
||||||
|
|
||||||
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
// Don't allocate any constant data at address zero or near it. Would be valid, but bug-prone.
|
||||||
// Follow Emscripten's example by using 1kB (4 bytes would probably do)
|
// Follow Emscripten's example by using 1kB (4 bytes would probably do)
|
||||||
|
@ -26,9 +28,10 @@ pub struct WasmBackend<'a> {
|
||||||
// Module-level data
|
// Module-level data
|
||||||
pub module_builder: ModuleBuilder,
|
pub module_builder: ModuleBuilder,
|
||||||
pub code_section_bytes: std::vec::Vec<u8>,
|
pub code_section_bytes: std::vec::Vec<u8>,
|
||||||
|
pub code_relocations: Vec<'a, RelocationEntry>,
|
||||||
_data_offset_map: MutMap<Literal<'a>, u32>,
|
_data_offset_map: MutMap<Literal<'a>, u32>,
|
||||||
_data_offset_next: u32,
|
_data_offset_next: u32,
|
||||||
proc_symbol_map: MutMap<Symbol, CodeLocation>,
|
proc_symbols: &'a [Symbol],
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
code_builder: CodeBuilder<'a>,
|
code_builder: CodeBuilder<'a>,
|
||||||
|
@ -40,15 +43,12 @@ pub struct WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmBackend<'a> {
|
impl<'a> WasmBackend<'a> {
|
||||||
pub fn new(env: &'a Env<'a>, num_procs: usize) -> Self {
|
pub fn new(env: &'a Env<'a>, proc_symbols: &'a [Symbol]) -> Self {
|
||||||
// Code section is prefixed with the number of Wasm functions
|
|
||||||
// For now, this is the same as the number of IR procedures (until we start inlining!)
|
|
||||||
let mut code_section_bytes = std::vec::Vec::with_capacity(4096);
|
let mut code_section_bytes = std::vec::Vec::with_capacity(4096);
|
||||||
|
|
||||||
// Reserve space for code section header: inner byte length and number of functions
|
// Code section header
|
||||||
// Padded to the maximum 5 bytes each, so we can update later without moving everything
|
code_section_bytes.reserve_padded_u32(); // byte length, to be written at the end
|
||||||
code_section_bytes.resize(10, 0);
|
code_section_bytes.encode_padded_u32(proc_symbols.len() as u32); // modified later in unit tests
|
||||||
overwrite_padded_u32(&mut code_section_bytes[5..10], num_procs as u32); // gets modified in unit tests
|
|
||||||
|
|
||||||
WasmBackend {
|
WasmBackend {
|
||||||
env,
|
env,
|
||||||
|
@ -58,7 +58,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
code_section_bytes,
|
code_section_bytes,
|
||||||
_data_offset_map: MutMap::default(),
|
_data_offset_map: MutMap::default(),
|
||||||
_data_offset_next: UNUSED_DATA_SECTION_BYTES,
|
_data_offset_next: UNUSED_DATA_SECTION_BYTES,
|
||||||
proc_symbol_map: MutMap::default(),
|
proc_symbols,
|
||||||
|
code_relocations: Vec::with_capacity_in(256, env.arena),
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
|
@ -81,7 +82,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
***********************************************************/
|
***********************************************************/
|
||||||
|
|
||||||
pub fn build_proc(&mut self, proc: Proc<'a>, sym: Symbol) -> Result<u32, String> {
|
pub fn build_proc(&mut self, proc: Proc<'a>, _sym: Symbol) -> Result<u32, String> {
|
||||||
// println!("\ngenerating procedure {:?}\n", sym);
|
// println!("\ngenerating procedure {:?}\n", sym);
|
||||||
|
|
||||||
// Use parity-wasm to add the signature in "types" and "functions" sections
|
// Use parity-wasm to add the signature in "types" and "functions" sections
|
||||||
|
@ -89,7 +90,6 @@ impl<'a> WasmBackend<'a> {
|
||||||
let empty_function_def = self.start_proc(&proc);
|
let empty_function_def = self.start_proc(&proc);
|
||||||
let location = self.module_builder.push_function(empty_function_def);
|
let location = self.module_builder.push_function(empty_function_def);
|
||||||
let function_index = location.body;
|
let function_index = location.body;
|
||||||
self.proc_symbol_map.insert(sym, location);
|
|
||||||
|
|
||||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||||
|
|
||||||
|
@ -141,9 +141,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.storage.stack_frame_pointer,
|
self.storage.stack_frame_pointer,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.code_builder
|
let relocs = self.code_builder.serialize(&mut self.code_section_bytes);
|
||||||
.serialize(&mut self.code_section_bytes)
|
self.code_relocations.extend(relocs);
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,13 +395,29 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
self.storage.load_symbols(&mut self.code_builder, wasm_args);
|
self.storage.load_symbols(&mut self.code_builder, wasm_args);
|
||||||
|
|
||||||
let function_location = self.proc_symbol_map.get(func_sym).ok_or(format!(
|
// Index of the called function in the code section
|
||||||
"Cannot find function {:?} called from {:?}",
|
// TODO: account for inlined functions when we start doing that (remember we emit procs out of order)
|
||||||
func_sym, sym
|
let func_index = match self.proc_symbols.iter().position(|s| s == func_sym) {
|
||||||
))?;
|
Some(i) => i as u32,
|
||||||
|
None => {
|
||||||
|
// TODO: actually useful linking! Push a relocation for it.
|
||||||
|
return Err(format!(
|
||||||
|
"Not yet supporteed: calling foreign function {:?}",
|
||||||
|
func_sym
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Index of the function's name in the symbol table
|
||||||
|
let symbol_index = func_index; // TODO: update this when we add other things to the symbol table
|
||||||
|
|
||||||
|
self.code_builder.call(
|
||||||
|
func_index,
|
||||||
|
symbol_index,
|
||||||
|
wasm_args.len(),
|
||||||
|
has_return_val,
|
||||||
|
);
|
||||||
|
|
||||||
self.code_builder
|
|
||||||
.call(function_location.body, wasm_args.len(), has_return_val);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::vec::{Drain, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use crate::{
|
use crate::module_builder::{IndexRelocType, RelocationEntry};
|
||||||
encode_f32, encode_f64, encode_i32, encode_i64, encode_u32, round_up_to_alignment, LocalId,
|
use crate::opcodes::*;
|
||||||
FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID,
|
use crate::serialize::SerialBuffer;
|
||||||
};
|
use crate::{round_up_to_alignment, LocalId, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
||||||
use crate::{opcodes::*, overwrite_padded_u32};
|
|
||||||
|
|
||||||
const DEBUG_LOG: bool = false;
|
|
||||||
|
|
||||||
/// Wasm value type. (Rust representation matches Wasm encoding)
|
/// Wasm value type. (Rust representation matches Wasm encoding)
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -143,6 +140,10 @@ pub struct CodeBuilder<'a> {
|
||||||
/// Our simulation model of the Wasm stack machine
|
/// Our simulation model of the Wasm stack machine
|
||||||
/// Keeps track of where Symbol values are in the VM stack
|
/// Keeps track of where Symbol values are in the VM stack
|
||||||
vm_stack: Vec<'a, Symbol>,
|
vm_stack: Vec<'a, Symbol>,
|
||||||
|
|
||||||
|
/// Linker info to help combine the Roc module with builtin & platform modules,
|
||||||
|
/// e.g. to modify call instructions when function indices change
|
||||||
|
relocations: Vec<'a, RelocationEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
|
@ -155,6 +156,7 @@ impl<'a> CodeBuilder<'a> {
|
||||||
preamble: Vec::with_capacity_in(32, arena),
|
preamble: Vec::with_capacity_in(32, arena),
|
||||||
inner_length: Vec::with_capacity_in(5, arena),
|
inner_length: Vec::with_capacity_in(5, arena),
|
||||||
vm_stack: Vec::with_capacity_in(32, arena),
|
vm_stack: Vec::with_capacity_in(32, arena),
|
||||||
|
relocations: Vec::with_capacity_in(32, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +218,7 @@ impl<'a> CodeBuilder<'a> {
|
||||||
let start = self.insert_bytes.len();
|
let start = self.insert_bytes.len();
|
||||||
|
|
||||||
self.insert_bytes.push(opcode);
|
self.insert_bytes.push(opcode);
|
||||||
encode_u32(&mut self.insert_bytes, immediate);
|
self.insert_bytes.encode_u32(immediate);
|
||||||
|
|
||||||
self.insert_locations.push(InsertLocation {
|
self.insert_locations.push(InsertLocation {
|
||||||
insert_at,
|
insert_at,
|
||||||
|
@ -313,14 +315,14 @@ impl<'a> CodeBuilder<'a> {
|
||||||
if *t == batch_type {
|
if *t == batch_type {
|
||||||
batch_size += 1;
|
batch_size += 1;
|
||||||
} else {
|
} else {
|
||||||
encode_u32(&mut self.preamble, batch_size);
|
self.preamble.encode_u32(batch_size);
|
||||||
self.preamble.push(batch_type as u8);
|
self.preamble.push(batch_type as u8);
|
||||||
batch_type = *t;
|
batch_type = *t;
|
||||||
batch_size = 1;
|
batch_size = 1;
|
||||||
num_batches += 1;
|
num_batches += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
encode_u32(&mut self.preamble, batch_size);
|
self.preamble.encode_u32(batch_size);
|
||||||
self.preamble.push(batch_type as u8);
|
self.preamble.push(batch_type as u8);
|
||||||
num_batches += 1;
|
num_batches += 1;
|
||||||
|
|
||||||
|
@ -333,7 +335,7 @@ impl<'a> CodeBuilder<'a> {
|
||||||
let old_len = self.preamble.len();
|
let old_len = self.preamble.len();
|
||||||
self.preamble.resize(old_len + 4, 0);
|
self.preamble.resize(old_len + 4, 0);
|
||||||
self.preamble.copy_within(1..old_len, 5);
|
self.preamble.copy_within(1..old_len, 5);
|
||||||
overwrite_padded_u32(&mut self.preamble[0..5], num_batches);
|
self.preamble.overwrite_padded_u32(0, num_batches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,14 +344,14 @@ impl<'a> CodeBuilder<'a> {
|
||||||
// Can't use the usual instruction methods because they push to self.code.
|
// Can't use the usual instruction methods because they push to self.code.
|
||||||
// This is the only case where we push instructions somewhere different.
|
// This is the only case where we push instructions somewhere different.
|
||||||
self.preamble.push(GETGLOBAL);
|
self.preamble.push(GETGLOBAL);
|
||||||
encode_u32(&mut self.preamble, STACK_POINTER_GLOBAL_ID);
|
self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID);
|
||||||
self.preamble.push(I32CONST);
|
self.preamble.push(I32CONST);
|
||||||
encode_i32(&mut self.preamble, frame_size);
|
self.preamble.encode_i32(frame_size);
|
||||||
self.preamble.push(I32SUB);
|
self.preamble.push(I32SUB);
|
||||||
self.preamble.push(TEELOCAL);
|
self.preamble.push(TEELOCAL);
|
||||||
encode_u32(&mut self.preamble, frame_pointer.0);
|
self.preamble.encode_u32(frame_pointer.0);
|
||||||
self.preamble.push(SETGLOBAL);
|
self.preamble.push(SETGLOBAL);
|
||||||
encode_u32(&mut self.preamble, STACK_POINTER_GLOBAL_ID);
|
self.preamble.encode_u32(STACK_POINTER_GLOBAL_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate instruction bytes to release a frame of stack memory on leaving the function
|
/// Generate instruction bytes to release a frame of stack memory on leaving the function
|
||||||
|
@ -381,27 +383,53 @@ impl<'a> CodeBuilder<'a> {
|
||||||
self.code.push(END);
|
self.code.push(END);
|
||||||
|
|
||||||
let inner_len = self.preamble.len() + self.code.len() + self.insert_bytes.len();
|
let inner_len = self.preamble.len() + self.code.len() + self.insert_bytes.len();
|
||||||
encode_u32(&mut self.inner_length, inner_len as u32);
|
self.inner_length.encode_u32(inner_len as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write out all the bytes in the right order
|
/// Write out all the bytes in the right order
|
||||||
pub fn serialize<W: std::io::Write>(&mut self, writer: &mut W) -> std::io::Result<()> {
|
pub fn serialize<T: SerialBuffer>(
|
||||||
writer.write_all(&self.inner_length)?;
|
&mut self,
|
||||||
writer.write_all(&self.preamble)?;
|
code_section_buf: &mut T,
|
||||||
|
) -> Drain<RelocationEntry> {
|
||||||
|
code_section_buf.append_slice(&self.inner_length);
|
||||||
|
code_section_buf.append_slice(&self.preamble);
|
||||||
|
|
||||||
// We created each insertion when a local was used for the _second_ time.
|
// Sort insertions. They are not created in order of assignment, but in order of *second* usage.
|
||||||
// But we want them in the order they were first assigned, which may not be the same.
|
|
||||||
self.insert_locations.sort_by_key(|loc| loc.insert_at);
|
self.insert_locations.sort_by_key(|loc| loc.insert_at);
|
||||||
|
|
||||||
let mut pos: usize = 0;
|
// Do the insertions & update relocation offsets
|
||||||
|
const CODE_SECTION_BODY_OFFSET: usize = 5;
|
||||||
|
let mut reloc_index = 0;
|
||||||
|
let mut code_pos: usize = 0;
|
||||||
for location in self.insert_locations.iter() {
|
for location in self.insert_locations.iter() {
|
||||||
writer.write_all(&self.code[pos..location.insert_at])?;
|
// Relocation offset needs to be an index into the body of the code section, but
|
||||||
writer.write_all(&self.insert_bytes[location.start..location.end])?;
|
// at this point it is an index into self.code. Need to adjust for all previous functions
|
||||||
pos = location.insert_at;
|
// in the code section, and for insertions in the current function.
|
||||||
|
let section_body_pos = code_section_buf.size() - CODE_SECTION_BODY_OFFSET;
|
||||||
|
while reloc_index < self.relocations.len()
|
||||||
|
&& self.relocations[reloc_index].offset() < location.insert_at as u32
|
||||||
|
{
|
||||||
|
let offset_ref = self.relocations[reloc_index].offset_mut();
|
||||||
|
*offset_ref += (section_body_pos - code_pos) as u32;
|
||||||
|
reloc_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
code_section_buf.append_slice(&self.code[code_pos..location.insert_at]);
|
||||||
|
code_section_buf.append_slice(&self.insert_bytes[location.start..location.end]);
|
||||||
|
code_pos = location.insert_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
let section_body_pos = code_section_buf.size() - CODE_SECTION_BODY_OFFSET;
|
||||||
|
while reloc_index < self.relocations.len() {
|
||||||
|
let offset_ref = self.relocations[reloc_index].offset_mut();
|
||||||
|
*offset_ref += (section_body_pos - code_pos) as u32;
|
||||||
|
reloc_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = self.code.len();
|
let len = self.code.len();
|
||||||
writer.write_all(&self.code[pos..len])
|
code_section_buf.append_slice(&self.code[code_pos..len]);
|
||||||
|
|
||||||
|
self.relocations.drain(0..)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
|
@ -426,15 +454,16 @@ impl<'a> CodeBuilder<'a> {
|
||||||
self.code.push(immediate);
|
self.code.push(immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inst_imm32(&mut self, opcode: u8, pops: usize, push: bool, immediate: u32) {
|
// public for use in test code
|
||||||
|
pub fn inst_imm32(&mut self, opcode: u8, pops: usize, push: bool, immediate: u32) {
|
||||||
self.inst(opcode, pops, push);
|
self.inst(opcode, pops, push);
|
||||||
encode_u32(&mut self.code, immediate);
|
self.code.encode_u32(immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inst_mem(&mut self, opcode: u8, pops: usize, push: bool, align: Align, offset: u32) {
|
fn inst_mem(&mut self, opcode: u8, pops: usize, push: bool, align: Align, offset: u32) {
|
||||||
self.inst(opcode, pops, push);
|
self.inst(opcode, pops, push);
|
||||||
self.code.push(align as u8);
|
self.code.push(align as u8);
|
||||||
encode_u32(&mut self.code, offset);
|
self.code.encode_u32(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
|
@ -469,13 +498,20 @@ impl<'a> CodeBuilder<'a> {
|
||||||
pub fn br_if(&mut self, levels: u32) {
|
pub fn br_if(&mut self, levels: u32) {
|
||||||
self.inst_imm32(BRIF, 1, false, levels);
|
self.inst_imm32(BRIF, 1, false, levels);
|
||||||
}
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
fn br_table() {
|
fn br_table() {
|
||||||
panic!("TODO");
|
panic!("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
instruction_no_args!(return_, RETURN, 0, false);
|
instruction_no_args!(return_, RETURN, 0, false);
|
||||||
|
|
||||||
pub fn call(&mut self, function_index: u32, n_args: usize, has_return_val: bool) {
|
pub fn call(
|
||||||
|
&mut self,
|
||||||
|
function_index: u32,
|
||||||
|
symbol_index: u32,
|
||||||
|
n_args: usize,
|
||||||
|
has_return_val: bool,
|
||||||
|
) {
|
||||||
let stack_depth = self.vm_stack.len();
|
let stack_depth = self.vm_stack.len();
|
||||||
if n_args > stack_depth {
|
if n_args > stack_depth {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -488,8 +524,21 @@ impl<'a> CodeBuilder<'a> {
|
||||||
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
|
self.vm_stack.push(Symbol::WASM_ANONYMOUS_STACK_VALUE);
|
||||||
}
|
}
|
||||||
self.code.push(CALL);
|
self.code.push(CALL);
|
||||||
encode_u32(&mut self.code, function_index);
|
|
||||||
|
// Write the index of the function to be called.
|
||||||
|
// Also make a RelocationEntry so the linker can see that this byte offset relates to a function by name.
|
||||||
|
// Here we initialise the offset to an index of self.code. After completing the function, we'll add
|
||||||
|
// other factors to make it relative to the code section. (All insertions will be known then.)
|
||||||
|
let offset = self.code.len() as u32;
|
||||||
|
self.code.encode_padded_u32(function_index);
|
||||||
|
self.relocations.push(RelocationEntry::Index {
|
||||||
|
type_id: IndexRelocType::FunctionIndexLeb,
|
||||||
|
offset,
|
||||||
|
symbol_index,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn call_indirect() {
|
fn call_indirect() {
|
||||||
panic!("Not implemented. Roc doesn't use function pointers");
|
panic!("Not implemented. Roc doesn't use function pointers");
|
||||||
}
|
}
|
||||||
|
@ -545,19 +594,19 @@ impl<'a> CodeBuilder<'a> {
|
||||||
}
|
}
|
||||||
pub fn i32_const(&mut self, x: i32) {
|
pub fn i32_const(&mut self, x: i32) {
|
||||||
self.inst(I32CONST, 0, true);
|
self.inst(I32CONST, 0, true);
|
||||||
encode_i32(&mut self.code, x);
|
self.code.encode_i32(x);
|
||||||
}
|
}
|
||||||
pub fn i64_const(&mut self, x: i64) {
|
pub fn i64_const(&mut self, x: i64) {
|
||||||
self.inst(I64CONST, 0, true);
|
self.inst(I64CONST, 0, true);
|
||||||
encode_i64(&mut self.code, x);
|
self.code.encode_i64(x);
|
||||||
}
|
}
|
||||||
pub fn f32_const(&mut self, x: f32) {
|
pub fn f32_const(&mut self, x: f32) {
|
||||||
self.inst(F32CONST, 0, true);
|
self.inst(F32CONST, 0, true);
|
||||||
encode_f32(&mut self.code, x);
|
self.code.encode_f32(x);
|
||||||
}
|
}
|
||||||
pub fn f64_const(&mut self, x: f64) {
|
pub fn f64_const(&mut self, x: f64) {
|
||||||
self.inst(F64CONST, 0, true);
|
self.inst(F64CONST, 0, true);
|
||||||
encode_f64(&mut self.code, x);
|
self.code.encode_f64(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Consider creating unified methods for numerical ops like 'eq' and 'add',
|
// TODO: Consider creating unified methods for numerical ops like 'eq' and 'add',
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
mod backend;
|
mod backend;
|
||||||
|
pub mod code_builder;
|
||||||
pub mod from_wasm32_memory;
|
pub mod from_wasm32_memory;
|
||||||
mod layout;
|
mod layout;
|
||||||
|
pub mod module_builder;
|
||||||
|
pub mod opcodes;
|
||||||
|
pub mod serialize;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub mod code_builder;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
mod opcodes;
|
|
||||||
|
|
||||||
use bumpalo::{self, collections::Vec, Bump};
|
use bumpalo::{self, collections::Vec, Bump};
|
||||||
use parity_wasm::builder;
|
use parity_wasm::builder;
|
||||||
|
|
||||||
|
@ -20,6 +18,10 @@ use roc_mono::layout::LayoutIds;
|
||||||
|
|
||||||
use crate::backend::WasmBackend;
|
use crate::backend::WasmBackend;
|
||||||
use crate::code_builder::{Align, CodeBuilder, ValueType};
|
use crate::code_builder::{Align, CodeBuilder, ValueType};
|
||||||
|
use crate::module_builder::{
|
||||||
|
LinkingSection, LinkingSubSection, RelocationSection, SectionId, SymInfo,
|
||||||
|
};
|
||||||
|
use crate::serialize::{SerialBuffer, Serialize};
|
||||||
|
|
||||||
const PTR_SIZE: u32 = 4;
|
const PTR_SIZE: u32 = 4;
|
||||||
const PTR_TYPE: ValueType = ValueType::I32;
|
const PTR_TYPE: ValueType = ValueType::I32;
|
||||||
|
@ -44,7 +46,7 @@ pub fn build_module<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<std::vec::Vec<u8>, String> {
|
) -> Result<std::vec::Vec<u8>, String> {
|
||||||
let (builder, code_section_bytes, _) = build_module_help(env, procedures)?;
|
let (builder, code_section_bytes) = build_module_help(env, procedures)?;
|
||||||
let mut module = builder.build();
|
let mut module = builder.build();
|
||||||
replace_code_section(&mut module, code_section_bytes);
|
replace_code_section(&mut module, code_section_bytes);
|
||||||
|
|
||||||
|
@ -56,26 +58,20 @@ pub fn build_module<'a>(
|
||||||
pub fn build_module_help<'a>(
|
pub fn build_module_help<'a>(
|
||||||
env: &'a Env,
|
env: &'a Env,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) -> Result<(builder::ModuleBuilder, std::vec::Vec<u8>, u32), String> {
|
) -> Result<(builder::ModuleBuilder, std::vec::Vec<u8>), String> {
|
||||||
let mut backend = WasmBackend::new(env, procedures.len());
|
let proc_symbols = Vec::from_iter_in(procedures.keys().map(|(sym, _)| *sym), env.arena);
|
||||||
|
let mut backend = WasmBackend::new(env, &proc_symbols);
|
||||||
let mut layout_ids = LayoutIds::default();
|
let mut layout_ids = LayoutIds::default();
|
||||||
|
let mut symbol_table_entries = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
|
|
||||||
// Sort procedures by occurrence order
|
for (i, ((sym, layout), proc)) in procedures.into_iter().enumerate() {
|
||||||
//
|
let proc_name = layout_ids
|
||||||
// We sort by the "name", but those are interned strings, and the name that is
|
.get(proc.name, &proc.ret_layout)
|
||||||
// interned first will have a lower number.
|
.to_symbol_string(proc.name, &env.interns);
|
||||||
//
|
symbol_table_entries.push(SymInfo::for_function(i as u32, proc_name));
|
||||||
// But, the name that occurs first is always `main` because it is in the (implicit)
|
|
||||||
// file header. Therefore sorting high to low will put other functions before main
|
let function_index = backend.build_proc(proc, sym)?;
|
||||||
//
|
|
||||||
// This means that for now other functions in the file have to be ordered "in reverse": if A
|
|
||||||
// uses B, then the name of A must first occur after the first occurrence of the name of B
|
|
||||||
let mut procedures = Vec::from_iter_in(procedures.into_iter(), env.arena);
|
|
||||||
procedures.sort_by(|a, b| b.0 .0.cmp(&a.0 .0));
|
|
||||||
|
|
||||||
let mut function_index: u32 = 0;
|
|
||||||
for ((sym, layout), proc) in procedures {
|
|
||||||
function_index = backend.build_proc(proc, sym)?;
|
|
||||||
if env.exposed_to_host.contains(&sym) {
|
if env.exposed_to_host.contains(&sym) {
|
||||||
let fn_name = layout_ids
|
let fn_name = layout_ids
|
||||||
.get_toplevel(sym, &layout)
|
.get_toplevel(sym, &layout)
|
||||||
|
@ -92,12 +88,42 @@ pub fn build_module_help<'a>(
|
||||||
|
|
||||||
// Update code section length
|
// Update code section length
|
||||||
let inner_length = (backend.code_section_bytes.len() - 5) as u32;
|
let inner_length = (backend.code_section_bytes.len() - 5) as u32;
|
||||||
overwrite_padded_u32(&mut backend.code_section_bytes[0..5], inner_length);
|
backend
|
||||||
|
.code_section_bytes
|
||||||
|
.overwrite_padded_u32(0, inner_length);
|
||||||
|
|
||||||
// Because of the sorting above, we know the last function in the `for` is the main function.
|
// linking metadata section
|
||||||
// Here we grab its index and return it, so that the test_wrapper is able to call it.
|
let mut linking_section_bytes = std::vec::Vec::with_capacity(symbol_table_entries.len() * 20);
|
||||||
// This is a workaround until we implement object files with symbols and relocations.
|
let linking_section = LinkingSection {
|
||||||
let main_function_index = function_index;
|
subsections: bumpalo::vec![in env.arena;
|
||||||
|
LinkingSubSection::SymbolTable(symbol_table_entries)
|
||||||
|
],
|
||||||
|
};
|
||||||
|
linking_section.serialize(&mut linking_section_bytes);
|
||||||
|
backend.module_builder = backend.module_builder.with_section(Section::Unparsed {
|
||||||
|
id: SectionId::Custom as u8,
|
||||||
|
payload: linking_section_bytes,
|
||||||
|
});
|
||||||
|
|
||||||
|
// We always output the code section at the same index relative to other sections, and we need that for relocations.
|
||||||
|
// TODO: If there's a data section, this will be 6 so we'll need logic for that
|
||||||
|
// TODO: Build a cleaner solution after we replace parity-wasm with our own module_builder
|
||||||
|
const CODE_SECTION_INDEX: u32 = 5;
|
||||||
|
|
||||||
|
let code_reloc_section = RelocationSection {
|
||||||
|
name: "reloc.CODE",
|
||||||
|
target_section_index: CODE_SECTION_INDEX,
|
||||||
|
entries: &backend.code_relocations,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut code_reloc_section_bytes = std::vec::Vec::with_capacity(256);
|
||||||
|
code_reloc_section.serialize(&mut code_reloc_section_bytes);
|
||||||
|
|
||||||
|
// Must come after linking section
|
||||||
|
backend.module_builder = backend.module_builder.with_section(Section::Unparsed {
|
||||||
|
id: SectionId::Custom as u8,
|
||||||
|
payload: code_reloc_section_bytes,
|
||||||
|
});
|
||||||
|
|
||||||
const MIN_MEMORY_SIZE_KB: u32 = 1024;
|
const MIN_MEMORY_SIZE_KB: u32 = 1024;
|
||||||
const PAGE_SIZE_KB: u32 = 64;
|
const PAGE_SIZE_KB: u32 = 64;
|
||||||
|
@ -119,24 +145,20 @@ pub fn build_module_help<'a>(
|
||||||
.build();
|
.build();
|
||||||
backend.module_builder.push_global(stack_pointer_global);
|
backend.module_builder.push_global(stack_pointer_global);
|
||||||
|
|
||||||
Ok((
|
Ok((backend.module_builder, backend.code_section_bytes))
|
||||||
backend.module_builder,
|
|
||||||
backend.code_section_bytes,
|
|
||||||
main_function_index,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace parity-wasm's code section with our own handmade one
|
/// Replace parity-wasm's code section with our own handmade one
|
||||||
pub fn replace_code_section(module: &mut Module, code_section_bytes: std::vec::Vec<u8>) {
|
pub fn replace_code_section(module: &mut Module, code_section_bytes: std::vec::Vec<u8>) {
|
||||||
let sections = module.sections_mut();
|
let sections = module.sections_mut();
|
||||||
let mut code_section_index = usize::MAX;
|
|
||||||
for (i, s) in sections.iter().enumerate() {
|
let code_section_index = sections
|
||||||
if let Section::Code(_) = s {
|
.iter()
|
||||||
code_section_index = i;
|
.position(|s| matches!(s, Section::Code(_)))
|
||||||
}
|
.unwrap();
|
||||||
}
|
|
||||||
sections[code_section_index] = Section::Unparsed {
|
sections[code_section_index] = Section::Unparsed {
|
||||||
id: CODE_SECTION_ID,
|
id: SectionId::Code as u8,
|
||||||
payload: code_section_bytes,
|
payload: code_section_bytes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -192,192 +214,3 @@ pub fn round_up_to_alignment(unaligned: i32, alignment_bytes: i32) -> i32 {
|
||||||
pub fn debug_panic<E: std::fmt::Debug>(error: E) {
|
pub fn debug_panic<E: std::fmt::Debug>(error: E) {
|
||||||
panic!("{:?}", error);
|
panic!("{:?}", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write an unsigned value into the provided buffer in LEB-128 format, returning byte length
|
|
||||||
///
|
|
||||||
/// All integers in Wasm are variable-length encoded, which saves space for small values.
|
|
||||||
/// The most significant bit indicates "more bytes are coming", and the other 7 are payload.
|
|
||||||
macro_rules! encode_uleb128 {
|
|
||||||
($name: ident, $ty: ty) => {
|
|
||||||
pub fn $name<'a>(buffer: &mut Vec<'a, u8>, value: $ty) -> usize {
|
|
||||||
let mut x = value;
|
|
||||||
let start_len = buffer.len();
|
|
||||||
while x >= 0x80 {
|
|
||||||
buffer.push(0x80 | ((x & 0x7f) as u8));
|
|
||||||
x >>= 7;
|
|
||||||
}
|
|
||||||
buffer.push(x as u8);
|
|
||||||
buffer.len() - start_len
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
encode_uleb128!(encode_u32, u32);
|
|
||||||
encode_uleb128!(encode_u64, u64);
|
|
||||||
|
|
||||||
/// Write a *signed* value into the provided buffer in LEB-128 format, returning byte length
|
|
||||||
macro_rules! encode_sleb128 {
|
|
||||||
($name: ident, $ty: ty) => {
|
|
||||||
pub fn $name<'a>(buffer: &mut Vec<'a, u8>, value: $ty) -> usize {
|
|
||||||
let mut x = value;
|
|
||||||
let start_len = buffer.len();
|
|
||||||
loop {
|
|
||||||
let byte = (x & 0x7f) as u8;
|
|
||||||
x >>= 7;
|
|
||||||
let byte_is_negative = (byte & 0x40) != 0;
|
|
||||||
if ((x == 0 && !byte_is_negative) || (x == -1 && byte_is_negative)) {
|
|
||||||
buffer.push(byte);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffer.push(byte | 0x80);
|
|
||||||
}
|
|
||||||
buffer.len() - start_len
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
encode_sleb128!(encode_i32, i32);
|
|
||||||
encode_sleb128!(encode_i64, i64);
|
|
||||||
|
|
||||||
/// No LEB encoding, and always little-endian regardless of compiler host.
|
|
||||||
macro_rules! encode_float {
|
|
||||||
($name: ident, $ty: ty) => {
|
|
||||||
pub fn $name<'a>(buffer: &mut Vec<'a, u8>, value: $ty) {
|
|
||||||
let mut x = value.to_bits();
|
|
||||||
let size = std::mem::size_of::<$ty>();
|
|
||||||
for _ in 0..size {
|
|
||||||
buffer.push((x & 0xff) as u8);
|
|
||||||
x >>= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
encode_float!(encode_f32, f32);
|
|
||||||
encode_float!(encode_f64, f64);
|
|
||||||
|
|
||||||
/// Overwrite a LEB-128 encoded u32 value, padded to maximum length (5 bytes)
|
|
||||||
///
|
|
||||||
/// We need some fixed-length values so we can overwrite them without moving all following bytes.
|
|
||||||
/// Many parts of the binary format are prefixed with their length, which we only know at the end.
|
|
||||||
/// And relocation values get updated during linking.
|
|
||||||
/// This can help us to avoid copies, which is good for speed, but there's a tradeoff with output size.
|
|
||||||
///
|
|
||||||
/// The value 3 is encoded as 0x83 0x80 0x80 0x80 0x00.
|
|
||||||
/// https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections
|
|
||||||
pub fn overwrite_padded_u32(buffer: &mut [u8], value: u32) {
|
|
||||||
let mut x = value;
|
|
||||||
for byte in buffer.iter_mut().take(4) {
|
|
||||||
*byte = 0x80 | ((x & 0x7f) as u8);
|
|
||||||
x >>= 7;
|
|
||||||
}
|
|
||||||
buffer[4] = x as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use bumpalo::{self, collections::Vec, Bump};
|
|
||||||
|
|
||||||
fn help_u32<'a>(arena: &'a Bump, value: u32) -> Vec<'a, u8> {
|
|
||||||
let mut buffer = Vec::with_capacity_in(5, arena);
|
|
||||||
encode_u32(&mut buffer, value);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_u32() {
|
|
||||||
let a = &Bump::new();
|
|
||||||
assert_eq!(help_u32(a, 0), &[0]);
|
|
||||||
assert_eq!(help_u32(a, 64), &[64]);
|
|
||||||
assert_eq!(help_u32(a, 0x7f), &[0x7f]);
|
|
||||||
assert_eq!(help_u32(a, 0x80), &[0x80, 0x01]);
|
|
||||||
assert_eq!(help_u32(a, 0x3fff), &[0xff, 0x7f]);
|
|
||||||
assert_eq!(help_u32(a, 0x4000), &[0x80, 0x80, 0x01]);
|
|
||||||
assert_eq!(help_u32(a, u32::MAX), &[0xff, 0xff, 0xff, 0xff, 0x0f]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn help_u64<'a>(arena: &'a Bump, value: u64) -> Vec<'a, u8> {
|
|
||||||
let mut buffer = Vec::with_capacity_in(10, arena);
|
|
||||||
encode_u64(&mut buffer, value);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_u64() {
|
|
||||||
let a = &Bump::new();
|
|
||||||
assert_eq!(help_u64(a, 0), &[0]);
|
|
||||||
assert_eq!(help_u64(a, 64), &[64]);
|
|
||||||
assert_eq!(help_u64(a, 0x7f), &[0x7f]);
|
|
||||||
assert_eq!(help_u64(a, 0x80), &[0x80, 0x01]);
|
|
||||||
assert_eq!(help_u64(a, 0x3fff), &[0xff, 0x7f]);
|
|
||||||
assert_eq!(help_u64(a, 0x4000), &[0x80, 0x80, 0x01]);
|
|
||||||
assert_eq!(
|
|
||||||
help_u64(a, u64::MAX),
|
|
||||||
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn help_i32<'a>(arena: &'a Bump, value: i32) -> Vec<'a, u8> {
|
|
||||||
let mut buffer = Vec::with_capacity_in(5, arena);
|
|
||||||
encode_i32(&mut buffer, value);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_i32() {
|
|
||||||
let a = &Bump::new();
|
|
||||||
assert_eq!(help_i32(a, 0), &[0]);
|
|
||||||
assert_eq!(help_i32(a, 1), &[1]);
|
|
||||||
assert_eq!(help_i32(a, -1), &[0x7f]);
|
|
||||||
assert_eq!(help_i32(a, 63), &[63]);
|
|
||||||
assert_eq!(help_i32(a, 64), &[0xc0, 0x0]);
|
|
||||||
assert_eq!(help_i32(a, -64), &[0x40]);
|
|
||||||
assert_eq!(help_i32(a, -65), &[0xbf, 0x7f]);
|
|
||||||
assert_eq!(help_i32(a, i32::MAX), &[0xff, 0xff, 0xff, 0xff, 0x07]);
|
|
||||||
assert_eq!(help_i32(a, i32::MIN), &[0x80, 0x80, 0x80, 0x80, 0x78]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn help_i64<'a>(arena: &'a Bump, value: i64) -> Vec<'a, u8> {
|
|
||||||
let mut buffer = Vec::with_capacity_in(10, arena);
|
|
||||||
encode_i64(&mut buffer, value);
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_i64() {
|
|
||||||
let a = &Bump::new();
|
|
||||||
assert_eq!(help_i64(a, 0), &[0]);
|
|
||||||
assert_eq!(help_i64(a, 1), &[1]);
|
|
||||||
assert_eq!(help_i64(a, -1), &[0x7f]);
|
|
||||||
assert_eq!(help_i64(a, 63), &[63]);
|
|
||||||
assert_eq!(help_i64(a, 64), &[0xc0, 0x0]);
|
|
||||||
assert_eq!(help_i64(a, -64), &[0x40]);
|
|
||||||
assert_eq!(help_i64(a, -65), &[0xbf, 0x7f]);
|
|
||||||
assert_eq!(
|
|
||||||
help_i64(a, i64::MAX),
|
|
||||||
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00],
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
help_i64(a, i64::MIN),
|
|
||||||
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_overwrite_u32_padded() {
|
|
||||||
let mut buffer = [0, 0, 0, 0, 0];
|
|
||||||
|
|
||||||
overwrite_padded_u32(&mut buffer, u32::MAX);
|
|
||||||
assert_eq!(buffer, [0xff, 0xff, 0xff, 0xff, 0x0f]);
|
|
||||||
|
|
||||||
overwrite_padded_u32(&mut buffer, 0);
|
|
||||||
assert_eq!(buffer, [0x80, 0x80, 0x80, 0x80, 0x00]);
|
|
||||||
|
|
||||||
overwrite_padded_u32(&mut buffer, 127);
|
|
||||||
assert_eq!(buffer, [0xff, 0x80, 0x80, 0x80, 0x00]);
|
|
||||||
|
|
||||||
overwrite_padded_u32(&mut buffer, 128);
|
|
||||||
assert_eq!(buffer, [0x80, 0x81, 0x80, 0x80, 0x00]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
499
compiler/gen_wasm/src/module_builder.rs
Normal file
499
compiler/gen_wasm/src/module_builder.rs
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
use bumpalo::collections::vec::Vec;
|
||||||
|
|
||||||
|
use crate::code_builder::Align;
|
||||||
|
use crate::serialize::{SerialBuffer, Serialize};
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum SectionId {
|
||||||
|
Custom = 0,
|
||||||
|
Type = 1,
|
||||||
|
Import = 2,
|
||||||
|
Function = 3,
|
||||||
|
Table = 4,
|
||||||
|
Memory = 5,
|
||||||
|
Global = 6,
|
||||||
|
Export = 7,
|
||||||
|
Start = 8,
|
||||||
|
Element = 9,
|
||||||
|
Code = 10,
|
||||||
|
Data = 11,
|
||||||
|
DataCount = 12,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SectionHeaderIndices {
|
||||||
|
size_index: usize,
|
||||||
|
body_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a section header, returning the position of the encoded length
|
||||||
|
fn _write_section_header<T: SerialBuffer>(buffer: &mut T, id: SectionId) -> SectionHeaderIndices {
|
||||||
|
buffer.append_byte(id as u8);
|
||||||
|
let size_index = buffer.reserve_padded_u32();
|
||||||
|
let body_index = buffer.size();
|
||||||
|
SectionHeaderIndices {
|
||||||
|
size_index,
|
||||||
|
body_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a custom section header, returning the position of the encoded length
|
||||||
|
fn write_custom_section_header<T: SerialBuffer>(
|
||||||
|
buffer: &mut T,
|
||||||
|
name: &str,
|
||||||
|
) -> SectionHeaderIndices {
|
||||||
|
// buffer.append_byte(SectionId::Custom as u8); // TODO: uncomment when we get rid of parity_wasm
|
||||||
|
let size_index = buffer.reserve_padded_u32();
|
||||||
|
let body_index = buffer.size();
|
||||||
|
name.serialize(buffer);
|
||||||
|
SectionHeaderIndices {
|
||||||
|
size_index,
|
||||||
|
body_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a section header with its final size, after writing the bytes
|
||||||
|
fn update_section_size<T: SerialBuffer>(buffer: &mut T, header_indices: SectionHeaderIndices) {
|
||||||
|
let size = buffer.size() - header_indices.body_index;
|
||||||
|
buffer.overwrite_padded_u32(header_indices.size_index, size as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_vector_with_count<'a, SB, S>(buffer: &mut SB, items: &Vec<'a, S>)
|
||||||
|
where
|
||||||
|
SB: SerialBuffer,
|
||||||
|
S: Serialize,
|
||||||
|
{
|
||||||
|
buffer.encode_u32(items.len() as u32);
|
||||||
|
for item in items.iter() {
|
||||||
|
item.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Relocation sections
|
||||||
|
*
|
||||||
|
* https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum IndexRelocType {
|
||||||
|
/// a function index encoded as a 5-byte [varuint32]. Used for the immediate argument of a `call` instruction.
|
||||||
|
FunctionIndexLeb = 0,
|
||||||
|
/// a function table index encoded as a 5-byte [varint32].
|
||||||
|
/// Used to refer to the immediate argument of a `i32.const` instruction, e.g. taking the address of a function.
|
||||||
|
TableIndexSleb = 1,
|
||||||
|
/// a function table index encoded as a [uint32], e.g. taking the address of a function in a static data initializer.
|
||||||
|
TableIndexI32 = 2,
|
||||||
|
/// a type index encoded as a 5-byte [varuint32], e.g. the type immediate in a `call_indirect`.
|
||||||
|
TypeIndexLeb = 6,
|
||||||
|
/// a global index encoded as a 5-byte [varuint32], e.g. the index immediate in a `get_global`.
|
||||||
|
GlobalIndexLeb = 7,
|
||||||
|
/// an event index encoded as a 5-byte [varuint32]. Used for the immediate argument of a `throw` and `if_except` instruction.
|
||||||
|
EventIndexLeb = 10,
|
||||||
|
/// a global index encoded as [uint32].
|
||||||
|
GlobalIndexI32 = 13,
|
||||||
|
/// the 64-bit counterpart of `R_WASM_TABLE_INDEX_SLEB`. A function table index encoded as a 10-byte [varint64].
|
||||||
|
/// Used to refer to the immediate argument of a `i64.const` instruction, e.g. taking the address of a function in Wasm64.
|
||||||
|
TableIndexSleb64 = 18,
|
||||||
|
/// the 64-bit counterpart of `R_WASM_TABLE_INDEX_I32`.
|
||||||
|
/// A function table index encoded as a [uint64], e.g. taking the address of a function in a static data initializer.
|
||||||
|
TableIndexI64 = 19,
|
||||||
|
/// a table number encoded as a 5-byte [varuint32]. Used for the table immediate argument in the table.* instructions.
|
||||||
|
TableNumberLeb = 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum OffsetRelocType {
|
||||||
|
/// a linear memory index encoded as a 5-byte [varuint32].
|
||||||
|
/// Used for the immediate argument of a `load` or `store` instruction, e.g. directly loading from or storing to a C++ global.
|
||||||
|
MemoryAddrLeb = 3,
|
||||||
|
/// a linear memory index encoded as a 5-byte [varint32].
|
||||||
|
/// Used for the immediate argument of a `i32.const` instruction, e.g. taking the address of a C++ global.
|
||||||
|
MemoryAddrSleb = 4,
|
||||||
|
/// a linear memory index encoded as a [uint32], e.g. taking the address of a C++ global in a static data initializer.
|
||||||
|
MemoryAddrI32 = 5,
|
||||||
|
/// a byte offset within code section for the specific function encoded as a [uint32].
|
||||||
|
/// The offsets start at the actual function code excluding its size field.
|
||||||
|
FunctionOffsetI32 = 8,
|
||||||
|
/// a byte offset from start of the specified section encoded as a [uint32].
|
||||||
|
SectionOffsetI32 = 9,
|
||||||
|
/// the 64-bit counterpart of `R_WASM_MEMORY_ADDR_LEB`. A 64-bit linear memory index encoded as a 10-byte [varuint64],
|
||||||
|
/// Used for the immediate argument of a `load` or `store` instruction on a 64-bit linear memory array.
|
||||||
|
MemoryAddrLeb64 = 14,
|
||||||
|
/// the 64-bit counterpart of `R_WASM_MEMORY_ADDR_SLEB`. A 64-bit linear memory index encoded as a 10-byte [varint64].
|
||||||
|
/// Used for the immediate argument of a `i64.const` instruction.
|
||||||
|
MemoryAddrSleb64 = 15,
|
||||||
|
/// the 64-bit counterpart of `R_WASM_MEMORY_ADDR`. A 64-bit linear memory index encoded as a [uint64],
|
||||||
|
/// e.g. taking the 64-bit address of a C++ global in a static data initializer.
|
||||||
|
MemoryAddrI64 = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RelocationEntry {
|
||||||
|
Index {
|
||||||
|
type_id: IndexRelocType,
|
||||||
|
offset: u32, // offset 0 means the next byte after section id and size
|
||||||
|
symbol_index: u32, // index in symbol table
|
||||||
|
},
|
||||||
|
Offset {
|
||||||
|
type_id: OffsetRelocType,
|
||||||
|
offset: u32, // offset 0 means the next byte after section id and size
|
||||||
|
symbol_index: u32, // index in symbol table
|
||||||
|
addend: i32, // addend to add to the address
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelocationEntry {
|
||||||
|
pub fn offset(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Index { offset, .. } => *offset,
|
||||||
|
Self::Offset { offset, .. } => *offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_mut(&mut self) -> &mut u32 {
|
||||||
|
match self {
|
||||||
|
Self::Index { offset, .. } => offset,
|
||||||
|
Self::Offset { offset, .. } => offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RelocationEntry {
|
||||||
|
pub fn for_function_call(offset: u32, symbol_index: u32) -> Self {
|
||||||
|
RelocationEntry::Index {
|
||||||
|
type_id: IndexRelocType::FunctionIndexLeb,
|
||||||
|
offset,
|
||||||
|
symbol_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for RelocationEntry {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
Self::Index {
|
||||||
|
type_id,
|
||||||
|
offset,
|
||||||
|
symbol_index,
|
||||||
|
} => {
|
||||||
|
buffer.append_byte(*type_id as u8);
|
||||||
|
buffer.encode_u32(*offset);
|
||||||
|
buffer.encode_u32(*symbol_index);
|
||||||
|
}
|
||||||
|
Self::Offset {
|
||||||
|
type_id,
|
||||||
|
offset,
|
||||||
|
symbol_index,
|
||||||
|
addend,
|
||||||
|
} => {
|
||||||
|
buffer.append_byte(*type_id as u8);
|
||||||
|
buffer.encode_u32(*offset);
|
||||||
|
buffer.encode_u32(*symbol_index);
|
||||||
|
buffer.encode_i32(*addend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RelocationSection<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
/// The *index* (not ID!) of the target section in the module
|
||||||
|
pub target_section_index: u32,
|
||||||
|
pub entries: &'a Vec<'a, RelocationEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for RelocationSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
let header_indices = write_custom_section_header(buffer, self.name);
|
||||||
|
buffer.encode_u32(self.target_section_index);
|
||||||
|
serialize_vector_with_count(buffer, self.entries);
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Linking section
|
||||||
|
*
|
||||||
|
* https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#linking-metadata-section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
/// Linking metadata for data segments
|
||||||
|
pub struct LinkingSegment {
|
||||||
|
pub name: String,
|
||||||
|
pub alignment: Align,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
impl Serialize for LinkingSegment {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linking metadata for init (start) functions
|
||||||
|
pub struct LinkingInitFunc {
|
||||||
|
pub priority: u32,
|
||||||
|
pub symbol_index: u32, // index in the symbol table, not the function index
|
||||||
|
}
|
||||||
|
impl Serialize for LinkingInitFunc {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//
|
||||||
|
// Common data
|
||||||
|
//
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum ComdatSymKind {
|
||||||
|
Data = 0,
|
||||||
|
Function = 1,
|
||||||
|
Global = 2,
|
||||||
|
Event = 3,
|
||||||
|
Table = 4,
|
||||||
|
Section = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ComdatSym {
|
||||||
|
pub kind: ComdatSymKind,
|
||||||
|
pub index: u32,
|
||||||
|
}
|
||||||
|
impl Serialize for ComdatSym {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linking metadata for common data
|
||||||
|
/// A COMDAT group may contain one or more functions, data segments, and/or custom sections.
|
||||||
|
/// The linker will include all of these elements with a given group name from one object file,
|
||||||
|
/// and will exclude any element with this group name from all other object files.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct LinkingComdat<'a> {
|
||||||
|
name: String,
|
||||||
|
flags: u32,
|
||||||
|
syms: Vec<'a, ComdatSym>,
|
||||||
|
}
|
||||||
|
impl<'a> Serialize for LinkingComdat<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------
|
||||||
|
//
|
||||||
|
// Symbol table
|
||||||
|
//
|
||||||
|
//----------------
|
||||||
|
|
||||||
|
/// Indicating that this is a weak symbol. When
|
||||||
|
/// linking multiple modules defining the same symbol, all weak definitions are
|
||||||
|
/// discarded if any strong definitions exist; then if multiple weak definitions
|
||||||
|
/// exist all but one (unspecified) are discarded; and finally it is an error if
|
||||||
|
/// more than one definition remains.
|
||||||
|
pub const WASM_SYM_BINDING_WEAK: u32 = 1;
|
||||||
|
|
||||||
|
/// Indicating that this is a local symbol (this is exclusive with `WASM_SYM_BINDING_WEAK`).
|
||||||
|
/// Local symbols are not to be exported, or linked to other modules/sections.
|
||||||
|
/// The names of all non-local symbols must be unique, but the names of local symbols
|
||||||
|
/// are not considered for uniqueness. A local function or global symbol cannot reference an import.
|
||||||
|
pub const WASM_SYM_BINDING_LOCAL: u32 = 2;
|
||||||
|
|
||||||
|
/// Indicating that this is a hidden symbol.
|
||||||
|
/// Hidden symbols are not to be exported when performing the final link, but
|
||||||
|
/// may be linked to other modules.
|
||||||
|
pub const WASM_SYM_VISIBILITY_HIDDEN: u32 = 4;
|
||||||
|
|
||||||
|
/// Indicating that this symbol is not defined.
|
||||||
|
/// For non-data symbols, this must match whether the symbol is an import
|
||||||
|
/// or is defined; for data symbols, determines whether a segment is specified.
|
||||||
|
pub const WASM_SYM_UNDEFINED: u32 = 0x10; // required if the symbol refers to an import
|
||||||
|
|
||||||
|
/// The symbol is intended to be exported from the
|
||||||
|
/// wasm module to the host environment. This differs from the visibility flags
|
||||||
|
/// in that it effects the static linker.
|
||||||
|
pub const WASM_SYM_EXPORTED: u32 = 0x20;
|
||||||
|
|
||||||
|
/// The symbol uses an explicit symbol name,
|
||||||
|
/// rather than reusing the name from a wasm import. This allows it to remap
|
||||||
|
/// imports from foreign WebAssembly modules into local symbols with different
|
||||||
|
/// names.
|
||||||
|
pub const WASM_SYM_EXPLICIT_NAME: u32 = 0x40; // use the name from the symbol table, not from the import
|
||||||
|
|
||||||
|
/// The symbol is intended to be included in the
|
||||||
|
/// linker output, regardless of whether it is used by the program.
|
||||||
|
pub const WASM_SYM_NO_STRIP: u32 = 0x80;
|
||||||
|
|
||||||
|
pub enum WasmObjectSymbol {
|
||||||
|
Defined { index: u32, name: String },
|
||||||
|
Imported { index: u32 },
|
||||||
|
}
|
||||||
|
impl Serialize for WasmObjectSymbol {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
Self::Defined { index, name } => {
|
||||||
|
buffer.encode_u32(*index);
|
||||||
|
buffer.encode_u32(name.len() as u32);
|
||||||
|
buffer.append_slice(name.as_bytes());
|
||||||
|
}
|
||||||
|
Self::Imported { index } => {
|
||||||
|
buffer.encode_u32(*index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum DataSymbol {
|
||||||
|
Defined {
|
||||||
|
name: String,
|
||||||
|
index: u32,
|
||||||
|
offset: u32,
|
||||||
|
size: u32,
|
||||||
|
},
|
||||||
|
Imported {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
impl Serialize for DataSymbol {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
Self::Defined {
|
||||||
|
name,
|
||||||
|
index,
|
||||||
|
offset,
|
||||||
|
size,
|
||||||
|
} => {
|
||||||
|
buffer.encode_u32(name.len() as u32);
|
||||||
|
buffer.append_slice(name.as_bytes());
|
||||||
|
buffer.encode_u32(*index);
|
||||||
|
buffer.encode_u32(*offset);
|
||||||
|
buffer.encode_u32(*size);
|
||||||
|
}
|
||||||
|
Self::Imported { name } => {
|
||||||
|
buffer.encode_u32(name.len() as u32);
|
||||||
|
buffer.append_slice(name.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// section index (not section id!)
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SectionIndex(u32);
|
||||||
|
|
||||||
|
pub enum SymInfoFields {
|
||||||
|
Function(WasmObjectSymbol),
|
||||||
|
Data(DataSymbol),
|
||||||
|
Global(WasmObjectSymbol),
|
||||||
|
Section(SectionIndex),
|
||||||
|
Event(WasmObjectSymbol),
|
||||||
|
Table(WasmObjectSymbol),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SymInfo {
|
||||||
|
flags: u32,
|
||||||
|
info: SymInfoFields,
|
||||||
|
}
|
||||||
|
impl SymInfo {
|
||||||
|
pub fn for_function(wasm_function_index: u32, name: String) -> Self {
|
||||||
|
let linking_symbol = WasmObjectSymbol::Defined {
|
||||||
|
index: wasm_function_index,
|
||||||
|
name,
|
||||||
|
};
|
||||||
|
SymInfo {
|
||||||
|
flags: 0,
|
||||||
|
info: SymInfoFields::Function(linking_symbol),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SymInfo {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_byte(match self.info {
|
||||||
|
SymInfoFields::Function(_) => 0,
|
||||||
|
SymInfoFields::Data(_) => 1,
|
||||||
|
SymInfoFields::Global(_) => 2,
|
||||||
|
SymInfoFields::Section(_) => 3,
|
||||||
|
SymInfoFields::Event(_) => 4,
|
||||||
|
SymInfoFields::Table(_) => 5,
|
||||||
|
});
|
||||||
|
buffer.encode_u32(self.flags);
|
||||||
|
match &self.info {
|
||||||
|
SymInfoFields::Function(x) => x.serialize(buffer),
|
||||||
|
SymInfoFields::Data(x) => x.serialize(buffer),
|
||||||
|
SymInfoFields::Global(x) => x.serialize(buffer),
|
||||||
|
SymInfoFields::Section(SectionIndex(x)) => {
|
||||||
|
buffer.encode_u32(*x);
|
||||||
|
}
|
||||||
|
SymInfoFields::Event(x) => x.serialize(buffer),
|
||||||
|
SymInfoFields::Table(x) => x.serialize(buffer),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
//
|
||||||
|
// Linking subsections
|
||||||
|
//
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
pub enum LinkingSubSection<'a> {
|
||||||
|
/// Extra metadata about the data segments.
|
||||||
|
SegmentInfo(Vec<'a, LinkingSegment>),
|
||||||
|
/// Specifies a list of constructor functions to be called at startup.
|
||||||
|
/// These constructors will be called in priority order after memory has been initialized.
|
||||||
|
InitFuncs(Vec<'a, LinkingInitFunc>),
|
||||||
|
/// Specifies the COMDAT groups of associated linking objects, which are linked only once and all together.
|
||||||
|
ComdatInfo(Vec<'a, LinkingComdat<'a>>),
|
||||||
|
/// Specifies extra information about the symbols present in the module.
|
||||||
|
SymbolTable(Vec<'a, SymInfo>),
|
||||||
|
}
|
||||||
|
impl<'a> Serialize for LinkingSubSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_byte(match self {
|
||||||
|
Self::SegmentInfo(_) => 5,
|
||||||
|
Self::InitFuncs(_) => 6,
|
||||||
|
Self::ComdatInfo(_) => 7,
|
||||||
|
Self::SymbolTable(_) => 8,
|
||||||
|
});
|
||||||
|
let payload_len_index = buffer.reserve_padded_u32();
|
||||||
|
let payload_start_index = buffer.size();
|
||||||
|
match self {
|
||||||
|
Self::SegmentInfo(items) => serialize_vector_with_count(buffer, items),
|
||||||
|
Self::InitFuncs(items) => serialize_vector_with_count(buffer, items),
|
||||||
|
Self::ComdatInfo(items) => serialize_vector_with_count(buffer, items),
|
||||||
|
Self::SymbolTable(items) => serialize_vector_with_count(buffer, items),
|
||||||
|
}
|
||||||
|
buffer.overwrite_padded_u32(
|
||||||
|
payload_len_index,
|
||||||
|
(buffer.size() - payload_start_index) as u32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LINKING_VERSION: u8 = 2;
|
||||||
|
|
||||||
|
pub struct LinkingSection<'a> {
|
||||||
|
pub subsections: Vec<'a, LinkingSubSection<'a>>,
|
||||||
|
}
|
||||||
|
impl<'a> Serialize for LinkingSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
let header_indices = write_custom_section_header(buffer, "linking");
|
||||||
|
buffer.append_byte(LINKING_VERSION);
|
||||||
|
for subsection in self.subsections.iter() {
|
||||||
|
subsection.serialize(buffer);
|
||||||
|
}
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
350
compiler/gen_wasm/src/serialize.rs
Normal file
350
compiler/gen_wasm/src/serialize.rs
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
use bumpalo::collections::vec::Vec;
|
||||||
|
|
||||||
|
/// Write an unsigned integer into the provided buffer in LEB-128 format, returning byte length
|
||||||
|
///
|
||||||
|
/// All integers in Wasm are variable-length encoded, which saves space for small values.
|
||||||
|
/// The most significant bit indicates "more bytes are coming", and the other 7 are payload.
|
||||||
|
macro_rules! encode_uleb128 {
|
||||||
|
($name: ident, $ty: ty) => {
|
||||||
|
fn $name(&mut self, value: $ty) -> usize {
|
||||||
|
let mut x = value;
|
||||||
|
let start_len = self.size();
|
||||||
|
while x >= 0x80 {
|
||||||
|
self.append_byte(0x80 | ((x & 0x7f) as u8));
|
||||||
|
x >>= 7;
|
||||||
|
}
|
||||||
|
self.append_byte(x as u8);
|
||||||
|
self.size() - start_len
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a signed integer into the provided buffer in LEB-128 format, returning byte length
|
||||||
|
macro_rules! encode_sleb128 {
|
||||||
|
($name: ident, $ty: ty) => {
|
||||||
|
fn $name(&mut self, value: $ty) -> usize {
|
||||||
|
let mut x = value;
|
||||||
|
let start_len = self.size();
|
||||||
|
loop {
|
||||||
|
let byte = (x & 0x7f) as u8;
|
||||||
|
x >>= 7;
|
||||||
|
let byte_is_negative = (byte & 0x40) != 0;
|
||||||
|
if ((x == 0 && !byte_is_negative) || (x == -1 && byte_is_negative)) {
|
||||||
|
self.append_byte(byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.append_byte(byte | 0x80);
|
||||||
|
}
|
||||||
|
self.size() - start_len
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! write_unencoded {
|
||||||
|
($name: ident, $ty: ty) => {
|
||||||
|
/// write an unencoded little-endian integer (only used in relocations)
|
||||||
|
fn $name(&mut self, value: $ty) {
|
||||||
|
let mut x = value;
|
||||||
|
let size = std::mem::size_of::<$ty>();
|
||||||
|
for _ in 0..size {
|
||||||
|
self.append_byte((x & 0xff) as u8);
|
||||||
|
x >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! encode_padded_sleb128 {
|
||||||
|
($name: ident, $ty: ty) => {
|
||||||
|
/// write a maximally-padded SLEB128 integer (only used in relocations)
|
||||||
|
fn $name(&mut self, value: $ty) {
|
||||||
|
let mut x = value;
|
||||||
|
let size = (std::mem::size_of::<$ty>() / 4) * 5;
|
||||||
|
for _ in 0..(size - 1) {
|
||||||
|
self.append_byte(0x80 | (x & 0x7f) as u8);
|
||||||
|
x >>= 7;
|
||||||
|
}
|
||||||
|
self.append_byte((x & 0x7f) as u8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SerialBuffer {
|
||||||
|
fn append_byte(&mut self, b: u8);
|
||||||
|
fn append_slice(&mut self, b: &[u8]);
|
||||||
|
fn size(&self) -> usize;
|
||||||
|
|
||||||
|
encode_uleb128!(encode_u32, u32);
|
||||||
|
encode_uleb128!(encode_u64, u64);
|
||||||
|
encode_sleb128!(encode_i32, i32);
|
||||||
|
encode_sleb128!(encode_i64, i64);
|
||||||
|
|
||||||
|
fn reserve_padded_u32(&mut self) -> usize;
|
||||||
|
fn encode_padded_u32(&mut self, value: u32) -> usize;
|
||||||
|
fn overwrite_padded_u32(&mut self, index: usize, value: u32);
|
||||||
|
|
||||||
|
fn encode_f32(&mut self, value: f32) {
|
||||||
|
self.write_unencoded_u32(value.to_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_f64(&mut self, value: f64) {
|
||||||
|
self.write_unencoded_u64(value.to_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
// methods for relocations
|
||||||
|
write_unencoded!(write_unencoded_u32, u32);
|
||||||
|
write_unencoded!(write_unencoded_u64, u64);
|
||||||
|
encode_padded_sleb128!(encode_padded_i32, i32);
|
||||||
|
encode_padded_sleb128!(encode_padded_i64, i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Serialize {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for str {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.encode_u32(self.len() as u32);
|
||||||
|
buffer.append_slice(self.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overwrite_padded_u32_help(buffer: &mut [u8], value: u32) {
|
||||||
|
let mut x = value;
|
||||||
|
for byte in buffer.iter_mut().take(4) {
|
||||||
|
*byte = 0x80 | ((x & 0x7f) as u8);
|
||||||
|
x >>= 7;
|
||||||
|
}
|
||||||
|
buffer[4] = x as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialBuffer for std::vec::Vec<u8> {
|
||||||
|
fn append_byte(&mut self, b: u8) {
|
||||||
|
self.push(b);
|
||||||
|
}
|
||||||
|
fn append_slice(&mut self, b: &[u8]) {
|
||||||
|
self.extend_from_slice(b);
|
||||||
|
}
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
fn reserve_padded_u32(&mut self) -> usize {
|
||||||
|
let index = self.len();
|
||||||
|
self.resize(index + 5, 0xff);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
fn encode_padded_u32(&mut self, value: u32) -> usize {
|
||||||
|
let index = self.len();
|
||||||
|
let new_len = index + 5;
|
||||||
|
self.resize(new_len, 0);
|
||||||
|
overwrite_padded_u32_help(&mut self[index..new_len], value);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
fn overwrite_padded_u32(&mut self, index: usize, value: u32) {
|
||||||
|
overwrite_padded_u32_help(&mut self[index..(index + 5)], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SerialBuffer for Vec<'a, u8> {
|
||||||
|
fn append_byte(&mut self, b: u8) {
|
||||||
|
self.push(b);
|
||||||
|
}
|
||||||
|
fn append_slice(&mut self, b: &[u8]) {
|
||||||
|
self.extend_from_slice(b);
|
||||||
|
}
|
||||||
|
fn size(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
fn reserve_padded_u32(&mut self) -> usize {
|
||||||
|
let index = self.len();
|
||||||
|
self.resize(index + 5, 0xff);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
fn encode_padded_u32(&mut self, value: u32) -> usize {
|
||||||
|
let index = self.len();
|
||||||
|
let new_len = index + 5;
|
||||||
|
self.resize(new_len, 0);
|
||||||
|
overwrite_padded_u32_help(&mut self[index..new_len], value);
|
||||||
|
index
|
||||||
|
}
|
||||||
|
fn overwrite_padded_u32(&mut self, index: usize, value: u32) {
|
||||||
|
overwrite_padded_u32_help(&mut self[index..(index + 5)], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use bumpalo::{self, collections::Vec, Bump};
|
||||||
|
|
||||||
|
fn help_u32<'a>(arena: &'a Bump, value: u32) -> Vec<'a, u8> {
|
||||||
|
let mut buffer = Vec::with_capacity_in(5, arena);
|
||||||
|
buffer.encode_u32(value);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_u32() {
|
||||||
|
let a = &Bump::new();
|
||||||
|
assert_eq!(help_u32(a, 0), &[0]);
|
||||||
|
assert_eq!(help_u32(a, 64), &[64]);
|
||||||
|
assert_eq!(help_u32(a, 0x7f), &[0x7f]);
|
||||||
|
assert_eq!(help_u32(a, 0x80), &[0x80, 0x01]);
|
||||||
|
assert_eq!(help_u32(a, 0x3fff), &[0xff, 0x7f]);
|
||||||
|
assert_eq!(help_u32(a, 0x4000), &[0x80, 0x80, 0x01]);
|
||||||
|
assert_eq!(help_u32(a, u32::MAX), &[0xff, 0xff, 0xff, 0xff, 0x0f]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_u64<'a>(arena: &'a Bump, value: u64) -> Vec<'a, u8> {
|
||||||
|
let mut buffer = Vec::with_capacity_in(10, arena);
|
||||||
|
buffer.encode_u64(value);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_u64() {
|
||||||
|
let a = &Bump::new();
|
||||||
|
assert_eq!(help_u64(a, 0), &[0]);
|
||||||
|
assert_eq!(help_u64(a, 64), &[64]);
|
||||||
|
assert_eq!(help_u64(a, 0x7f), &[0x7f]);
|
||||||
|
assert_eq!(help_u64(a, 0x80), &[0x80, 0x01]);
|
||||||
|
assert_eq!(help_u64(a, 0x3fff), &[0xff, 0x7f]);
|
||||||
|
assert_eq!(help_u64(a, 0x4000), &[0x80, 0x80, 0x01]);
|
||||||
|
assert_eq!(
|
||||||
|
help_u64(a, u64::MAX),
|
||||||
|
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_i32<'a>(arena: &'a Bump, value: i32) -> Vec<'a, u8> {
|
||||||
|
let mut buffer = Vec::with_capacity_in(5, arena);
|
||||||
|
buffer.encode_i32(value);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_i32() {
|
||||||
|
let a = &Bump::new();
|
||||||
|
assert_eq!(help_i32(a, 0), &[0]);
|
||||||
|
assert_eq!(help_i32(a, 1), &[1]);
|
||||||
|
assert_eq!(help_i32(a, -1), &[0x7f]);
|
||||||
|
assert_eq!(help_i32(a, 63), &[63]);
|
||||||
|
assert_eq!(help_i32(a, 64), &[0xc0, 0x0]);
|
||||||
|
assert_eq!(help_i32(a, -64), &[0x40]);
|
||||||
|
assert_eq!(help_i32(a, -65), &[0xbf, 0x7f]);
|
||||||
|
assert_eq!(help_i32(a, i32::MAX), &[0xff, 0xff, 0xff, 0xff, 0x07]);
|
||||||
|
assert_eq!(help_i32(a, i32::MIN), &[0x80, 0x80, 0x80, 0x80, 0x78]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_i64<'a>(arena: &'a Bump, value: i64) -> Vec<'a, u8> {
|
||||||
|
let mut buffer = Vec::with_capacity_in(10, arena);
|
||||||
|
buffer.encode_i64(value);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_i64() {
|
||||||
|
let a = &Bump::new();
|
||||||
|
assert_eq!(help_i64(a, 0), &[0]);
|
||||||
|
assert_eq!(help_i64(a, 1), &[1]);
|
||||||
|
assert_eq!(help_i64(a, -1), &[0x7f]);
|
||||||
|
assert_eq!(help_i64(a, 63), &[63]);
|
||||||
|
assert_eq!(help_i64(a, 64), &[0xc0, 0x0]);
|
||||||
|
assert_eq!(help_i64(a, -64), &[0x40]);
|
||||||
|
assert_eq!(help_i64(a, -65), &[0xbf, 0x7f]);
|
||||||
|
assert_eq!(
|
||||||
|
help_i64(a, i64::MAX),
|
||||||
|
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
help_i64(a, i64::MIN),
|
||||||
|
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_overwrite_u32_padded() {
|
||||||
|
let mut buffer = [0, 0, 0, 0, 0];
|
||||||
|
|
||||||
|
overwrite_padded_u32_help(&mut buffer, u32::MAX);
|
||||||
|
assert_eq!(buffer, [0xff, 0xff, 0xff, 0xff, 0x0f]);
|
||||||
|
|
||||||
|
overwrite_padded_u32_help(&mut buffer, 0);
|
||||||
|
assert_eq!(buffer, [0x80, 0x80, 0x80, 0x80, 0x00]);
|
||||||
|
|
||||||
|
overwrite_padded_u32_help(&mut buffer, 127);
|
||||||
|
assert_eq!(buffer, [0xff, 0x80, 0x80, 0x80, 0x00]);
|
||||||
|
|
||||||
|
overwrite_padded_u32_help(&mut buffer, 128);
|
||||||
|
assert_eq!(buffer, [0x80, 0x81, 0x80, 0x80, 0x00]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_unencoded_u32() {
|
||||||
|
let mut buffer = std::vec::Vec::with_capacity(4);
|
||||||
|
|
||||||
|
buffer.write_unencoded_u32(0);
|
||||||
|
assert_eq!(buffer, &[0, 0, 0, 0]);
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
buffer.write_unencoded_u32(u32::MAX);
|
||||||
|
assert_eq!(buffer, &[0xff, 0xff, 0xff, 0xff]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_unencoded_u64() {
|
||||||
|
let mut buffer = std::vec::Vec::with_capacity(8);
|
||||||
|
|
||||||
|
buffer.write_unencoded_u64(0);
|
||||||
|
assert_eq!(buffer, &[0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
buffer.write_unencoded_u64(u64::MAX);
|
||||||
|
assert_eq!(buffer, &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_pad_i32(val: i32) -> std::vec::Vec<u8> {
|
||||||
|
let mut buffer = std::vec::Vec::with_capacity(5);
|
||||||
|
buffer.encode_padded_i32(val);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_padded_i32() {
|
||||||
|
assert_eq!(help_pad_i32(0), &[0x80, 0x80, 0x80, 0x80, 0x00]);
|
||||||
|
assert_eq!(help_pad_i32(1), &[0x81, 0x80, 0x80, 0x80, 0x00]);
|
||||||
|
assert_eq!(help_pad_i32(-1), &[0xff, 0xff, 0xff, 0xff, 0x7f]);
|
||||||
|
assert_eq!(help_pad_i32(i32::MAX), &[0xff, 0xff, 0xff, 0xff, 0x07]);
|
||||||
|
assert_eq!(help_pad_i32(i32::MIN), &[0x80, 0x80, 0x80, 0x80, 0x78]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help_pad_i64(val: i64) -> std::vec::Vec<u8> {
|
||||||
|
let mut buffer = std::vec::Vec::with_capacity(10);
|
||||||
|
buffer.encode_padded_i64(val);
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_padded_i64() {
|
||||||
|
assert_eq!(
|
||||||
|
help_pad_i64(0),
|
||||||
|
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
help_pad_i64(1),
|
||||||
|
&[0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
help_pad_i64(-1),
|
||||||
|
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
help_pad_i64(i64::MAX),
|
||||||
|
&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00],
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
help_pad_i64(i64::MIN),
|
||||||
|
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,916 +0,0 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate indoc;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod helpers;
|
|
||||||
|
|
||||||
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
|
||||||
mod wasm_records {
|
|
||||||
// #[test]
|
|
||||||
// fn basic_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { y: 17, x: 15, z: 19 }.x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: 17, z: 19 }.y
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 17,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: 17, z: 19 }.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn nested_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 12,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 2,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn f64_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
|
||||||
//
|
|
||||||
// rec.x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15.1,
|
|
||||||
// f64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
|
||||||
//
|
|
||||||
// rec.y
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 17.2,
|
|
||||||
// f64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
|
||||||
//
|
|
||||||
// rec.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19.3,
|
|
||||||
// f64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn fn_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// getRec = \x -> { y: 17, x, z: 19 }
|
|
||||||
|
|
||||||
// (getRec 15).x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { x: 15, y: 17, z: 19 }
|
|
||||||
|
|
||||||
// rec.y
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 17,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { x: 15, y: 17, z: 19 }
|
|
||||||
|
|
||||||
// rec.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { x: 15, y: 17, z: 19 }
|
|
||||||
|
|
||||||
// rec.z + rec.x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 34,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn def_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { y: 17, x: 15, z: 19 }
|
|
||||||
//
|
|
||||||
// rec.x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 15,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { x: 15, y: 17, z: 19 }
|
|
||||||
//
|
|
||||||
// rec.y
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 17,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { x: 15, y: 17, z: 19 }
|
|
||||||
//
|
|
||||||
// rec.z
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn when_on_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// when { x: 0x2 } is
|
|
||||||
// { x } -> x + 3
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 5,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn when_record_with_guard_pattern() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// when { x: 0x2, y: 3.14 } is
|
|
||||||
// { x: var } -> var + 3
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 5,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn let_with_record_pattern() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { x } = { x: 0x2, y: 3.14 }
|
|
||||||
//
|
|
||||||
// x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 2,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn record_guard_pattern() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// when { x: 0x2, y: 3.14 } is
|
|
||||||
// { x: 0x4 } -> 5
|
|
||||||
// { x } -> x + 3
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 5,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[test]
|
|
||||||
// fn twice_record_access() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// x = {a: 0x2, b: 0x3 }
|
|
||||||
//
|
|
||||||
// x.a + x.b
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 5,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// #[test]
|
|
||||||
// fn empty_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// v = {}
|
|
||||||
//
|
|
||||||
// v
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// (),
|
|
||||||
// ()
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i64_record1_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
3,
|
|
||||||
i64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i64_record2_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3, y: 5 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3, 5),
|
|
||||||
(i64, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i64_record3_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3, y: 5, z: 17 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3, 5, 17),
|
|
||||||
(i64, i64, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f64_record2_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3.1, y: 5.1 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3.1, 5.1),
|
|
||||||
(f64, f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn f64_record3_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3.1, y: 5.1, z: 17.1 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3.1, 5.1, 17.1),
|
|
||||||
(f64, f64, f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bool_record4_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
record : { a : Bool, b : Bool, c : Bool, d : Bool }
|
|
||||||
record = { a: True, b: False, c : False, d : True }
|
|
||||||
|
|
||||||
record
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[true, false, false, true],
|
|
||||||
[bool; 4]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i64_record9_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[3, 5, 17, 1, 9, 12, 13, 14, 15],
|
|
||||||
[i64; 9]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn bool_literal() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
x : Bool
|
|
||||||
x = True
|
|
||||||
|
|
||||||
x
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
bool
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_when_use_default() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
// f = \r ->
|
|
||||||
// when r is
|
|
||||||
// { x: Blue, y ? 3 } -> y
|
|
||||||
// { x: Red, y ? 5 } -> y
|
|
||||||
|
|
||||||
// main =
|
|
||||||
// a = f { x: Blue, y: 7 }
|
|
||||||
// b = f { x: Blue }
|
|
||||||
// c = f { x: Red, y: 11 }
|
|
||||||
// d = f { x: Red }
|
|
||||||
|
|
||||||
// a * b * c * d
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 3 * 5 * 7 * 11,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_when_use_default_nested() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \r ->
|
|
||||||
// when r is
|
|
||||||
// { x: Blue, y ? 3 } -> y
|
|
||||||
// { x: Red, y ? 5 } -> y
|
|
||||||
|
|
||||||
// a = f { x: Blue, y: 7 }
|
|
||||||
// b = f { x: Blue }
|
|
||||||
// c = f { x: Red, y: 11 }
|
|
||||||
// d = f { x: Red }
|
|
||||||
|
|
||||||
// a * b * c * d
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 3 * 5 * 7 * 11,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_when_no_use_default() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
// f = \r ->
|
|
||||||
// { x ? 10, y } = r
|
|
||||||
// x + y
|
|
||||||
|
|
||||||
// main =
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_when_no_use_default_nested() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \r ->
|
|
||||||
// { x ? 10, y } = r
|
|
||||||
// x + y
|
|
||||||
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_let_use_default() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
// f = \r ->
|
|
||||||
// { x ? 10, y } = r
|
|
||||||
// x + y
|
|
||||||
|
|
||||||
// main =
|
|
||||||
// f { y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_let_no_use_default() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
// f = \r ->
|
|
||||||
// { x ? 10, y } = r
|
|
||||||
// x + y
|
|
||||||
|
|
||||||
// main =
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_let_no_use_default_nested() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \r ->
|
|
||||||
// { x ? 10, y } = r
|
|
||||||
// x + y
|
|
||||||
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_function_use_default() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \{ x ? 10, y } -> x + y
|
|
||||||
|
|
||||||
// f { y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 19,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// #[ignore]
|
|
||||||
// fn optional_field_function_no_use_default() {
|
|
||||||
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// app "test" provides [ main ] to "./platform"
|
|
||||||
|
|
||||||
// f = \{ x ? 10, y } -> x + y
|
|
||||||
|
|
||||||
// main =
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// #[ignore]
|
|
||||||
// fn optional_field_function_no_use_default_nested() {
|
|
||||||
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \{ x ? 10, y } -> x + y
|
|
||||||
|
|
||||||
// f { x: 4, y: 9 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 13,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_singleton_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// when { x : 4 } is
|
|
||||||
// { x ? 3 } -> x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 4,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn optional_field_empty_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// when { } is
|
|
||||||
// { x ? 3 } -> x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 3,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_3() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ x: 3, y: 5, z: 4 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3, 5, 4),
|
|
||||||
(i64, i64, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_4() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3, b: 5, c: 4, d: 2 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[3, 5, 4, 2],
|
|
||||||
[i64; 4]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_5() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[3, 5, 4, 2, 1],
|
|
||||||
[i64; 5]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_6() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[3, 5, 4, 2, 1, 7],
|
|
||||||
[i64; 6]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_7() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
[3, 5, 4, 2, 1, 7, 8],
|
|
||||||
[i64; 7]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_float_int() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 3.14, b: 0x1 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(3.14, 0x1),
|
|
||||||
(f64, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_int_float() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 0x1, b: 3.14 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(0x1, 3.14),
|
|
||||||
(i64, f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_float_float() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 6.28, b: 3.14 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(6.28, 3.14),
|
|
||||||
(f64, f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn return_record_float_float_float() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
{ a: 6.28, b: 3.14, c: 0.1 }
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(6.28, 3.14, 0.1),
|
|
||||||
(f64, f64, f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn return_nested_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// (0x0, (6.28, 3.14, 0.1)),
|
|
||||||
// (i64, (f64, f64, f64))
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn accessor() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 7,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn accessor_single_element_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// .foo { foo: 4 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 4,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn update_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { foo: 42, bar: 6 }
|
|
||||||
|
|
||||||
// { rec & foo: rec.foo + 1 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// (6, 43),
|
|
||||||
// (i64, i64)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn update_single_element_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// rec = { foo: 42}
|
|
||||||
|
|
||||||
// { rec & foo: rec.foo + 1 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 43,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn booleans_in_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
|
||||||
// (true, true),
|
|
||||||
// (bool, bool)
|
|
||||||
// );
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
|
|
||||||
// (false, true),
|
|
||||||
// (bool, bool)
|
|
||||||
// );
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
|
|
||||||
// (true, false),
|
|
||||||
// (bool, bool)
|
|
||||||
// );
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
|
|
||||||
// (false, false),
|
|
||||||
// (bool, bool)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn alignment_in_record() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
|
|
||||||
// (32i64, true, 2u8),
|
|
||||||
// (i64, bool, u8)
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stack_memory_return_from_branch() {
|
|
||||||
// stack memory pointer should end up in the right place after returning from a branch
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
stackMemoryJunk = { x: 999, y: 111 }
|
|
||||||
if True then
|
|
||||||
{ x: 123, y: 321 }
|
|
||||||
else
|
|
||||||
stackMemoryJunk
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
(123, 321),
|
|
||||||
(i64, i64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn blue_and_present() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \r ->
|
|
||||||
// when r is
|
|
||||||
// { x: Blue, y ? 3 } -> y
|
|
||||||
// { x: Red, y ? 5 } -> y
|
|
||||||
|
|
||||||
// f { x: Blue, y: 7 }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 7,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn blue_and_absent() {
|
|
||||||
// assert_evals_to!(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// f = \r ->
|
|
||||||
// when r is
|
|
||||||
// { x: Blue, y ? 3 } -> y
|
|
||||||
// { x: Red, y ? 5 } -> y
|
|
||||||
|
|
||||||
// f { x: Blue }
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// 3,
|
|
||||||
// i64
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1004,7 +1004,7 @@ impl ModuleTiming {
|
||||||
.checked_sub(*read_roc_file)
|
.checked_sub(*read_roc_file)
|
||||||
};
|
};
|
||||||
|
|
||||||
calculate(end_time.duration_since(*start_time)).unwrap_or_else(Duration::default)
|
calculate(end_time.duration_since(*start_time)).unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,4 @@ snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub enum LowLevel {
|
||||||
ListMap,
|
ListMap,
|
||||||
ListMap2,
|
ListMap2,
|
||||||
ListMap3,
|
ListMap3,
|
||||||
|
ListMap4,
|
||||||
ListMapWithIndex,
|
ListMapWithIndex,
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalk,
|
ListWalk,
|
||||||
|
@ -211,6 +212,7 @@ macro_rules! higher_order {
|
||||||
ListMap
|
ListMap
|
||||||
| ListMap2
|
| ListMap2
|
||||||
| ListMap3
|
| ListMap3
|
||||||
|
| ListMap4
|
||||||
| ListMapWithIndex
|
| ListMapWithIndex
|
||||||
| ListKeepIf
|
| ListKeepIf
|
||||||
| ListWalk
|
| ListWalk
|
||||||
|
@ -243,6 +245,7 @@ impl LowLevel {
|
||||||
ListMap => 1,
|
ListMap => 1,
|
||||||
ListMap2 => 2,
|
ListMap2 => 2,
|
||||||
ListMap3 => 3,
|
ListMap3 => 3,
|
||||||
|
ListMap4 => 4,
|
||||||
ListMapWithIndex => 1,
|
ListMapWithIndex => 1,
|
||||||
ListKeepIf => 1,
|
ListKeepIf => 1,
|
||||||
ListWalk => 2,
|
ListWalk => 2,
|
||||||
|
|
|
@ -1060,6 +1060,7 @@ define_builtins! {
|
||||||
37 LIST_MIN_LT: "#minlt"
|
37 LIST_MIN_LT: "#minlt"
|
||||||
38 LIST_MAX: "max"
|
38 LIST_MAX: "max"
|
||||||
39 LIST_MAX_GT: "#maxGt"
|
39 LIST_MAX_GT: "#maxGt"
|
||||||
|
40 LIST_MAP4: "map4"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -29,7 +29,6 @@ roc_builtins = { path = "../builtins" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -561,6 +561,84 @@ fn build_tuple_type(builder: &mut impl TypeContext, layouts: &[Layout]) -> Resul
|
||||||
builder.add_tuple_type(&field_types)
|
builder.add_tuple_type(&field_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum KeepResult {
|
||||||
|
Errs = ERR_TAG_ID,
|
||||||
|
Oks = OK_TAG_ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeepResult {
|
||||||
|
fn invert(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
KeepResult::Errs => KeepResult::Oks,
|
||||||
|
KeepResult::Oks => KeepResult::Errs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum ResultRepr<'a> {
|
||||||
|
Int1,
|
||||||
|
NonRecursive { err: Layout<'a>, ok: Layout<'a> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ResultRepr<'a> {
|
||||||
|
fn from_layout(layout: &Layout<'a>) -> Self {
|
||||||
|
match layout {
|
||||||
|
Layout::Union(UnionLayout::NonRecursive(tags)) => ResultRepr::NonRecursive {
|
||||||
|
err: tags[ERR_TAG_ID as usize][0],
|
||||||
|
ok: tags[OK_TAG_ID as usize][0],
|
||||||
|
},
|
||||||
|
Layout::Builtin(Builtin::Int1) => ResultRepr::Int1,
|
||||||
|
other => unreachable!("unexpected layout: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unwrap(
|
||||||
|
&self,
|
||||||
|
builder: &mut FuncDefBuilder,
|
||||||
|
block: BlockId,
|
||||||
|
err_or_ok: ValueId,
|
||||||
|
keep_tag_id: u32,
|
||||||
|
) -> Result<ValueId> {
|
||||||
|
match self {
|
||||||
|
ResultRepr::NonRecursive { .. } => {
|
||||||
|
let unwrapped = builder.add_unwrap_union(block, err_or_ok, keep_tag_id)?;
|
||||||
|
|
||||||
|
builder.add_get_tuple_field(block, unwrapped, 0)
|
||||||
|
}
|
||||||
|
ResultRepr::Int1 => builder.add_make_tuple(block, &[]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_loop(
|
||||||
|
builder: &mut FuncDefBuilder,
|
||||||
|
block: BlockId,
|
||||||
|
state_type: TypeId,
|
||||||
|
init_state: ValueId,
|
||||||
|
make_body: impl for<'a> FnOnce(&'a mut FuncDefBuilder, BlockId, ValueId) -> Result<ValueId>,
|
||||||
|
) -> Result<ValueId> {
|
||||||
|
let sub_block = builder.add_block();
|
||||||
|
let (loop_cont, loop_arg) = builder.declare_continuation(sub_block, state_type, state_type)?;
|
||||||
|
let body = builder.add_block();
|
||||||
|
let ret_branch = builder.add_block();
|
||||||
|
let loop_branch = builder.add_block();
|
||||||
|
let new_state = make_body(builder, loop_branch, loop_arg)?;
|
||||||
|
let unreachable = builder.add_jump(loop_branch, loop_cont, new_state, state_type)?;
|
||||||
|
let result = builder.add_choice(
|
||||||
|
body,
|
||||||
|
&[
|
||||||
|
BlockExpr(ret_branch, loop_arg),
|
||||||
|
BlockExpr(loop_branch, unreachable),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
builder.define_continuation(loop_cont, BlockExpr(body, result))?;
|
||||||
|
let unreachable = builder.add_jump(sub_block, loop_cont, init_state, state_type)?;
|
||||||
|
builder.add_sub_block(block, BlockExpr(sub_block, unreachable))
|
||||||
|
}
|
||||||
|
|
||||||
fn call_spec(
|
fn call_spec(
|
||||||
builder: &mut FuncDefBuilder,
|
builder: &mut FuncDefBuilder,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
|
@ -613,6 +691,7 @@ fn call_spec(
|
||||||
HigherOrderLowLevel {
|
HigherOrderLowLevel {
|
||||||
specialization_id,
|
specialization_id,
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
|
update_mode,
|
||||||
op,
|
op,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -620,194 +699,405 @@ fn call_spec(
|
||||||
function_env,
|
function_env,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
use crate::low_level::HigherOrder::*;
|
||||||
|
|
||||||
let array = specialization_id.to_bytes();
|
let array = specialization_id.to_bytes();
|
||||||
let spec_var = CalleeSpecVar(&array);
|
let spec_var = CalleeSpecVar(&array);
|
||||||
|
|
||||||
|
let mode = update_mode.to_bytes();
|
||||||
|
let update_mode_var = UpdateModeVar(&mode);
|
||||||
|
|
||||||
let it = arg_layouts.iter().copied();
|
let it = arg_layouts.iter().copied();
|
||||||
let bytes = func_name_bytes_help(*function_name, it, *ret_layout);
|
let bytes = func_name_bytes_help(*function_name, it, *ret_layout);
|
||||||
let name = FuncName(&bytes);
|
let name = FuncName(&bytes);
|
||||||
let module = MOD_APP;
|
let module = MOD_APP;
|
||||||
|
|
||||||
use crate::low_level::HigherOrder::*;
|
let closure_env = env.symbols[function_env];
|
||||||
|
|
||||||
|
macro_rules! call_function {
|
||||||
|
($builder: expr, $block:expr, [$($arg:expr),+ $(,)?]) => {{
|
||||||
|
let argument = if closure_env_layout.is_none() {
|
||||||
|
$builder.add_make_tuple($block, &[$($arg),+])?
|
||||||
|
} else {
|
||||||
|
$builder.add_make_tuple($block, &[$($arg),+, closure_env])?
|
||||||
|
};
|
||||||
|
|
||||||
|
$builder.add_call($block, spec_var, module, name, argument)?
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
DictWalk { xs, state } => {
|
DictWalk { xs, state } => {
|
||||||
let dict = env.symbols[xs];
|
let dict = env.symbols[xs];
|
||||||
let state = env.symbols[state];
|
let state = env.symbols[state];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell = builder.add_get_tuple_field(block, dict, DICT_CELL_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, dict, DICT_BAG_INDEX)?;
|
||||||
|
|
||||||
let first = builder.add_bag_get(block, bag)?;
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
let key = builder.add_get_tuple_field(block, first, 0)?;
|
let key = builder.add_get_tuple_field(block, element, 0)?;
|
||||||
let val = builder.add_get_tuple_field(block, first, 1)?;
|
let val = builder.add_get_tuple_field(block, element, 1)?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let new_state = call_function!(builder, block, [state, key, val]);
|
||||||
builder.add_make_tuple(block, &[state, key, val])?
|
|
||||||
} else {
|
Ok(new_state)
|
||||||
builder.add_make_tuple(block, &[state, key, val, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let state_layout = arg_layouts[0];
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
let init_state = state;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListWalk { xs, state }
|
ListWalk { xs, state } | ListWalkBackwards { xs, state } => {
|
||||||
| ListWalkBackwards { xs, state }
|
|
||||||
| ListWalkUntil { xs, state } => {
|
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
let state = env.symbols[state];
|
let state = env.symbols[state];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
let first = builder.add_bag_get(block, bag)?;
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let new_state = call_function!(builder, block, [state, element]);
|
||||||
builder.add_make_tuple(block, &[state, first])?
|
|
||||||
} else {
|
Ok(new_state)
|
||||||
builder.add_make_tuple(block, &[state, first, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let state_layout = arg_layouts[0];
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
let init_state = state;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
}
|
||||||
|
ListWalkUntil { xs, state } => {
|
||||||
|
let list = env.symbols[xs];
|
||||||
|
let state = env.symbols[state];
|
||||||
|
|
||||||
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let continue_or_stop = call_function!(builder, block, [state, element]);
|
||||||
|
|
||||||
|
// just assume it is a continue
|
||||||
|
let unwrapped = builder.add_unwrap_union(block, continue_or_stop, 0)?;
|
||||||
|
let new_state = builder.add_get_tuple_field(block, unwrapped, 0)?;
|
||||||
|
|
||||||
|
Ok(new_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_layout = arg_layouts[0];
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
let init_state = state;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMapWithIndex { xs } => {
|
ListMapWithIndex { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
let first = builder.add_bag_get(block, bag)?;
|
let element = builder.add_bag_get(block, input_bag)?;
|
||||||
let index = builder.add_make_tuple(block, &[])?;
|
let index = builder.add_make_tuple(block, &[])?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let new_element = call_function!(builder, block, [index, element]);
|
||||||
builder.add_make_tuple(block, &[index, first])?
|
|
||||||
} else {
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
builder.add_make_tuple(block, &[index, first, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMap { xs } => {
|
ListMap { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
let element = builder.add_bag_get(block, input_bag)?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let new_element = call_function!(builder, block, [element]);
|
||||||
builder.add_make_tuple(block, &[elem1])?
|
|
||||||
} else {
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
builder.add_make_tuple(block, &[elem1, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListSortWith { xs } => {
|
ListSortWith { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag1 = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell1 = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, state, LIST_BAG_INDEX)?;
|
||||||
|
let cell = builder.add_get_tuple_field(block, state, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
let element_1 = builder.add_bag_get(block, bag)?;
|
||||||
|
let element_2 = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let _ = call_function!(builder, block, [element_1, element_2]);
|
||||||
builder.add_make_tuple(block, &[elem1, elem1])?
|
|
||||||
} else {
|
builder.add_update(block, update_mode_var, cell)?;
|
||||||
builder.add_make_tuple(block, &[elem1, elem1, closure_env])?
|
|
||||||
|
let new_cell = builder.add_new_heap_cell(block)?;
|
||||||
|
builder.add_make_tuple(block, &[new_cell, bag])
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(&arg_layouts[0]));
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
let init_state = list;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListMap2 { xs, ys } => {
|
ListMap2 { xs, ys } => {
|
||||||
let list1 = env.symbols[xs];
|
let list1 = env.symbols[xs];
|
||||||
let list2 = env.symbols[ys];
|
let list2 = env.symbols[ys];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
let input_bag_1 =
|
||||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
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 bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||||
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||||
let elem2 = builder.add_bag_get(block, bag2)?;
|
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let new_element = call_function!(builder, block, [element_1, element_2]);
|
||||||
builder.add_make_tuple(block, &[elem1, elem2])?
|
|
||||||
} else {
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
builder.add_make_tuple(block, &[elem1, elem2, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||||
|
let state_type = layout_spec(builder, &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 } => {
|
ListMap3 { xs, ys, zs } => {
|
||||||
let list1 = env.symbols[xs];
|
let list1 = env.symbols[xs];
|
||||||
let list2 = env.symbols[ys];
|
let list2 = env.symbols[ys];
|
||||||
let list3 = env.symbols[zs];
|
let list3 = env.symbols[zs];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag1 = builder.add_get_tuple_field(block, list1, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
let _cell1 = builder.add_get_tuple_field(block, list1, LIST_CELL_INDEX)?;
|
let input_bag_1 =
|
||||||
let elem1 = builder.add_bag_get(block, bag1)?;
|
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 bag2 = builder.add_get_tuple_field(block, list2, LIST_BAG_INDEX)?;
|
let element_1 = builder.add_bag_get(block, input_bag_1)?;
|
||||||
let _cell2 = builder.add_get_tuple_field(block, list2, LIST_CELL_INDEX)?;
|
let element_2 = builder.add_bag_get(block, input_bag_2)?;
|
||||||
let elem2 = builder.add_bag_get(block, bag2)?;
|
let element_3 = builder.add_bag_get(block, input_bag_3)?;
|
||||||
|
|
||||||
let bag3 = builder.add_get_tuple_field(block, list3, LIST_BAG_INDEX)?;
|
let new_element =
|
||||||
let _cell3 = builder.add_get_tuple_field(block, list3, LIST_CELL_INDEX)?;
|
call_function!(builder, block, [element_1, element_2, element_3]);
|
||||||
let elem3 = builder.add_bag_get(block, bag3)?;
|
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
list_append(builder, block, update_mode_var, state, new_element)
|
||||||
builder.add_make_tuple(block, &[elem1, elem2, elem3])?
|
|
||||||
} else {
|
|
||||||
builder.add_make_tuple(block, &[elem1, elem2, elem3, closure_env])?
|
|
||||||
};
|
};
|
||||||
builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
|
let output_element_type = layout_spec(builder, ret_layout)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||||
|
let state_type = layout_spec(builder, &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(builder, ret_layout)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(ret_layout));
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
ListKeepIf { xs } | ListKeepOks { xs } | ListKeepErrs { xs } => {
|
ListKeepIf { xs } => {
|
||||||
let list = env.symbols[xs];
|
let list = env.symbols[xs];
|
||||||
let closure_env = env.symbols[function_env];
|
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
// let _cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, state, LIST_BAG_INDEX)?;
|
||||||
|
let cell = builder.add_get_tuple_field(block, state, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
let first = builder.add_bag_get(block, bag)?;
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
let argument = if closure_env_layout.is_none() {
|
let _ = call_function!(builder, block, [element]);
|
||||||
builder.add_make_tuple(block, &[first])?
|
|
||||||
} else {
|
// NOTE: we assume the element is not kept
|
||||||
builder.add_make_tuple(block, &[first, closure_env])?
|
builder.add_update(block, update_mode_var, cell)?;
|
||||||
|
|
||||||
|
let removed = builder.add_bag_remove(block, bag)?;
|
||||||
|
|
||||||
|
// decrement the removed element
|
||||||
|
let removed_element = builder.add_get_tuple_field(block, removed, 1)?;
|
||||||
|
builder.add_recursive_touch(block, removed_element)?;
|
||||||
|
|
||||||
|
let new_bag = builder.add_get_tuple_field(block, removed, 0)?;
|
||||||
|
let new_cell = builder.add_new_heap_cell(block)?;
|
||||||
|
|
||||||
|
builder.add_make_tuple(block, &[new_cell, new_bag])
|
||||||
};
|
};
|
||||||
let result = builder.add_call(block, spec_var, module, name, argument)?;
|
|
||||||
let unit = builder.add_tuple_type(&[])?;
|
let state_layout = Layout::Builtin(Builtin::List(&arg_layouts[0]));
|
||||||
builder.add_unknown_with(block, &[result], unit)?;
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
let init_state = list;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
|
}
|
||||||
|
ListKeepOks { xs } | ListKeepErrs { xs } => {
|
||||||
|
let list = env.symbols[xs];
|
||||||
|
|
||||||
|
let keep_result = match op {
|
||||||
|
ListKeepOks { .. } => KeepResult::Oks,
|
||||||
|
ListKeepErrs { .. } => KeepResult::Errs,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result_repr = ResultRepr::from_layout(ret_layout);
|
||||||
|
|
||||||
|
let output_element_layout = match (keep_result, result_repr) {
|
||||||
|
(KeepResult::Errs, ResultRepr::NonRecursive { err, .. }) => err,
|
||||||
|
(KeepResult::Oks, ResultRepr::NonRecursive { ok, .. }) => ok,
|
||||||
|
(_, ResultRepr::Int1) => Layout::Struct(&[]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
|
||||||
|
let element = builder.add_bag_get(block, bag)?;
|
||||||
|
|
||||||
|
let err_or_ok = call_function!(builder, block, [element]);
|
||||||
|
|
||||||
|
let kept_branch = builder.add_block();
|
||||||
|
let not_kept_branch = builder.add_block();
|
||||||
|
|
||||||
|
let element_kept = {
|
||||||
|
let block = kept_branch;
|
||||||
|
|
||||||
|
// a Result can be represented as a Int1
|
||||||
|
let new_element = result_repr.unwrap(
|
||||||
|
builder,
|
||||||
|
block,
|
||||||
|
err_or_ok,
|
||||||
|
keep_result as u32,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
list_append(builder, block, update_mode_var, state, new_element)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let element_not_kept = {
|
||||||
|
let block = not_kept_branch;
|
||||||
|
|
||||||
|
// a Result can be represented as a Int1
|
||||||
|
let dropped_element = result_repr.unwrap(
|
||||||
|
builder,
|
||||||
|
block,
|
||||||
|
err_or_ok,
|
||||||
|
keep_result.invert() as u32,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// decrement the element we will not keep
|
||||||
|
builder.add_recursive_touch(block, dropped_element)?;
|
||||||
|
|
||||||
|
state
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.add_choice(
|
||||||
|
block,
|
||||||
|
&[
|
||||||
|
BlockExpr(not_kept_branch, element_not_kept),
|
||||||
|
BlockExpr(kept_branch, element_kept),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_element_type = layout_spec(builder, &output_element_layout)?;
|
||||||
|
let init_state = new_list(builder, block, output_element_type)?;
|
||||||
|
|
||||||
|
let state_layout = Layout::Builtin(Builtin::List(&output_element_layout));
|
||||||
|
let state_type = layout_spec(builder, &state_layout)?;
|
||||||
|
|
||||||
|
add_loop(builder, block, state_type, init_state, loop_body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO overly pessimstic
|
|
||||||
// filter_map because one of the arguments is a function name, which
|
|
||||||
// is not defined in the env
|
|
||||||
let arguments: Vec<_> = call
|
|
||||||
.arguments
|
|
||||||
.iter()
|
|
||||||
.filter_map(|symbol| env.symbols.get(symbol))
|
|
||||||
.copied()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let result_type = layout_spec(builder, layout)?;
|
|
||||||
|
|
||||||
builder.add_unknown_with(block, &arguments, result_type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list_append(
|
||||||
|
builder: &mut FuncDefBuilder,
|
||||||
|
block: BlockId,
|
||||||
|
update_mode_var: UpdateModeVar,
|
||||||
|
list: ValueId,
|
||||||
|
to_insert: ValueId,
|
||||||
|
) -> Result<ValueId> {
|
||||||
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
|
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
let _unit = builder.add_update(block, update_mode_var, cell)?;
|
||||||
|
|
||||||
|
let new_bag = builder.add_bag_insert(block, bag, to_insert)?;
|
||||||
|
|
||||||
|
let new_cell = builder.add_new_heap_cell(block)?;
|
||||||
|
builder.add_make_tuple(block, &[new_cell, new_bag])
|
||||||
|
}
|
||||||
|
|
||||||
fn lowlevel_spec(
|
fn lowlevel_spec(
|
||||||
builder: &mut FuncDefBuilder,
|
builder: &mut FuncDefBuilder,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
|
@ -883,6 +1173,10 @@ fn lowlevel_spec(
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
||||||
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
||||||
|
// decrement the overwritten element
|
||||||
|
let overwritten = builder.add_bag_get(block, bag)?;
|
||||||
|
let _unit = builder.add_recursive_touch(block, overwritten)?;
|
||||||
|
|
||||||
let _unit = builder.add_update(block, update_mode_var, cell)?;
|
let _unit = builder.add_update(block, update_mode_var, cell)?;
|
||||||
|
|
||||||
builder.add_bag_insert(block, bag, to_insert)?;
|
builder.add_bag_insert(block, bag, to_insert)?;
|
||||||
|
@ -916,16 +1210,7 @@ fn lowlevel_spec(
|
||||||
let list = env.symbols[&arguments[0]];
|
let list = env.symbols[&arguments[0]];
|
||||||
let to_insert = env.symbols[&arguments[1]];
|
let to_insert = env.symbols[&arguments[1]];
|
||||||
|
|
||||||
let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
|
list_append(builder, block, update_mode_var, list, to_insert)
|
||||||
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
|
||||||
|
|
||||||
let _unit = builder.add_update(block, update_mode_var, cell)?;
|
|
||||||
|
|
||||||
// TODO new heap cell
|
|
||||||
builder.add_bag_insert(block, bag, to_insert)?;
|
|
||||||
|
|
||||||
let new_cell = builder.add_new_heap_cell(block)?;
|
|
||||||
builder.add_make_tuple(block, &[new_cell, bag])
|
|
||||||
}
|
}
|
||||||
StrToUtf8 => {
|
StrToUtf8 => {
|
||||||
let string = env.symbols[&arguments[0]];
|
let string = env.symbols[&arguments[0]];
|
||||||
|
@ -1425,8 +1710,8 @@ fn static_list_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||||
builder.add_tuple_type(&[cell, bag])
|
builder.add_tuple_type(&[cell, bag])
|
||||||
}
|
}
|
||||||
|
|
||||||
// const OK_TAG_ID: u8 = 1u8;
|
const OK_TAG_ID: u32 = 1;
|
||||||
// const ERR_TAG_ID: u8 = 0u8;
|
const ERR_TAG_ID: u32 = 0;
|
||||||
|
|
||||||
const LIST_CELL_INDEX: u32 = 0;
|
const LIST_CELL_INDEX: u32 = 0;
|
||||||
const LIST_BAG_INDEX: u32 = 1;
|
const LIST_BAG_INDEX: u32 = 1;
|
||||||
|
|
|
@ -651,6 +651,21 @@ impl<'a> BorrowInfState<'a> {
|
||||||
self.own_var(*zs);
|
self.own_var(*zs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListMap4 { xs, ys, zs, ws } => {
|
||||||
|
// own the lists if the function wants to own the element
|
||||||
|
if !function_ps[0].borrow {
|
||||||
|
self.own_var(*xs);
|
||||||
|
}
|
||||||
|
if !function_ps[1].borrow {
|
||||||
|
self.own_var(*ys);
|
||||||
|
}
|
||||||
|
if !function_ps[2].borrow {
|
||||||
|
self.own_var(*zs);
|
||||||
|
}
|
||||||
|
if !function_ps[3].borrow {
|
||||||
|
self.own_var(*ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
ListSortWith { xs } => {
|
ListSortWith { xs } => {
|
||||||
// always own the input list
|
// always own the input list
|
||||||
self.own_var(*xs);
|
self.own_var(*xs);
|
||||||
|
@ -933,6 +948,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, function, closure_data]),
|
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, function, closure_data]),
|
||||||
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
|
||||||
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
|
||||||
|
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),
|
||||||
ListKeepIf | ListKeepOks | ListKeepErrs => {
|
ListKeepIf | ListKeepOks | ListKeepErrs => {
|
||||||
arena.alloc_slice_copy(&[owned, function, closure_data])
|
arena.alloc_slice_copy(&[owned, function, closure_data])
|
||||||
}
|
}
|
||||||
|
|
|
@ -467,6 +467,7 @@ impl<'a> Context<'a> {
|
||||||
op,
|
op,
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
|
update_mode,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
function_name,
|
function_name,
|
||||||
|
@ -485,6 +486,7 @@ impl<'a> Context<'a> {
|
||||||
closure_env_layout: *closure_env_layout,
|
closure_env_layout: *closure_env_layout,
|
||||||
function_owns_closure_data: true,
|
function_owns_closure_data: true,
|
||||||
specialization_id: *specialization_id,
|
specialization_id: *specialization_id,
|
||||||
|
update_mode: *update_mode,
|
||||||
function_name: *function_name,
|
function_name: *function_name,
|
||||||
function_env: *function_env,
|
function_env: *function_env,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
|
@ -576,6 +578,27 @@ impl<'a> Context<'a> {
|
||||||
|
|
||||||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
}
|
}
|
||||||
|
ListMap4 { xs, ys, zs, ws } => {
|
||||||
|
let borrows = [
|
||||||
|
function_ps[0].borrow,
|
||||||
|
function_ps[1].borrow,
|
||||||
|
function_ps[2].borrow,
|
||||||
|
function_ps[3].borrow,
|
||||||
|
FUNCTION,
|
||||||
|
CLOSURE_DATA,
|
||||||
|
];
|
||||||
|
|
||||||
|
let b = self.add_dec_after_lowlevel(arguments, &borrows, b, b_live_vars);
|
||||||
|
|
||||||
|
let b = decref_if_owned!(function_ps[0].borrow, *xs, b);
|
||||||
|
let b = decref_if_owned!(function_ps[1].borrow, *ys, b);
|
||||||
|
let b = decref_if_owned!(function_ps[2].borrow, *zs, b);
|
||||||
|
let b = decref_if_owned!(function_ps[3].borrow, *ws, b);
|
||||||
|
|
||||||
|
let v = create_call!(function_ps.get(3));
|
||||||
|
|
||||||
|
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
|
}
|
||||||
ListMapWithIndex { xs } => {
|
ListMapWithIndex { xs } => {
|
||||||
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
|
let borrows = [function_ps[1].borrow, FUNCTION, CLOSURE_DATA];
|
||||||
|
|
||||||
|
|
|
@ -1181,6 +1181,10 @@ pub enum CallType<'a> {
|
||||||
|
|
||||||
/// specialization id of the function argument, used for name generation
|
/// specialization id of the function argument, used for name generation
|
||||||
specialization_id: CallSpecId,
|
specialization_id: CallSpecId,
|
||||||
|
|
||||||
|
/// update mode of the higher order lowlevel itself
|
||||||
|
update_mode: UpdateModeId,
|
||||||
|
|
||||||
/// function layout, used for name generation
|
/// function layout, used for name generation
|
||||||
arg_layouts: &'a [Layout<'a>],
|
arg_layouts: &'a [Layout<'a>],
|
||||||
ret_layout: Layout<'a>,
|
ret_layout: Layout<'a>,
|
||||||
|
@ -4016,11 +4020,12 @@ pub fn with_hole<'a>(
|
||||||
lambda_set,
|
lambda_set,
|
||||||
op,
|
op,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
|top_level_function, closure_data, closure_env_layout, specialization_id| self::Call {
|
|(top_level_function, closure_data, closure_env_layout, specialization_id, update_mode)| self::Call {
|
||||||
call_type: CallType::HigherOrderLowLevel {
|
call_type: CallType::HigherOrderLowLevel {
|
||||||
op: crate::low_level::HigherOrder::$ho { $($x,)* },
|
op: crate::low_level::HigherOrder::$ho { $($x,)* },
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
|
update_mode,
|
||||||
function_owns_closure_data: false,
|
function_owns_closure_data: false,
|
||||||
function_env: closure_data_symbol,
|
function_env: closure_data_symbol,
|
||||||
function_name: top_level_function,
|
function_name: top_level_function,
|
||||||
|
@ -4144,6 +4149,16 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
match_on_closure_argument!(ListMap3, [xs, ys, zs])
|
match_on_closure_argument!(ListMap3, [xs, ys, zs])
|
||||||
}
|
}
|
||||||
|
ListMap4 => {
|
||||||
|
debug_assert_eq!(arg_symbols.len(), 5);
|
||||||
|
|
||||||
|
let xs = arg_symbols[0];
|
||||||
|
let ys = arg_symbols[1];
|
||||||
|
let zs = arg_symbols[2];
|
||||||
|
let ws = arg_symbols[3];
|
||||||
|
|
||||||
|
match_on_closure_argument!(ListMap4, [xs, ys, zs, ws])
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let call = self::Call {
|
let call = self::Call {
|
||||||
call_type: CallType::LowLevel {
|
call_type: CallType::LowLevel {
|
||||||
|
@ -6192,7 +6207,7 @@ fn reuse_function_symbol<'a>(
|
||||||
// and closures by unification. Here we record whether this function captures
|
// and closures by unification. Here we record whether this function captures
|
||||||
// anything.
|
// anything.
|
||||||
let captures = partial_proc.captured_symbols.captures();
|
let captures = partial_proc.captured_symbols.captures();
|
||||||
let captured = partial_proc.captured_symbols.clone();
|
let captured = partial_proc.captured_symbols;
|
||||||
|
|
||||||
match res_layout {
|
match res_layout {
|
||||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
|
@ -7903,6 +7918,8 @@ pub fn num_argument_to_int_or_float(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ToLowLevelCallArguments<'a> = (Symbol, Symbol, Option<Layout<'a>>, CallSpecId, UpdateModeId);
|
||||||
|
|
||||||
/// Use the lambda set to figure out how to make a lowlevel call
|
/// Use the lambda set to figure out how to make a lowlevel call
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||||
|
@ -7916,7 +7933,7 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
match lambda_set.runtime_representation() {
|
match lambda_set.runtime_representation() {
|
||||||
Layout::Union(union_layout) => {
|
Layout::Union(union_layout) => {
|
||||||
|
@ -7951,12 +7968,14 @@ where
|
||||||
Layout::Struct(_) => match lambda_set.set.get(0) {
|
Layout::Struct(_) => match lambda_set.set.get(0) {
|
||||||
Some((function_symbol, _)) => {
|
Some((function_symbol, _)) => {
|
||||||
let call_spec_id = env.next_call_specialization_id();
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
let call = to_lowlevel_call(
|
let update_mode = env.next_update_mode_id();
|
||||||
|
let call = to_lowlevel_call((
|
||||||
*function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
lambda_set.is_represented(),
|
lambda_set.is_represented(),
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
update_mode,
|
||||||
|
));
|
||||||
|
|
||||||
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
build_call(env, call, assigned, return_layout, env.arena.alloc(hole))
|
||||||
}
|
}
|
||||||
|
@ -8021,7 +8040,7 @@ fn lowlevel_union_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -8035,12 +8054,14 @@ where
|
||||||
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
|
let hole = Stmt::Jump(join_point_id, env.arena.alloc([assigned]));
|
||||||
|
|
||||||
let call_spec_id = env.next_call_specialization_id();
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
let call = to_lowlevel_call(
|
let update_mode = env.next_update_mode_id();
|
||||||
|
let call = to_lowlevel_call((
|
||||||
*function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
update_mode,
|
||||||
|
));
|
||||||
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
let stmt = build_call(env, call, assigned, return_layout, env.arena.alloc(hole));
|
||||||
|
|
||||||
branches.push((i as u64, BranchInfo::None, stmt));
|
branches.push((i as u64, BranchInfo::None, stmt));
|
||||||
|
@ -8458,7 +8479,7 @@ fn lowlevel_enum_lambda_set_to_switch<'a, ToLowLevelCall>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a>
|
) -> Stmt<'a>
|
||||||
where
|
where
|
||||||
ToLowLevelCall: Fn(Symbol, Symbol, Option<Layout<'a>>, CallSpecId) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
debug_assert!(!lambda_set.is_empty());
|
debug_assert!(!lambda_set.is_empty());
|
||||||
|
|
||||||
|
@ -8472,12 +8493,14 @@ where
|
||||||
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
|
let hole = Stmt::Jump(join_point_id, env.arena.alloc([result_symbol]));
|
||||||
|
|
||||||
let call_spec_id = env.next_call_specialization_id();
|
let call_spec_id = env.next_call_specialization_id();
|
||||||
let call = to_lowlevel_call(
|
let update_mode = env.next_update_mode_id();
|
||||||
|
let call = to_lowlevel_call((
|
||||||
*function_symbol,
|
*function_symbol,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
call_spec_id,
|
call_spec_id,
|
||||||
);
|
update_mode,
|
||||||
|
));
|
||||||
let stmt = build_call(
|
let stmt = build_call(
|
||||||
env,
|
env,
|
||||||
call,
|
call,
|
||||||
|
|
|
@ -2,18 +2,55 @@ use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum HigherOrder {
|
pub enum HigherOrder {
|
||||||
ListMap { xs: Symbol },
|
ListMap {
|
||||||
ListMap2 { xs: Symbol, ys: Symbol },
|
xs: Symbol,
|
||||||
ListMap3 { xs: Symbol, ys: Symbol, zs: Symbol },
|
},
|
||||||
ListMapWithIndex { xs: Symbol },
|
ListMap2 {
|
||||||
ListKeepIf { xs: Symbol },
|
xs: Symbol,
|
||||||
ListWalk { xs: Symbol, state: Symbol },
|
ys: Symbol,
|
||||||
ListWalkUntil { xs: Symbol, state: Symbol },
|
},
|
||||||
ListWalkBackwards { xs: Symbol, state: Symbol },
|
ListMap3 {
|
||||||
ListKeepOks { xs: Symbol },
|
xs: Symbol,
|
||||||
ListKeepErrs { xs: Symbol },
|
ys: Symbol,
|
||||||
ListSortWith { xs: Symbol },
|
zs: Symbol,
|
||||||
DictWalk { xs: Symbol, state: Symbol },
|
},
|
||||||
|
ListMap4 {
|
||||||
|
xs: Symbol,
|
||||||
|
ys: Symbol,
|
||||||
|
zs: Symbol,
|
||||||
|
ws: Symbol,
|
||||||
|
},
|
||||||
|
ListMapWithIndex {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
|
ListKeepIf {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
|
ListWalk {
|
||||||
|
xs: Symbol,
|
||||||
|
state: Symbol,
|
||||||
|
},
|
||||||
|
ListWalkUntil {
|
||||||
|
xs: Symbol,
|
||||||
|
state: Symbol,
|
||||||
|
},
|
||||||
|
ListWalkBackwards {
|
||||||
|
xs: Symbol,
|
||||||
|
state: Symbol,
|
||||||
|
},
|
||||||
|
ListKeepOks {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
|
ListKeepErrs {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
|
ListSortWith {
|
||||||
|
xs: Symbol,
|
||||||
|
},
|
||||||
|
DictWalk {
|
||||||
|
xs: Symbol,
|
||||||
|
state: Symbol,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HigherOrder {
|
impl HigherOrder {
|
||||||
|
@ -22,6 +59,7 @@ impl HigherOrder {
|
||||||
HigherOrder::ListMap { .. } => 1,
|
HigherOrder::ListMap { .. } => 1,
|
||||||
HigherOrder::ListMap2 { .. } => 2,
|
HigherOrder::ListMap2 { .. } => 2,
|
||||||
HigherOrder::ListMap3 { .. } => 3,
|
HigherOrder::ListMap3 { .. } => 3,
|
||||||
|
HigherOrder::ListMap4 { .. } => 4,
|
||||||
HigherOrder::ListMapWithIndex { .. } => 2,
|
HigherOrder::ListMapWithIndex { .. } => 2,
|
||||||
HigherOrder::ListKeepIf { .. } => 1,
|
HigherOrder::ListKeepIf { .. } => 1,
|
||||||
HigherOrder::ListWalk { .. } => 2,
|
HigherOrder::ListWalk { .. } => 2,
|
||||||
|
@ -128,202 +166,3 @@ enum FirstOrder {
|
||||||
Hash,
|
Hash,
|
||||||
ExpectTrue,
|
ExpectTrue,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
enum FirstOrHigher {
|
|
||||||
First(FirstOrder),
|
|
||||||
Higher(HigherOrder),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_low_level(low_level: &LowLevel, arguments: &[Symbol]) -> FirstOrHigher {
|
|
||||||
use FirstOrHigher::*;
|
|
||||||
use FirstOrder::*;
|
|
||||||
use HigherOrder::*;
|
|
||||||
|
|
||||||
match low_level {
|
|
||||||
LowLevel::StrConcat => First(StrConcat),
|
|
||||||
LowLevel::StrJoinWith => First(StrJoinWith),
|
|
||||||
LowLevel::StrIsEmpty => First(StrIsEmpty),
|
|
||||||
LowLevel::StrStartsWith => First(StrStartsWith),
|
|
||||||
LowLevel::StrStartsWithCodePt => First(StrStartsWithCodePt),
|
|
||||||
LowLevel::StrEndsWith => First(StrEndsWith),
|
|
||||||
LowLevel::StrSplit => First(StrSplit),
|
|
||||||
LowLevel::StrCountGraphemes => First(StrCountGraphemes),
|
|
||||||
LowLevel::StrFromInt => First(StrFromInt),
|
|
||||||
LowLevel::StrFromUtf8 => First(StrFromUtf8),
|
|
||||||
LowLevel::StrFromUtf8Range => First(StrFromUtf8Range),
|
|
||||||
LowLevel::StrToUtf8 => First(StrToUtf8),
|
|
||||||
LowLevel::StrRepeat => First(StrRepeat),
|
|
||||||
LowLevel::StrFromFloat => First(StrFromFloat),
|
|
||||||
LowLevel::ListLen => First(ListLen),
|
|
||||||
LowLevel::ListGetUnsafe => First(ListGetUnsafe),
|
|
||||||
LowLevel::ListSet => First(ListSet),
|
|
||||||
LowLevel::ListDrop => First(ListDrop),
|
|
||||||
LowLevel::ListDropAt => First(ListDropAt),
|
|
||||||
LowLevel::ListSingle => First(ListSingle),
|
|
||||||
LowLevel::ListRepeat => First(ListRepeat),
|
|
||||||
LowLevel::ListReverse => First(ListReverse),
|
|
||||||
LowLevel::ListConcat => First(ListConcat),
|
|
||||||
LowLevel::ListContains => First(ListContains),
|
|
||||||
LowLevel::ListAppend => First(ListAppend),
|
|
||||||
LowLevel::ListPrepend => First(ListPrepend),
|
|
||||||
LowLevel::ListJoin => First(ListJoin),
|
|
||||||
LowLevel::ListRange => First(ListRange),
|
|
||||||
LowLevel::ListSwap => First(ListSwap),
|
|
||||||
LowLevel::DictSize => First(DictSize),
|
|
||||||
LowLevel::DictEmpty => First(DictEmpty),
|
|
||||||
LowLevel::DictInsert => First(DictInsert),
|
|
||||||
LowLevel::DictRemove => First(DictRemove),
|
|
||||||
LowLevel::DictContains => First(DictContains),
|
|
||||||
LowLevel::DictGetUnsafe => First(DictGetUnsafe),
|
|
||||||
LowLevel::DictKeys => First(DictKeys),
|
|
||||||
LowLevel::DictValues => First(DictValues),
|
|
||||||
LowLevel::DictUnion => First(DictUnion),
|
|
||||||
LowLevel::DictIntersection => First(DictIntersection),
|
|
||||||
LowLevel::DictDifference => First(DictDifference),
|
|
||||||
LowLevel::SetFromList => First(SetFromList),
|
|
||||||
LowLevel::NumAdd => First(NumAdd),
|
|
||||||
LowLevel::NumAddWrap => First(NumAddWrap),
|
|
||||||
LowLevel::NumAddChecked => First(NumAddChecked),
|
|
||||||
LowLevel::NumSub => First(NumSub),
|
|
||||||
LowLevel::NumSubWrap => First(NumSubWrap),
|
|
||||||
LowLevel::NumSubChecked => First(NumSubChecked),
|
|
||||||
LowLevel::NumMul => First(NumMul),
|
|
||||||
LowLevel::NumMulWrap => First(NumMulWrap),
|
|
||||||
LowLevel::NumMulChecked => First(NumMulChecked),
|
|
||||||
LowLevel::NumGt => First(NumGt),
|
|
||||||
LowLevel::NumGte => First(NumGte),
|
|
||||||
LowLevel::NumLt => First(NumLt),
|
|
||||||
LowLevel::NumLte => First(NumLte),
|
|
||||||
LowLevel::NumCompare => First(NumCompare),
|
|
||||||
LowLevel::NumDivUnchecked => First(NumDivUnchecked),
|
|
||||||
LowLevel::NumRemUnchecked => First(NumRemUnchecked),
|
|
||||||
LowLevel::NumIsMultipleOf => First(NumIsMultipleOf),
|
|
||||||
LowLevel::NumAbs => First(NumAbs),
|
|
||||||
LowLevel::NumNeg => First(NumNeg),
|
|
||||||
LowLevel::NumSin => First(NumSin),
|
|
||||||
LowLevel::NumCos => First(NumCos),
|
|
||||||
LowLevel::NumSqrtUnchecked => First(NumSqrtUnchecked),
|
|
||||||
LowLevel::NumLogUnchecked => First(NumLogUnchecked),
|
|
||||||
LowLevel::NumRound => First(NumRound),
|
|
||||||
LowLevel::NumToFloat => First(NumToFloat),
|
|
||||||
LowLevel::NumPow => First(NumPow),
|
|
||||||
LowLevel::NumCeiling => First(NumCeiling),
|
|
||||||
LowLevel::NumPowInt => First(NumPowInt),
|
|
||||||
LowLevel::NumFloor => First(NumFloor),
|
|
||||||
LowLevel::NumIsFinite => First(NumIsFinite),
|
|
||||||
LowLevel::NumAtan => First(NumAtan),
|
|
||||||
LowLevel::NumAcos => First(NumAcos),
|
|
||||||
LowLevel::NumAsin => First(NumAsin),
|
|
||||||
LowLevel::NumBitwiseAnd => First(NumBitwiseAnd),
|
|
||||||
LowLevel::NumBitwiseXor => First(NumBitwiseXor),
|
|
||||||
LowLevel::NumBitwiseOr => First(NumBitwiseOr),
|
|
||||||
LowLevel::NumShiftLeftBy => First(NumShiftLeftBy),
|
|
||||||
LowLevel::NumShiftRightBy => First(NumShiftRightBy),
|
|
||||||
LowLevel::NumBytesToU16 => First(NumBytesToU16),
|
|
||||||
LowLevel::NumBytesToU32 => First(NumBytesToU32),
|
|
||||||
LowLevel::NumShiftRightZfBy => First(NumShiftRightZfBy),
|
|
||||||
LowLevel::NumIntCast => First(NumIntCast),
|
|
||||||
LowLevel::Eq => First(Eq),
|
|
||||||
LowLevel::NotEq => First(NotEq),
|
|
||||||
LowLevel::And => First(And),
|
|
||||||
LowLevel::Or => First(Or),
|
|
||||||
LowLevel::Not => First(Not),
|
|
||||||
LowLevel::Hash => First(Hash),
|
|
||||||
LowLevel::ExpectTrue => First(ExpectTrue),
|
|
||||||
LowLevel::ListMap => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListMap {
|
|
||||||
xs: arguments[0],
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListMap2 => {
|
|
||||||
debug_assert_eq!(arguments.len(), 4);
|
|
||||||
Higher(ListMap2 {
|
|
||||||
xs: arguments[0],
|
|
||||||
ys: arguments[1],
|
|
||||||
function_name: arguments[2],
|
|
||||||
function_env: arguments[3],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListMap3 => {
|
|
||||||
debug_assert_eq!(arguments.len(), 5);
|
|
||||||
Higher(ListMap3 {
|
|
||||||
xs: arguments[0],
|
|
||||||
ys: arguments[1],
|
|
||||||
zs: arguments[2],
|
|
||||||
function_name: arguments[3],
|
|
||||||
function_env: arguments[4],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListMapWithIndex => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListMapWithIndex {
|
|
||||||
xs: arguments[0],
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListKeepIf => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListKeepIf {
|
|
||||||
xs: arguments[0],
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListWalk => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListWalk {
|
|
||||||
xs: arguments[0],
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListWalkUntil => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListWalkUntil {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListWalkBackwards => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListWalkBackwards {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListKeepOks => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListKeepOks {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListKeepErrs => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListKeepErrs {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::ListSortWith => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(ListSortWith {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
LowLevel::DictWalk => {
|
|
||||||
debug_assert_eq!(arguments.len(), 3);
|
|
||||||
Higher(DictWalk {
|
|
||||||
function_name: arguments[1],
|
|
||||||
function_env: arguments[2],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::ast::CommentOrNewline;
|
use crate::ast::CommentOrNewline;
|
||||||
use crate::ast::Spaceable;
|
use crate::ast::Spaceable;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
self, and, backtrackable, BadInputError, Col, Parser,
|
self, and, backtrackable, BadInputError, Col, Parser, Progress::*, Row, State,
|
||||||
Progress::{self, *},
|
|
||||||
Row, State,
|
|
||||||
};
|
};
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -181,109 +179,6 @@ where
|
||||||
spaces_help_help(min_indent, space_problem, indent_problem)
|
spaces_help_help(min_indent, space_problem, indent_problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spaces_till_end_of_line<'a, E: 'a>(
|
|
||||||
tab_problem: fn(Row, Col) -> E,
|
|
||||||
) -> impl Parser<'a, Option<&'a str>, E> {
|
|
||||||
move |_, mut state: State<'a>| {
|
|
||||||
let mut bytes = state.bytes;
|
|
||||||
let mut row = state.line;
|
|
||||||
let mut col = state.column;
|
|
||||||
|
|
||||||
for c in bytes {
|
|
||||||
match c {
|
|
||||||
b' ' => {
|
|
||||||
bytes = &bytes[1..];
|
|
||||||
col += 1;
|
|
||||||
}
|
|
||||||
b'\n' => {
|
|
||||||
bytes = &bytes[1..];
|
|
||||||
row += 1;
|
|
||||||
col = 0;
|
|
||||||
|
|
||||||
state.line = row;
|
|
||||||
state.column = col;
|
|
||||||
state.bytes = bytes;
|
|
||||||
|
|
||||||
return Ok((MadeProgress, None, state));
|
|
||||||
}
|
|
||||||
b'\r' => {
|
|
||||||
bytes = &bytes[1..];
|
|
||||||
}
|
|
||||||
b'\t' => {
|
|
||||||
return Err((
|
|
||||||
MadeProgress,
|
|
||||||
tab_problem(row, col),
|
|
||||||
State {
|
|
||||||
line: row,
|
|
||||||
column: col,
|
|
||||||
..state
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
b'#' => match chomp_line_comment(bytes) {
|
|
||||||
Ok(comment) => {
|
|
||||||
state.line += 1;
|
|
||||||
state.column = 0;
|
|
||||||
|
|
||||||
let width = 1 + comment.len();
|
|
||||||
if let Some(b'\n') = bytes.get(width) {
|
|
||||||
state.bytes = &bytes[width + 1..];
|
|
||||||
} else {
|
|
||||||
state.bytes = &bytes[width..];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok((MadeProgress, Some(comment), state));
|
|
||||||
}
|
|
||||||
Err(_) => unreachable!("we check the first character is a #"),
|
|
||||||
},
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.column == col {
|
|
||||||
Ok((NoProgress, None, state))
|
|
||||||
} else {
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
None,
|
|
||||||
State {
|
|
||||||
column: col,
|
|
||||||
bytes,
|
|
||||||
..state
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chomp_line_comment(buffer: &[u8]) -> Result<&str, Progress> {
|
|
||||||
if let Some(b'#') = buffer.get(0) {
|
|
||||||
if (&buffer[1..]).starts_with(b"# ") {
|
|
||||||
// this is a doc comment, not a line comment
|
|
||||||
Err(NoProgress)
|
|
||||||
} else {
|
|
||||||
use encode_unicode::CharExt;
|
|
||||||
|
|
||||||
let mut chomped = 1;
|
|
||||||
|
|
||||||
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
|
|
||||||
if ch == '\n' {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
chomped += width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let comment_bytes = &buffer[1..chomped];
|
|
||||||
let comment = unsafe { std::str::from_utf8_unchecked(comment_bytes) };
|
|
||||||
|
|
||||||
Ok(comment)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(NoProgress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn spaces_help_help<'a, E>(
|
fn spaces_help_help<'a, E>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
|
|
|
@ -13,7 +13,6 @@ roc_parse = { path = "../parse" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -27,7 +27,6 @@ roc_builtins = { path = "../builtins" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -20,7 +20,6 @@ roc_problem = { path = "../problem" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
|
|
|
@ -21,7 +21,6 @@ roc_builtins = { path = "../builtins" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -28,7 +28,6 @@ im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
either = "1.6.1"
|
either = "1.6.1"
|
||||||
indoc = "0.3.3"
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
inkwell = { path = "../../vendor/inkwell" }
|
inkwell = { path = "../../vendor/inkwell" }
|
||||||
target-lexicon = "0.12.2"
|
target-lexicon = "0.12.2"
|
||||||
|
@ -38,11 +37,11 @@ wasmer-wasi = "2.0.0"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
maplit = "1.0.1"
|
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
indoc = "0.3.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
|
@ -31,9 +31,9 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||||
use roc_gen_llvm::llvm::build::PanicTagId;
|
use roc_gen_llvm::llvm::build::PanicTagId;
|
||||||
|
|
||||||
use libc::c_char;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
match PanicTagId::try_from(tag_id) {
|
match PanicTagId::try_from(tag_id) {
|
||||||
Ok(PanicTagId::NullTerminatedString) => {
|
Ok(PanicTagId::NullTerminatedString) => {
|
||||||
|
@ -763,6 +763,37 @@ fn list_map_closure() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_map4_group() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.map4 [1,2,3] [3,2,1] [2,1,3] [3,1,2] (\a, b, c, d -> Group a b c d)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[(1, 3, 2, 3), (2, 2, 1, 1), (3, 1, 3, 2)]),
|
||||||
|
RocList<(i64, i64, i64, i64)>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_map4_different_length() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.map4
|
||||||
|
["h", "i", "j", "k"]
|
||||||
|
["o", "p", "q"]
|
||||||
|
["l", "m"]
|
||||||
|
["a"]
|
||||||
|
(\a, b, c, d -> Str.concat a (Str.concat b (Str.concat c d)))
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[RocStr::from_slice("hola".as_bytes()),]),
|
||||||
|
RocList<RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_map3_group() {
|
fn list_map3_group() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -434,8 +434,8 @@ fn wasm_roc_panic(address: u32, tag_id: u32) {
|
||||||
let width = 100;
|
let width = 100;
|
||||||
let c_ptr = (ptr.deref(memory, 0, width)).unwrap();
|
let c_ptr = (ptr.deref(memory, 0, width)).unwrap();
|
||||||
|
|
||||||
use libc::c_char;
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
let slice = unsafe { CStr::from_ptr(c_ptr as *const _ as *const c_char) };
|
let slice = unsafe { CStr::from_ptr(c_ptr as *const _ as *const c_char) };
|
||||||
string = slice.to_str().unwrap();
|
string = slice.to_str().unwrap();
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,35 +5,16 @@ authors = ["The Roc Contributors"]
|
||||||
license = "UPL-1.0"
|
license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dev-dependencies]
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_problem = { path = "../problem" }
|
|
||||||
roc_types = { path = "../types" }
|
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_unify = { path = "../unify" }
|
|
||||||
roc_solve = { path = "../solve" }
|
|
||||||
roc_reporting = { path = "../reporting" }
|
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_parse = { path = "../parse" }
|
|
||||||
roc_build = { path = "../build" }
|
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
test_mono_macros = { path = "../test_mono_macros" }
|
test_mono_macros = { path = "../test_mono_macros" }
|
||||||
im = "14" # im and im-rc should always have the same version!
|
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
|
||||||
either = "1.6.1"
|
|
||||||
indoc = "0.3.3"
|
|
||||||
libc = "0.2"
|
|
||||||
target-lexicon = "0.12.2"
|
|
||||||
libloading = "0.6"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
indoc = "0.3.3"
|
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
indoc = "0.3.3"
|
||||||
|
|
|
@ -11,5 +11,4 @@ proc-macro = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0.39", features = ["full", "extra-traits"] }
|
syn = { version = "1.0.39", features = ["full", "extra-traits"] }
|
||||||
quote = "1.0.7"
|
quote = "1.0.7"
|
||||||
darling = "0.10.2"
|
|
||||||
proc-macro2 = "1.0.24"
|
proc-macro2 = "1.0.24"
|
||||||
|
|
31
compiler/test_wasm/Cargo.toml
Normal file
31
compiler/test_wasm/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "test_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# roc_module = { path = "../module" }
|
||||||
|
# roc_mono = { path = "../mono" }
|
||||||
|
|
||||||
|
# # TODO: switch to parity-wasm 0.44 once it's out (allows bumpalo vectors in some places)
|
||||||
|
parity-wasm = { git = "https://github.com/brian-carroll/parity-wasm", branch = "master" }
|
||||||
|
|
||||||
|
wasmer = "2.0.0"
|
||||||
|
wasmer-wasi = "2.0.0"
|
||||||
|
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_std = { path = "../../roc_std" }
|
||||||
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
roc_gen_wasm = { path = "../gen_wasm" }
|
||||||
|
roc_can = { path = "../can" }
|
||||||
|
roc_builtins = { path = "../builtins" }
|
||||||
|
roc_load = { path = "../load" }
|
||||||
|
roc_types = { path = "../types" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
indoc = "0.3.3"
|
||||||
|
pretty_assertions = "0.5.1"
|
||||||
|
libc = "0.2"
|
||||||
|
target-lexicon = "0.12.2"
|
||||||
|
tempfile = "3.1.0"
|
|
@ -67,18 +67,12 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
use roc_load::file::MonomorphizedModule;
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
procedures: top_procedures,
|
procedures,
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
..
|
..
|
||||||
} = loaded;
|
} = loaded;
|
||||||
|
|
||||||
let mut procedures = MutMap::default();
|
|
||||||
|
|
||||||
for (key, proc) in top_procedures {
|
|
||||||
procedures.insert(key, proc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can comment and uncomment this block out to get more useful information
|
// You can comment and uncomment this block out to get more useful information
|
||||||
// while you're working on the wasm backend!
|
// while you're working on the wasm backend!
|
||||||
// {
|
// {
|
||||||
|
@ -95,6 +89,13 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
// println!("=================================\n");
|
// println!("=================================\n");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
debug_assert_eq!(exposed_to_host.len(), 1);
|
||||||
|
let main_fn_symbol = loaded.entry_point.symbol;
|
||||||
|
let main_fn_index = procedures
|
||||||
|
.keys()
|
||||||
|
.position(|(s, _)| *s == main_fn_symbol)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let exposed_to_host = exposed_to_host.keys().copied().collect::<MutSet<_>>();
|
let exposed_to_host = exposed_to_host.keys().copied().collect::<MutSet<_>>();
|
||||||
|
|
||||||
let env = roc_gen_wasm::Env {
|
let env = roc_gen_wasm::Env {
|
||||||
|
@ -103,7 +104,7 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut builder, mut code_section_bytes, main_function_index) =
|
let (mut builder, mut code_section_bytes) =
|
||||||
roc_gen_wasm::build_module_help(&env, procedures).unwrap();
|
roc_gen_wasm::build_module_help(&env, procedures).unwrap();
|
||||||
|
|
||||||
T::insert_test_wrapper(
|
T::insert_test_wrapper(
|
||||||
|
@ -111,14 +112,14 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
&mut builder,
|
&mut builder,
|
||||||
&mut code_section_bytes,
|
&mut code_section_bytes,
|
||||||
TEST_WRAPPER_NAME,
|
TEST_WRAPPER_NAME,
|
||||||
main_function_index,
|
main_fn_index as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut parity_module = builder.build();
|
let mut parity_module = builder.build();
|
||||||
replace_code_section(&mut parity_module, code_section_bytes);
|
replace_code_section(&mut parity_module, code_section_bytes);
|
||||||
let module_bytes = parity_module.into_bytes().unwrap();
|
let module_bytes = parity_module.into_bytes().unwrap();
|
||||||
|
|
||||||
// for debugging (e.g. with wasm2wat)
|
// for debugging (e.g. with wasm2wat or wasm-objdump)
|
||||||
if false {
|
if false {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
extern crate bumpalo;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod wasm32_test_result;
|
pub mod wasm32_test_result;
|
|
@ -3,7 +3,7 @@ use parity_wasm::elements::Internal;
|
||||||
|
|
||||||
use roc_gen_wasm::code_builder::{Align, CodeBuilder, ValueType};
|
use roc_gen_wasm::code_builder::{Align, CodeBuilder, ValueType};
|
||||||
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
||||||
use roc_gen_wasm::{overwrite_padded_u32, LocalId};
|
use roc_gen_wasm::{serialize::SerialBuffer, LocalId};
|
||||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||||
|
|
||||||
pub trait Wasm32TestResult {
|
pub trait Wasm32TestResult {
|
||||||
|
@ -30,15 +30,15 @@ pub trait Wasm32TestResult {
|
||||||
let mut code_builder = CodeBuilder::new(arena);
|
let mut code_builder = CodeBuilder::new(arena);
|
||||||
Self::build_wrapper_body(&mut code_builder, main_function_index);
|
Self::build_wrapper_body(&mut code_builder, main_function_index);
|
||||||
|
|
||||||
code_builder.serialize(code_section_bytes).unwrap();
|
code_builder.serialize(code_section_bytes);
|
||||||
|
|
||||||
let mut num_procs = 0;
|
let mut num_procs = 0;
|
||||||
for (i, byte) in code_section_bytes[5..10].iter().enumerate() {
|
for (i, byte) in code_section_bytes[5..10].iter().enumerate() {
|
||||||
num_procs += ((byte & 0x7f) as u32) << (i * 7);
|
num_procs += ((byte & 0x7f) as u32) << (i * 7);
|
||||||
}
|
}
|
||||||
let inner_length = (code_section_bytes.len() - 5) as u32;
|
let inner_length = (code_section_bytes.len() - 5) as u32;
|
||||||
overwrite_padded_u32(&mut code_section_bytes[0..5], inner_length);
|
code_section_bytes.overwrite_padded_u32(0, inner_length);
|
||||||
overwrite_padded_u32(&mut code_section_bytes[5..10], num_procs + 1);
|
code_section_bytes.overwrite_padded_u32(5, num_procs + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32);
|
fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32);
|
||||||
|
@ -53,7 +53,8 @@ macro_rules! build_wrapper_body_primitive {
|
||||||
let frame_size = 8;
|
let frame_size = 8;
|
||||||
|
|
||||||
code_builder.get_local(frame_pointer_id);
|
code_builder.get_local(frame_pointer_id);
|
||||||
code_builder.call(main_function_index, 0, true);
|
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
||||||
|
code_builder.inst_imm32(roc_gen_wasm::opcodes::CALL, 0, true, main_function_index);
|
||||||
code_builder.$store_instruction($align, 0);
|
code_builder.$store_instruction($align, 0);
|
||||||
code_builder.get_local(frame_pointer_id);
|
code_builder.get_local(frame_pointer_id);
|
||||||
|
|
||||||
|
@ -80,7 +81,8 @@ fn build_wrapper_body_stack_memory(
|
||||||
let frame_pointer = Some(local_id);
|
let frame_pointer = Some(local_id);
|
||||||
|
|
||||||
code_builder.get_local(local_id);
|
code_builder.get_local(local_id);
|
||||||
code_builder.call(main_function_index, 0, true);
|
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
||||||
|
code_builder.inst_imm32(roc_gen_wasm::opcodes::CALL, 0, true, main_function_index);
|
||||||
code_builder.get_local(local_id);
|
code_builder.get_local(local_id);
|
||||||
code_builder.finalize(local_types, size as i32, frame_pointer);
|
code_builder.finalize(local_types, size as i32, frame_pointer);
|
||||||
}
|
}
|
3
compiler/test_wasm/src/lib.rs
Normal file
3
compiler/test_wasm/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod helpers;
|
||||||
|
pub mod wasm_num;
|
||||||
|
pub mod wasm_records;
|
1052
compiler/test_wasm/src/wasm_num.rs
Normal file
1052
compiler/test_wasm/src/wasm_num.rs
Normal file
File diff suppressed because it is too large
Load diff
911
compiler/test_wasm/src/wasm_records.rs
Normal file
911
compiler/test_wasm/src/wasm_records.rs
Normal file
|
@ -0,0 +1,911 @@
|
||||||
|
#![cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||||
|
use crate::assert_evals_to;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn basic_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { y: 17, x: 15, z: 19 }.x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: 17, z: 19 }.y
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 17,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: 17, z: 19 }.z
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn nested_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 12,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 2,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.z
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn f64_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||||
|
//
|
||||||
|
// rec.x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15.1,
|
||||||
|
// f64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||||
|
//
|
||||||
|
// rec.y
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 17.2,
|
||||||
|
// f64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||||
|
//
|
||||||
|
// rec.z
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19.3,
|
||||||
|
// f64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn fn_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// getRec = \x -> { y: 17, x, z: 19 }
|
||||||
|
|
||||||
|
// (getRec 15).x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { x: 15, y: 17, z: 19 }
|
||||||
|
|
||||||
|
// rec.y
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 17,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { x: 15, y: 17, z: 19 }
|
||||||
|
|
||||||
|
// rec.z
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { x: 15, y: 17, z: 19 }
|
||||||
|
|
||||||
|
// rec.z + rec.x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 34,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn def_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { y: 17, x: 15, z: 19 }
|
||||||
|
//
|
||||||
|
// rec.x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 15,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { x: 15, y: 17, z: 19 }
|
||||||
|
//
|
||||||
|
// rec.y
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 17,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { x: 15, y: 17, z: 19 }
|
||||||
|
//
|
||||||
|
// rec.z
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn when_on_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when { x: 0x2 } is
|
||||||
|
// { x } -> x + 3
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 5,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn when_record_with_guard_pattern() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when { x: 0x2, y: 3.14 } is
|
||||||
|
// { x: var } -> var + 3
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 5,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn let_with_record_pattern() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { x } = { x: 0x2, y: 3.14 }
|
||||||
|
//
|
||||||
|
// x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 2,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn record_guard_pattern() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when { x: 0x2, y: 3.14 } is
|
||||||
|
// { x: 0x4 } -> 5
|
||||||
|
// { x } -> x + 3
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 5,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn twice_record_access() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// x = {a: 0x2, b: 0x3 }
|
||||||
|
//
|
||||||
|
// x.a + x.b
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 5,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// #[test]
|
||||||
|
// fn empty_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// v = {}
|
||||||
|
//
|
||||||
|
// v
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// (),
|
||||||
|
// ()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_record1_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_record2_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3, y: 5 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3, 5),
|
||||||
|
(i64, i64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_record3_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3, y: 5, z: 17 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3, 5, 17),
|
||||||
|
(i64, i64, i64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f64_record2_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3.1, y: 5.1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3.1, 5.1),
|
||||||
|
(f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn f64_record3_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3.1, y: 5.1, z: 17.1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3.1, 5.1, 17.1),
|
||||||
|
(f64, f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_record4_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
record : { a : Bool, b : Bool, c : Bool, d : Bool }
|
||||||
|
record = { a: True, b: False, c : False, d : True }
|
||||||
|
|
||||||
|
record
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[true, false, false, true],
|
||||||
|
[bool; 4]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_record9_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 17, 1, 9, 12, 13, 14, 15],
|
||||||
|
[i64; 9]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_literal() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : Bool
|
||||||
|
x = True
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
bool
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_when_use_default() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
// f = \r ->
|
||||||
|
// when r is
|
||||||
|
// { x: Blue, y ? 3 } -> y
|
||||||
|
// { x: Red, y ? 5 } -> y
|
||||||
|
|
||||||
|
// main =
|
||||||
|
// a = f { x: Blue, y: 7 }
|
||||||
|
// b = f { x: Blue }
|
||||||
|
// c = f { x: Red, y: 11 }
|
||||||
|
// d = f { x: Red }
|
||||||
|
|
||||||
|
// a * b * c * d
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 3 * 5 * 7 * 11,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_when_use_default_nested() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \r ->
|
||||||
|
// when r is
|
||||||
|
// { x: Blue, y ? 3 } -> y
|
||||||
|
// { x: Red, y ? 5 } -> y
|
||||||
|
|
||||||
|
// a = f { x: Blue, y: 7 }
|
||||||
|
// b = f { x: Blue }
|
||||||
|
// c = f { x: Red, y: 11 }
|
||||||
|
// d = f { x: Red }
|
||||||
|
|
||||||
|
// a * b * c * d
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 3 * 5 * 7 * 11,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_when_no_use_default() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
// f = \r ->
|
||||||
|
// { x ? 10, y } = r
|
||||||
|
// x + y
|
||||||
|
|
||||||
|
// main =
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_when_no_use_default_nested() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \r ->
|
||||||
|
// { x ? 10, y } = r
|
||||||
|
// x + y
|
||||||
|
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_let_use_default() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
// f = \r ->
|
||||||
|
// { x ? 10, y } = r
|
||||||
|
// x + y
|
||||||
|
|
||||||
|
// main =
|
||||||
|
// f { y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_let_no_use_default() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
// f = \r ->
|
||||||
|
// { x ? 10, y } = r
|
||||||
|
// x + y
|
||||||
|
|
||||||
|
// main =
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_let_no_use_default_nested() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \r ->
|
||||||
|
// { x ? 10, y } = r
|
||||||
|
// x + y
|
||||||
|
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_function_use_default() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \{ x ? 10, y } -> x + y
|
||||||
|
|
||||||
|
// f { y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 19,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// #[ignore]
|
||||||
|
// fn optional_field_function_no_use_default() {
|
||||||
|
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
// f = \{ x ? 10, y } -> x + y
|
||||||
|
|
||||||
|
// main =
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// #[ignore]
|
||||||
|
// fn optional_field_function_no_use_default_nested() {
|
||||||
|
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \{ x ? 10, y } -> x + y
|
||||||
|
|
||||||
|
// f { x: 4, y: 9 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 13,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_singleton_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when { x : 4 } is
|
||||||
|
// { x ? 3 } -> x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 4,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn optional_field_empty_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// when { } is
|
||||||
|
// { x ? 3 } -> x
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 3,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_3() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ x: 3, y: 5, z: 4 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3, 5, 4),
|
||||||
|
(i64, i64, i64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_4() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 4, d: 2 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 4, 2],
|
||||||
|
[i64; 4]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_5() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 4, 2, 1],
|
||||||
|
[i64; 5]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_6() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 4, 2, 1, 7],
|
||||||
|
[i64; 6]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_7() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
[3, 5, 4, 2, 1, 7, 8],
|
||||||
|
[i64; 7]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_float_int() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 3.14, b: 0x1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(3.14, 0x1),
|
||||||
|
(f64, i64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_int_float() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 0x1, b: 3.14 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(0x1, 3.14),
|
||||||
|
(i64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_float_float() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 6.28, b: 3.14 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(6.28, 3.14),
|
||||||
|
(f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn return_record_float_float_float() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{ a: 6.28, b: 3.14, c: 0.1 }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(6.28, 3.14, 0.1),
|
||||||
|
(f64, f64, f64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn return_nested_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// (0x0, (6.28, 3.14, 0.1)),
|
||||||
|
// (i64, (f64, f64, f64))
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn accessor() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 7,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn accessor_single_element_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// .foo { foo: 4 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 4,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn update_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { foo: 42, bar: 6 }
|
||||||
|
|
||||||
|
// { rec & foo: rec.foo + 1 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// (6, 43),
|
||||||
|
// (i64, i64)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn update_single_element_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// rec = { foo: 42}
|
||||||
|
|
||||||
|
// { rec & foo: rec.foo + 1 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 43,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn booleans_in_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
||||||
|
// (true, true),
|
||||||
|
// (bool, bool)
|
||||||
|
// );
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
|
||||||
|
// (false, true),
|
||||||
|
// (bool, bool)
|
||||||
|
// );
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
|
||||||
|
// (true, false),
|
||||||
|
// (bool, bool)
|
||||||
|
// );
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
|
||||||
|
// (false, false),
|
||||||
|
// (bool, bool)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn alignment_in_record() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
|
||||||
|
// (32i64, true, 2u8),
|
||||||
|
// (i64, bool, u8)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_memory_return_from_branch() {
|
||||||
|
// stack memory pointer should end up in the right place after returning from a branch
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
stackMemoryJunk = { x: 999, y: 111 }
|
||||||
|
if True then
|
||||||
|
{ x: 123, y: 321 }
|
||||||
|
else
|
||||||
|
stackMemoryJunk
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
(123, 321),
|
||||||
|
(i64, i64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn blue_and_present() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \r ->
|
||||||
|
// when r is
|
||||||
|
// { x: Blue, y ? 3 } -> y
|
||||||
|
// { x: Red, y ? 5 } -> y
|
||||||
|
|
||||||
|
// f { x: Blue, y: 7 }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 7,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn blue_and_absent() {
|
||||||
|
// assert_evals_to!(
|
||||||
|
// indoc!(
|
||||||
|
// r#"
|
||||||
|
// f = \r ->
|
||||||
|
// when r is
|
||||||
|
// { x: Blue, y ? 3 } -> y
|
||||||
|
// { x: Red, y ? 5 } -> y
|
||||||
|
|
||||||
|
// f { x: Blue }
|
||||||
|
// "#
|
||||||
|
// ),
|
||||||
|
// 3,
|
||||||
|
// i64
|
||||||
|
// );
|
||||||
|
// }
|
|
@ -14,7 +14,6 @@ static_assertions = "1.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -12,7 +12,6 @@ roc_types = { path = "../types" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -14,7 +14,6 @@ roc_load = { path = "../compiler/load" }
|
||||||
roc_builtins = { path = "../compiler/builtins" }
|
roc_builtins = { path = "../compiler/builtins" }
|
||||||
roc_can = { path = "../compiler/can" }
|
roc_can = { path = "../compiler/can" }
|
||||||
roc_code_markup = { path = "../code_markup"}
|
roc_code_markup = { path = "../code_markup"}
|
||||||
roc_fmt = { path = "../compiler/fmt" }
|
|
||||||
roc_module = { path = "../compiler/module" }
|
roc_module = { path = "../compiler/module" }
|
||||||
roc_region = { path = "../compiler/region" }
|
roc_region = { path = "../compiler/region" }
|
||||||
roc_types = { path = "../compiler/types" }
|
roc_types = { path = "../compiler/types" }
|
||||||
|
@ -25,6 +24,5 @@ snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
|
|
@ -6,6 +6,12 @@ license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "An editor for Roc"
|
description = "An editor for Roc"
|
||||||
|
|
||||||
|
[package.metadata.cargo-udeps.ignore]
|
||||||
|
# confy is currently unused but should not be removed
|
||||||
|
normal = ["confy"]
|
||||||
|
#development = []
|
||||||
|
#build = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_ast = { path = "../ast" }
|
roc_ast = { path = "../ast" }
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
|
@ -19,7 +25,6 @@ roc_module = { path = "../compiler/module" }
|
||||||
roc_problem = { path = "../compiler/problem" }
|
roc_problem = { path = "../compiler/problem" }
|
||||||
roc_types = { path = "../compiler/types" }
|
roc_types = { path = "../compiler/types" }
|
||||||
roc_unify = { path = "../compiler/unify" }
|
roc_unify = { path = "../compiler/unify" }
|
||||||
roc_fmt = { path = "../compiler/fmt" }
|
|
||||||
roc_reporting = { path = "../compiler/reporting" }
|
roc_reporting = { path = "../compiler/reporting" }
|
||||||
roc_solve = { path = "../compiler/solve" }
|
roc_solve = { path = "../compiler/solve" }
|
||||||
ven_graph = { path = "../vendor/pathfinding" }
|
ven_graph = { path = "../vendor/pathfinding" }
|
||||||
|
@ -33,7 +38,6 @@ winit = "0.24"
|
||||||
wgpu = "0.10"
|
wgpu = "0.10"
|
||||||
glyph_brush = "0.7"
|
glyph_brush = "0.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
zerocopy = "0.3"
|
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
wgpu_glyph = "0.14"
|
wgpu_glyph = "0.14"
|
||||||
|
@ -42,18 +46,13 @@ snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
colored = "2"
|
colored = "2"
|
||||||
pest = "2.1"
|
pest = "2.1"
|
||||||
pest_derive = "2.1"
|
pest_derive = "2.1"
|
||||||
ropey = "1.2.0"
|
|
||||||
copypasta = "0.7.1"
|
copypasta = "0.7.1"
|
||||||
indoc = "1.0"
|
|
||||||
palette = "0.5"
|
palette = "0.5"
|
||||||
# confy is currently unused but should not be removed
|
|
||||||
confy = { git = 'https://github.com/rust-cli/confy', features = [
|
confy = { git = 'https://github.com/rust-cli/confy', features = [
|
||||||
"yaml_conf"
|
"yaml_conf"
|
||||||
], default-features = false }
|
], default-features = false }
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
nonempty = "0.6.0"
|
nonempty = "0.6.0"
|
||||||
tempfile = "3.2.0"
|
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
rodio = "0.14.0"
|
rodio = "0.14.0"
|
||||||
threadpool = "1.8.1"
|
threadpool = "1.8.1"
|
||||||
|
@ -64,7 +63,9 @@ features = ["derive"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.6"
|
pretty_assertions = "0.6"
|
||||||
maplit = "1.0.1"
|
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
quickcheck_macros = "1.0"
|
quickcheck_macros = "1.0"
|
||||||
rand = "0.8.2"
|
rand = "0.8.2"
|
||||||
|
indoc = "1.0"
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
|
|
@ -79,7 +79,9 @@ fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Err
|
||||||
compatible_surface: Some(&surface),
|
compatible_surface: Some(&surface),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Request adapter");
|
.expect(r#"Request adapter
|
||||||
|
If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor
|
||||||
|
"#);
|
||||||
|
|
||||||
adapter
|
adapter
|
||||||
.request_device(
|
.request_device(
|
||||||
|
|
4
examples/cli/platform/Cargo.lock
generated
4
examples/cli/platform/Cargo.lock
generated
|
@ -12,9 +12,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.100"
|
version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
|
|
|
@ -11,7 +11,7 @@ app "false"
|
||||||
# 1) The input files are considered too large to just read in at once. Instead it is read via buffer or line.
|
# 1) The input files are considered too large to just read in at once. Instead it is read via buffer or line.
|
||||||
# 2) The output is also considered too large to generate in memory. It must be printed as we go via buffer or line.
|
# 2) The output is also considered too large to generate in memory. It must be printed as we go via buffer or line.
|
||||||
|
|
||||||
# I think one of the biggest issues with this implementation is that it doesn't return the the platform frequently enough.
|
# I think one of the biggest issues with this implementation is that it doesn't return to the platform frequently enough.
|
||||||
# What I mean by that is we build a chain of all Tasks period and return that to the host.
|
# What I mean by that is we build a chain of all Tasks period and return that to the host.
|
||||||
# In something like the elm architecture you return a single step with one Task.
|
# In something like the elm architecture you return a single step with one Task.
|
||||||
# The huge difference here is when it comes to things like stack overflows.
|
# The huge difference here is when it comes to things like stack overflows.
|
||||||
|
|
|
@ -30,12 +30,12 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||||
libc::malloc(size)
|
libc::malloc(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn roc_realloc(
|
pub unsafe extern "C" fn roc_realloc(
|
||||||
c_ptr: *mut c_void,
|
c_ptr: *mut c_void,
|
||||||
new_size: usize,
|
new_size: usize,
|
||||||
_old_size: usize,
|
_old_size: usize,
|
||||||
|
@ -45,12 +45,12 @@ pub unsafe fn roc_realloc(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||||
libc::free(c_ptr)
|
libc::free(c_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||||
match tag_id {
|
match tag_id {
|
||||||
0 => {
|
0 => {
|
||||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||||
|
@ -73,7 +73,7 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn rust_main() -> i32 {
|
pub extern "C" fn rust_main() -> i32 {
|
||||||
let arg = env::args().skip(1).next().unwrap();
|
let arg = env::args().skip(1).next().unwrap();
|
||||||
let arg = RocStr::from_slice(arg.as_bytes());
|
let arg = RocStr::from_slice(arg.as_bytes());
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_getLine() -> RocStr {
|
pub extern "C" fn roc_fx_getLine() -> RocStr {
|
||||||
use std::io::{self, BufRead};
|
use std::io::{self, BufRead};
|
||||||
|
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
|
@ -125,7 +125,7 @@ pub fn roc_fx_getLine() -> RocStr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_getChar() -> u8 {
|
pub extern "C" fn roc_fx_getChar() -> u8 {
|
||||||
use std::io::{self, BufRead};
|
use std::io::{self, BufRead};
|
||||||
let mut buffer = [0];
|
let mut buffer = [0];
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ pub fn roc_fx_getChar() -> u8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_putLine(line: RocStr) -> () {
|
pub extern "C" fn roc_fx_putLine(line: RocStr) -> () {
|
||||||
let bytes = line.as_slice();
|
let bytes = line.as_slice();
|
||||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
println!("{}", string);
|
println!("{}", string);
|
||||||
|
@ -154,7 +154,7 @@ pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_putRaw(line: RocStr) -> () {
|
pub extern "C" fn roc_fx_putRaw(line: RocStr) -> () {
|
||||||
let bytes = line.as_slice();
|
let bytes = line.as_slice();
|
||||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
print!("{}", string);
|
print!("{}", string);
|
||||||
|
@ -167,7 +167,7 @@ pub fn roc_fx_putRaw(line: RocStr) -> () {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
|
pub extern "C" fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
|
||||||
let br = unsafe { &mut *br_ptr };
|
let br = unsafe { &mut *br_ptr };
|
||||||
let mut line1 = String::default();
|
let mut line1 = String::default();
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ pub fn roc_fx_getFileLine(br_ptr: *mut BufReader<File>) -> RocStr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
|
pub extern "C" fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
|
||||||
let br = unsafe { &mut *br_ptr };
|
let br = unsafe { &mut *br_ptr };
|
||||||
let mut buffer = [0; 0x10 /* This is intentially small to ensure correct implementation */];
|
let mut buffer = [0; 0x10 /* This is intentially small to ensure correct implementation */];
|
||||||
|
|
||||||
|
@ -190,22 +190,25 @@ pub fn roc_fx_getFileBytes(br_ptr: *mut BufReader<File>) -> RocList<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) -> () {
|
pub extern "C" fn roc_fx_closeFile(br_ptr: *mut BufReader<File>) -> () {
|
||||||
unsafe {
|
unsafe {
|
||||||
Box::from_raw(br_ptr);
|
Box::from_raw(br_ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_openFile(name: RocStr) -> *mut BufReader<File> {
|
pub extern "C" fn roc_fx_openFile(name: RocStr) -> *mut BufReader<File> {
|
||||||
let f = File::open(name.as_str()).expect("Unable to open file");
|
let f = File::open(name.as_str()).expect("Unable to open file");
|
||||||
let br = BufReader::new(f);
|
let br = BufReader::new(f);
|
||||||
|
|
||||||
|
// don't mess with the refcount!
|
||||||
|
core::mem::forget(name);
|
||||||
|
|
||||||
Box::into_raw(Box::new(br))
|
Box::into_raw(Box::new(br))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn roc_fx_withFileOpen(name: RocStr, buffer: *const u8) -> () {
|
pub extern "C" fn roc_fx_withFileOpen(name: RocStr, buffer: *const u8) -> () {
|
||||||
// let f = File::open(name.as_str()).expect("Unable to open file");
|
// let f = File::open(name.as_str()).expect("Unable to open file");
|
||||||
// let mut br = BufReader::new(f);
|
// let mut br = BufReader::new(f);
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
use libc::c_char;
|
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "roc__mainForHost_1_exposed"]
|
#[link_name = "roc__mainForHost_1_exposed"]
|
||||||
|
|
41
nix/zig.nix
Normal file
41
nix/zig.nix
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{ pkgs }:
|
||||||
|
|
||||||
|
let
|
||||||
|
version = "0.8.0";
|
||||||
|
|
||||||
|
osName = if pkgs.stdenv.isDarwin then "macos" else "linux";
|
||||||
|
|
||||||
|
splitSystem = builtins.split "-" builtins.currentSystem;
|
||||||
|
arch = builtins.elemAt splitSystem 0;
|
||||||
|
isAarch64 = arch == "aarch64";
|
||||||
|
|
||||||
|
archiveName = "zig-${osName}-${arch}-${version}";
|
||||||
|
|
||||||
|
# If your system is not aarch64, we assume it's x86_64
|
||||||
|
sha256 = if pkgs.stdenv.isDarwin then
|
||||||
|
if isAarch64 then
|
||||||
|
"b32d13f66d0e1ff740b3326d66a469ee6baddbd7211fa111c066d3bd57683111"
|
||||||
|
else
|
||||||
|
"279f9360b5cb23103f0395dc4d3d0d30626e699b1b4be55e98fd985b62bc6fbe"
|
||||||
|
else if isAarch64 then
|
||||||
|
"ee204ca2c2037952cf3f8b10c609373a08a291efa4af7b3c73be0f2b27720470"
|
||||||
|
else
|
||||||
|
"502625d3da3ae595c5f44a809a87714320b7a40e6dff4a895b5fa7df3391d01e";
|
||||||
|
in pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "zig";
|
||||||
|
version = version;
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
inherit sha256;
|
||||||
|
name = "${archiveName}.tar.xz";
|
||||||
|
url = "https://ziglang.org/download/${version}/${archiveName}.tar.xz";
|
||||||
|
};
|
||||||
|
phases = [ "unpackPhase" ];
|
||||||
|
unpackPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
tar -xf $src
|
||||||
|
cp ${archiveName}/zig $out/zig
|
||||||
|
cp -r ${archiveName}/lib $out/lib
|
||||||
|
ln -s "$out/zig" "$out/bin/zig"
|
||||||
|
chmod +x $out/bin/zig
|
||||||
|
'';
|
||||||
|
}
|
|
@ -136,6 +136,7 @@ impl<T> RocList<T> {
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
|
assert!(capacity > 0);
|
||||||
assert!(slice.len() <= capacity);
|
assert!(slice.len() <= capacity);
|
||||||
|
|
||||||
let ptr = slice.as_ptr();
|
let ptr = slice.as_ptr();
|
||||||
|
@ -197,7 +198,12 @@ impl<T> RocList<T> {
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
Self::from_slice_with_capacity(slice, slice.len())
|
// Avoid allocation with empty list.
|
||||||
|
if slice.is_empty() {
|
||||||
|
Self::default()
|
||||||
|
} else {
|
||||||
|
Self::from_slice_with_capacity(slice, slice.len())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[T] {
|
pub fn as_slice(&self) -> &[T] {
|
||||||
|
|
35
shell.nix
35
shell.nix
|
@ -32,6 +32,7 @@ let
|
||||||
|
|
||||||
llvmPkgs = pkgs.llvmPackages_12;
|
llvmPkgs = pkgs.llvmPackages_12;
|
||||||
|
|
||||||
|
zig = import ./nix/zig.nix { inherit pkgs; };
|
||||||
debugir = import ./nix/debugir.nix { inherit pkgs; };
|
debugir = import ./nix/debugir.nix { inherit pkgs; };
|
||||||
|
|
||||||
inputs = with pkgs; [
|
inputs = with pkgs; [
|
||||||
|
@ -70,9 +71,39 @@ in pkgs.mkShell {
|
||||||
|
|
||||||
# Additional Env vars
|
# Additional Env vars
|
||||||
LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}";
|
LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}";
|
||||||
|
NIX_GLIBC_PATH =
|
||||||
|
if pkgs.stdenv.isLinux then "${pkgs.glibc_multi.out}/lib" else "";
|
||||||
LD_LIBRARY_PATH = with pkgs;
|
LD_LIBRARY_PATH = with pkgs;
|
||||||
lib.makeLibraryPath
|
lib.makeLibraryPath
|
||||||
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
|
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
|
||||||
NIX_GLIBC_PATH =
|
|
||||||
if pkgs.stdenv.isLinux then "${pkgs.glibc_multi.out}/lib" else "";
|
COREAUDIO_SDK_PATH = if pkgs.stdenv.isDarwin then
|
||||||
|
# The coreaudio-sys crate is configured to look for things in whatever the
|
||||||
|
# output of `xcrun --sdk macosx --show-sdk-path` is. However, this does not
|
||||||
|
# always contain the right frameworks, and it uses system versions instead of
|
||||||
|
# what we control via Nix. Instead of having to run a lot of extra scripts
|
||||||
|
# to set our systems up to build, we can just create a SDK directory with
|
||||||
|
# the same layout as the `MacOSX{version}.sdk` that XCode produces.
|
||||||
|
pkgs.symlinkJoin {
|
||||||
|
name = "sdk";
|
||||||
|
paths = with pkgs.darwin.apple_sdk.frameworks; [
|
||||||
|
AudioToolbox
|
||||||
|
AudioUnit
|
||||||
|
CoreAudio
|
||||||
|
CoreFoundation
|
||||||
|
CoreMIDI
|
||||||
|
OpenAL
|
||||||
|
];
|
||||||
|
postBuild = ''
|
||||||
|
mkdir $out/System
|
||||||
|
mv $out/Library $out/System
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# TODO: I'm not 100% confident that this being blank won't cause issues for
|
||||||
|
# Nix-on-Linux development. It may be sufficient to use the pkgs.symlinkJoin
|
||||||
|
# above regardless of system! That'd set us up for cross-compilation as well.
|
||||||
|
"";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue