mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge branch 'trunk' into dropfirst-builtin
This commit is contained in:
commit
208bce563a
85 changed files with 4871 additions and 3098 deletions
4
AUTHORS
4
AUTHORS
|
@ -43,3 +43,7 @@ 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>
|
||||||
|
Luiz Carlos L. G. de Oliveira <luizcarlos1405@gmail.com>
|
||||||
|
Oleksii Skidan <al.skidan@gmail.com>
|
||||||
|
Martin Janiczek <martin@janiczek.cz>
|
||||||
|
|
|
@ -213,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)
|
||||||
|
|
120
Cargo.lock
generated
120
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"
|
||||||
|
@ -2781,11 +2774,6 @@ dependencies = [
|
||||||
"syn 1.0.76",
|
"syn 1.0.76",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parity-wasm"
|
|
||||||
version = "0.44.0"
|
|
||||||
source = "git+https://github.com/brian-carroll/parity-wasm?branch=master#373f655f64d2260a2e9665811f7b6ed17f9db705"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -3579,9 +3567,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 +3599,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 +3616,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 +3642,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 +3706,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 +3723,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 +3730,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 +3760,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 +3774,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 +3784,6 @@ dependencies = [
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"rodio",
|
"rodio",
|
||||||
"ropey",
|
|
||||||
"serde",
|
"serde",
|
||||||
"snafu",
|
"snafu",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -3817,7 +3793,6 @@ dependencies = [
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"wgpu_glyph",
|
"wgpu_glyph",
|
||||||
"winit",
|
"winit",
|
||||||
"zerocopy",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3828,7 +3803,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 +3821,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 +3856,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,8 +3885,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc 0.3.6",
|
"indoc 0.3.6",
|
||||||
"libc",
|
|
||||||
"parity-wasm",
|
|
||||||
"pretty_assertions 0.5.1",
|
"pretty_assertions 0.5.1",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
@ -3992,7 +3960,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 +3976,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 +4017,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 +4039,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 +4062,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 +4088,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 +4103,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 +4131,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 +4576,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 +4617,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 +4649,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 +4666,33 @@ 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",
|
||||||
|
"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 +5694,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:
|
||||||
|
@ -81,7 +81,7 @@ test-rust:
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release && sccache --show-stats
|
cargo test --release && sccache --show-stats
|
||||||
# run i386 (32-bit linux) cli tests
|
# run i386 (32-bit linux) cli tests
|
||||||
RUN echo "4" | cargo run --release -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --release --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
cargo test --release --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ If you're curious about where the language's name and logo came from,
|
||||||
## State of Roc
|
## State of Roc
|
||||||
|
|
||||||
Roc is not ready for production yet. You are likely to encounter bugs. Publishing packages or documentation is not yet supported.
|
Roc is not ready for production yet. You are likely to encounter bugs. Publishing packages or documentation is not yet supported.
|
||||||
Many programs can however be compiled correctly. Check out [examples](examples) and [examples/benchmarks](examples/benchmarks). There are minimal platforms for Rust, Zig, C and an HTTP server. We are hard at work to make programming in Roc a delightful experience!
|
Many programs can however be compiled correctly. Check out [examples](examples) and [examples/benchmarks](examples/benchmarks). There are minimal platforms for Rust, Zig, C, Swift and an HTTP server. We are hard at work to make programming in Roc a delightful experience!
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ By using systems-level programming languages like C and C++, platform authors sa
|
||||||
Roc is designed to make the "systems-level platform, higher-level application" experience as nice as possible.
|
Roc is designed to make the "systems-level platform, higher-level application" experience as nice as possible.
|
||||||
|
|
||||||
* **Application** authors code exclusively in Roc. It's a language designed for nice ergonomics. The syntax resembles Ruby or CoffeeScript, and it has a fast compiler with full type inference.
|
* **Application** authors code exclusively in Roc. It's a language designed for nice ergonomics. The syntax resembles Ruby or CoffeeScript, and it has a fast compiler with full type inference.
|
||||||
* **Platform** authors code almost exclusively in a systems-level language like C, C++, Rust, or [Zig](https://ziglang.org/), except for the thin Roc API they expose to application authors. Roc application code compiles to machine code, and production builds of Roc apps benefit from the same [LLVM](https://llvm.org/) optimizations that C++, Rust, and Zig do. Roc application authors do not need to know this lower-level code exists; all they have to interact with is the platform's API, which is exposed as an ordinary Roc API.
|
* **Platform** authors code almost exclusively in a systems-level language like C, C++, Rust, Swift or [Zig](https://ziglang.org/), except for the thin Roc API they expose to application authors. Roc application code compiles to machine code, and production builds of Roc apps benefit from the same [LLVM](https://llvm.org/) optimizations that C++, Rust, Swift and Zig do. Roc application authors do not need to know this lower-level code exists; all they have to interact with is the platform's API, which is exposed as an ordinary Roc API.
|
||||||
|
|
||||||
Every Roc application is built on top of exactly one Roc platform. There is no such thing as a Roc application that runs without a platform, and there is no default platform. You must choose one!
|
Every Roc application is built on top of exactly one Roc platform. There is no such thing as a Roc application that runs without a platform, and there is no default platform. You must choose one!
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -14,26 +14,31 @@ path = "src/main.rs"
|
||||||
test = false
|
test = false
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["target-x86", "llvm", "editor"]
|
default = ["target-aarch64", "target-x86_64", "target-wasm32", "llvm", "editor"]
|
||||||
wasm32-cli-run = []
|
|
||||||
i386-cli-run = []
|
wasm32-cli-run = ["target-wasm32"]
|
||||||
|
i386-cli-run = ["target-x86"]
|
||||||
|
|
||||||
# This is a separate feature because when we generate docs on Netlify,
|
# This is a separate feature because when we generate docs on Netlify,
|
||||||
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
||||||
llvm = ["inkwell", "roc_gen_llvm", "roc_build/llvm"]
|
llvm = ["inkwell", "roc_gen_llvm", "roc_build/llvm"]
|
||||||
editor = ["roc_editor"]
|
editor = ["roc_editor"]
|
||||||
|
|
||||||
target-x86 = []
|
|
||||||
|
|
||||||
# arm and wasm give linker errors on some platforms
|
# Compiling for a different platform than the host can cause linker errors.
|
||||||
target-arm = []
|
target-arm = ["roc_build/target-arm"]
|
||||||
target-webassembly = []
|
target-aarch64 = ["roc_build/target-aarch64"]
|
||||||
|
target-x86 = ["roc_build/target-x86"]
|
||||||
|
target-x86_64 = ["roc_build/target-x86_64"]
|
||||||
|
target-wasm32 = ["roc_build/target-wasm32"]
|
||||||
|
|
||||||
target-all = [
|
target-all = [
|
||||||
"target-x86",
|
"target-aarch64",
|
||||||
"target-arm",
|
"target-arm",
|
||||||
"target-webassembly"
|
"target-x86",
|
||||||
|
"target-x86_64",
|
||||||
|
"target-wasm32"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,7 +70,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 +82,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 +93,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"
|
||||||
|
|
|
@ -3,7 +3,6 @@ use roc_build::{
|
||||||
link::{link, rebuild_host, LinkType},
|
link::{link, rebuild_host, LinkType},
|
||||||
program,
|
program,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_can::builtins::builtin_defs_map;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
|
@ -12,7 +11,6 @@ use roc_mono::ir::OptLevel;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
use tempfile::Builder;
|
use tempfile::Builder;
|
||||||
|
|
||||||
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
fn report_timing(buf: &mut String, label: &str, duration: Duration) {
|
||||||
|
@ -45,7 +43,6 @@ pub struct BuiltFile {
|
||||||
pub total_time: Duration,
|
pub total_time: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn build_file<'a>(
|
pub fn build_file<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -179,20 +176,15 @@ pub fn build_file<'a>(
|
||||||
program::report_problems_monomorphized(&mut loaded);
|
program::report_problems_monomorphized(&mut loaded);
|
||||||
let loaded = loaded;
|
let loaded = loaded;
|
||||||
|
|
||||||
let code_gen_timing = match opt_level {
|
let code_gen_timing = program::gen_from_mono_module(
|
||||||
OptLevel::Normal | OptLevel::Optimize => program::gen_from_mono_module_llvm(
|
arena,
|
||||||
arena,
|
loaded,
|
||||||
loaded,
|
&roc_file_path,
|
||||||
&roc_file_path,
|
target,
|
||||||
target,
|
app_o_file,
|
||||||
app_o_file,
|
opt_level,
|
||||||
opt_level,
|
emit_debug_info,
|
||||||
emit_debug_info,
|
);
|
||||||
),
|
|
||||||
OptLevel::Development => {
|
|
||||||
program::gen_from_mono_module_dev(arena, loaded, target, app_o_file)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
buf.push('\n');
|
buf.push('\n');
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -217,7 +217,6 @@ pub enum BuildConfig {
|
||||||
BuildAndRun { roc_file_arg_index: usize },
|
BuildAndRun { roc_file_arg_index: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
use build::build_file;
|
use build::build_file;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
|
@ -13,14 +13,8 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::ffi::{OsStr, OsString};
|
||||||
|
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
use roc_cli::build;
|
use roc_cli::build;
|
||||||
|
|
||||||
#[cfg(not(feature = "llvm"))]
|
|
||||||
fn build(_matches: &clap::ArgMatches, _config: BuildConfig) -> io::Result<i32> {
|
|
||||||
panic!("Building without LLVM is not currently supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
let matches = build_app().get_matches();
|
let matches = build_app().get_matches();
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,12 @@ mod cli_run {
|
||||||
eprintln!("WARNING: skipping testing example {} because the test is broken right now!", example.filename);
|
eprintln!("WARNING: skipping testing example {} because the test is broken right now!", example.filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
"hello-swift" => {
|
||||||
|
if cfg!(not(target_os = "macos")) {
|
||||||
|
eprintln!("WARNING: skipping testing example {} because it only works on MacOS.", example.filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +289,14 @@ mod cli_run {
|
||||||
expected_ending:"Hello, World!\n",
|
expected_ending:"Hello, World!\n",
|
||||||
use_valgrind: true,
|
use_valgrind: true,
|
||||||
},
|
},
|
||||||
|
hello_swift:"hello-swift" => Example {
|
||||||
|
filename: "Hello.roc",
|
||||||
|
executable_filename: "hello-swift",
|
||||||
|
stdin: &[],
|
||||||
|
input_file: None,
|
||||||
|
expected_ending:"Hello Swift, meet Roc\n",
|
||||||
|
use_valgrind: true,
|
||||||
|
},
|
||||||
hello_web:"hello-web" => Example {
|
hello_web:"hello-web" => Example {
|
||||||
filename: "Hello.roc",
|
filename: "Hello.roc",
|
||||||
executable_filename: "hello-web",
|
executable_filename: "hello-web",
|
||||||
|
|
|
@ -20,14 +20,13 @@ roc_solve = { path = "../solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
||||||
roc_gen_wasm = { path = "../gen_wasm" }
|
roc_gen_wasm = { path = "../gen_wasm", optional = true }
|
||||||
roc_gen_dev = { path = "../gen_dev" }
|
roc_gen_dev = { path = "../gen_dev", default-features = false, optional = true }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
roc_std = { path = "../../roc_std" }
|
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,16 +35,18 @@ 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"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["llvm", "target-webassembly", "target-aarch64"]
|
default = ["llvm", "target-aarch64", "target-x86_64", "target-wasm32"]
|
||||||
target-arm = []
|
target-arm = []
|
||||||
target-aarch64 = []
|
target-aarch64 = ["roc_gen_dev/target-aarch64"]
|
||||||
target-webassembly = []
|
target-x86 = []
|
||||||
|
target-x86_64 = ["roc_gen_dev/target-x86_64"]
|
||||||
|
target-wasm32 = ["roc_gen_wasm"]
|
||||||
|
|
||||||
# This is a separate feature because when we generate docs on Netlify,
|
# This is a separate feature because when we generate docs on Netlify,
|
||||||
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
||||||
llvm = ["inkwell", "roc_gen_llvm"]
|
llvm = ["inkwell", "roc_gen_llvm"]
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -300,6 +300,40 @@ pub fn build_c_host_native(
|
||||||
command.output().unwrap()
|
command.output().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_swift_host_native(
|
||||||
|
env_path: &str,
|
||||||
|
env_home: &str,
|
||||||
|
dest: &str,
|
||||||
|
sources: &[&str],
|
||||||
|
opt_level: OptLevel,
|
||||||
|
shared_lib_path: Option<&Path>,
|
||||||
|
objc_header_path: Option<&str>,
|
||||||
|
) -> Output {
|
||||||
|
if shared_lib_path.is_some() {
|
||||||
|
unimplemented!("Linking a shared library to Swift not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut command = Command::new("swiftc");
|
||||||
|
command
|
||||||
|
.env_clear()
|
||||||
|
.env("PATH", &env_path)
|
||||||
|
.env("HOME", &env_home)
|
||||||
|
.args(sources)
|
||||||
|
.arg("-emit-object")
|
||||||
|
.arg("-parse-as-library")
|
||||||
|
.args(&["-o", dest]);
|
||||||
|
|
||||||
|
if let Some(objc_header) = objc_header_path {
|
||||||
|
command.args(&["-import-objc-header", objc_header]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches!(opt_level, OptLevel::Optimize) {
|
||||||
|
command.arg("-O");
|
||||||
|
}
|
||||||
|
|
||||||
|
command.output().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rebuild_host(
|
pub fn rebuild_host(
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
target: &Triple,
|
target: &Triple,
|
||||||
|
@ -312,6 +346,9 @@ pub fn rebuild_host(
|
||||||
let rust_host_src = host_input_path.with_file_name("host.rs");
|
let rust_host_src = host_input_path.with_file_name("host.rs");
|
||||||
let rust_host_dest = host_input_path.with_file_name("rust_host.o");
|
let rust_host_dest = host_input_path.with_file_name("rust_host.o");
|
||||||
let cargo_host_src = host_input_path.with_file_name("Cargo.toml");
|
let cargo_host_src = host_input_path.with_file_name("Cargo.toml");
|
||||||
|
let swift_host_src = host_input_path.with_file_name("host.swift");
|
||||||
|
let swift_host_header_src = host_input_path.with_file_name("host.h");
|
||||||
|
|
||||||
let host_dest_native = host_input_path.with_file_name(if shared_lib_path.is_some() {
|
let host_dest_native = host_input_path.with_file_name(if shared_lib_path.is_some() {
|
||||||
"dynhost"
|
"dynhost"
|
||||||
} else {
|
} else {
|
||||||
|
@ -372,6 +409,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),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -526,6 +577,20 @@ pub fn rebuild_host(
|
||||||
shared_lib_path,
|
shared_lib_path,
|
||||||
);
|
);
|
||||||
validate_output("host.c", "clang", output);
|
validate_output("host.c", "clang", output);
|
||||||
|
} else if swift_host_src.exists() {
|
||||||
|
// Compile host.swift, if it exists
|
||||||
|
let output = build_swift_host_native(
|
||||||
|
&env_path,
|
||||||
|
&env_home,
|
||||||
|
host_dest_native.to_str().unwrap(),
|
||||||
|
&[swift_host_src.to_str().unwrap()],
|
||||||
|
opt_level,
|
||||||
|
shared_lib_path,
|
||||||
|
swift_host_header_src
|
||||||
|
.exists()
|
||||||
|
.then(|| swift_host_header_src.to_str().unwrap()),
|
||||||
|
);
|
||||||
|
validate_output("host.swift", "swiftc", output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +741,7 @@ fn link_linux(
|
||||||
.args(&[
|
.args(&[
|
||||||
"--gc-sections",
|
"--gc-sections",
|
||||||
"--eh-frame-hdr",
|
"--eh-frame-hdr",
|
||||||
"--arch",
|
"-A",
|
||||||
arch_str(target),
|
arch_str(target),
|
||||||
"-pie",
|
"-pie",
|
||||||
libcrt_path.join("crti.o").to_str().unwrap(),
|
libcrt_path.join("crti.o").to_str().unwrap(),
|
||||||
|
@ -724,22 +789,14 @@ fn link_macos(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This path only exists on macOS Big Sur, and it causes ld errors
|
|
||||||
// on Catalina if it's specified with -L, so we replace it with a
|
|
||||||
// redundant -lSystem if the directory isn't there.
|
|
||||||
let big_sur_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib";
|
|
||||||
let big_sur_fix = if Path::new(big_sur_path).exists() {
|
|
||||||
format!("-L{}", big_sur_path)
|
|
||||||
} else {
|
|
||||||
String::from("-lSystem")
|
|
||||||
};
|
|
||||||
|
|
||||||
let arch = match target.architecture {
|
let arch = match target.architecture {
|
||||||
Architecture::Aarch64(_) => "arm64".to_string(),
|
Architecture::Aarch64(_) => "arm64".to_string(),
|
||||||
_ => target.architecture.to_string(),
|
_ => target.architecture.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ld_child = Command::new("ld")
|
let mut ld_command = Command::new("ld");
|
||||||
|
|
||||||
|
ld_command
|
||||||
// NOTE: order of arguments to `ld` matters here!
|
// NOTE: order of arguments to `ld` matters here!
|
||||||
// The `-l` flags should go after the `.o` arguments
|
// The `-l` flags should go after the `.o` arguments
|
||||||
// Don't allow LD_ env vars to affect this
|
// Don't allow LD_ env vars to affect this
|
||||||
|
@ -753,34 +810,43 @@ fn link_macos(
|
||||||
link_type_arg,
|
link_type_arg,
|
||||||
"-arch",
|
"-arch",
|
||||||
&arch,
|
&arch,
|
||||||
|
"-macos_version_min",
|
||||||
|
"10.15",
|
||||||
])
|
])
|
||||||
.args(input_paths)
|
.args(input_paths);
|
||||||
.args(&[
|
|
||||||
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
let sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib";
|
||||||
// for discussion and further references
|
if Path::new(sdk_path).exists() {
|
||||||
&big_sur_fix,
|
ld_command.arg(format!("-L{}", sdk_path));
|
||||||
"-lSystem",
|
ld_command.arg(format!("-L{}/swift", sdk_path));
|
||||||
"-lresolv",
|
};
|
||||||
"-lpthread",
|
|
||||||
// "-lrt", // TODO shouldn't we need this?
|
ld_command.args(&[
|
||||||
// "-lc_nonshared", // TODO shouldn't we need this?
|
// Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274
|
||||||
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
// for discussion and further references
|
||||||
// "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli
|
"-lSystem",
|
||||||
// "Security",
|
"-lresolv",
|
||||||
// Output
|
"-lpthread",
|
||||||
"-o",
|
// "-lrt", // TODO shouldn't we need this?
|
||||||
output_path.to_str().unwrap(), // app
|
// "-lc_nonshared", // TODO shouldn't we need this?
|
||||||
])
|
// "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840
|
||||||
.spawn()?;
|
// "-framework", // Uncomment this line & the following ro run the `rand` crate in examples/cli
|
||||||
|
// "Security",
|
||||||
|
// Output
|
||||||
|
"-o",
|
||||||
|
output_path.to_str().unwrap(), // app
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut ld_child = ld_command.spawn()?;
|
||||||
|
|
||||||
match target.architecture {
|
match target.architecture {
|
||||||
Architecture::Aarch64(_) => {
|
Architecture::Aarch64(_) => {
|
||||||
ld_child.wait()?;
|
ld_child.wait()?;
|
||||||
let child = Command::new("codesign")
|
let codesign_child = Command::new("codesign")
|
||||||
.args(&["-s", "-", output_path.to_str().unwrap()])
|
.args(&["-s", "-", output_path.to_str().unwrap()])
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
|
|
||||||
Ok((child, output_path))
|
Ok((codesign_child, output_path))
|
||||||
}
|
}
|
||||||
_ => Ok((ld_child, output_path)),
|
_ => Ok((ld_child, output_path)),
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use roc_gen_llvm::llvm::build::module_from_builtins;
|
||||||
pub use roc_gen_llvm::llvm::build::FunctionIterator;
|
pub use roc_gen_llvm::llvm::build::FunctionIterator;
|
||||||
use roc_load::file::{LoadedModule, MonomorphizedModule};
|
use roc_load::file::{LoadedModule, MonomorphizedModule};
|
||||||
use roc_module::symbol::{Interns, ModuleId};
|
use roc_module::symbol::{Interns, ModuleId};
|
||||||
#[cfg(feature = "llvm")]
|
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -19,6 +18,7 @@ pub struct CodeGenTiming {
|
||||||
|
|
||||||
// TODO: If modules besides this one start needing to know which version of
|
// TODO: If modules besides this one start needing to know which version of
|
||||||
// llvm we're using, consider moving me somewhere else.
|
// llvm we're using, consider moving me somewhere else.
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
const LLVM_VERSION: &str = "12";
|
const LLVM_VERSION: &str = "12";
|
||||||
|
|
||||||
// TODO instead of finding exhaustiveness problems in monomorphization, find
|
// TODO instead of finding exhaustiveness problems in monomorphization, find
|
||||||
|
@ -171,6 +171,50 @@ fn report_problems_help(
|
||||||
problems_reported
|
problems_reported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "llvm"))]
|
||||||
|
pub fn gen_from_mono_module(
|
||||||
|
arena: &bumpalo::Bump,
|
||||||
|
loaded: MonomorphizedModule,
|
||||||
|
_roc_file_path: &Path,
|
||||||
|
target: &target_lexicon::Triple,
|
||||||
|
app_o_file: &Path,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
_emit_debug_info: bool,
|
||||||
|
) -> CodeGenTiming {
|
||||||
|
match opt_level {
|
||||||
|
OptLevel::Optimize => {
|
||||||
|
todo!("Return this error message in a better way: optimized builds not supported without llvm backend");
|
||||||
|
}
|
||||||
|
OptLevel::Normal | OptLevel::Development => {
|
||||||
|
gen_from_mono_module_dev(arena, loaded, target, app_o_file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
pub fn gen_from_mono_module(
|
||||||
|
arena: &bumpalo::Bump,
|
||||||
|
loaded: MonomorphizedModule,
|
||||||
|
roc_file_path: &Path,
|
||||||
|
target: &target_lexicon::Triple,
|
||||||
|
app_o_file: &Path,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
emit_debug_info: bool,
|
||||||
|
) -> CodeGenTiming {
|
||||||
|
match opt_level {
|
||||||
|
OptLevel::Normal | OptLevel::Optimize => gen_from_mono_module_llvm(
|
||||||
|
arena,
|
||||||
|
loaded,
|
||||||
|
roc_file_path,
|
||||||
|
target,
|
||||||
|
app_o_file,
|
||||||
|
opt_level,
|
||||||
|
emit_debug_info,
|
||||||
|
),
|
||||||
|
OptLevel::Development => gen_from_mono_module_dev(arena, loaded, target, app_o_file),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO how should imported modules factor into this? What if those use builtins too?
|
// TODO how should imported modules factor into this? What if those use builtins too?
|
||||||
// TODO this should probably use more helper functions
|
// TODO this should probably use more helper functions
|
||||||
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
||||||
|
@ -404,7 +448,7 @@ pub fn gen_from_mono_module_llvm(
|
||||||
emit_o_file,
|
emit_o_file,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "target-wasm32")]
|
||||||
pub fn gen_from_mono_module_dev(
|
pub fn gen_from_mono_module_dev(
|
||||||
arena: &bumpalo::Bump,
|
arena: &bumpalo::Bump,
|
||||||
loaded: MonomorphizedModule,
|
loaded: MonomorphizedModule,
|
||||||
|
@ -415,13 +459,31 @@ pub fn gen_from_mono_module_dev(
|
||||||
|
|
||||||
match target.architecture {
|
match target.architecture {
|
||||||
Architecture::Wasm32 => gen_from_mono_module_dev_wasm32(arena, loaded, app_o_file),
|
Architecture::Wasm32 => gen_from_mono_module_dev_wasm32(arena, loaded, app_o_file),
|
||||||
Architecture::X86_64 => {
|
Architecture::X86_64 | Architecture::Aarch64(_) => {
|
||||||
gen_from_mono_module_dev_assembly(arena, loaded, target, app_o_file)
|
gen_from_mono_module_dev_assembly(arena, loaded, target, app_o_file)
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "target-wasm32"))]
|
||||||
|
pub fn gen_from_mono_module_dev(
|
||||||
|
arena: &bumpalo::Bump,
|
||||||
|
loaded: MonomorphizedModule,
|
||||||
|
target: &target_lexicon::Triple,
|
||||||
|
app_o_file: &Path,
|
||||||
|
) -> CodeGenTiming {
|
||||||
|
use target_lexicon::Architecture;
|
||||||
|
|
||||||
|
match target.architecture {
|
||||||
|
Architecture::X86_64 | Architecture::Aarch64(_) => {
|
||||||
|
gen_from_mono_module_dev_assembly(arena, loaded, target, app_o_file)
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "target-wasm32")]
|
||||||
fn gen_from_mono_module_dev_wasm32(
|
fn gen_from_mono_module_dev_wasm32(
|
||||||
arena: &bumpalo::Bump,
|
arena: &bumpalo::Bump,
|
||||||
loaded: MonomorphizedModule,
|
loaded: MonomorphizedModule,
|
||||||
|
|
|
@ -48,7 +48,9 @@ pub fn target_triple_str(target: &Triple) -> &'static str {
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
pub fn init_arch(target: &Triple) {
|
pub fn init_arch(target: &Triple) {
|
||||||
match target.architecture {
|
match target.architecture {
|
||||||
Architecture::X86_64 | Architecture::X86_32(_) => {
|
Architecture::X86_64 | Architecture::X86_32(_)
|
||||||
|
if cfg!(any(feature = "target-x86", feature = "target-x86_64")) =>
|
||||||
|
{
|
||||||
Target::initialize_x86(&InitializationConfig::default());
|
Target::initialize_x86(&InitializationConfig::default());
|
||||||
}
|
}
|
||||||
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => {
|
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => {
|
||||||
|
@ -57,7 +59,7 @@ pub fn init_arch(target: &Triple) {
|
||||||
Architecture::Arm(_) if cfg!(feature = "target-arm") => {
|
Architecture::Arm(_) if cfg!(feature = "target-arm") => {
|
||||||
Target::initialize_arm(&InitializationConfig::default());
|
Target::initialize_arm(&InitializationConfig::default());
|
||||||
}
|
}
|
||||||
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => {
|
Architecture::Wasm32 if cfg!(feature = "target-wasm32") => {
|
||||||
Target::initialize_webassembly(&InitializationConfig::default());
|
Target::initialize_webassembly(&InitializationConfig::default());
|
||||||
}
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
|
@ -75,8 +77,8 @@ pub fn arch_str(target: &Triple) -> &'static str {
|
||||||
//
|
//
|
||||||
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
|
// https://stackoverflow.com/questions/15036909/clang-how-to-list-supported-target-architectures
|
||||||
match target.architecture {
|
match target.architecture {
|
||||||
Architecture::X86_64 => "x86-64",
|
Architecture::X86_64 if cfg!(feature = "target-x86_64") => "x86-64",
|
||||||
Architecture::X86_32(_) => "x86",
|
Architecture::X86_32(_) if cfg!(feature = "target-x86") => "x86",
|
||||||
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => "aarch64",
|
Architecture::Aarch64(_) if cfg!(feature = "target-aarch64") => "aarch64",
|
||||||
Architecture::Arm(_) if cfg!(feature = "target-arm") => "arm",
|
Architecture::Arm(_) if cfg!(feature = "target-arm") => "arm",
|
||||||
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => "wasm32",
|
Architecture::Wasm32 if cfg!(feature = "target-webassembly") => "wasm32",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::def::Def;
|
use crate::def::Def;
|
||||||
use crate::expr::Expr::*;
|
use crate::expr::{ClosureData, Expr::*};
|
||||||
use crate::expr::{Expr, Recursive, WhenBranch};
|
use crate::expr::{Expr, Recursive, WhenBranch};
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use roc_collections::all::SendMap;
|
use roc_collections::all::SendMap;
|
||||||
|
@ -2955,7 +2955,7 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
|
|
||||||
let wrapper = Closure {
|
let wrapper = Closure(ClosureData {
|
||||||
function_type: wrapper_var,
|
function_type: wrapper_var,
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -2969,7 +2969,7 @@ fn set_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
(Variable::EMPTY_RECORD, no_region(Pattern::Underscore)),
|
(Variable::EMPTY_RECORD, no_region(Pattern::Underscore)),
|
||||||
],
|
],
|
||||||
loc_body: Box::new(no_region(call_func)),
|
loc_body: Box::new(no_region(call_func)),
|
||||||
};
|
});
|
||||||
|
|
||||||
let body = RunLowLevel {
|
let body = RunLowLevel {
|
||||||
op: LowLevel::DictWalk,
|
op: LowLevel::DictWalk,
|
||||||
|
@ -3984,7 +3984,7 @@ fn defn_help(
|
||||||
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
.map(|(var, symbol)| (var, no_region(Identifier(symbol))))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -3994,7 +3994,7 @@ fn defn_help(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments: closure_args,
|
arguments: closure_args,
|
||||||
loc_body: Box::new(no_region(body)),
|
loc_body: Box::new(no_region(body)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::annotation::canonicalize_annotation;
|
use crate::annotation::canonicalize_annotation;
|
||||||
use crate::annotation::IntroducedVariables;
|
use crate::annotation::IntroducedVariables;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
|
use crate::expr::ClosureData;
|
||||||
use crate::expr::Expr::{self, *};
|
use crate::expr::Expr::{self, *};
|
||||||
use crate::expr::{
|
use crate::expr::{
|
||||||
canonicalize_expr, local_successors, references_from_call, references_from_local, Output,
|
canonicalize_expr, local_successors, references_from_call, references_from_local, Output,
|
||||||
|
@ -670,10 +671,10 @@ fn group_to_declaration(
|
||||||
let mut new_def = can_def.clone();
|
let mut new_def = can_def.clone();
|
||||||
|
|
||||||
// Determine recursivity of closures that are not tail-recursive
|
// Determine recursivity of closures that are not tail-recursive
|
||||||
if let Closure {
|
if let Closure(ClosureData {
|
||||||
recursive: recursive @ Recursive::NotRecursive,
|
recursive: recursive @ Recursive::NotRecursive,
|
||||||
..
|
..
|
||||||
} = &mut new_def.loc_expr.value
|
}) = &mut new_def.loc_expr.value
|
||||||
{
|
{
|
||||||
*recursive = closure_recursivity(*symbol, closures);
|
*recursive = closure_recursivity(*symbol, closures);
|
||||||
}
|
}
|
||||||
|
@ -698,10 +699,10 @@ fn group_to_declaration(
|
||||||
let mut new_def = can_def.clone();
|
let mut new_def = can_def.clone();
|
||||||
|
|
||||||
// Determine recursivity of closures that are not tail-recursive
|
// Determine recursivity of closures that are not tail-recursive
|
||||||
if let Closure {
|
if let Closure(ClosureData {
|
||||||
recursive: recursive @ Recursive::NotRecursive,
|
recursive: recursive @ Recursive::NotRecursive,
|
||||||
..
|
..
|
||||||
} = &mut new_def.loc_expr.value
|
}) = &mut new_def.loc_expr.value
|
||||||
{
|
{
|
||||||
*recursive = closure_recursivity(symbol, closures);
|
*recursive = closure_recursivity(symbol, closures);
|
||||||
}
|
}
|
||||||
|
@ -838,7 +839,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
Located {
|
Located {
|
||||||
value: Closure {
|
value: Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -848,7 +849,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments: underscores,
|
arguments: underscores,
|
||||||
loc_body: Box::new(body_expr),
|
loc_body: Box::new(body_expr),
|
||||||
},
|
}),
|
||||||
region: loc_ann.region,
|
region: loc_ann.region,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1001,7 +1002,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
if let (
|
if let (
|
||||||
&ast::Pattern::Identifier(_name),
|
&ast::Pattern::Identifier(_name),
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
&Pattern::Identifier(ref defined_symbol),
|
||||||
&Closure {
|
&Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1011,7 +1012,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
loc_body: ref body,
|
loc_body: ref body,
|
||||||
ref captured_symbols,
|
ref captured_symbols,
|
||||||
..
|
..
|
||||||
},
|
}),
|
||||||
) = (
|
) = (
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
&loc_can_pattern.value,
|
&loc_can_pattern.value,
|
||||||
|
@ -1049,7 +1050,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
});
|
});
|
||||||
|
|
||||||
// renamed_closure_def = Some(&defined_symbol);
|
// renamed_closure_def = Some(&defined_symbol);
|
||||||
loc_can_expr.value = Closure {
|
loc_can_expr.value = Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1059,7 +1060,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
recursive: is_recursive,
|
recursive: is_recursive,
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
loc_body: body.clone(),
|
loc_body: body.clone(),
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
|
@ -1144,7 +1145,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
if let (
|
if let (
|
||||||
&ast::Pattern::Identifier(_name),
|
&ast::Pattern::Identifier(_name),
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
&Pattern::Identifier(ref defined_symbol),
|
||||||
&Closure {
|
&Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1154,7 +1155,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
loc_body: ref body,
|
loc_body: ref body,
|
||||||
ref captured_symbols,
|
ref captured_symbols,
|
||||||
..
|
..
|
||||||
},
|
}),
|
||||||
) = (
|
) = (
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
&loc_can_pattern.value,
|
&loc_can_pattern.value,
|
||||||
|
@ -1191,7 +1192,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
refs.lookups = refs.lookups.without(defined_symbol);
|
refs.lookups = refs.lookups.without(defined_symbol);
|
||||||
});
|
});
|
||||||
|
|
||||||
loc_can_expr.value = Closure {
|
loc_can_expr.value = Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1201,7 +1202,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
recursive: is_recursive,
|
recursive: is_recursive,
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
loc_body: body.clone(),
|
loc_body: body.clone(),
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
|
|
|
@ -102,17 +102,7 @@ pub enum Expr {
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
},
|
},
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData),
|
||||||
function_type: Variable,
|
|
||||||
closure_type: Variable,
|
|
||||||
closure_ext_var: Variable,
|
|
||||||
return_type: Variable,
|
|
||||||
name: Symbol,
|
|
||||||
captured_symbols: Vec<(Symbol, Variable)>,
|
|
||||||
recursive: Recursive,
|
|
||||||
arguments: Vec<(Variable, Located<Pattern>)>,
|
|
||||||
loc_body: Box<Located<Expr>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Product Types
|
// Product Types
|
||||||
Record {
|
Record {
|
||||||
|
@ -173,6 +163,18 @@ pub enum Expr {
|
||||||
// Compiles, but will crash if reached
|
// Compiles, but will crash if reached
|
||||||
RuntimeError(RuntimeError),
|
RuntimeError(RuntimeError),
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct ClosureData {
|
||||||
|
pub function_type: Variable,
|
||||||
|
pub closure_type: Variable,
|
||||||
|
pub closure_ext_var: Variable,
|
||||||
|
pub return_type: Variable,
|
||||||
|
pub name: Symbol,
|
||||||
|
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||||
|
pub recursive: Recursive,
|
||||||
|
pub arguments: Vec<(Variable, Located<Pattern>)>,
|
||||||
|
pub loc_body: Box<Located<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
|
@ -572,7 +574,7 @@ pub fn canonicalize_expr<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -582,7 +584,7 @@ pub fn canonicalize_expr<'a>(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments: can_args,
|
arguments: can_args,
|
||||||
loc_body: Box::new(loc_body_expr),
|
loc_body: Box::new(loc_body_expr),
|
||||||
},
|
}),
|
||||||
output,
|
output,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1403,7 +1405,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
LetNonRec(Box::new(def), Box::new(loc_expr), var)
|
LetNonRec(Box::new(def), Box::new(loc_expr), var)
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1413,14 +1415,14 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
} => {
|
}) => {
|
||||||
let loc_expr = *loc_body;
|
let loc_expr = *loc_body;
|
||||||
let loc_expr = Located {
|
let loc_expr = Located {
|
||||||
value: inline_calls(var_store, scope, loc_expr.value),
|
value: inline_calls(var_store, scope, loc_expr.value),
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
};
|
};
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1430,7 +1432,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(loc_expr),
|
loc_body: Box::new(loc_expr),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Record { record_var, fields } => {
|
Record { record_var, fields } => {
|
||||||
|
@ -1492,12 +1494,12 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
loc_expr:
|
loc_expr:
|
||||||
Located {
|
Located {
|
||||||
value:
|
value:
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
recursive,
|
recursive,
|
||||||
arguments: params,
|
arguments: params,
|
||||||
loc_body: boxed_body,
|
loc_body: boxed_body,
|
||||||
..
|
..
|
||||||
},
|
}),
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::def::{canonicalize_defs, sort_can_defs, Declaration, Def};
|
use crate::def::{canonicalize_defs, sort_can_defs, Declaration, Def};
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use crate::expr::{Expr, Output};
|
use crate::expr::{ClosureData, Expr, Output};
|
||||||
use crate::operator::desugar_def;
|
use crate::operator::desugar_def;
|
||||||
use crate::pattern::Pattern;
|
use crate::pattern::Pattern;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
|
@ -416,13 +416,13 @@ fn fix_values_captured_in_closure_expr(
|
||||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
name,
|
name,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
loc_body,
|
||||||
..
|
..
|
||||||
} => {
|
}) => {
|
||||||
captured_symbols.retain(|(s, _)| !no_capture_symbols.contains(s));
|
captured_symbols.retain(|(s, _)| !no_capture_symbols.contains(s));
|
||||||
captured_symbols.retain(|(s, _)| s != name);
|
captured_symbols.retain(|(s, _)| s != name);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod test_can {
|
||||||
use crate::helpers::{can_expr_with, test_home, CanExprOut};
|
use crate::helpers::{can_expr_with, test_home, CanExprOut};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::expr::Expr::{self, *};
|
use roc_can::expr::Expr::{self, *};
|
||||||
use roc_can::expr::Recursive;
|
use roc_can::expr::{ClosureData, Recursive};
|
||||||
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
use roc_problem::can::{CycleEntry, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use std::{f64, i64};
|
use std::{f64, i64};
|
||||||
|
@ -654,10 +654,10 @@ mod test_can {
|
||||||
match expr {
|
match expr {
|
||||||
LetRec(assignments, body, _) => {
|
LetRec(assignments, body, _) => {
|
||||||
match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
match &assignments.get(i).map(|def| &def.loc_expr.value) {
|
||||||
Some(Closure {
|
Some(Closure(ClosureData {
|
||||||
recursive: recursion,
|
recursive: recursion,
|
||||||
..
|
..
|
||||||
}) => recursion.clone(),
|
})) => recursion.clone(),
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||||
}
|
}
|
||||||
|
@ -676,10 +676,10 @@ mod test_can {
|
||||||
get_closure(&body.value, i - 1)
|
get_closure(&body.value, i - 1)
|
||||||
} else {
|
} else {
|
||||||
match &def.loc_expr.value {
|
match &def.loc_expr.value {
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
recursive: recursion,
|
recursive: recursion,
|
||||||
..
|
..
|
||||||
} => recursion.clone(),
|
}) => recursion.clone(),
|
||||||
other => {
|
other => {
|
||||||
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
panic!("assignment at {} is not a closure, but a {:?}", i, other)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_can::def::{Declaration, Def};
|
||||||
use roc_can::expected::Expected::{self, *};
|
use roc_can::expected::Expected::{self, *};
|
||||||
use roc_can::expected::PExpected;
|
use roc_can::expected::PExpected;
|
||||||
use roc_can::expr::Expr::{self, *};
|
use roc_can::expr::Expr::{self, *};
|
||||||
use roc_can::expr::{Field, WhenBranch};
|
use roc_can::expr::{ClosureData, Field, WhenBranch};
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_collections::all::{ImMap, Index, MutSet, SendMap};
|
use roc_collections::all::{ImMap, Index, MutSet, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
|
@ -333,7 +333,7 @@ pub fn constrain_expr(
|
||||||
// make lookup constraint to lookup this symbol's type in the environment
|
// make lookup constraint to lookup this symbol's type in the environment
|
||||||
Lookup(*symbol, expected, region)
|
Lookup(*symbol, expected, region)
|
||||||
}
|
}
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: fn_var,
|
function_type: fn_var,
|
||||||
closure_type: closure_var,
|
closure_type: closure_var,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -343,7 +343,7 @@ pub fn constrain_expr(
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
name,
|
name,
|
||||||
..
|
..
|
||||||
} => {
|
}) => {
|
||||||
// NOTE defs are treated somewhere else!
|
// NOTE defs are treated somewhere else!
|
||||||
let loc_body_expr = &**boxed;
|
let loc_body_expr = &**boxed;
|
||||||
|
|
||||||
|
@ -1203,7 +1203,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||||
// instead of the more generic "something is wrong with the body of `f`"
|
// instead of the more generic "something is wrong with the body of `f`"
|
||||||
match (&def.loc_expr.value, &signature) {
|
match (&def.loc_expr.value, &signature) {
|
||||||
(
|
(
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: fn_var,
|
function_type: fn_var,
|
||||||
closure_type: closure_var,
|
closure_type: closure_var,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1213,7 +1213,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||||
loc_body,
|
loc_body,
|
||||||
name,
|
name,
|
||||||
..
|
..
|
||||||
},
|
}),
|
||||||
Type::Function(arg_types, signature_closure_type, ret_type),
|
Type::Function(arg_types, signature_closure_type, ret_type),
|
||||||
) => {
|
) => {
|
||||||
// NOTE if we ever have problems with the closure, the ignored `_closure_type`
|
// NOTE if we ever have problems with the closure, the ignored `_closure_type`
|
||||||
|
@ -1561,7 +1561,7 @@ pub fn rec_defs_help(
|
||||||
// instead of the more generic "something is wrong with the body of `f`"
|
// instead of the more generic "something is wrong with the body of `f`"
|
||||||
match (&def.loc_expr.value, &signature) {
|
match (&def.loc_expr.value, &signature) {
|
||||||
(
|
(
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: fn_var,
|
function_type: fn_var,
|
||||||
closure_type: closure_var,
|
closure_type: closure_var,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
|
@ -1571,7 +1571,7 @@ pub fn rec_defs_help(
|
||||||
loc_body,
|
loc_body,
|
||||||
name,
|
name,
|
||||||
..
|
..
|
||||||
},
|
}),
|
||||||
Type::Function(arg_types, _closure_type, ret_type),
|
Type::Function(arg_types, _closure_type, ret_type),
|
||||||
) => {
|
) => {
|
||||||
// NOTE if we ever have trouble with closure type unification, the ignored
|
// NOTE if we ever have trouble with closure type unification, the ignored
|
||||||
|
|
|
@ -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,25 +20,26 @@ 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]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
|
roc_build = { path = "../build" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
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 = []
|
||||||
|
target-x86_64 = []
|
||||||
|
|
|
@ -831,6 +831,7 @@ impl<
|
||||||
fields: &'a [Symbol],
|
fields: &'a [Symbol],
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
|
|
||||||
if let Layout::Struct(field_layouts) = layout {
|
if let Layout::Struct(field_layouts) = layout {
|
||||||
if struct_size > 0 {
|
if struct_size > 0 {
|
||||||
let offset = self.claim_stack_size(struct_size)?;
|
let offset = self.claim_stack_size(struct_size)?;
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub fn build_module<'a>(
|
||||||
architecture: TargetArch::X86_64,
|
architecture: TargetArch::X86_64,
|
||||||
binary_format: TargetBF::Elf,
|
binary_format: TargetBF::Elf,
|
||||||
..
|
..
|
||||||
} => {
|
} if cfg!(feature = "target-x86_64") => {
|
||||||
let backend: Backend64Bit<
|
let backend: Backend64Bit<
|
||||||
x86_64::X86_64GeneralReg,
|
x86_64::X86_64GeneralReg,
|
||||||
x86_64::X86_64FloatReg,
|
x86_64::X86_64FloatReg,
|
||||||
|
@ -46,7 +46,7 @@ pub fn build_module<'a>(
|
||||||
architecture: TargetArch::X86_64,
|
architecture: TargetArch::X86_64,
|
||||||
binary_format: TargetBF::Macho,
|
binary_format: TargetBF::Macho,
|
||||||
..
|
..
|
||||||
} => {
|
} if cfg!(feature = "target-x86_64") => {
|
||||||
let backend: Backend64Bit<
|
let backend: Backend64Bit<
|
||||||
x86_64::X86_64GeneralReg,
|
x86_64::X86_64GeneralReg,
|
||||||
x86_64::X86_64FloatReg,
|
x86_64::X86_64FloatReg,
|
||||||
|
@ -68,7 +68,7 @@ pub fn build_module<'a>(
|
||||||
architecture: TargetArch::Aarch64(_),
|
architecture: TargetArch::Aarch64(_),
|
||||||
binary_format: TargetBF::Elf,
|
binary_format: TargetBF::Elf,
|
||||||
..
|
..
|
||||||
} => {
|
} if cfg!(feature = "target-aarch64") => {
|
||||||
let backend: Backend64Bit<
|
let backend: Backend64Bit<
|
||||||
aarch64::AArch64GeneralReg,
|
aarch64::AArch64GeneralReg,
|
||||||
aarch64::AArch64FloatReg,
|
aarch64::AArch64FloatReg,
|
||||||
|
|
|
@ -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,12 +10,9 @@ roc_collections = { path = "../collections" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
# 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" }
|
|
||||||
|
|
||||||
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 +22,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"
|
||||||
|
|
|
@ -225,6 +225,4 @@ The Module is a _specification_ for how to create an Instance of the program. Th
|
||||||
|
|
||||||
A WebAssembly module is equivalent to an executable file. It doesn't normally need relocations since at the WebAssembly layer, there is no Address Space Layout Randomisation. If it has relocations then it's an object file.
|
A WebAssembly module is equivalent to an executable file. It doesn't normally need relocations since at the WebAssembly layer, there is no Address Space Layout Randomisation. If it has relocations then it's an object file.
|
||||||
|
|
||||||
The [official spec](https://webassembly.github.io/spec/core/binary/modules.html#sections) lists the sections that are part of the final module. It doesn't mention any sections for relocations or symbol names, but it has room for "custom sections" that in practice seem to be used for that.
|
The [official spec](https://webassembly.github.io/spec/core/binary/modules.html#sections) lists the sections that are part of the final module. It doesn't mention any sections for relocations or symbol names, but it does support "custom" sections. Conventions to use those for linking are documented in the WebAssembly `tool-conventions` repo [here](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md) and it mentions that LLVM is using those conventions.
|
||||||
|
|
||||||
The WebAssembly `tool-conventions` repo has a document on [linking](https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md), and the `parity_wasm` crate supports "name" and "relocation" [sections](https://docs.rs/parity-wasm/0.42.2/parity_wasm/elements/enum.Section.html).
|
|
||||||
|
|
|
@ -1,37 +1,36 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use parity_wasm::builder;
|
|
||||||
use parity_wasm::builder::{FunctionDefinition, ModuleBuilder};
|
|
||||||
|
|
||||||
|
use code_builder::Align;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::Layout;
|
||||||
|
|
||||||
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, CopyMemoryConfig, Env, LocalId, PTR_TYPE};
|
use crate::wasm_module::linking::{LinkingSection, RelocationSection};
|
||||||
|
use crate::wasm_module::sections::{
|
||||||
|
CodeSection, DataMode, DataSection, DataSegment, ExportSection, FunctionSection, GlobalSection,
|
||||||
|
ImportSection, MemorySection, TypeSection, WasmModule,
|
||||||
|
};
|
||||||
|
use crate::wasm_module::{
|
||||||
|
code_builder, BlockType, CodeBuilder, ConstExpr, Export, ExportType, Global, GlobalType,
|
||||||
|
LocalId, Signature, ValueType,
|
||||||
|
};
|
||||||
|
use crate::{copy_memory, CopyMemoryConfig, Env, 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)
|
||||||
const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
|
const UNUSED_DATA_SECTION_BYTES: u32 = 1024;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
struct LabelId(u32);
|
|
||||||
|
|
||||||
pub struct WasmBackend<'a> {
|
pub struct WasmBackend<'a> {
|
||||||
env: &'a Env<'a>,
|
env: &'a Env<'a>,
|
||||||
|
|
||||||
// Module-level data
|
// Module-level data
|
||||||
pub module_builder: ModuleBuilder,
|
pub module: WasmModule<'a>,
|
||||||
pub code_section_bytes: std::vec::Vec<u8>,
|
next_literal_addr: u32,
|
||||||
pub code_relocations: Vec<'a, RelocationEntry>,
|
proc_symbols: Vec<'a, Symbol>,
|
||||||
_data_offset_map: MutMap<Literal<'a>, u32>,
|
|
||||||
_data_offset_next: u32,
|
|
||||||
proc_symbols: &'a [Symbol],
|
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
code_builder: CodeBuilder<'a>,
|
code_builder: CodeBuilder<'a>,
|
||||||
|
@ -43,23 +42,56 @@ pub struct WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WasmBackend<'a> {
|
impl<'a> WasmBackend<'a> {
|
||||||
pub fn new(env: &'a Env<'a>, proc_symbols: &'a [Symbol]) -> Self {
|
pub fn new(env: &'a Env<'a>, proc_symbols: Vec<'a, Symbol>) -> Self {
|
||||||
let mut code_section_bytes = std::vec::Vec::with_capacity(4096);
|
const MEMORY_INIT_SIZE: u32 = 1024 * 1024;
|
||||||
|
|
||||||
// Code section header
|
let mut module = WasmModule {
|
||||||
code_section_bytes.reserve_padded_u32(); // byte length, to be written at the end
|
types: TypeSection::new(env.arena),
|
||||||
code_section_bytes.encode_padded_u32(proc_symbols.len() as u32); // modified later in unit tests
|
import: ImportSection::new(env.arena),
|
||||||
|
function: FunctionSection::new(env.arena),
|
||||||
|
table: (), // Unused in Roc (mainly for function pointers)
|
||||||
|
memory: MemorySection::new(MEMORY_INIT_SIZE),
|
||||||
|
global: GlobalSection::new(env.arena),
|
||||||
|
export: ExportSection::new(env.arena),
|
||||||
|
start: (), // Entry function. In Roc this would be part of the platform.
|
||||||
|
element: (), // Unused in Roc (related to table section)
|
||||||
|
code: CodeSection::new(env.arena),
|
||||||
|
data: DataSection::new(env.arena),
|
||||||
|
linking: LinkingSection::new(env.arena),
|
||||||
|
reloc_code: RelocationSection::new(env.arena, "reloc.CODE"),
|
||||||
|
reloc_data: RelocationSection::new(env.arena, "reloc.DATA"),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.export.entries.push(Export {
|
||||||
|
name: "memory".to_string(),
|
||||||
|
ty: ExportType::Mem,
|
||||||
|
index: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let stack_pointer_global = Global {
|
||||||
|
ty: GlobalType {
|
||||||
|
value_type: ValueType::I32,
|
||||||
|
is_mutable: true,
|
||||||
|
},
|
||||||
|
init: ConstExpr::I32(MEMORY_INIT_SIZE as i32),
|
||||||
|
};
|
||||||
|
module.global.entries.push(stack_pointer_global);
|
||||||
|
|
||||||
|
let literal_segment = DataSegment {
|
||||||
|
mode: DataMode::Active {
|
||||||
|
offset: ConstExpr::I32(UNUSED_DATA_SECTION_BYTES as i32),
|
||||||
|
},
|
||||||
|
init: Vec::with_capacity_in(64, env.arena),
|
||||||
|
};
|
||||||
|
module.data.segments.push(literal_segment);
|
||||||
|
|
||||||
WasmBackend {
|
WasmBackend {
|
||||||
env,
|
env,
|
||||||
|
|
||||||
// Module-level data
|
// Module-level data
|
||||||
module_builder: builder::module(),
|
module,
|
||||||
code_section_bytes,
|
next_literal_addr: UNUSED_DATA_SECTION_BYTES,
|
||||||
_data_offset_map: MutMap::default(),
|
|
||||||
_data_offset_next: UNUSED_DATA_SECTION_BYTES,
|
|
||||||
proc_symbols,
|
proc_symbols,
|
||||||
code_relocations: Vec::with_capacity_in(256, env.arena),
|
|
||||||
|
|
||||||
// Function-level data
|
// Function-level data
|
||||||
block_depth: 0,
|
block_depth: 0,
|
||||||
|
@ -69,8 +101,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset function-level data
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.code_builder.clear();
|
// Push the completed CodeBuilder into the module and swap it for a new empty one
|
||||||
|
let mut swap_code_builder = CodeBuilder::new(self.env.arena);
|
||||||
|
std::mem::swap(&mut swap_code_builder, &mut self.code_builder);
|
||||||
|
self.module.code.code_builders.push(swap_code_builder);
|
||||||
|
|
||||||
self.storage.clear();
|
self.storage.clear();
|
||||||
self.joinpoint_label_map.clear();
|
self.joinpoint_label_map.clear();
|
||||||
assert_eq!(self.block_depth, 0);
|
assert_eq!(self.block_depth, 0);
|
||||||
|
@ -82,52 +119,43 @@ 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<(), String> {
|
||||||
// println!("\ngenerating procedure {:?}\n", sym);
|
// println!("\ngenerating procedure {:?}\n", _sym);
|
||||||
|
|
||||||
// Use parity-wasm to add the signature in "types" and "functions" sections
|
self.start_proc(&proc);
|
||||||
// but no instructions, since we are building our own code section
|
|
||||||
let empty_function_def = self.start_proc(&proc);
|
|
||||||
let location = self.module_builder.push_function(empty_function_def);
|
|
||||||
let function_index = location.body;
|
|
||||||
|
|
||||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||||
|
|
||||||
self.finalize_proc()?;
|
self.finalize_proc()?;
|
||||||
self.reset();
|
self.reset();
|
||||||
|
|
||||||
// println!("\nfinished generating {:?}\n", sym);
|
// println!("\nfinished generating {:?}\n", _sym);
|
||||||
|
|
||||||
Ok(function_index)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_proc(&mut self, proc: &Proc<'a>) -> FunctionDefinition {
|
fn start_proc(&mut self, proc: &Proc<'a>) {
|
||||||
let ret_layout = WasmLayout::new(&proc.ret_layout);
|
let ret_layout = WasmLayout::new(&proc.ret_layout);
|
||||||
|
let ret_type = if ret_layout.is_stack_memory() {
|
||||||
let signature_builder = if let WasmLayout::StackMemory { .. } = ret_layout {
|
|
||||||
self.storage.arg_types.push(PTR_TYPE);
|
self.storage.arg_types.push(PTR_TYPE);
|
||||||
self.start_block(BlockType::NoResult); // block to ensure all paths pop stack memory (if any)
|
self.start_block(BlockType::NoResult); // block to ensure all paths pop stack memory (if any)
|
||||||
builder::signature()
|
None
|
||||||
} else {
|
} else {
|
||||||
let ret_type = ret_layout.value_type();
|
let ty = ret_layout.value_type();
|
||||||
self.start_block(BlockType::Value(ret_type)); // block to ensure all paths pop stack memory (if any)
|
self.start_block(BlockType::Value(ty)); // block to ensure all paths pop stack memory (if any)
|
||||||
builder::signature().with_result(ret_type.to_parity_wasm())
|
Some(ty)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (layout, symbol) in proc.args {
|
for (layout, symbol) in proc.args {
|
||||||
self.storage.allocate(
|
let arg_layout = WasmLayout::new(layout);
|
||||||
&WasmLayout::new(layout),
|
self.storage
|
||||||
*symbol,
|
.allocate(&arg_layout, *symbol, StoredValueKind::Parameter);
|
||||||
StoredValueKind::Parameter,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let parity_params = self.storage.arg_types.iter().map(|t| t.to_parity_wasm());
|
self.module.add_function_signature(Signature {
|
||||||
|
param_types: self.storage.arg_types.clone(),
|
||||||
let signature = signature_builder.with_params(parity_params).build_sig();
|
ret_type,
|
||||||
|
});
|
||||||
// parity-wasm FunctionDefinition with no instructions
|
|
||||||
builder::function().with_signature(signature).build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_proc(&mut self) -> Result<(), String> {
|
fn finalize_proc(&mut self) -> Result<(), String> {
|
||||||
|
@ -141,8 +169,6 @@ impl<'a> WasmBackend<'a> {
|
||||||
self.storage.stack_frame_pointer,
|
self.storage.stack_frame_pointer,
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocs = self.code_builder.serialize(&mut self.code_section_bytes);
|
|
||||||
self.code_relocations.extend(relocs);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,9 +204,9 @@ impl<'a> WasmBackend<'a> {
|
||||||
_ => StoredValueKind::Variable,
|
_ => StoredValueKind::Variable,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.storage.allocate(&wasm_layout, *sym, kind);
|
let sym_storage = self.storage.allocate(&wasm_layout, *sym, kind);
|
||||||
|
|
||||||
self.build_expr(sym, expr, layout)?;
|
self.build_expr(sym, expr, layout, &sym_storage)?;
|
||||||
|
|
||||||
// For primitives, we record that this symbol is at the top of the VM stack
|
// For primitives, we record that this symbol is at the top of the VM stack
|
||||||
// (For other values, we wrote to memory and there's nothing on the VM stack)
|
// (For other values, we wrote to memory and there's nothing on the VM stack)
|
||||||
|
@ -367,9 +393,11 @@ impl<'a> WasmBackend<'a> {
|
||||||
sym: &Symbol,
|
sym: &Symbol,
|
||||||
expr: &Expr<'a>,
|
expr: &Expr<'a>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
storage: &StoredValue,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let wasm_layout = WasmLayout::new(layout);
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal(lit) => self.load_literal(lit, layout),
|
Expr::Literal(lit) => self.load_literal(lit, storage),
|
||||||
|
|
||||||
Expr::Call(roc_mono::ir::Call {
|
Expr::Call(roc_mono::ir::Call {
|
||||||
call_type,
|
call_type,
|
||||||
|
@ -379,7 +407,6 @@ impl<'a> WasmBackend<'a> {
|
||||||
// TODO: See if we can make this more efficient
|
// TODO: See if we can make this more efficient
|
||||||
// Recreating the same WasmLayout again, rather than passing it down,
|
// Recreating the same WasmLayout again, rather than passing it down,
|
||||||
// to match signature of Backend::build_expr
|
// to match signature of Backend::build_expr
|
||||||
let wasm_layout = WasmLayout::new(layout);
|
|
||||||
|
|
||||||
let mut wasm_args_tmp: Vec<Symbol>;
|
let mut wasm_args_tmp: Vec<Symbol>;
|
||||||
let (wasm_args, has_return_val) = match wasm_layout {
|
let (wasm_args, has_return_val) = match wasm_layout {
|
||||||
|
@ -433,32 +460,69 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_literal(&mut self, lit: &Literal<'a>, layout: &Layout<'a>) -> Result<(), String> {
|
fn load_literal(&mut self, lit: &Literal<'a>, storage: &StoredValue) -> Result<(), String> {
|
||||||
match lit {
|
let not_supported_error = || Err(format!("Literal value {:?} is not yet implemented", lit));
|
||||||
Literal::Bool(x) => self.code_builder.i32_const(*x as i32),
|
|
||||||
Literal::Byte(x) => self.code_builder.i32_const(*x as i32),
|
match storage {
|
||||||
Literal::Int(x) => match layout {
|
StoredValue::VirtualMachineStack { value_type, .. } => {
|
||||||
Layout::Builtin(Builtin::Int64) => self.code_builder.i64_const(*x as i64),
|
match (lit, value_type) {
|
||||||
Layout::Builtin(
|
(Literal::Float(x), ValueType::F64) => self.code_builder.f64_const(*x as f64),
|
||||||
Builtin::Int32
|
(Literal::Float(x), ValueType::F32) => self.code_builder.f32_const(*x as f32),
|
||||||
| Builtin::Int16
|
(Literal::Int(x), ValueType::I64) => self.code_builder.i64_const(*x as i64),
|
||||||
| Builtin::Int8
|
(Literal::Int(x), ValueType::I32) => self.code_builder.i32_const(*x as i32),
|
||||||
| Builtin::Int1
|
(Literal::Bool(x), ValueType::I32) => self.code_builder.i32_const(*x as i32),
|
||||||
| Builtin::Usize,
|
(Literal::Byte(x), ValueType::I32) => self.code_builder.i32_const(*x as i32),
|
||||||
) => self.code_builder.i32_const(*x as i32),
|
_ => {
|
||||||
x => {
|
return not_supported_error();
|
||||||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredValue::StackMemory { location, .. } => match lit {
|
||||||
|
Literal::Str(s) => {
|
||||||
|
// Load the stack memory address where we want to write
|
||||||
|
let (local_id, offset) =
|
||||||
|
location.local_and_offset(self.storage.stack_frame_pointer);
|
||||||
|
self.code_builder.get_local(local_id);
|
||||||
|
self.code_builder.i32_const(offset as i32);
|
||||||
|
self.code_builder.i32_add();
|
||||||
|
|
||||||
|
// For either small or regular strings, we write 8 bytes to stack memory
|
||||||
|
let mut stack_mem_bytes = [0; 8];
|
||||||
|
|
||||||
|
let len = s.len();
|
||||||
|
if len < 8 {
|
||||||
|
// Small string payload
|
||||||
|
stack_mem_bytes[0..len].clone_from_slice(s.as_bytes());
|
||||||
|
// Small string flag and length
|
||||||
|
stack_mem_bytes[7] = 0x80 | (len as u8);
|
||||||
|
} else {
|
||||||
|
// Store the bytes in the data section, to be initialised on module load.
|
||||||
|
// Point there instead of to the heap
|
||||||
|
let literal_bytes: &mut Vec<'a, u8> =
|
||||||
|
&mut self.module.data.segments[0].init;
|
||||||
|
literal_bytes.extend_from_slice(s.as_bytes());
|
||||||
|
|
||||||
|
// Calculate bytes for elements and length
|
||||||
|
let len32 = len as u32;
|
||||||
|
let elements_addr = self.next_literal_addr;
|
||||||
|
self.next_literal_addr += len32;
|
||||||
|
stack_mem_bytes[0..3].clone_from_slice(&elements_addr.to_le_bytes());
|
||||||
|
stack_mem_bytes[4..8].clone_from_slice(&len32.to_le_bytes());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Since we have 64 bits of data, we can squeeze them into one instruction
|
||||||
|
self.code_builder
|
||||||
|
.i64_const(i64::from_le_bytes(stack_mem_bytes));
|
||||||
|
self.code_builder.i64_store(Align::Bytes4, offset);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return not_supported_error();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Literal::Float(x) => match layout {
|
|
||||||
Layout::Builtin(Builtin::Float64) => self.code_builder.f64_const(*x as f64),
|
_ => {
|
||||||
Layout::Builtin(Builtin::Float32) => self.code_builder.f32_const(*x as f32),
|
return not_supported_error();
|
||||||
x => {
|
|
||||||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
x => {
|
|
||||||
return Err(format!("loading literal, {:?}, is not yet implemented", x));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use roc_mono::layout::{Layout, UnionLayout};
|
use roc_mono::layout::{Layout, UnionLayout};
|
||||||
|
|
||||||
use crate::{code_builder::ValueType, PTR_SIZE, PTR_TYPE};
|
use crate::{wasm_module::ValueType, PTR_SIZE, PTR_TYPE};
|
||||||
|
|
||||||
// See README for background information on Wasm locals, memory and function calls
|
// See README for background information on Wasm locals, memory and function calls
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -71,11 +71,7 @@ impl WasmLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn is_stack_memory(&self) -> bool {
|
||||||
pub fn stack_memory(&self) -> u32 {
|
matches!(self, Self::StackMemory { .. })
|
||||||
match self {
|
|
||||||
Self::StackMemory { size, .. } => *size,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
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;
|
||||||
|
pub mod wasm_module;
|
||||||
|
|
||||||
use bumpalo::{self, collections::Vec, Bump};
|
use bumpalo::{self, collections::Vec, Bump};
|
||||||
use parity_wasm::builder;
|
|
||||||
|
|
||||||
use parity_wasm::elements::{Instruction, Internal, Module, Section};
|
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::ir::{Proc, ProcLayout};
|
use roc_mono::ir::{Proc, ProcLayout};
|
||||||
use roc_mono::layout::LayoutIds;
|
use roc_mono::layout::LayoutIds;
|
||||||
|
|
||||||
use crate::backend::WasmBackend;
|
use crate::backend::WasmBackend;
|
||||||
use crate::code_builder::{Align, CodeBuilder, ValueType};
|
use crate::wasm_module::{
|
||||||
use crate::module_builder::{
|
Align, CodeBuilder, Export, ExportType, LinkingSubSection, LocalId, SymInfo, ValueType,
|
||||||
LinkingSection, LinkingSubSection, RelocationSection, SectionId, SymInfo,
|
WasmModule,
|
||||||
};
|
};
|
||||||
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;
|
||||||
|
@ -29,13 +23,6 @@ const PTR_TYPE: ValueType = ValueType::I32;
|
||||||
pub const STACK_POINTER_GLOBAL_ID: u32 = 0;
|
pub const STACK_POINTER_GLOBAL_ID: u32 = 0;
|
||||||
pub const FRAME_ALIGNMENT_BYTES: i32 = 16;
|
pub const FRAME_ALIGNMENT_BYTES: i32 = 16;
|
||||||
|
|
||||||
/// Code section ID from spec
|
|
||||||
/// https://webassembly.github.io/spec/core/binary/modules.html#sections
|
|
||||||
pub const CODE_SECTION_ID: u8 = 10;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct LocalId(pub u32);
|
|
||||||
|
|
||||||
pub struct Env<'a> {
|
pub struct Env<'a> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub interns: Interns,
|
pub interns: Interns,
|
||||||
|
@ -46,21 +33,19 @@ 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 mut wasm_module = build_module_help(env, procedures)?;
|
||||||
let mut module = builder.build();
|
let mut buffer = std::vec::Vec::with_capacity(4096);
|
||||||
replace_code_section(&mut module, code_section_bytes);
|
wasm_module.serialize(&mut buffer);
|
||||||
|
Ok(buffer)
|
||||||
module
|
|
||||||
.into_bytes()
|
|
||||||
.map_err(|e| -> String { format!("Error serialising Wasm module {:?}", e) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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>), String> {
|
) -> Result<WasmModule<'a>, String> {
|
||||||
let proc_symbols = Vec::from_iter_in(procedures.keys().map(|(sym, _)| *sym), env.arena);
|
let proc_symbols = Vec::from_iter_in(procedures.keys().map(|(sym, _)| *sym), env.arena);
|
||||||
let mut backend = WasmBackend::new(env, &proc_symbols);
|
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);
|
let mut symbol_table_entries = Vec::with_capacity_in(procedures.len(), env.arena);
|
||||||
|
|
||||||
|
@ -70,97 +55,25 @@ pub fn build_module_help<'a>(
|
||||||
.to_symbol_string(proc.name, &env.interns);
|
.to_symbol_string(proc.name, &env.interns);
|
||||||
symbol_table_entries.push(SymInfo::for_function(i as u32, proc_name));
|
symbol_table_entries.push(SymInfo::for_function(i as u32, proc_name));
|
||||||
|
|
||||||
let function_index = backend.build_proc(proc, sym)?;
|
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)
|
||||||
.to_symbol_string(sym, &env.interns);
|
.to_symbol_string(sym, &env.interns);
|
||||||
|
|
||||||
let export = builder::export()
|
backend.module.export.entries.push(Export {
|
||||||
.field(fn_name.as_str())
|
name: fn_name,
|
||||||
.with_internal(Internal::Function(function_index))
|
ty: ExportType::Func,
|
||||||
.build();
|
index: i as u32,
|
||||||
|
});
|
||||||
backend.module_builder.push_export(export);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update code section length
|
let symbol_table = LinkingSubSection::SymbolTable(symbol_table_entries);
|
||||||
let inner_length = (backend.code_section_bytes.len() - 5) as u32;
|
backend.module.linking.subsections.push(symbol_table);
|
||||||
backend
|
|
||||||
.code_section_bytes
|
|
||||||
.overwrite_padded_u32(0, inner_length);
|
|
||||||
|
|
||||||
// linking metadata section
|
Ok(backend.module)
|
||||||
let mut linking_section_bytes = std::vec::Vec::with_capacity(symbol_table_entries.len() * 20);
|
|
||||||
let linking_section = LinkingSection {
|
|
||||||
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 PAGE_SIZE_KB: u32 = 64;
|
|
||||||
|
|
||||||
let memory = builder::MemoryBuilder::new()
|
|
||||||
.with_min(MIN_MEMORY_SIZE_KB / PAGE_SIZE_KB)
|
|
||||||
.build();
|
|
||||||
backend.module_builder.push_memory(memory);
|
|
||||||
let memory_export = builder::export()
|
|
||||||
.field("memory")
|
|
||||||
.with_internal(Internal::Memory(0))
|
|
||||||
.build();
|
|
||||||
backend.module_builder.push_export(memory_export);
|
|
||||||
|
|
||||||
let stack_pointer_global = builder::global()
|
|
||||||
.with_type(parity_wasm::elements::ValueType::I32)
|
|
||||||
.mutable()
|
|
||||||
.init_expr(Instruction::I32Const((MIN_MEMORY_SIZE_KB * 1024) as i32))
|
|
||||||
.build();
|
|
||||||
backend.module_builder.push_global(stack_pointer_global);
|
|
||||||
|
|
||||||
Ok((backend.module_builder, backend.code_section_bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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>) {
|
|
||||||
let sections = module.sections_mut();
|
|
||||||
|
|
||||||
let code_section_index = sections
|
|
||||||
.iter()
|
|
||||||
.position(|s| matches!(s, Section::Code(_)))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
sections[code_section_index] = Section::Unparsed {
|
|
||||||
id: SectionId::Code as u8,
|
|
||||||
payload: code_section_bytes,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CopyMemoryConfig {
|
pub struct CopyMemoryConfig {
|
||||||
|
|
|
@ -4,9 +4,9 @@ use bumpalo::Bump;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use crate::code_builder::{CodeBuilder, ValueType, VirtualMachineSymbolState};
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::WasmLayout;
|
||||||
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, LocalId, PTR_SIZE, PTR_TYPE};
|
use crate::wasm_module::{CodeBuilder, LocalId, ValueType, VirtualMachineSymbolState};
|
||||||
|
use crate::{copy_memory, round_up_to_alignment, CopyMemoryConfig, PTR_SIZE, PTR_TYPE};
|
||||||
|
|
||||||
pub enum StoredValueKind {
|
pub enum StoredValueKind {
|
||||||
Parameter,
|
Parameter,
|
||||||
|
@ -291,7 +291,7 @@ impl<'a> Storage<'a> {
|
||||||
| StoredValue::Local {
|
| StoredValue::Local {
|
||||||
value_type, size, ..
|
value_type, size, ..
|
||||||
} => {
|
} => {
|
||||||
use crate::code_builder::Align::*;
|
use crate::wasm_module::Align::*;
|
||||||
code_builder.get_local(to_ptr);
|
code_builder.get_local(to_ptr);
|
||||||
self.load_symbols(code_builder, &[from_symbol]);
|
self.load_symbols(code_builder, &[from_symbol]);
|
||||||
match (value_type, size) {
|
match (value_type, size) {
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
use bumpalo::collections::vec::{Drain, Vec};
|
use bumpalo::collections::vec::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::module_builder::{IndexRelocType, RelocationEntry};
|
use super::linking::{IndexRelocType, RelocationEntry};
|
||||||
use crate::opcodes::*;
|
use super::opcodes::*;
|
||||||
use crate::serialize::SerialBuffer;
|
use super::serialize::{SerialBuffer, Serialize};
|
||||||
use crate::{round_up_to_alignment, LocalId, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
use crate::{round_up_to_alignment, FRAME_ALIGNMENT_BYTES, STACK_POINTER_GLOBAL_ID};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct LocalId(pub u32);
|
||||||
|
|
||||||
/// Wasm value type. (Rust representation matches Wasm encoding)
|
/// Wasm value type. (Rust representation matches Wasm encoding)
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -20,15 +23,9 @@ pub enum ValueType {
|
||||||
F64 = 0x7c,
|
F64 = 0x7c,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a bit unfortunate. Will go away if we generate our own Types section.
|
impl Serialize for ValueType {
|
||||||
impl ValueType {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
pub fn to_parity_wasm(&self) -> parity_wasm::elements::ValueType {
|
buffer.append_u8(*self as u8);
|
||||||
match self {
|
|
||||||
Self::I32 => parity_wasm::elements::ValueType::I32,
|
|
||||||
Self::I64 => parity_wasm::elements::ValueType::I64,
|
|
||||||
Self::F32 => parity_wasm::elements::ValueType::F32,
|
|
||||||
Self::F64 => parity_wasm::elements::ValueType::F64,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +89,8 @@ pub enum VirtualMachineSymbolState {
|
||||||
|
|
||||||
// An instruction (local.set or local.tee) to be inserted into the function code
|
// An instruction (local.set or local.tee) to be inserted into the function code
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct InsertLocation {
|
struct Insertion {
|
||||||
insert_at: usize,
|
at: usize,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
}
|
}
|
||||||
|
@ -124,7 +121,7 @@ pub struct CodeBuilder<'a> {
|
||||||
insert_bytes: Vec<'a, u8>,
|
insert_bytes: Vec<'a, u8>,
|
||||||
|
|
||||||
/// Code locations where the insert_bytes should go
|
/// Code locations where the insert_bytes should go
|
||||||
insert_locations: Vec<'a, InsertLocation>,
|
insertions: Vec<'a, Insertion>,
|
||||||
|
|
||||||
/// Bytes for local variable declarations and stack-frame setup code.
|
/// Bytes for local variable declarations and stack-frame setup code.
|
||||||
/// We can't write this until we've finished the main code. But it goes
|
/// We can't write this until we've finished the main code. But it goes
|
||||||
|
@ -151,7 +148,7 @@ impl<'a> CodeBuilder<'a> {
|
||||||
pub fn new(arena: &'a Bump) -> Self {
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
CodeBuilder {
|
CodeBuilder {
|
||||||
code: Vec::with_capacity_in(1024, arena),
|
code: Vec::with_capacity_in(1024, arena),
|
||||||
insert_locations: Vec::with_capacity_in(32, arena),
|
insertions: Vec::with_capacity_in(32, arena),
|
||||||
insert_bytes: Vec::with_capacity_in(64, arena),
|
insert_bytes: Vec::with_capacity_in(64, arena),
|
||||||
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),
|
||||||
|
@ -160,15 +157,6 @@ impl<'a> CodeBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.code.clear();
|
|
||||||
self.insert_locations.clear();
|
|
||||||
self.insert_bytes.clear();
|
|
||||||
self.preamble.clear();
|
|
||||||
self.inner_length.clear();
|
|
||||||
self.vm_stack.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
||||||
|
|
||||||
SYMBOLS
|
SYMBOLS
|
||||||
|
@ -220,8 +208,8 @@ impl<'a> CodeBuilder<'a> {
|
||||||
self.insert_bytes.push(opcode);
|
self.insert_bytes.push(opcode);
|
||||||
self.insert_bytes.encode_u32(immediate);
|
self.insert_bytes.encode_u32(immediate);
|
||||||
|
|
||||||
self.insert_locations.push(InsertLocation {
|
self.insertions.push(Insertion {
|
||||||
insert_at,
|
at: insert_at,
|
||||||
start,
|
start,
|
||||||
end: self.insert_bytes.len(),
|
end: self.insert_bytes.len(),
|
||||||
});
|
});
|
||||||
|
@ -384,52 +372,56 @@ impl<'a> CodeBuilder<'a> {
|
||||||
|
|
||||||
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();
|
||||||
self.inner_length.encode_u32(inner_len as u32);
|
self.inner_length.encode_u32(inner_len as u32);
|
||||||
}
|
|
||||||
|
|
||||||
/// Write out all the bytes in the right order
|
|
||||||
pub fn serialize<T: SerialBuffer>(
|
|
||||||
&mut self,
|
|
||||||
code_section_buf: &mut T,
|
|
||||||
) -> Drain<RelocationEntry> {
|
|
||||||
code_section_buf.append_slice(&self.inner_length);
|
|
||||||
code_section_buf.append_slice(&self.preamble);
|
|
||||||
|
|
||||||
// Sort insertions. They are not created in order of assignment, but in order of *second* usage.
|
// Sort insertions. They are not created in order of assignment, but in order of *second* usage.
|
||||||
self.insert_locations.sort_by_key(|loc| loc.insert_at);
|
self.insertions.sort_by_key(|ins| ins.at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize all byte vectors in the right order
|
||||||
|
/// Also update relocation offsets relative to the provided base offset in the buffer
|
||||||
|
pub fn serialize_with_relocs<T: SerialBuffer>(
|
||||||
|
&self,
|
||||||
|
buffer: &mut T,
|
||||||
|
final_relocs: &mut Vec<'a, RelocationEntry>,
|
||||||
|
reloc_base_offset: usize,
|
||||||
|
) {
|
||||||
|
buffer.append_slice(&self.inner_length);
|
||||||
|
buffer.append_slice(&self.preamble);
|
||||||
|
|
||||||
// Do the insertions & update relocation offsets
|
// Do the insertions & update relocation offsets
|
||||||
const CODE_SECTION_BODY_OFFSET: usize = 5;
|
|
||||||
let mut reloc_index = 0;
|
let mut reloc_index = 0;
|
||||||
let mut code_pos: usize = 0;
|
let mut code_pos = 0;
|
||||||
for location in self.insert_locations.iter() {
|
let mut insert_iter = self.insertions.iter();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let next_insert = insert_iter.next();
|
||||||
|
let next_pos = next_insert.map(|i| i.at).unwrap_or_else(|| self.code.len());
|
||||||
|
|
||||||
// Relocation offset needs to be an index into the body of the code section, but
|
// Relocation offset needs to be an index into the body of the code section, but
|
||||||
// at this point it is an index into self.code. Need to adjust for all previous functions
|
// at this point it is an index into self.code. Need to adjust for all previous functions
|
||||||
// in the code section, and for insertions in the current function.
|
// in the code section, and for insertions in the current function.
|
||||||
let section_body_pos = code_section_buf.size() - CODE_SECTION_BODY_OFFSET;
|
let section_body_pos = buffer.size() - reloc_base_offset;
|
||||||
while reloc_index < self.relocations.len()
|
while reloc_index < self.relocations.len()
|
||||||
&& self.relocations[reloc_index].offset() < location.insert_at as u32
|
&& self.relocations[reloc_index].offset() < next_pos as u32
|
||||||
{
|
{
|
||||||
let offset_ref = self.relocations[reloc_index].offset_mut();
|
let mut reloc_clone = self.relocations[reloc_index].clone();
|
||||||
*offset_ref += (section_body_pos - code_pos) as u32;
|
*reloc_clone.offset_mut() += (section_body_pos - code_pos) as u32;
|
||||||
|
final_relocs.push(reloc_clone);
|
||||||
reloc_index += 1;
|
reloc_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
code_section_buf.append_slice(&self.code[code_pos..location.insert_at]);
|
buffer.append_slice(&self.code[code_pos..next_pos]);
|
||||||
code_section_buf.append_slice(&self.insert_bytes[location.start..location.end]);
|
|
||||||
code_pos = location.insert_at;
|
match next_insert {
|
||||||
|
Some(Insertion { at, start, end }) => {
|
||||||
|
buffer.append_slice(&self.insert_bytes[*start..*end]);
|
||||||
|
code_pos = *at;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
code_section_buf.append_slice(&self.code[code_pos..len]);
|
|
||||||
|
|
||||||
self.relocations.drain(0..)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************
|
/**********************************************************
|
|
@ -1,73 +1,9 @@
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use crate::code_builder::Align;
|
use super::sections::{update_section_size, write_custom_section_header};
|
||||||
use crate::serialize::{SerialBuffer, Serialize};
|
use super::serialize::{SerialBuffer, Serialize};
|
||||||
|
use super::Align;
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
*
|
*
|
||||||
|
@ -132,7 +68,7 @@ pub enum OffsetRelocType {
|
||||||
MemoryAddrI64 = 16,
|
MemoryAddrI64 = 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RelocationEntry {
|
pub enum RelocationEntry {
|
||||||
Index {
|
Index {
|
||||||
type_id: IndexRelocType,
|
type_id: IndexRelocType,
|
||||||
|
@ -181,7 +117,7 @@ impl Serialize for RelocationEntry {
|
||||||
offset,
|
offset,
|
||||||
symbol_index,
|
symbol_index,
|
||||||
} => {
|
} => {
|
||||||
buffer.append_byte(*type_id as u8);
|
buffer.append_u8(*type_id as u8);
|
||||||
buffer.encode_u32(*offset);
|
buffer.encode_u32(*offset);
|
||||||
buffer.encode_u32(*symbol_index);
|
buffer.encode_u32(*symbol_index);
|
||||||
}
|
}
|
||||||
|
@ -191,7 +127,7 @@ impl Serialize for RelocationEntry {
|
||||||
symbol_index,
|
symbol_index,
|
||||||
addend,
|
addend,
|
||||||
} => {
|
} => {
|
||||||
buffer.append_byte(*type_id as u8);
|
buffer.append_u8(*type_id as u8);
|
||||||
buffer.encode_u32(*offset);
|
buffer.encode_u32(*offset);
|
||||||
buffer.encode_u32(*symbol_index);
|
buffer.encode_u32(*symbol_index);
|
||||||
buffer.encode_i32(*addend);
|
buffer.encode_i32(*addend);
|
||||||
|
@ -204,16 +140,28 @@ impl Serialize for RelocationEntry {
|
||||||
pub struct RelocationSection<'a> {
|
pub struct RelocationSection<'a> {
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
/// The *index* (not ID!) of the target section in the module
|
/// The *index* (not ID!) of the target section in the module
|
||||||
pub target_section_index: u32,
|
pub target_section_index: Option<u32>,
|
||||||
pub entries: &'a Vec<'a, RelocationEntry>,
|
pub entries: Vec<'a, RelocationEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RelocationSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump, name: &'a str) -> Self {
|
||||||
|
RelocationSection {
|
||||||
|
name,
|
||||||
|
target_section_index: None,
|
||||||
|
entries: Vec::with_capacity_in(64, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for RelocationSection<'a> {
|
impl<'a> Serialize for RelocationSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
let header_indices = write_custom_section_header(buffer, self.name);
|
if !self.entries.is_empty() {
|
||||||
buffer.encode_u32(self.target_section_index);
|
let header_indices = write_custom_section_header(buffer, self.name);
|
||||||
serialize_vector_with_count(buffer, self.entries);
|
buffer.encode_u32(self.target_section_index.unwrap());
|
||||||
update_section_size(buffer, header_indices);
|
self.entries.serialize(buffer);
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +179,7 @@ pub struct LinkingSegment {
|
||||||
pub alignment: Align,
|
pub alignment: Align,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for LinkingSegment {
|
impl Serialize for LinkingSegment {
|
||||||
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
todo!();
|
todo!();
|
||||||
|
@ -242,17 +191,16 @@ pub struct LinkingInitFunc {
|
||||||
pub priority: u32,
|
pub priority: u32,
|
||||||
pub symbol_index: u32, // index in the symbol table, not the function index
|
pub symbol_index: u32, // index in the symbol table, not the function index
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for LinkingInitFunc {
|
impl Serialize for LinkingInitFunc {
|
||||||
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------
|
//------------------------------------------------
|
||||||
//
|
|
||||||
// Common data
|
// Common data
|
||||||
//
|
//------------------------------------------------
|
||||||
//----------------
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
@ -269,6 +217,7 @@ pub struct ComdatSym {
|
||||||
pub kind: ComdatSymKind,
|
pub kind: ComdatSymKind,
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ComdatSym {
|
impl Serialize for ComdatSym {
|
||||||
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
todo!();
|
todo!();
|
||||||
|
@ -285,17 +234,16 @@ pub struct LinkingComdat<'a> {
|
||||||
flags: u32,
|
flags: u32,
|
||||||
syms: Vec<'a, ComdatSym>,
|
syms: Vec<'a, ComdatSym>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for LinkingComdat<'a> {
|
impl<'a> Serialize for LinkingComdat<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------
|
//------------------------------------------------
|
||||||
//
|
|
||||||
// Symbol table
|
// Symbol table
|
||||||
//
|
//------------------------------------------------
|
||||||
//----------------
|
|
||||||
|
|
||||||
/// Indicating that this is a weak symbol. When
|
/// Indicating that this is a weak symbol. When
|
||||||
/// linking multiple modules defining the same symbol, all weak definitions are
|
/// linking multiple modules defining the same symbol, all weak definitions are
|
||||||
|
@ -339,6 +287,7 @@ pub enum WasmObjectSymbol {
|
||||||
Defined { index: u32, name: String },
|
Defined { index: u32, name: String },
|
||||||
Imported { index: u32 },
|
Imported { index: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for WasmObjectSymbol {
|
impl Serialize for WasmObjectSymbol {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -365,6 +314,7 @@ pub enum DataSymbol {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for DataSymbol {
|
impl Serialize for DataSymbol {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
match self {
|
match self {
|
||||||
|
@ -405,6 +355,7 @@ pub struct SymInfo {
|
||||||
flags: u32,
|
flags: u32,
|
||||||
info: SymInfoFields,
|
info: SymInfoFields,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SymInfo {
|
impl SymInfo {
|
||||||
pub fn for_function(wasm_function_index: u32, name: String) -> Self {
|
pub fn for_function(wasm_function_index: u32, name: String) -> Self {
|
||||||
let linking_symbol = WasmObjectSymbol::Defined {
|
let linking_symbol = WasmObjectSymbol::Defined {
|
||||||
|
@ -420,7 +371,7 @@ impl SymInfo {
|
||||||
|
|
||||||
impl Serialize for SymInfo {
|
impl Serialize for SymInfo {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
buffer.append_byte(match self.info {
|
buffer.append_u8(match self.info {
|
||||||
SymInfoFields::Function(_) => 0,
|
SymInfoFields::Function(_) => 0,
|
||||||
SymInfoFields::Data(_) => 1,
|
SymInfoFields::Data(_) => 1,
|
||||||
SymInfoFields::Global(_) => 2,
|
SymInfoFields::Global(_) => 2,
|
||||||
|
@ -442,11 +393,9 @@ impl Serialize for SymInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------
|
//----------------------------------------------------------------
|
||||||
//
|
|
||||||
// Linking subsections
|
// Linking subsections
|
||||||
//
|
//----------------------------------------------------------------
|
||||||
//--------------------------------
|
|
||||||
|
|
||||||
pub enum LinkingSubSection<'a> {
|
pub enum LinkingSubSection<'a> {
|
||||||
/// Extra metadata about the data segments.
|
/// Extra metadata about the data segments.
|
||||||
|
@ -459,9 +408,10 @@ pub enum LinkingSubSection<'a> {
|
||||||
/// Specifies extra information about the symbols present in the module.
|
/// Specifies extra information about the symbols present in the module.
|
||||||
SymbolTable(Vec<'a, SymInfo>),
|
SymbolTable(Vec<'a, SymInfo>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for LinkingSubSection<'a> {
|
impl<'a> Serialize for LinkingSubSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
buffer.append_byte(match self {
|
buffer.append_u8(match self {
|
||||||
Self::SegmentInfo(_) => 5,
|
Self::SegmentInfo(_) => 5,
|
||||||
Self::InitFuncs(_) => 6,
|
Self::InitFuncs(_) => 6,
|
||||||
Self::ComdatInfo(_) => 7,
|
Self::ComdatInfo(_) => 7,
|
||||||
|
@ -470,10 +420,10 @@ impl<'a> Serialize for LinkingSubSection<'a> {
|
||||||
let payload_len_index = buffer.reserve_padded_u32();
|
let payload_len_index = buffer.reserve_padded_u32();
|
||||||
let payload_start_index = buffer.size();
|
let payload_start_index = buffer.size();
|
||||||
match self {
|
match self {
|
||||||
Self::SegmentInfo(items) => serialize_vector_with_count(buffer, items),
|
Self::SegmentInfo(items) => items.serialize(buffer),
|
||||||
Self::InitFuncs(items) => serialize_vector_with_count(buffer, items),
|
Self::InitFuncs(items) => items.serialize(buffer),
|
||||||
Self::ComdatInfo(items) => serialize_vector_with_count(buffer, items),
|
Self::ComdatInfo(items) => items.serialize(buffer),
|
||||||
Self::SymbolTable(items) => serialize_vector_with_count(buffer, items),
|
Self::SymbolTable(items) => items.serialize(buffer),
|
||||||
}
|
}
|
||||||
buffer.overwrite_padded_u32(
|
buffer.overwrite_padded_u32(
|
||||||
payload_len_index,
|
payload_len_index,
|
||||||
|
@ -482,15 +432,28 @@ impl<'a> Serialize for LinkingSubSection<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Linking metadata section
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
const LINKING_VERSION: u8 = 2;
|
const LINKING_VERSION: u8 = 2;
|
||||||
|
|
||||||
pub struct LinkingSection<'a> {
|
pub struct LinkingSection<'a> {
|
||||||
pub subsections: Vec<'a, LinkingSubSection<'a>>,
|
pub subsections: Vec<'a, LinkingSubSection<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> LinkingSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
LinkingSection {
|
||||||
|
subsections: Vec::with_capacity_in(1, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for LinkingSection<'a> {
|
impl<'a> Serialize for LinkingSection<'a> {
|
||||||
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
let header_indices = write_custom_section_header(buffer, "linking");
|
let header_indices = write_custom_section_header(buffer, "linking");
|
||||||
buffer.append_byte(LINKING_VERSION);
|
buffer.append_u8(LINKING_VERSION);
|
||||||
for subsection in self.subsections.iter() {
|
for subsection in self.subsections.iter() {
|
||||||
subsection.serialize(buffer);
|
subsection.serialize(buffer);
|
||||||
}
|
}
|
11
compiler/gen_wasm/src/wasm_module/mod.rs
Normal file
11
compiler/gen_wasm/src/wasm_module/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
pub mod code_builder;
|
||||||
|
pub mod linking;
|
||||||
|
pub mod opcodes;
|
||||||
|
pub mod sections;
|
||||||
|
pub mod serialize;
|
||||||
|
|
||||||
|
pub use code_builder::{
|
||||||
|
Align, BlockType, CodeBuilder, LocalId, ValueType, VirtualMachineSymbolState,
|
||||||
|
};
|
||||||
|
pub use linking::{LinkingSubSection, SymInfo};
|
||||||
|
pub use sections::{ConstExpr, Export, ExportType, Global, GlobalType, Signature, WasmModule};
|
633
compiler/gen_wasm/src/wasm_module/sections.rs
Normal file
633
compiler/gen_wasm/src/wasm_module/sections.rs
Normal file
|
@ -0,0 +1,633 @@
|
||||||
|
use bumpalo::collections::vec::Vec;
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
|
use super::linking::{LinkingSection, RelocationEntry, RelocationSection};
|
||||||
|
use super::opcodes;
|
||||||
|
use super::serialize::{SerialBuffer, Serialize};
|
||||||
|
use super::{CodeBuilder, ValueType};
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Helpers
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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_u8(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
|
||||||
|
pub fn write_custom_section_header<T: SerialBuffer>(
|
||||||
|
buffer: &mut T,
|
||||||
|
name: &str,
|
||||||
|
) -> SectionHeaderIndices {
|
||||||
|
buffer.append_u8(SectionId::Custom as u8);
|
||||||
|
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
|
||||||
|
pub 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a section that is just a vector of some struct
|
||||||
|
fn serialize_vector_section<B: SerialBuffer, T: Serialize>(
|
||||||
|
buffer: &mut B,
|
||||||
|
section_id: SectionId,
|
||||||
|
subsections: &[T],
|
||||||
|
) {
|
||||||
|
if !subsections.is_empty() {
|
||||||
|
let header_indices = write_section_header(buffer, section_id);
|
||||||
|
subsections.serialize(buffer);
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Type section
|
||||||
|
* Deduplicated list of function type signatures
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct Signature<'a> {
|
||||||
|
pub param_types: Vec<'a, ValueType>,
|
||||||
|
pub ret_type: Option<ValueType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for Signature<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_u8(0x60);
|
||||||
|
self.param_types.serialize(buffer);
|
||||||
|
self.ret_type.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TypeSection<'a> {
|
||||||
|
/// Private. See WasmModule::add_function_signature
|
||||||
|
signatures: Vec<'a, Signature<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TypeSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
TypeSection {
|
||||||
|
signatures: Vec::with_capacity_in(8, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a matching signature or insert a new one. Return the index.
|
||||||
|
fn insert(&mut self, signature: Signature<'a>) -> u32 {
|
||||||
|
// Using linear search because we need to preserve indices stored in
|
||||||
|
// the Function section. (Also for practical sizes it's fast)
|
||||||
|
let maybe_index = self.signatures.iter().position(|s| *s == signature);
|
||||||
|
match maybe_index {
|
||||||
|
Some(index) => index as u32,
|
||||||
|
None => {
|
||||||
|
let index = self.signatures.len();
|
||||||
|
self.signatures.push(signature);
|
||||||
|
index as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for TypeSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
serialize_vector_section(buffer, SectionId::Type, &self.signatures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Import section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum RefType {
|
||||||
|
Func = 0x70,
|
||||||
|
Extern = 0x6f,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TableType {
|
||||||
|
pub ref_type: RefType,
|
||||||
|
pub limits: Limits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for TableType {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_u8(self.ref_type as u8);
|
||||||
|
self.limits.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ImportDesc {
|
||||||
|
Func { signature_index: u32 },
|
||||||
|
Table { ty: TableType },
|
||||||
|
Mem { limits: Limits },
|
||||||
|
Global { ty: GlobalType },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Import {
|
||||||
|
pub module: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: ImportDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Import {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
self.module.serialize(buffer);
|
||||||
|
self.name.serialize(buffer);
|
||||||
|
match &self.description {
|
||||||
|
ImportDesc::Func { signature_index } => {
|
||||||
|
buffer.append_u8(0);
|
||||||
|
buffer.encode_u32(*signature_index);
|
||||||
|
}
|
||||||
|
ImportDesc::Table { ty } => {
|
||||||
|
buffer.append_u8(1);
|
||||||
|
ty.serialize(buffer);
|
||||||
|
}
|
||||||
|
ImportDesc::Mem { limits } => {
|
||||||
|
buffer.append_u8(2);
|
||||||
|
limits.serialize(buffer);
|
||||||
|
}
|
||||||
|
ImportDesc::Global { ty } => {
|
||||||
|
buffer.append_u8(3);
|
||||||
|
ty.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ImportSection<'a> {
|
||||||
|
entries: Vec<'a, Import>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ImportSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
ImportSection {
|
||||||
|
entries: bumpalo::vec![in arena],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for ImportSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
serialize_vector_section(buffer, SectionId::Import, &self.entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Function section
|
||||||
|
* Maps function indices (Code section) to signature indices (Type section)
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
pub struct FunctionSection<'a> {
|
||||||
|
/// Private. See WasmModule::add_function_signature
|
||||||
|
signature_indices: Vec<'a, u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FunctionSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
FunctionSection {
|
||||||
|
signature_indices: Vec::with_capacity_in(8, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for FunctionSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
serialize_vector_section(buffer, SectionId::Function, &self.signature_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Memory section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
pub enum Limits {
|
||||||
|
Min(u32),
|
||||||
|
MinMax(u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Limits {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
Self::Min(min) => {
|
||||||
|
buffer.append_u8(0);
|
||||||
|
buffer.encode_u32(*min);
|
||||||
|
}
|
||||||
|
Self::MinMax(min, max) => {
|
||||||
|
buffer.append_u8(1);
|
||||||
|
buffer.encode_u32(*min);
|
||||||
|
buffer.encode_u32(*max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemorySection(Option<Limits>);
|
||||||
|
|
||||||
|
impl MemorySection {
|
||||||
|
pub const PAGE_SIZE: u32 = 64 * 1024;
|
||||||
|
|
||||||
|
pub fn new(bytes: u32) -> Self {
|
||||||
|
if bytes == 0 {
|
||||||
|
MemorySection(None)
|
||||||
|
} else {
|
||||||
|
let pages = (bytes + Self::PAGE_SIZE - 1) / Self::PAGE_SIZE;
|
||||||
|
MemorySection(Some(Limits::Min(pages)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min_size(&self) -> Option<u32> {
|
||||||
|
match self {
|
||||||
|
MemorySection(Some(Limits::Min(min))) | MemorySection(Some(Limits::MinMax(min, _))) => {
|
||||||
|
Some(min * Self::PAGE_SIZE)
|
||||||
|
}
|
||||||
|
MemorySection(None) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for MemorySection {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
if let Some(limits) = &self.0 {
|
||||||
|
let header_indices = write_section_header(buffer, SectionId::Memory);
|
||||||
|
buffer.append_u8(1);
|
||||||
|
limits.serialize(buffer);
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Global section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
pub struct GlobalType {
|
||||||
|
pub value_type: ValueType,
|
||||||
|
pub is_mutable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for GlobalType {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_u8(self.value_type as u8);
|
||||||
|
buffer.append_u8(self.is_mutable as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constant expression for initialising globals or data segments
|
||||||
|
/// Note: This is restricted for simplicity, but the spec allows arbitrary constant expressions
|
||||||
|
pub enum ConstExpr {
|
||||||
|
I32(i32),
|
||||||
|
I64(i64),
|
||||||
|
F32(f32),
|
||||||
|
F64(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ConstExpr {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
ConstExpr::I32(x) => {
|
||||||
|
buffer.append_u8(opcodes::I32CONST);
|
||||||
|
buffer.encode_i32(*x);
|
||||||
|
}
|
||||||
|
ConstExpr::I64(x) => {
|
||||||
|
buffer.append_u8(opcodes::I64CONST);
|
||||||
|
buffer.encode_i64(*x);
|
||||||
|
}
|
||||||
|
ConstExpr::F32(x) => {
|
||||||
|
buffer.append_u8(opcodes::F32CONST);
|
||||||
|
buffer.encode_f32(*x);
|
||||||
|
}
|
||||||
|
ConstExpr::F64(x) => {
|
||||||
|
buffer.append_u8(opcodes::F64CONST);
|
||||||
|
buffer.encode_f64(*x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.append_u8(opcodes::END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Global {
|
||||||
|
/// Type and mutability of the global
|
||||||
|
pub ty: GlobalType,
|
||||||
|
/// Initial value of the global.
|
||||||
|
pub init: ConstExpr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Global {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
self.ty.serialize(buffer);
|
||||||
|
self.init.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlobalSection<'a> {
|
||||||
|
pub entries: Vec<'a, Global>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GlobalSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
GlobalSection {
|
||||||
|
entries: Vec::with_capacity_in(1, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for GlobalSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
serialize_vector_section(buffer, SectionId::Global, &self.entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Export section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum ExportType {
|
||||||
|
Func = 0,
|
||||||
|
Table = 1,
|
||||||
|
Mem = 2,
|
||||||
|
Global = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Export {
|
||||||
|
pub name: String,
|
||||||
|
pub ty: ExportType,
|
||||||
|
pub index: u32,
|
||||||
|
}
|
||||||
|
impl Serialize for Export {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
self.name.serialize(buffer);
|
||||||
|
buffer.append_u8(self.ty as u8);
|
||||||
|
buffer.encode_u32(self.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExportSection<'a> {
|
||||||
|
pub entries: Vec<'a, Export>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ExportSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
ExportSection {
|
||||||
|
entries: bumpalo::vec![in arena],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for ExportSection<'a> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
serialize_vector_section(buffer, SectionId::Export, &self.entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Code section (see also code_builder.rs)
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CodeSection<'a> {
|
||||||
|
pub code_builders: Vec<'a, CodeBuilder<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CodeSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
CodeSection {
|
||||||
|
code_builders: Vec::with_capacity_in(8, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize the code builders for all functions, and get code relocations with final offsets
|
||||||
|
pub fn serialize_mut<T: SerialBuffer>(
|
||||||
|
&mut self,
|
||||||
|
buffer: &mut T,
|
||||||
|
relocations: &mut Vec<'a, RelocationEntry>,
|
||||||
|
) {
|
||||||
|
let header_indices = write_section_header(buffer, SectionId::Code);
|
||||||
|
buffer.encode_u32(self.code_builders.len() as u32);
|
||||||
|
|
||||||
|
for code_builder in self.code_builders.iter_mut() {
|
||||||
|
code_builder.serialize_with_relocs(buffer, relocations, header_indices.body_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Data section
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
|
||||||
|
pub enum DataMode {
|
||||||
|
/// A data segment that auto-loads into memory on instantiation
|
||||||
|
Active { offset: ConstExpr },
|
||||||
|
/// A data segment that can be loaded with the `memory.init` instruction
|
||||||
|
Passive,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DataSegment<'a> {
|
||||||
|
pub mode: DataMode,
|
||||||
|
pub init: Vec<'a, u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DataSegment<'_> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match &self.mode {
|
||||||
|
DataMode::Active { offset } => {
|
||||||
|
buffer.append_u8(0);
|
||||||
|
offset.serialize(buffer);
|
||||||
|
}
|
||||||
|
DataMode::Passive => {
|
||||||
|
buffer.append_u8(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DataSection<'a> {
|
||||||
|
pub segments: Vec<'a, DataSegment<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DataSection<'a> {
|
||||||
|
pub fn new(arena: &'a Bump) -> Self {
|
||||||
|
DataSection {
|
||||||
|
segments: Vec::with_capacity_in(1, arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.segments.is_empty() || self.segments.iter().all(|seg| seg.init.is_empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for DataSection<'_> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
if !self.is_empty() {
|
||||||
|
serialize_vector_section(buffer, SectionId::Data, &self.segments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_data_count_section<'a, T: SerialBuffer>(buffer: &mut T, data_section: &DataSection<'a>) {
|
||||||
|
if !data_section.is_empty() {
|
||||||
|
let header_indices = write_section_header(buffer, SectionId::DataCount);
|
||||||
|
buffer.encode_u32(data_section.segments.len() as u32);
|
||||||
|
update_section_size(buffer, header_indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* Module
|
||||||
|
*
|
||||||
|
* https://webassembly.github.io/spec/core/binary/modules.html
|
||||||
|
*
|
||||||
|
*******************************************************************/
|
||||||
|
pub struct WasmModule<'a> {
|
||||||
|
pub types: TypeSection<'a>,
|
||||||
|
pub import: ImportSection<'a>,
|
||||||
|
pub function: FunctionSection<'a>,
|
||||||
|
/// Dummy placeholder for tables (used for function pointers and host references)
|
||||||
|
pub table: (),
|
||||||
|
pub memory: MemorySection,
|
||||||
|
pub global: GlobalSection<'a>,
|
||||||
|
pub export: ExportSection<'a>,
|
||||||
|
/// Dummy placeholder for start function. In Roc, this would be part of the platform.
|
||||||
|
pub start: (),
|
||||||
|
/// Dummy placeholder for table elements. Roc does not use tables.
|
||||||
|
pub element: (),
|
||||||
|
pub code: CodeSection<'a>,
|
||||||
|
pub data: DataSection<'a>,
|
||||||
|
pub linking: LinkingSection<'a>,
|
||||||
|
pub reloc_code: RelocationSection<'a>,
|
||||||
|
pub reloc_data: RelocationSection<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> WasmModule<'a> {
|
||||||
|
pub const WASM_VERSION: u32 = 1;
|
||||||
|
|
||||||
|
/// Create entries in the Type and Function sections for a function signature
|
||||||
|
pub fn add_function_signature(&mut self, signature: Signature<'a>) {
|
||||||
|
let index = self.types.insert(signature);
|
||||||
|
self.function.signature_indices.push(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unit_arg)]
|
||||||
|
pub fn serialize<T: SerialBuffer>(&mut self, buffer: &mut T) {
|
||||||
|
buffer.append_u8(0);
|
||||||
|
buffer.append_slice("asm".as_bytes());
|
||||||
|
buffer.write_unencoded_u32(Self::WASM_VERSION);
|
||||||
|
|
||||||
|
let mut index: u32 = 0;
|
||||||
|
let mut prev_size = buffer.size();
|
||||||
|
|
||||||
|
self.types.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.import.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.function.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.table.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.memory.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.global.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.export.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.start.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.element.serialize(buffer);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
// Data count section has no independent data, just helps the runtime
|
||||||
|
// to validate code references to the data section in a single pass
|
||||||
|
write_data_count_section(buffer, &self.data);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.reloc_code.target_section_index = Some(index);
|
||||||
|
self.code
|
||||||
|
.serialize_mut(buffer, &mut self.reloc_code.entries);
|
||||||
|
maybe_increment_section(buffer.size(), &mut prev_size, &mut index);
|
||||||
|
|
||||||
|
self.data.serialize(buffer);
|
||||||
|
self.reloc_data.target_section_index = Some(index);
|
||||||
|
|
||||||
|
self.linking.serialize(buffer);
|
||||||
|
self.reloc_code.serialize(buffer);
|
||||||
|
self.reloc_data.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maybe_increment_section(size: usize, prev_size: &mut usize, index: &mut u32) {
|
||||||
|
if size > *prev_size {
|
||||||
|
*index += 1;
|
||||||
|
*prev_size = size;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,66 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use bumpalo::collections::vec::Vec;
|
use bumpalo::collections::vec::Vec;
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for u8 {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.append_u8(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for u32 {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.encode_u32(*self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unit is used as a placeholder in parts of the Wasm spec we don't use yet
|
||||||
|
impl Serialize for () {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, _buffer: &mut T) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize> Serialize for [S] {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.encode_u32(self.len() as u32);
|
||||||
|
for item in self.iter() {
|
||||||
|
item.serialize(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Vec<'_, u8> {
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
buffer.encode_u32(self.len() as u32);
|
||||||
|
buffer.append_slice(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Serialize> Serialize for Option<S> {
|
||||||
|
/// serialize Option as a vector of length 1 or 0
|
||||||
|
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
|
||||||
|
match self {
|
||||||
|
Some(x) => {
|
||||||
|
buffer.append_u8(1);
|
||||||
|
x.serialize(buffer);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
buffer.append_u8(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Write an unsigned integer into the provided buffer in LEB-128 format, returning byte length
|
/// 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.
|
/// All integers in Wasm are variable-length encoded, which saves space for small values.
|
||||||
|
@ -10,10 +71,10 @@ macro_rules! encode_uleb128 {
|
||||||
let mut x = value;
|
let mut x = value;
|
||||||
let start_len = self.size();
|
let start_len = self.size();
|
||||||
while x >= 0x80 {
|
while x >= 0x80 {
|
||||||
self.append_byte(0x80 | ((x & 0x7f) as u8));
|
self.append_u8(0x80 | ((x & 0x7f) as u8));
|
||||||
x >>= 7;
|
x >>= 7;
|
||||||
}
|
}
|
||||||
self.append_byte(x as u8);
|
self.append_u8(x as u8);
|
||||||
self.size() - start_len
|
self.size() - start_len
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -30,10 +91,10 @@ macro_rules! encode_sleb128 {
|
||||||
x >>= 7;
|
x >>= 7;
|
||||||
let byte_is_negative = (byte & 0x40) != 0;
|
let byte_is_negative = (byte & 0x40) != 0;
|
||||||
if ((x == 0 && !byte_is_negative) || (x == -1 && byte_is_negative)) {
|
if ((x == 0 && !byte_is_negative) || (x == -1 && byte_is_negative)) {
|
||||||
self.append_byte(byte);
|
self.append_u8(byte);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.append_byte(byte | 0x80);
|
self.append_u8(byte | 0x80);
|
||||||
}
|
}
|
||||||
self.size() - start_len
|
self.size() - start_len
|
||||||
}
|
}
|
||||||
|
@ -47,7 +108,7 @@ macro_rules! write_unencoded {
|
||||||
let mut x = value;
|
let mut x = value;
|
||||||
let size = std::mem::size_of::<$ty>();
|
let size = std::mem::size_of::<$ty>();
|
||||||
for _ in 0..size {
|
for _ in 0..size {
|
||||||
self.append_byte((x & 0xff) as u8);
|
self.append_u8((x & 0xff) as u8);
|
||||||
x >>= 8;
|
x >>= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,17 +122,19 @@ macro_rules! encode_padded_sleb128 {
|
||||||
let mut x = value;
|
let mut x = value;
|
||||||
let size = (std::mem::size_of::<$ty>() / 4) * 5;
|
let size = (std::mem::size_of::<$ty>() / 4) * 5;
|
||||||
for _ in 0..(size - 1) {
|
for _ in 0..(size - 1) {
|
||||||
self.append_byte(0x80 | (x & 0x7f) as u8);
|
self.append_u8(0x80 | (x & 0x7f) as u8);
|
||||||
x >>= 7;
|
x >>= 7;
|
||||||
}
|
}
|
||||||
self.append_byte((x & 0x7f) as u8);
|
self.append_u8((x & 0x7f) as u8);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SerialBuffer {
|
pub trait SerialBuffer: Debug {
|
||||||
fn append_byte(&mut self, b: u8);
|
fn append_u8(&mut self, b: u8);
|
||||||
|
fn overwrite_u8(&mut self, index: usize, b: u8);
|
||||||
fn append_slice(&mut self, b: &[u8]);
|
fn append_slice(&mut self, b: &[u8]);
|
||||||
|
|
||||||
fn size(&self) -> usize;
|
fn size(&self) -> usize;
|
||||||
|
|
||||||
encode_uleb128!(encode_u32, u32);
|
encode_uleb128!(encode_u32, u32);
|
||||||
|
@ -98,17 +161,6 @@ pub trait SerialBuffer {
|
||||||
encode_padded_sleb128!(encode_padded_i64, i64);
|
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) {
|
fn overwrite_padded_u32_help(buffer: &mut [u8], value: u32) {
|
||||||
let mut x = value;
|
let mut x = value;
|
||||||
for byte in buffer.iter_mut().take(4) {
|
for byte in buffer.iter_mut().take(4) {
|
||||||
|
@ -119,9 +171,12 @@ fn overwrite_padded_u32_help(buffer: &mut [u8], value: u32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerialBuffer for std::vec::Vec<u8> {
|
impl SerialBuffer for std::vec::Vec<u8> {
|
||||||
fn append_byte(&mut self, b: u8) {
|
fn append_u8(&mut self, b: u8) {
|
||||||
self.push(b);
|
self.push(b);
|
||||||
}
|
}
|
||||||
|
fn overwrite_u8(&mut self, index: usize, b: u8) {
|
||||||
|
self[index] = b;
|
||||||
|
}
|
||||||
fn append_slice(&mut self, b: &[u8]) {
|
fn append_slice(&mut self, b: &[u8]) {
|
||||||
self.extend_from_slice(b);
|
self.extend_from_slice(b);
|
||||||
}
|
}
|
||||||
|
@ -146,9 +201,12 @@ impl SerialBuffer for std::vec::Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SerialBuffer for Vec<'a, u8> {
|
impl<'a> SerialBuffer for Vec<'a, u8> {
|
||||||
fn append_byte(&mut self, b: u8) {
|
fn append_u8(&mut self, b: u8) {
|
||||||
self.push(b);
|
self.push(b);
|
||||||
}
|
}
|
||||||
|
fn overwrite_u8(&mut self, index: usize, b: u8) {
|
||||||
|
self[index] = b;
|
||||||
|
}
|
||||||
fn append_slice(&mut self, b: &[u8]) {
|
fn append_slice(&mut self, b: &[u8]) {
|
||||||
self.extend_from_slice(b);
|
self.extend_from_slice(b);
|
||||||
}
|
}
|
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
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use roc_can::annotation::IntroducedVariables;
|
use roc_can::annotation::IntroducedVariables;
|
||||||
use roc_can::def::{Declaration, Def};
|
use roc_can::def::{Declaration, Def};
|
||||||
use roc_can::env::Env;
|
use roc_can::env::Env;
|
||||||
use roc_can::expr::{Expr, Recursive};
|
use roc_can::expr::{ClosureData, Expr, Recursive};
|
||||||
use roc_can::pattern::Pattern;
|
use roc_can::pattern::Pattern;
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
use roc_collections::all::{MutSet, SendMap};
|
use roc_collections::all::{MutSet, SendMap};
|
||||||
|
@ -117,7 +117,7 @@ fn build_effect_always(
|
||||||
|
|
||||||
let body = Expr::Var(value_symbol);
|
let body = Expr::Var(value_symbol);
|
||||||
|
|
||||||
Expr::Closure {
|
Expr::Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -127,7 +127,7 @@ fn build_effect_always(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(body)),
|
loc_body: Box::new(Located::at_zero(body)),
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// \value -> @Effect \{} -> value
|
// \value -> @Effect \{} -> value
|
||||||
|
@ -146,7 +146,7 @@ fn build_effect_always(
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let function_var = var_store.fresh();
|
let function_var = var_store.fresh();
|
||||||
let closure = Expr::Closure {
|
let closure = Expr::Closure(ClosureData {
|
||||||
function_type: function_var,
|
function_type: function_var,
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -156,7 +156,7 @@ fn build_effect_always(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(body)),
|
loc_body: Box::new(Located::at_zero(body)),
|
||||||
};
|
});
|
||||||
|
|
||||||
(function_var, closure)
|
(function_var, closure)
|
||||||
};
|
};
|
||||||
|
@ -295,7 +295,7 @@ fn build_effect_map(
|
||||||
Located::at_zero(empty_record_pattern(var_store)),
|
Located::at_zero(empty_record_pattern(var_store)),
|
||||||
)];
|
)];
|
||||||
|
|
||||||
Expr::Closure {
|
Expr::Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -308,7 +308,7 @@ fn build_effect_map(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(mapper_call)),
|
loc_body: Box::new(Located::at_zero(mapper_call)),
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let arguments = vec![
|
let arguments = vec![
|
||||||
|
@ -339,7 +339,7 @@ fn build_effect_map(
|
||||||
};
|
};
|
||||||
|
|
||||||
let function_var = var_store.fresh();
|
let function_var = var_store.fresh();
|
||||||
let map_closure = Expr::Closure {
|
let map_closure = Expr::Closure(ClosureData {
|
||||||
function_type: function_var,
|
function_type: function_var,
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -349,7 +349,7 @@ fn build_effect_map(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(body)),
|
loc_body: Box::new(Located::at_zero(body)),
|
||||||
};
|
});
|
||||||
|
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ fn build_effect_after(
|
||||||
];
|
];
|
||||||
|
|
||||||
let function_var = var_store.fresh();
|
let function_var = var_store.fresh();
|
||||||
let after_closure = Expr::Closure {
|
let after_closure = Expr::Closure(ClosureData {
|
||||||
function_type: function_var,
|
function_type: function_var,
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -519,7 +519,7 @@ fn build_effect_after(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(to_effect_call)),
|
loc_body: Box::new(Located::at_zero(to_effect_call)),
|
||||||
};
|
});
|
||||||
|
|
||||||
let mut introduced_variables = IntroducedVariables::default();
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
|
@ -653,7 +653,7 @@ pub fn build_host_exposed_def(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let effect_closure = Expr::Closure {
|
let effect_closure = Expr::Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -666,7 +666,7 @@ pub fn build_host_exposed_def(
|
||||||
Located::at_zero(empty_record_pattern(var_store)),
|
Located::at_zero(empty_record_pattern(var_store)),
|
||||||
)],
|
)],
|
||||||
loc_body: Box::new(Located::at_zero(low_level_call)),
|
loc_body: Box::new(Located::at_zero(low_level_call)),
|
||||||
};
|
});
|
||||||
|
|
||||||
let body = Expr::Tag {
|
let body = Expr::Tag {
|
||||||
variant_var: var_store.fresh(),
|
variant_var: var_store.fresh(),
|
||||||
|
@ -675,7 +675,7 @@ pub fn build_host_exposed_def(
|
||||||
arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))],
|
arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))],
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr::Closure {
|
Expr::Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -685,7 +685,7 @@ pub fn build_host_exposed_def(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body: Box::new(Located::at_zero(body)),
|
loc_body: Box::new(Located::at_zero(body)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// not a function
|
// not a function
|
||||||
|
@ -717,7 +717,7 @@ pub fn build_host_exposed_def(
|
||||||
destructs: vec![],
|
destructs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let effect_closure = Expr::Closure {
|
let effect_closure = Expr::Closure(ClosureData {
|
||||||
function_type: var_store.fresh(),
|
function_type: var_store.fresh(),
|
||||||
closure_type: var_store.fresh(),
|
closure_type: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
|
@ -727,7 +727,7 @@ pub fn build_host_exposed_def(
|
||||||
recursive: Recursive::NotRecursive,
|
recursive: Recursive::NotRecursive,
|
||||||
arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))],
|
arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))],
|
||||||
loc_body: Box::new(Located::at_zero(low_level_call)),
|
loc_body: Box::new(Located::at_zero(low_level_call)),
|
||||||
};
|
});
|
||||||
|
|
||||||
Expr::Tag {
|
Expr::Tag {
|
||||||
variant_var: var_store.fresh(),
|
variant_var: var_store.fresh(),
|
||||||
|
|
|
@ -3958,7 +3958,10 @@ fn make_specializations<'a>(
|
||||||
|
|
||||||
let mut procs = Procs::new_in(arena);
|
let mut procs = Procs::new_in(arena);
|
||||||
|
|
||||||
procs.partial_procs = procs_base.partial_procs;
|
for (symbol, partial_proc) in procs_base.partial_procs.into_iter() {
|
||||||
|
procs.partial_procs.insert(symbol, partial_proc);
|
||||||
|
}
|
||||||
|
|
||||||
procs.module_thunks = procs_base.module_thunks;
|
procs.module_thunks = procs_base.module_thunks;
|
||||||
procs.runtime_errors = procs_base.runtime_errors;
|
procs.runtime_errors = procs_base.runtime_errors;
|
||||||
procs.imported_module_thunks = procs_base.imported_module_thunks;
|
procs.imported_module_thunks = procs_base.imported_module_thunks;
|
||||||
|
@ -4126,6 +4129,7 @@ fn add_def_to_module<'a>(
|
||||||
exposed_to_host: &MutMap<Symbol, Variable>,
|
exposed_to_host: &MutMap<Symbol, Variable>,
|
||||||
is_recursive: bool,
|
is_recursive: bool,
|
||||||
) {
|
) {
|
||||||
|
use roc_can::expr::ClosureData;
|
||||||
use roc_can::expr::Expr::*;
|
use roc_can::expr::Expr::*;
|
||||||
use roc_can::pattern::Pattern::*;
|
use roc_can::pattern::Pattern::*;
|
||||||
|
|
||||||
|
@ -4134,14 +4138,14 @@ fn add_def_to_module<'a>(
|
||||||
let is_exposed = exposed_to_host.contains_key(&symbol);
|
let is_exposed = exposed_to_host.contains_key(&symbol);
|
||||||
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type: annotation,
|
function_type: annotation,
|
||||||
return_type: ret_var,
|
return_type: ret_var,
|
||||||
arguments: loc_args,
|
arguments: loc_args,
|
||||||
loc_body,
|
loc_body,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
..
|
..
|
||||||
} => {
|
}) => {
|
||||||
// this is a top-level definition, it should not capture anything
|
// this is a top-level definition, it should not capture anything
|
||||||
debug_assert!(captured_symbols.is_empty());
|
debug_assert!(captured_symbols.is_empty());
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::layout::{
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
|
use roc_can::expr::ClosureData;
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -71,6 +72,61 @@ pub struct EntryPoint<'a> {
|
||||||
pub layout: ProcLayout<'a>,
|
pub layout: ProcLayout<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct PartialProcId(usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct PartialProcs<'a> {
|
||||||
|
/// maps a function name (symbol) to an index
|
||||||
|
symbols: Vec<'a, Symbol>,
|
||||||
|
|
||||||
|
partial_procs: Vec<'a, PartialProc<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialProcs<'a> {
|
||||||
|
fn new_in(arena: &'a Bump) -> Self {
|
||||||
|
Self {
|
||||||
|
symbols: Vec::new_in(arena),
|
||||||
|
partial_procs: Vec::new_in(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn contains_key(&self, symbol: Symbol) -> bool {
|
||||||
|
self.symbol_to_id(symbol).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn symbol_to_id(&self, symbol: Symbol) -> Option<PartialProcId> {
|
||||||
|
self.symbols
|
||||||
|
.iter()
|
||||||
|
.position(|s| *s == symbol)
|
||||||
|
.map(PartialProcId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_symbol(&self, symbol: Symbol) -> Option<&PartialProc<'a>> {
|
||||||
|
let id = self.symbol_to_id(symbol)?;
|
||||||
|
|
||||||
|
Some(self.get_id(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_id(&self, id: PartialProcId) -> &PartialProc<'a> {
|
||||||
|
&self.partial_procs[id.0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, symbol: Symbol, partial_proc: PartialProc<'a>) -> PartialProcId {
|
||||||
|
debug_assert!(
|
||||||
|
!self.contains_key(symbol),
|
||||||
|
"The {:?} is inserted as a partial proc twice: that's a bug!",
|
||||||
|
symbol,
|
||||||
|
);
|
||||||
|
|
||||||
|
let id = PartialProcId(self.symbols.len());
|
||||||
|
|
||||||
|
self.symbols.push(symbol);
|
||||||
|
self.partial_procs.push(partial_proc);
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct PartialProc<'a> {
|
pub struct PartialProc<'a> {
|
||||||
pub annotation: Variable,
|
pub annotation: Variable,
|
||||||
|
@ -129,7 +185,7 @@ impl<'a> PartialProc<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum CapturedSymbols<'a> {
|
pub enum CapturedSymbols<'a> {
|
||||||
None,
|
None,
|
||||||
Captured(&'a [(Symbol, Variable)]),
|
Captured(&'a [(Symbol, Variable)]),
|
||||||
|
@ -418,7 +474,7 @@ impl<'a> ExternalSpecializations<'a> {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Procs<'a> {
|
pub struct Procs<'a> {
|
||||||
pub partial_procs: BumpMap<Symbol, PartialProc<'a>>,
|
pub partial_procs: PartialProcs<'a>,
|
||||||
pub imported_module_thunks: &'a [Symbol],
|
pub imported_module_thunks: &'a [Symbol],
|
||||||
pub module_thunks: &'a [Symbol],
|
pub module_thunks: &'a [Symbol],
|
||||||
pub pending_specializations:
|
pub pending_specializations:
|
||||||
|
@ -432,7 +488,7 @@ pub struct Procs<'a> {
|
||||||
impl<'a> Procs<'a> {
|
impl<'a> Procs<'a> {
|
||||||
pub fn new_in(arena: &'a Bump) -> Self {
|
pub fn new_in(arena: &'a Bump) -> Self {
|
||||||
Self {
|
Self {
|
||||||
partial_procs: BumpMap::new_in(arena),
|
partial_procs: PartialProcs::new_in(arena),
|
||||||
imported_module_thunks: &[],
|
imported_module_thunks: &[],
|
||||||
module_thunks: &[],
|
module_thunks: &[],
|
||||||
pending_specializations: Some(BumpMap::new_in(arena)),
|
pending_specializations: Some(BumpMap::new_in(arena)),
|
||||||
|
@ -459,6 +515,10 @@ impl<'a> Procs<'a> {
|
||||||
self.module_thunks.iter().any(|x| *x == symbol)
|
self.module_thunks.iter().any(|x| *x == symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_partial_proc<'b>(&'b self, symbol: Symbol) -> Option<&'b PartialProc<'a>> {
|
||||||
|
self.partial_procs.get_symbol(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_specialized_procs_without_rc(
|
pub fn get_specialized_procs_without_rc(
|
||||||
self,
|
self,
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -535,9 +595,9 @@ impl<'a> Procs<'a> {
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
add_pending(pending_specializations, symbol, layout, pending);
|
add_pending(pending_specializations, symbol, layout, pending);
|
||||||
|
|
||||||
match self.partial_procs.entry(symbol) {
|
match self.partial_procs.symbol_to_id(symbol) {
|
||||||
Entry::Occupied(occupied) => {
|
Some(occupied) => {
|
||||||
let existing = occupied.get();
|
let existing = self.partial_procs.get_id(occupied);
|
||||||
// if we're adding the same partial proc twice, they must be the actual same!
|
// if we're adding the same partial proc twice, they must be the actual same!
|
||||||
//
|
//
|
||||||
// NOTE we can't skip extra work! we still need to make the specialization for this
|
// NOTE we can't skip extra work! we still need to make the specialization for this
|
||||||
|
@ -549,7 +609,7 @@ impl<'a> Procs<'a> {
|
||||||
|
|
||||||
// the partial proc is already in there, do nothing
|
// the partial proc is already in there, do nothing
|
||||||
}
|
}
|
||||||
Entry::Vacant(vacant) => {
|
None => {
|
||||||
let pattern_symbols = pattern_symbols.into_bump_slice();
|
let pattern_symbols = pattern_symbols.into_bump_slice();
|
||||||
|
|
||||||
let partial_proc = PartialProc {
|
let partial_proc = PartialProc {
|
||||||
|
@ -560,7 +620,7 @@ impl<'a> Procs<'a> {
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
};
|
};
|
||||||
|
|
||||||
vacant.insert(partial_proc);
|
self.partial_procs.insert(symbol, partial_proc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -572,8 +632,10 @@ impl<'a> Procs<'a> {
|
||||||
|
|
||||||
let outside_layout = layout;
|
let outside_layout = layout;
|
||||||
|
|
||||||
let partial_proc;
|
let partial_proc_id = if let Some(partial_proc_id) =
|
||||||
if let Some(existing) = self.partial_procs.get(&symbol) {
|
self.partial_procs.symbol_to_id(symbol)
|
||||||
|
{
|
||||||
|
let existing = self.partial_procs.get_id(partial_proc_id);
|
||||||
// if we're adding the same partial proc twice, they must be the actual same!
|
// if we're adding the same partial proc twice, they must be the actual same!
|
||||||
//
|
//
|
||||||
// NOTE we can't skip extra work! we still need to make the specialization for this
|
// NOTE we can't skip extra work! we still need to make the specialization for this
|
||||||
|
@ -583,21 +645,29 @@ impl<'a> Procs<'a> {
|
||||||
debug_assert_eq!(captured_symbols, existing.captured_symbols);
|
debug_assert_eq!(captured_symbols, existing.captured_symbols);
|
||||||
debug_assert_eq!(is_self_recursive, existing.is_self_recursive);
|
debug_assert_eq!(is_self_recursive, existing.is_self_recursive);
|
||||||
|
|
||||||
partial_proc = existing.clone();
|
partial_proc_id
|
||||||
} else {
|
} else {
|
||||||
let pattern_symbols = pattern_symbols.into_bump_slice();
|
let pattern_symbols = pattern_symbols.into_bump_slice();
|
||||||
|
|
||||||
partial_proc = PartialProc {
|
let partial_proc = PartialProc {
|
||||||
annotation,
|
annotation,
|
||||||
pattern_symbols,
|
pattern_symbols,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
body: body.value,
|
body: body.value,
|
||||||
is_self_recursive,
|
is_self_recursive,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
match specialize(env, self, symbol, layout_cache, pending, partial_proc)
|
self.partial_procs.insert(symbol, partial_proc)
|
||||||
{
|
};
|
||||||
|
|
||||||
|
match specialize(
|
||||||
|
env,
|
||||||
|
self,
|
||||||
|
symbol,
|
||||||
|
layout_cache,
|
||||||
|
pending,
|
||||||
|
partial_proc_id,
|
||||||
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
let top_level = ProcLayout::from_raw(env.arena, layout);
|
let top_level = ProcLayout::from_raw(env.arena, layout);
|
||||||
|
|
||||||
|
@ -662,9 +732,8 @@ impl<'a> Procs<'a> {
|
||||||
None => {
|
None => {
|
||||||
let symbol = name;
|
let symbol = name;
|
||||||
|
|
||||||
// TODO should pending_procs hold a Rc<Proc>?
|
let partial_proc_id = match self.partial_procs.symbol_to_id(symbol) {
|
||||||
let partial_proc = match self.partial_procs.get(&symbol) {
|
Some(p) => p,
|
||||||
Some(p) => p.clone(),
|
|
||||||
None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home),
|
None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -691,7 +760,7 @@ impl<'a> Procs<'a> {
|
||||||
layout_cache,
|
layout_cache,
|
||||||
fn_var,
|
fn_var,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
) {
|
) {
|
||||||
Ok((proc, _ignore_layout)) => {
|
Ok((proc, _ignore_layout)) => {
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||||
|
@ -1726,14 +1795,14 @@ pub fn specialize_all<'a>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Entry::Vacant(vacant) => {
|
Entry::Vacant(vacant) => {
|
||||||
match procs.partial_procs.get(&name) {
|
match procs.partial_procs.symbol_to_id(name) {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
// Mark this proc as in-progress, so if we're dealing with
|
// Mark this proc as in-progress, so if we're dealing with
|
||||||
// mutually recursive functions, we don't loop forever.
|
// mutually recursive functions, we don't loop forever.
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
vacant.insert(InProgress);
|
vacant.insert(InProgress);
|
||||||
|
|
||||||
v.clone()
|
v
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// TODO this assumes the specialization is done by another module
|
// TODO this assumes the specialization is done by another module
|
||||||
|
@ -1808,8 +1877,8 @@ fn specialize_externals_others_need<'a>(
|
||||||
|
|
||||||
let name = *symbol;
|
let name = *symbol;
|
||||||
|
|
||||||
let partial_proc = match procs.partial_procs.get(&name) {
|
let partial_proc_id = match procs.partial_procs.symbol_to_id(name) {
|
||||||
Some(v) => v.clone(),
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
panic!("Cannot find a partial proc for {:?}", name);
|
panic!("Cannot find a partial proc for {:?}", name);
|
||||||
}
|
}
|
||||||
|
@ -1823,7 +1892,7 @@ fn specialize_externals_others_need<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
solved_type,
|
solved_type,
|
||||||
BumpMap::new_in(env.arena),
|
BumpMap::new_in(env.arena),
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
let top_level = ProcLayout::from_raw(env.arena, layout);
|
let top_level = ProcLayout::from_raw(env.arena, layout);
|
||||||
|
@ -1901,32 +1970,28 @@ fn specialize_external<'a>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
fn_var: Variable,
|
fn_var: Variable,
|
||||||
host_exposed_variables: &[(Symbol, Variable)],
|
host_exposed_variables: &[(Symbol, Variable)],
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc_id: PartialProcId,
|
||||||
) -> Result<Proc<'a>, LayoutProblem> {
|
) -> Result<Proc<'a>, LayoutProblem> {
|
||||||
let PartialProc {
|
let partial_proc = procs.partial_procs.get_id(partial_proc_id);
|
||||||
annotation,
|
let captured_symbols = partial_proc.captured_symbols;
|
||||||
pattern_symbols,
|
|
||||||
captured_symbols,
|
|
||||||
body,
|
|
||||||
is_self_recursive,
|
|
||||||
} = partial_proc;
|
|
||||||
|
|
||||||
// unify the called function with the specialized signature, then specialize the function body
|
// unify the called function with the specialized signature, then specialize the function body
|
||||||
let snapshot = env.subs.snapshot();
|
let snapshot = env.subs.snapshot();
|
||||||
let cache_snapshot = layout_cache.snapshot();
|
let cache_snapshot = layout_cache.snapshot();
|
||||||
|
|
||||||
let _unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
let _unified = roc_unify::unify::unify(env.subs, partial_proc.annotation, fn_var);
|
||||||
|
|
||||||
// This will not hold for programs with type errors
|
// This will not hold for programs with type errors
|
||||||
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
// let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||||
// debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
// debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
||||||
|
|
||||||
// if this is a closure, add the closure record argument
|
// if this is a closure, add the closure record argument
|
||||||
let pattern_symbols = match captured_symbols {
|
let pattern_symbols = match partial_proc.captured_symbols {
|
||||||
CapturedSymbols::None => pattern_symbols,
|
CapturedSymbols::None => partial_proc.pattern_symbols,
|
||||||
CapturedSymbols::Captured([]) => pattern_symbols,
|
CapturedSymbols::Captured([]) => partial_proc.pattern_symbols,
|
||||||
CapturedSymbols::Captured(_) => {
|
CapturedSymbols::Captured(_) => {
|
||||||
let mut temp = Vec::from_iter_in(pattern_symbols.iter().copied(), env.arena);
|
let mut temp =
|
||||||
|
Vec::from_iter_in(partial_proc.pattern_symbols.iter().copied(), env.arena);
|
||||||
temp.push(Symbol::ARG_CLOSURE);
|
temp.push(Symbol::ARG_CLOSURE);
|
||||||
temp.into_bump_slice()
|
temp.into_bump_slice()
|
||||||
}
|
}
|
||||||
|
@ -2023,12 +2088,13 @@ fn specialize_external<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let recursivity = if is_self_recursive {
|
let recursivity = if partial_proc.is_self_recursive {
|
||||||
SelfRecursive::SelfRecursive(JoinPointId(env.unique_symbol()))
|
SelfRecursive::SelfRecursive(JoinPointId(env.unique_symbol()))
|
||||||
} else {
|
} else {
|
||||||
SelfRecursive::NotSelfRecursive
|
SelfRecursive::NotSelfRecursive
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let body = partial_proc.body.clone();
|
||||||
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||||
|
|
||||||
match specialized {
|
match specialized {
|
||||||
|
@ -2419,13 +2485,13 @@ struct SpecializeFailure<'a> {
|
||||||
|
|
||||||
type SpecializeSuccess<'a> = (Proc<'a>, RawFunctionLayout<'a>);
|
type SpecializeSuccess<'a> = (Proc<'a>, RawFunctionLayout<'a>);
|
||||||
|
|
||||||
fn specialize<'a>(
|
fn specialize<'a, 'b>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &'b mut Procs<'a>,
|
||||||
proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
pending: PendingSpecialization,
|
pending: PendingSpecialization,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc_id: PartialProcId,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||||
let PendingSpecialization {
|
let PendingSpecialization {
|
||||||
solved_type,
|
solved_type,
|
||||||
|
@ -2440,7 +2506,7 @@ fn specialize<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
&solved_type,
|
&solved_type,
|
||||||
host_exposed_aliases,
|
host_exposed_aliases,
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2470,7 +2536,7 @@ fn specialize_solved_type<'a>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
solved_type: &SolvedType,
|
solved_type: &SolvedType,
|
||||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc_id: PartialProcId,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||||
specialize_variable_help(
|
specialize_variable_help(
|
||||||
env,
|
env,
|
||||||
|
@ -2479,7 +2545,7 @@ fn specialize_solved_type<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|env| introduce_solved_type_to_subs(env, solved_type),
|
|env| introduce_solved_type_to_subs(env, solved_type),
|
||||||
host_exposed_aliases,
|
host_exposed_aliases,
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2490,7 +2556,7 @@ fn specialize_variable<'a>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
fn_var: Variable,
|
fn_var: Variable,
|
||||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc_id: PartialProcId,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||||
specialize_variable_help(
|
specialize_variable_help(
|
||||||
env,
|
env,
|
||||||
|
@ -2499,7 +2565,7 @@ fn specialize_variable<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|_| fn_var,
|
|_| fn_var,
|
||||||
host_exposed_aliases,
|
host_exposed_aliases,
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2510,7 +2576,7 @@ fn specialize_variable_help<'a, F>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
fn_var_thunk: F,
|
fn_var_thunk: F,
|
||||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc_id: PartialProcId,
|
||||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
|
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Env<'a, '_>) -> Variable,
|
F: FnOnce(&mut Env<'a, '_>) -> Variable,
|
||||||
|
@ -2541,7 +2607,8 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
// make sure rigid variables in the annotation are converted to flex variables
|
// make sure rigid variables in the annotation are converted to flex variables
|
||||||
instantiate_rigids(env.subs, partial_proc.annotation);
|
let annotation_var = procs.partial_procs.get_id(partial_proc_id).annotation;
|
||||||
|
instantiate_rigids(env.subs, annotation_var);
|
||||||
|
|
||||||
let mut host_exposed_variables = Vec::with_capacity_in(host_exposed_aliases.len(), env.arena);
|
let mut host_exposed_variables = Vec::with_capacity_in(host_exposed_aliases.len(), env.arena);
|
||||||
|
|
||||||
|
@ -2558,7 +2625,7 @@ where
|
||||||
layout_cache,
|
layout_cache,
|
||||||
fn_var,
|
fn_var,
|
||||||
&host_exposed_variables,
|
&host_exposed_variables,
|
||||||
partial_proc,
|
partial_proc_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
match specialized {
|
match specialized {
|
||||||
|
@ -2633,7 +2700,7 @@ fn specialize_naked_symbol<'a>(
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
if procs.is_module_thunk(symbol) {
|
if procs.is_module_thunk(symbol) {
|
||||||
let partial_proc = procs.partial_procs.get(&symbol).unwrap();
|
let partial_proc = procs.get_partial_proc(symbol).unwrap();
|
||||||
let fn_var = partial_proc.annotation;
|
let fn_var = partial_proc.annotation;
|
||||||
|
|
||||||
// This is a top-level declaration, which will code gen to a 0-arity thunk.
|
// This is a top-level declaration, which will code gen to a 0-arity thunk.
|
||||||
|
@ -2847,42 +2914,9 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LetNonRec(def, cont, _) => {
|
LetNonRec(def, cont, _) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
||||||
if let Closure {
|
if let Closure(closure_data) = def.loc_expr.value {
|
||||||
function_type,
|
register_noncapturing_closure(env, procs, layout_cache, symbol, closure_data);
|
||||||
return_type,
|
|
||||||
recursive,
|
|
||||||
arguments,
|
|
||||||
loc_body: boxed_body,
|
|
||||||
captured_symbols,
|
|
||||||
..
|
|
||||||
} = def.loc_expr.value
|
|
||||||
{
|
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
|
||||||
// That Load looks up the pointer, which we won't use here!
|
|
||||||
|
|
||||||
let loc_body = *boxed_body;
|
|
||||||
|
|
||||||
let is_self_recursive =
|
|
||||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
|
||||||
|
|
||||||
// this should be a top-level declaration, and hence have no captured symbols
|
|
||||||
// if we ever do hit this (and it's not a bug), we should make sure to put the
|
|
||||||
// captured symbols into a CapturedSymbols and give it to PartialProc::from_named_function
|
|
||||||
debug_assert!(captured_symbols.is_empty());
|
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
|
||||||
env,
|
|
||||||
layout_cache,
|
|
||||||
function_type,
|
|
||||||
arguments,
|
|
||||||
loc_body,
|
|
||||||
CapturedSymbols::None,
|
|
||||||
is_self_recursive,
|
|
||||||
return_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.partial_procs.insert(*symbol, partial_proc);
|
|
||||||
|
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
|
@ -2894,9 +2928,6 @@ pub fn with_hole<'a>(
|
||||||
hole,
|
hole,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = def.loc_pattern.value {
|
|
||||||
// special-case the form `let x = E in x`
|
// special-case the form `let x = E in x`
|
||||||
// not doing so will drop the `hole`
|
// not doing so will drop the `hole`
|
||||||
match &cont.value {
|
match &cont.value {
|
||||||
|
@ -3024,36 +3055,15 @@ pub fn with_hole<'a>(
|
||||||
// because Roc is strict, only functions can be recursive!
|
// because Roc is strict, only functions can be recursive!
|
||||||
for def in defs.into_iter() {
|
for def in defs.into_iter() {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure {
|
if let Closure(closure_data) = def.loc_expr.value {
|
||||||
function_type,
|
register_noncapturing_closure(
|
||||||
return_type,
|
|
||||||
recursive,
|
|
||||||
arguments,
|
|
||||||
loc_body: boxed_body,
|
|
||||||
..
|
|
||||||
} = def.loc_expr.value
|
|
||||||
{
|
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
|
||||||
// That Load looks up the pointer, which we won't use here!
|
|
||||||
|
|
||||||
let loc_body = *boxed_body;
|
|
||||||
|
|
||||||
let is_self_recursive =
|
|
||||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
|
||||||
env,
|
env,
|
||||||
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
function_type,
|
*symbol,
|
||||||
arguments,
|
closure_data,
|
||||||
loc_body,
|
|
||||||
CapturedSymbols::None,
|
|
||||||
is_self_recursive,
|
|
||||||
return_type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
procs.partial_procs.insert(*symbol, partial_proc);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3749,7 +3759,7 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure {
|
Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
return_type,
|
return_type,
|
||||||
name,
|
name,
|
||||||
|
@ -3757,7 +3767,7 @@ pub fn with_hole<'a>(
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
loc_body: boxed_body,
|
loc_body: boxed_body,
|
||||||
..
|
..
|
||||||
} => {
|
}) => {
|
||||||
let loc_body = *boxed_body;
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
let raw = layout_cache.raw_from_var(env.arena, function_type, env.subs);
|
let raw = layout_cache.raw_from_var(env.arena, function_type, env.subs);
|
||||||
|
@ -3814,11 +3824,11 @@ pub fn with_hole<'a>(
|
||||||
// if it's in there, it's a call by name, otherwise it's a call by pointer
|
// if it's in there, it's a call by name, otherwise it's a call by pointer
|
||||||
let is_known = |key| {
|
let is_known = |key| {
|
||||||
// a proc in this module, or an imported symbol
|
// a proc in this module, or an imported symbol
|
||||||
procs.partial_procs.contains_key(key) || env.is_imported_symbol(*key)
|
procs.partial_procs.contains_key(key) || env.is_imported_symbol(key)
|
||||||
};
|
};
|
||||||
|
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(proc_name) if is_known(&proc_name) => {
|
roc_can::expr::Expr::Var(proc_name) if is_known(proc_name) => {
|
||||||
// a call by a known name
|
// a call by a known name
|
||||||
call_by_name(
|
call_by_name(
|
||||||
env,
|
env,
|
||||||
|
@ -4633,6 +4643,131 @@ fn sorted_field_symbols<'a>(
|
||||||
field_symbols_temp
|
field_symbols_temp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a closure that does capture symbols (because it is top-level) to the list of partial procs
|
||||||
|
fn register_noncapturing_closure<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
closure_name: Symbol,
|
||||||
|
closure_data: ClosureData,
|
||||||
|
) {
|
||||||
|
let ClosureData {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
captured_symbols,
|
||||||
|
..
|
||||||
|
} = closure_data;
|
||||||
|
|
||||||
|
// Extract Procs, but discard the resulting Expr::Load.
|
||||||
|
// That Load looks up the pointer, which we won't use here!
|
||||||
|
|
||||||
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
|
let is_self_recursive = !matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
|
// this should be a top-level declaration, and hence have no captured symbols
|
||||||
|
// if we ever do hit this (and it's not a bug), we should make sure to put the
|
||||||
|
// captured symbols into a CapturedSymbols and give it to PartialProc::from_named_function
|
||||||
|
debug_assert!(captured_symbols.is_empty());
|
||||||
|
|
||||||
|
let partial_proc = PartialProc::from_named_function(
|
||||||
|
env,
|
||||||
|
layout_cache,
|
||||||
|
function_type,
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
CapturedSymbols::None,
|
||||||
|
is_self_recursive,
|
||||||
|
return_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.partial_procs.insert(closure_name, partial_proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a closure that may capture symbols to the list of partial procs
|
||||||
|
fn register_capturing_closure<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
closure_name: Symbol,
|
||||||
|
closure_data: ClosureData,
|
||||||
|
) {
|
||||||
|
// the function surrounding the closure definition may be specialized multiple times,
|
||||||
|
// hence in theory this partial proc may be added multiple times. That would be wasteful
|
||||||
|
// so we check whether this partial proc is already there.
|
||||||
|
//
|
||||||
|
// (the `gen_primitives::task_always_twice` test has this behavior)
|
||||||
|
if !procs.partial_procs.contains_key(closure_name) {
|
||||||
|
let ClosureData {
|
||||||
|
function_type,
|
||||||
|
return_type,
|
||||||
|
closure_type,
|
||||||
|
closure_ext_var,
|
||||||
|
recursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: boxed_body,
|
||||||
|
captured_symbols,
|
||||||
|
..
|
||||||
|
} = closure_data;
|
||||||
|
let loc_body = *boxed_body;
|
||||||
|
|
||||||
|
let is_self_recursive = !matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
||||||
|
|
||||||
|
// does this function capture any local values?
|
||||||
|
let function_layout = layout_cache.raw_from_var(env.arena, function_type, env.subs);
|
||||||
|
|
||||||
|
let captured_symbols = match function_layout {
|
||||||
|
Ok(RawFunctionLayout::Function(_, lambda_set, _)) => {
|
||||||
|
if let Layout::Struct(&[]) = lambda_set.runtime_representation() {
|
||||||
|
CapturedSymbols::None
|
||||||
|
} else {
|
||||||
|
let mut temp = Vec::from_iter_in(captured_symbols, env.arena);
|
||||||
|
temp.sort();
|
||||||
|
CapturedSymbols::Captured(temp.into_bump_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(RawFunctionLayout::ZeroArgumentThunk(_)) => {
|
||||||
|
// top-level thunks cannot capture any variables
|
||||||
|
debug_assert!(
|
||||||
|
captured_symbols.is_empty(),
|
||||||
|
"{:?} with layout {:?} {:?} {:?}",
|
||||||
|
&captured_symbols,
|
||||||
|
function_layout,
|
||||||
|
env.subs,
|
||||||
|
(function_type, closure_type, closure_ext_var),
|
||||||
|
);
|
||||||
|
CapturedSymbols::None
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// just allow this. see https://github.com/rtfeldman/roc/issues/1585
|
||||||
|
if captured_symbols.is_empty() {
|
||||||
|
CapturedSymbols::None
|
||||||
|
} else {
|
||||||
|
let mut temp = Vec::from_iter_in(captured_symbols, env.arena);
|
||||||
|
temp.sort();
|
||||||
|
CapturedSymbols::Captured(temp.into_bump_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let partial_proc = PartialProc::from_named_function(
|
||||||
|
env,
|
||||||
|
layout_cache,
|
||||||
|
function_type,
|
||||||
|
arguments,
|
||||||
|
loc_body,
|
||||||
|
captured_symbols,
|
||||||
|
is_self_recursive,
|
||||||
|
return_type,
|
||||||
|
);
|
||||||
|
|
||||||
|
procs.partial_procs.insert(closure_name, partial_proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_can<'a>(
|
pub fn from_can<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
variable: Variable,
|
variable: Variable,
|
||||||
|
@ -4752,35 +4887,15 @@ pub fn from_can<'a>(
|
||||||
// Now that we know for sure it's a closure, get an owned
|
// Now that we know for sure it's a closure, get an owned
|
||||||
// version of these variant args so we can use them properly.
|
// version of these variant args so we can use them properly.
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
Closure {
|
Closure(closure_data) => {
|
||||||
function_type,
|
register_capturing_closure(
|
||||||
return_type,
|
|
||||||
recursive,
|
|
||||||
arguments,
|
|
||||||
loc_body: boxed_body,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
|
||||||
// That Load looks up the pointer, which we won't use here!
|
|
||||||
|
|
||||||
let loc_body = *boxed_body;
|
|
||||||
|
|
||||||
let is_self_recursive =
|
|
||||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
|
||||||
env,
|
env,
|
||||||
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
function_type,
|
*symbol,
|
||||||
arguments,
|
closure_data,
|
||||||
loc_body,
|
|
||||||
CapturedSymbols::None,
|
|
||||||
is_self_recursive,
|
|
||||||
return_type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
procs.partial_procs.insert(*symbol, partial_proc);
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_ => unreachable!("recursive value is not a function"),
|
_ => unreachable!("recursive value is not a function"),
|
||||||
|
@ -4793,90 +4908,12 @@ pub fn from_can<'a>(
|
||||||
}
|
}
|
||||||
LetNonRec(def, cont, outer_annotation) => {
|
LetNonRec(def, cont, outer_annotation) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
if let Closure { .. } = &def.loc_expr.value {
|
|
||||||
// Now that we know for sure it's a closure, get an owned
|
|
||||||
// version of these variant args so we can use them properly.
|
|
||||||
match def.loc_expr.value {
|
|
||||||
Closure {
|
|
||||||
function_type,
|
|
||||||
return_type,
|
|
||||||
closure_type,
|
|
||||||
closure_ext_var,
|
|
||||||
recursive,
|
|
||||||
arguments,
|
|
||||||
loc_body: boxed_body,
|
|
||||||
captured_symbols,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// Extract Procs, but discard the resulting Expr::Load.
|
|
||||||
// That Load looks up the pointer, which we won't use here!
|
|
||||||
|
|
||||||
let loc_body = *boxed_body;
|
|
||||||
|
|
||||||
let is_self_recursive =
|
|
||||||
!matches!(recursive, roc_can::expr::Recursive::NotRecursive);
|
|
||||||
|
|
||||||
// does this function capture any local values?
|
|
||||||
let function_layout =
|
|
||||||
layout_cache.raw_from_var(env.arena, function_type, env.subs);
|
|
||||||
|
|
||||||
let captured_symbols = match function_layout {
|
|
||||||
Ok(RawFunctionLayout::Function(_, lambda_set, _)) => {
|
|
||||||
if let Layout::Struct(&[]) = lambda_set.runtime_representation()
|
|
||||||
{
|
|
||||||
CapturedSymbols::None
|
|
||||||
} else {
|
|
||||||
let mut temp =
|
|
||||||
Vec::from_iter_in(captured_symbols, env.arena);
|
|
||||||
temp.sort();
|
|
||||||
CapturedSymbols::Captured(temp.into_bump_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(RawFunctionLayout::ZeroArgumentThunk(_)) => {
|
|
||||||
// top-level thunks cannot capture any variables
|
|
||||||
debug_assert!(
|
|
||||||
captured_symbols.is_empty(),
|
|
||||||
"{:?} with layout {:?} {:?} {:?}",
|
|
||||||
&captured_symbols,
|
|
||||||
function_layout,
|
|
||||||
env.subs,
|
|
||||||
(function_type, closure_type, closure_ext_var),
|
|
||||||
);
|
|
||||||
CapturedSymbols::None
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// just allow this. see https://github.com/rtfeldman/roc/issues/1585
|
|
||||||
if captured_symbols.is_empty() {
|
|
||||||
CapturedSymbols::None
|
|
||||||
} else {
|
|
||||||
let mut temp =
|
|
||||||
Vec::from_iter_in(captured_symbols, env.arena);
|
|
||||||
temp.sort();
|
|
||||||
CapturedSymbols::Captured(temp.into_bump_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let partial_proc = PartialProc::from_named_function(
|
|
||||||
env,
|
|
||||||
layout_cache,
|
|
||||||
function_type,
|
|
||||||
arguments,
|
|
||||||
loc_body,
|
|
||||||
captured_symbols,
|
|
||||||
is_self_recursive,
|
|
||||||
return_type,
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.partial_procs.insert(*symbol, partial_proc);
|
|
||||||
|
|
||||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
|
roc_can::expr::Expr::Closure(closure_data) => {
|
||||||
|
register_capturing_closure(env, procs, layout_cache, *symbol, closure_data);
|
||||||
|
|
||||||
|
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
}
|
||||||
roc_can::expr::Expr::Var(original) => {
|
roc_can::expr::Expr::Var(original) => {
|
||||||
// a variable is aliased, e.g.
|
// a variable is aliased, e.g.
|
||||||
//
|
//
|
||||||
|
@ -6012,7 +6049,7 @@ fn can_reuse_symbol<'a>(
|
||||||
|
|
||||||
if env.is_imported_symbol(symbol) {
|
if env.is_imported_symbol(symbol) {
|
||||||
Imported(symbol)
|
Imported(symbol)
|
||||||
} else if procs.partial_procs.contains_key(&symbol) {
|
} else if procs.partial_procs.contains_key(symbol) {
|
||||||
LocalFunction(symbol)
|
LocalFunction(symbol)
|
||||||
} else {
|
} else {
|
||||||
Value(symbol)
|
Value(symbol)
|
||||||
|
@ -6101,7 +6138,7 @@ fn reuse_function_symbol<'a>(
|
||||||
result: Stmt<'a>,
|
result: Stmt<'a>,
|
||||||
original: Symbol,
|
original: Symbol,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
match procs.partial_procs.get(&original) {
|
match procs.get_partial_proc(original) {
|
||||||
None => {
|
None => {
|
||||||
match arg_var {
|
match arg_var {
|
||||||
Some(arg_var) if env.is_imported_symbol(original) => {
|
Some(arg_var) if env.is_imported_symbol(original) => {
|
||||||
|
@ -6170,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, _) => {
|
||||||
|
@ -6674,7 +6711,7 @@ fn call_by_name_help<'a>(
|
||||||
assign_to_symbols(env, procs, layout_cache, iter, result)
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let opt_partial_proc = procs.partial_procs.get(&proc_name);
|
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -6691,9 +6728,6 @@ fn call_by_name_help<'a>(
|
||||||
|
|
||||||
match opt_partial_proc {
|
match opt_partial_proc {
|
||||||
Some(partial_proc) => {
|
Some(partial_proc) => {
|
||||||
// TODO should pending_procs hold a Rc<Proc> to avoid this .clone()?
|
|
||||||
let partial_proc = partial_proc.clone();
|
|
||||||
|
|
||||||
// Mark this proc as in-progress, so if we're dealing with
|
// Mark this proc as in-progress, so if we're dealing with
|
||||||
// mutually recursive functions, we don't loop forever.
|
// mutually recursive functions, we don't loop forever.
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
|
@ -6773,10 +6807,11 @@ fn call_by_name_module_thunk<'a>(
|
||||||
let inner_layout = *ret_layout;
|
let inner_layout = *ret_layout;
|
||||||
|
|
||||||
// If we've already specialized this one, no further work is needed.
|
// If we've already specialized this one, no further work is needed.
|
||||||
if procs
|
let already_specialized = procs
|
||||||
.specialized
|
.specialized
|
||||||
.contains_key(&(proc_name, top_level_layout))
|
.contains_key(&(proc_name, top_level_layout));
|
||||||
{
|
|
||||||
|
if already_specialized {
|
||||||
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
||||||
} else {
|
} else {
|
||||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||||
|
@ -6811,13 +6846,10 @@ fn call_by_name_module_thunk<'a>(
|
||||||
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
force_thunk(env, proc_name, inner_layout, assigned, hole)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let opt_partial_proc = procs.partial_procs.get(&proc_name);
|
let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name);
|
||||||
|
|
||||||
match opt_partial_proc {
|
match opt_partial_proc {
|
||||||
Some(partial_proc) => {
|
Some(partial_proc) => {
|
||||||
// TODO should pending_procs hold a Rc<Proc> to avoid this .clone()?
|
|
||||||
let partial_proc = partial_proc.clone();
|
|
||||||
|
|
||||||
// Mark this proc as in-progress, so if we're dealing with
|
// Mark this proc as in-progress, so if we're dealing with
|
||||||
// mutually recursive functions, we don't loop forever.
|
// mutually recursive functions, we don't loop forever.
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
|
@ -6934,7 +6966,7 @@ fn call_specialized_proc<'a>(
|
||||||
|
|
||||||
match procs
|
match procs
|
||||||
.partial_procs
|
.partial_procs
|
||||||
.get(&proc_name)
|
.get_symbol(proc_name)
|
||||||
.map(|pp| &pp.captured_symbols)
|
.map(|pp| &pp.captured_symbols)
|
||||||
{
|
{
|
||||||
Some(&CapturedSymbols::Captured(captured_symbols)) => {
|
Some(&CapturedSymbols::Captured(captured_symbols)) => {
|
||||||
|
|
|
@ -724,6 +724,14 @@ impl<'a, 'b> Env<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn round_up_to_alignment(width: u32, alignment: u32) -> u32 {
|
||||||
|
if alignment != 0 && width % alignment > 0 {
|
||||||
|
width + alignment - (width % alignment)
|
||||||
|
} else {
|
||||||
|
width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Layout<'a> {
|
impl<'a> Layout<'a> {
|
||||||
fn new_help<'b>(
|
fn new_help<'b>(
|
||||||
env: &mut Env<'a, 'b>,
|
env: &mut Env<'a, 'b>,
|
||||||
|
@ -859,11 +867,7 @@ impl<'a> Layout<'a> {
|
||||||
let width = self.stack_size_without_alignment(pointer_size);
|
let width = self.stack_size_without_alignment(pointer_size);
|
||||||
let alignment = self.alignment_bytes(pointer_size);
|
let alignment = self.alignment_bytes(pointer_size);
|
||||||
|
|
||||||
if alignment != 0 && width % alignment > 0 {
|
round_up_to_alignment(width, alignment)
|
||||||
width + alignment - (width % alignment)
|
|
||||||
} else {
|
|
||||||
width
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stack_size_without_alignment(&self, pointer_size: u32) -> u32 {
|
fn stack_size_without_alignment(&self, pointer_size: u32) -> u32 {
|
||||||
|
@ -885,6 +889,8 @@ impl<'a> Layout<'a> {
|
||||||
|
|
||||||
match variant {
|
match variant {
|
||||||
NonRecursive(fields) => {
|
NonRecursive(fields) => {
|
||||||
|
let tag_id_builtin = variant.tag_id_builtin();
|
||||||
|
|
||||||
fields
|
fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tag_layout| {
|
.map(|tag_layout| {
|
||||||
|
@ -894,9 +900,10 @@ impl<'a> Layout<'a> {
|
||||||
.sum::<u32>()
|
.sum::<u32>()
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
|
.map(|w| round_up_to_alignment(w, tag_id_builtin.alignment_bytes(pointer_size)))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
// the size of the tag_id
|
// the size of the tag_id
|
||||||
+ variant.tag_id_builtin().stack_size(pointer_size)
|
+ tag_id_builtin.stack_size(pointer_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
Recursive(_)
|
Recursive(_)
|
||||||
|
@ -924,13 +931,28 @@ impl<'a> Layout<'a> {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
|
||||||
match variant {
|
match variant {
|
||||||
NonRecursive(tags) => tags
|
NonRecursive(tags) => {
|
||||||
.iter()
|
let max_alignment = tags
|
||||||
.map(|x| x.iter())
|
.iter()
|
||||||
.flatten()
|
.flat_map(|layouts| {
|
||||||
.map(|x| x.alignment_bytes(pointer_size))
|
layouts
|
||||||
.max()
|
.iter()
|
||||||
.unwrap_or(0),
|
.map(|layout| layout.alignment_bytes(pointer_size))
|
||||||
|
})
|
||||||
|
.max();
|
||||||
|
|
||||||
|
match max_alignment {
|
||||||
|
Some(align) => {
|
||||||
|
let tag_id_builtin = variant.tag_id_builtin();
|
||||||
|
|
||||||
|
round_up_to_alignment(
|
||||||
|
align,
|
||||||
|
tag_id_builtin.alignment_bytes(pointer_size),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
Recursive(_)
|
Recursive(_)
|
||||||
| NullableWrapped { .. }
|
| NullableWrapped { .. }
|
||||||
| NullableUnwrapped { .. }
|
| NullableUnwrapped { .. }
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -5,6 +5,23 @@ use crate::assert_evals_to;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use roc_std::{RocList, RocStr};
|
use roc_std::{RocList, RocStr};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn width_and_alignment_u8_u8() {
|
||||||
|
use roc_mono::layout::Builtin;
|
||||||
|
use roc_mono::layout::Layout;
|
||||||
|
use roc_mono::layout::UnionLayout;
|
||||||
|
|
||||||
|
let t = &[Layout::Builtin(Builtin::Int8)] as &[_];
|
||||||
|
let tt = [t, t];
|
||||||
|
|
||||||
|
let layout = Layout::Union(UnionLayout::NonRecursive(&tt));
|
||||||
|
|
||||||
|
// at the moment, the tag id uses an I64, so
|
||||||
|
let ptr_width = 8;
|
||||||
|
assert_eq!(layout.alignment_bytes(ptr_width), 8);
|
||||||
|
assert_eq!(layout.stack_size(ptr_width), 16);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn applied_tag_nothing_ir() {
|
fn applied_tag_nothing_ir() {
|
||||||
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"
|
||||||
|
|
28
compiler/test_wasm/Cargo.toml
Normal file
28
compiler/test_wasm/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[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" }
|
||||||
|
|
||||||
|
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"
|
|
@ -2,11 +2,9 @@ use std::cell::Cell;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
||||||
use roc_can::builtins::builtin_defs_map;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_gen_wasm::replace_code_section;
|
|
||||||
// use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
|
||||||
use crate::helpers::wasm32_test_result::Wasm32TestResult;
|
|
||||||
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
use roc_gen_wasm::from_wasm32_memory::FromWasm32Memory;
|
||||||
|
|
||||||
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
const TEST_WRAPPER_NAME: &str = "test_wrapper";
|
||||||
|
@ -104,20 +102,17 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
|
||||||
exposed_to_host,
|
exposed_to_host,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut builder, mut code_section_bytes) =
|
let mut wasm_module = 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(
|
||||||
arena,
|
arena,
|
||||||
&mut builder,
|
&mut wasm_module,
|
||||||
&mut code_section_bytes,
|
|
||||||
TEST_WRAPPER_NAME,
|
TEST_WRAPPER_NAME,
|
||||||
main_fn_index as u32,
|
main_fn_index as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut parity_module = builder.build();
|
let mut module_bytes = std::vec::Vec::with_capacity(4096);
|
||||||
replace_code_section(&mut parity_module, code_section_bytes);
|
wasm_module.serialize(&mut module_bytes);
|
||||||
let module_bytes = parity_module.into_bytes().unwrap();
|
|
||||||
|
|
||||||
// for debugging (e.g. with wasm2wat or wasm-objdump)
|
// for debugging (e.g. with wasm2wat or wasm-objdump)
|
||||||
if false {
|
if false {
|
|
@ -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;
|
|
@ -1,44 +1,33 @@
|
||||||
use parity_wasm::builder;
|
use bumpalo::collections::Vec;
|
||||||
use parity_wasm::elements::Internal;
|
|
||||||
|
|
||||||
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::{serialize::SerialBuffer, LocalId};
|
use roc_gen_wasm::wasm_module::opcodes;
|
||||||
|
use roc_gen_wasm::wasm_module::{
|
||||||
|
Align, CodeBuilder, Export, ExportType, LocalId, Signature, ValueType, WasmModule,
|
||||||
|
};
|
||||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||||
|
|
||||||
pub trait Wasm32TestResult {
|
pub trait Wasm32TestResult {
|
||||||
fn insert_test_wrapper<'a>(
|
fn insert_test_wrapper<'a>(
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
module_builder: &mut builder::ModuleBuilder,
|
wasm_module: &mut WasmModule<'a>,
|
||||||
code_section_bytes: &mut std::vec::Vec<u8>,
|
|
||||||
wrapper_name: &str,
|
wrapper_name: &str,
|
||||||
main_function_index: u32,
|
main_function_index: u32,
|
||||||
) {
|
) {
|
||||||
let signature = builder::signature()
|
wasm_module.add_function_signature(Signature {
|
||||||
.with_result(parity_wasm::elements::ValueType::I32)
|
param_types: Vec::with_capacity_in(0, arena),
|
||||||
.build_sig();
|
ret_type: Some(ValueType::I32),
|
||||||
|
});
|
||||||
|
|
||||||
// parity-wasm FunctionDefinition with no instructions
|
wasm_module.export.entries.push(Export {
|
||||||
let empty_fn_def = builder::function().with_signature(signature).build();
|
name: wrapper_name.to_string(),
|
||||||
let location = module_builder.push_function(empty_fn_def);
|
ty: ExportType::Func,
|
||||||
let export = builder::export()
|
index: wasm_module.code.code_builders.len() as u32,
|
||||||
.field(wrapper_name)
|
});
|
||||||
.with_internal(Internal::Function(location.body))
|
|
||||||
.build();
|
|
||||||
module_builder.push_export(export);
|
|
||||||
|
|
||||||
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);
|
||||||
|
wasm_module.code.code_builders.push(code_builder);
|
||||||
code_builder.serialize(code_section_bytes);
|
|
||||||
|
|
||||||
let mut num_procs = 0;
|
|
||||||
for (i, byte) in code_section_bytes[5..10].iter().enumerate() {
|
|
||||||
num_procs += ((byte & 0x7f) as u32) << (i * 7);
|
|
||||||
}
|
|
||||||
let inner_length = (code_section_bytes.len() - 5) as u32;
|
|
||||||
code_section_bytes.overwrite_padded_u32(0, inner_length);
|
|
||||||
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);
|
||||||
|
@ -54,7 +43,7 @@ macro_rules! build_wrapper_body_primitive {
|
||||||
|
|
||||||
code_builder.get_local(frame_pointer_id);
|
code_builder.get_local(frame_pointer_id);
|
||||||
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
// 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.inst_imm32(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);
|
||||||
|
|
||||||
|
@ -82,7 +71,7 @@ fn build_wrapper_body_stack_memory(
|
||||||
|
|
||||||
code_builder.get_local(local_id);
|
code_builder.get_local(local_id);
|
||||||
// Raw "call" instruction. Don't bother with symbol & relocation since we're not going to link.
|
// 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.inst_imm32(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);
|
||||||
}
|
}
|
4
compiler/test_wasm/src/lib.rs
Normal file
4
compiler/test_wasm/src/lib.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
mod helpers;
|
||||||
|
pub mod wasm_num;
|
||||||
|
pub mod wasm_records;
|
||||||
|
pub mod wasm_str;
|
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
|
||||||
|
// );
|
||||||
|
// }
|
1060
compiler/test_wasm/src/wasm_str.rs
Normal file
1060
compiler/test_wasm/src/wasm_str.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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"] }
|
||||||
|
|
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"
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
1
examples/hello-swift/.gitignore
vendored
Normal file
1
examples/hello-swift/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hello-swift
|
10
examples/hello-swift/Hello.roc
Normal file
10
examples/hello-swift/Hello.roc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
app "hello-swift"
|
||||||
|
packages { base: "platform" }
|
||||||
|
imports []
|
||||||
|
provides [ main ] to base
|
||||||
|
|
||||||
|
main =
|
||||||
|
host = "Swift"
|
||||||
|
app = "Roc"
|
||||||
|
|
||||||
|
"Hello \(host), meet \(app)"
|
10
examples/hello-swift/platform/Package-Config.roc
Normal file
10
examples/hello-swift/platform/Package-Config.roc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
platform examples/hello-swift
|
||||||
|
requires {}{ main : Str }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [ mainForHost ]
|
||||||
|
effects fx.Effect {}
|
||||||
|
|
||||||
|
mainForHost : Str
|
||||||
|
mainForHost = main
|
8
examples/hello-swift/platform/host.h
Normal file
8
examples/hello-swift/platform/host.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct RocStr {
|
||||||
|
char* bytes;
|
||||||
|
size_t len;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct RocStr roc__mainForHost_1_exposed();
|
58
examples/hello-swift/platform/host.swift
Normal file
58
examples/hello-swift/platform/host.swift
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@_cdecl("roc_alloc")
|
||||||
|
func rocAlloc(size: Int, _alignment: UInt) -> UInt {
|
||||||
|
guard let ptr = malloc(size) else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return UInt(bitPattern: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@_cdecl("roc_dealloc")
|
||||||
|
func rocDealloc(ptr: UInt, _alignment: UInt) {
|
||||||
|
free(UnsafeMutableRawPointer(bitPattern: ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
@_cdecl("roc_realloc")
|
||||||
|
func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UInt {
|
||||||
|
guard let ptr = realloc(UnsafeMutableRawPointer(bitPattern: ptr), newSize) else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return UInt(bitPattern: ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RocStr {
|
||||||
|
var isSmallString: Bool {
|
||||||
|
len < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var length: Int {
|
||||||
|
if isSmallString {
|
||||||
|
var len = len
|
||||||
|
let count = MemoryLayout.size(ofValue: len)
|
||||||
|
let bytes = Data(bytes: &len, count: count)
|
||||||
|
let lastByte = bytes[count - 1]
|
||||||
|
return Int(lastByte ^ 0b1000_0000)
|
||||||
|
} else {
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var string: String {
|
||||||
|
if isSmallString {
|
||||||
|
let data: Data = withUnsafePointer(to: self) { ptr in
|
||||||
|
Data(bytes: ptr, count: length)
|
||||||
|
}
|
||||||
|
return String(data: data, encoding: .utf8)!
|
||||||
|
} else {
|
||||||
|
let data = Data(bytes: bytes, count: len)
|
||||||
|
return String(data: data, encoding: .utf8)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@_cdecl("main")
|
||||||
|
func main() -> UInt8 {
|
||||||
|
print(roc__mainForHost_1_exposed().string)
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -1235,7 +1235,7 @@ target (for example, WebAssembly) at runtime it will be the same as `U32` instea
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
* `List.len : List * -> Nat`
|
* `List.len : List * -> Nat`
|
||||||
* `List.get : List elem, Nat -> List elem`
|
* `List.get : List elem, Nat -> Result elem [ OutOfBounds ]*`
|
||||||
* `List.set : List elem, Nat, elem -> List elem`
|
* `List.set : List elem, Nat, elem -> List elem`
|
||||||
|
|
||||||
As with floats, which integer type to use depends on the values you want to support
|
As with floats, which integer type to use depends on the values you want to support
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue