mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 22:09:09 +00:00
Merge branch 'main' into builtin-json
This commit is contained in:
commit
dc43290647
199 changed files with 10195 additions and 3810 deletions
|
@ -3,6 +3,7 @@ test-gen-llvm = "test -p test_gen"
|
|||
test-gen-dev = "test -p roc_gen_dev -p test_gen --no-default-features --features gen-dev"
|
||||
test-gen-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --features gen-wasm"
|
||||
test-gen-llvm-wasm = "test -p roc_gen_wasm -p test_gen --no-default-features --features gen-llvm-wasm"
|
||||
uitest = "test -p uitest"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
# Rust compiler flags for minimum-sized .wasm binary in the web REPL
|
||||
|
|
250
Cargo.lock
generated
250
Cargo.lock
generated
|
@ -232,6 +232,12 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
|
||||
|
||||
[[package]]
|
||||
name = "bitmaps"
|
||||
version = "2.1.0"
|
||||
|
@ -420,7 +426,7 @@ version = "2.34.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"textwrap 0.11.0",
|
||||
"unicode-width",
|
||||
]
|
||||
|
@ -432,14 +438,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex 0.2.4",
|
||||
"indexmap",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap 0.16.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098"
|
||||
dependencies = [
|
||||
"bitflags 2.0.2",
|
||||
"clap_derive",
|
||||
"clap_lex 0.3.3",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
|
@ -449,6 +483,15 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cli_utils"
|
||||
version = "0.0.1"
|
||||
|
@ -493,7 +536,7 @@ version = "0.24.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"core-foundation 0.9.3",
|
||||
|
@ -509,7 +552,7 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics-types",
|
||||
|
@ -656,7 +699,7 @@ version = "0.19.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.7.0",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -668,7 +711,7 @@ version = "0.22.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
|
@ -681,7 +724,7 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation 0.9.3",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
|
@ -881,7 +924,7 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"libloading",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -1134,7 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"rustix",
|
||||
"rustix 0.36.9",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
|
@ -1419,7 +1462,7 @@ version = "0.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"gpu-alloc-types",
|
||||
]
|
||||
|
||||
|
@ -1429,7 +1472,7 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1438,7 +1481,7 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"gpu-descriptor-types",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
@ -1449,7 +1492,7 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1520,6 +1563,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "hexf-parse"
|
||||
version = "0.2.1"
|
||||
|
@ -1745,6 +1794,18 @@ version = "2.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix 0.37.3",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
|
@ -1848,6 +1909,17 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtest-mimic"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7b603516767d1ab23d0de09d023e62966c3322f7148297c35cf3d97aa8b37fa"
|
||||
dependencies = [
|
||||
"clap 4.1.11",
|
||||
"termcolor",
|
||||
"threadpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
|
@ -1860,6 +1932,12 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||
|
||||
[[package]]
|
||||
name = "llvm-sys"
|
||||
version = "130.0.7"
|
||||
|
@ -1898,7 +1976,7 @@ version = "0.1.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6f2d7176b94027af58085a2c9d27c4e416586caba409c314569213901d6068"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
@ -1980,7 +2058,7 @@ version = "0.23.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
|
@ -2048,7 +2126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3012f2dbcc79e8e0b5825a4836a7106a75dd9b2fe42c528163be0f572538c705"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"codespan-reporting",
|
||||
"hexf-parse",
|
||||
"indexmap",
|
||||
|
@ -2065,7 +2143,7 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"jni-sys",
|
||||
"ndk-sys",
|
||||
"num_enum",
|
||||
|
@ -2127,7 +2205,7 @@ version = "0.22.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
|
@ -2140,7 +2218,7 @@ version = "0.23.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
|
@ -2153,7 +2231,7 @@ version = "0.24.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
|
@ -2165,7 +2243,7 @@ version = "0.26.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"static_assertions",
|
||||
|
@ -2646,6 +2724,30 @@ dependencies = [
|
|||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
|
@ -2668,7 +2770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
|
@ -2688,7 +2790,7 @@ version = "0.9.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
@ -2843,7 +2945,7 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3193,6 +3295,7 @@ dependencies = [
|
|||
"hashbrown 0.13.2",
|
||||
"im",
|
||||
"im-rc",
|
||||
"smallvec",
|
||||
"wyhash",
|
||||
]
|
||||
|
||||
|
@ -3427,17 +3530,23 @@ dependencies = [
|
|||
name = "roc_glue"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bumpalo",
|
||||
"cli_utils",
|
||||
"dircpy",
|
||||
"fnv",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"libc",
|
||||
"libloading",
|
||||
"pretty_assertions",
|
||||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_gen_llvm",
|
||||
"roc_linker",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
|
@ -3811,6 +3920,7 @@ dependencies = [
|
|||
"indoc",
|
||||
"insta",
|
||||
"lazy_static",
|
||||
"libtest-mimic",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
|
@ -3834,6 +3944,7 @@ dependencies = [
|
|||
"roc_types",
|
||||
"roc_unify",
|
||||
"tempfile",
|
||||
"test_solve_helpers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3908,7 +4019,7 @@ dependencies = [
|
|||
name = "roc_unify"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"roc_collections",
|
||||
"roc_debug_flags",
|
||||
"roc_error_macros",
|
||||
|
@ -3964,11 +4075,25 @@ version = "0.36.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"errno 0.2.8",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.1.4",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno 0.3.0",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.1",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
|
@ -4016,7 +4141,7 @@ name = "rustyline"
|
|||
version = "9.1.1"
|
||||
source = "git+https://github.com/roc-lang/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if 1.0.0",
|
||||
"clipboard-win 4.5.0",
|
||||
"dirs-next",
|
||||
|
@ -4308,7 +4433,7 @@ version = "0.15.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"calloop",
|
||||
"dlib",
|
||||
"lazy_static",
|
||||
|
@ -4327,7 +4452,7 @@ version = "0.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"dlib",
|
||||
"lazy_static",
|
||||
"log",
|
||||
|
@ -4394,7 +4519,7 @@ version = "0.2.0+1.5.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
|
@ -4611,6 +4736,32 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_solve_helpers"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"indoc",
|
||||
"insta",
|
||||
"lazy_static",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_can",
|
||||
"roc_derive",
|
||||
"roc_late_solve",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_packaging",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_problem",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_syntax"
|
||||
version = "0.0.1"
|
||||
|
@ -4921,6 +5072,29 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
|
||||
|
||||
[[package]]
|
||||
name = "uitest"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"indoc",
|
||||
"insta",
|
||||
"lazy_static",
|
||||
"libtest-mimic",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"tempfile",
|
||||
"test_solve_helpers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
|
@ -5194,7 +5368,7 @@ version = "0.29.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"libc",
|
||||
"nix 0.24.3",
|
||||
|
@ -5233,7 +5407,7 @@ version = "0.29.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"wayland-client",
|
||||
"wayland-commons",
|
||||
"wayland-scanner",
|
||||
|
@ -5318,7 +5492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c4688c000eb841ca55f7b35db659b78d6e1cd77d7caf8fb929f4e181f754047d"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg_aliases",
|
||||
"codespan-reporting",
|
||||
"copyless",
|
||||
|
@ -5343,7 +5517,7 @@ dependencies = [
|
|||
"arrayvec 0.7.2",
|
||||
"ash",
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"block",
|
||||
"core-graphics-types",
|
||||
"d3d12",
|
||||
|
@ -5378,7 +5552,7 @@ version = "0.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "549533d9e1cdd4b4cda7718d33ff500fc4c34b5467b71d76b547ae0324f3b2a2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5520,7 +5694,7 @@ version = "0.26.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cocoa",
|
||||
"core-foundation 0.9.3",
|
||||
"core-graphics 0.22.3",
|
||||
|
|
|
@ -71,6 +71,7 @@ version = "0.0.1"
|
|||
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-15", features = ["llvm13-0"] }
|
||||
|
||||
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
|
||||
backtrace = "0.3.67"
|
||||
base64-url = "1.4.13"
|
||||
bincode = "1.3.3"
|
||||
bitflags = "1.3.2"
|
||||
|
@ -111,6 +112,7 @@ lazy_static = "1.4.0"
|
|||
libc = "0.2.139" # update roc_std/Cargo.toml on change
|
||||
libfuzzer-sys = "0.4"
|
||||
libloading = "0.7.4"
|
||||
libtest-mimic = "0.6.0"
|
||||
log = "0.4.17"
|
||||
mach_object = "0.1"
|
||||
maplit = "1.0.2"
|
||||
|
@ -145,6 +147,7 @@ serde-xml-rs = "0.6.0"
|
|||
serde_json = "1.0.94" # update roc_std/Cargo.toml on change
|
||||
serial_test = "1.0.0"
|
||||
signal-hook = "0.3.15"
|
||||
smallvec = { version = "1.10.0", features = ["const_generics", "const_new"] }
|
||||
snafu = { version = "0.7.4", features = ["backtraces"] }
|
||||
static_assertions = "1.1.0" # update roc_std/Cargo.toml on change
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
|
|
|
@ -24,6 +24,7 @@ If you would like your company to become a corporate sponsor of Roc's developmen
|
|||
|
||||
We'd also like to express our gratitude to each and every one of our fantastic [GitHub sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
|
||||
|
||||
* [Jonas Schell](https://github.com/Ocupe)
|
||||
* [Christopher Dolan](https://github.com/cdolan)
|
||||
* [Nick Gravgaard](https://github.com/nickgravgaard)
|
||||
* [Aaron White](https://github.com/aaronwhite)
|
||||
|
|
|
@ -7,10 +7,12 @@ use bumpalo::Bump;
|
|||
use clap::{Arg, ArgMatches, Command, ValueSource};
|
||||
use roc_build::link::{LinkType, LinkingStrategy};
|
||||
use roc_build::program::{
|
||||
standard_load_config, BuildFileError, BuildOrdering, BuiltFile, CodeGenBackend, CodeGenOptions,
|
||||
handle_error_module, handle_loading_problem, standard_load_config, BuildFileError,
|
||||
BuildOrdering, BuiltFile, CodeGenBackend, CodeGenOptions, DEFAULT_ROC_FILENAME,
|
||||
};
|
||||
use roc_error_macros::{internal_error, user_error};
|
||||
use roc_load::{ExpectMetadata, LoadingProblem, Threading};
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{ExpectMetadata, Threading};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_packaging::tarball::Compression;
|
||||
|
@ -33,8 +35,6 @@ use tempfile::TempDir;
|
|||
mod format;
|
||||
pub use format::format;
|
||||
|
||||
const DEFAULT_ROC_FILENAME: &str = "main.roc";
|
||||
|
||||
pub const CMD_BUILD: &str = "build";
|
||||
pub const CMD_RUN: &str = "run";
|
||||
pub const CMD_DEV: &str = "dev";
|
||||
|
@ -64,7 +64,8 @@ pub const FLAG_CHECK: &str = "check";
|
|||
pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb";
|
||||
pub const ROC_FILE: &str = "ROC_FILE";
|
||||
pub const ROC_DIR: &str = "ROC_DIR";
|
||||
pub const GLUE_FILE: &str = "GLUE_FILE";
|
||||
pub const GLUE_DIR: &str = "GLUE_DIR";
|
||||
pub const GLUE_SPEC: &str = "GLUE_SPEC";
|
||||
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
||||
|
||||
|
@ -279,17 +280,24 @@ pub fn build_app<'a>() -> Command<'a> {
|
|||
.subcommand(Command::new(CMD_GLUE)
|
||||
.about("Generate glue code between a platform's Roc API and its host language")
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file for the platform module")
|
||||
Arg::new(GLUE_SPEC)
|
||||
.help("The specification for how to translate Roc types into output files.")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(GLUE_FILE)
|
||||
.help("The filename for the generated glue code\n(Currently, this must be a .rs file because only Rust glue generation is supported so far.)")
|
||||
Arg::new(GLUE_DIR)
|
||||
.help("The directory for the generated glue code.\nNote: The implementation can write to any file in this directory.")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file whose exposed types should be translated.")
|
||||
.allow_invalid_utf8(true)
|
||||
.required(false)
|
||||
.default_value(DEFAULT_ROC_FILENAME)
|
||||
)
|
||||
)
|
||||
.subcommand(Command::new(CMD_GEN_STUB_LIB)
|
||||
.about("Generate a stubbed shared library that can be used for linking a platform binary.\nThe stubbed library has prototypes, but no function bodies.\n\nNote: This command will be removed in favor of just using `roc build` once all platforms support the surgical linker")
|
||||
|
@ -359,7 +367,6 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
|
|||
#[cfg(not(windows))]
|
||||
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||
use roc_build::program::report_problems_monomorphized;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
|
||||
use roc_packaging::cache;
|
||||
use roc_target::TargetInfo;
|
||||
|
@ -593,15 +600,6 @@ pub fn build(
|
|||
// so we don't want to spend time freeing these values
|
||||
let arena = ManuallyDrop::new(Bump::new());
|
||||
|
||||
let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) {
|
||||
CodeGenBackend::Wasm
|
||||
} else {
|
||||
match matches.is_present(FLAG_DEV) {
|
||||
true => CodeGenBackend::Assembly,
|
||||
false => CodeGenBackend::Llvm,
|
||||
}
|
||||
};
|
||||
|
||||
let opt_level = if let BuildConfig::BuildAndRunIfNoErrors = config {
|
||||
OptLevel::Development
|
||||
} else {
|
||||
|
@ -617,6 +615,25 @@ pub fn build(
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) {
|
||||
CodeGenBackend::Wasm
|
||||
} else {
|
||||
match matches.is_present(FLAG_DEV) {
|
||||
true => CodeGenBackend::Assembly,
|
||||
false => {
|
||||
let backend_mode = match opt_level {
|
||||
OptLevel::Development => LlvmBackendMode::BinaryDev,
|
||||
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
|
||||
LlvmBackendMode::Binary
|
||||
}
|
||||
};
|
||||
|
||||
CodeGenBackend::Llvm(backend_mode)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let emit_debug_info = matches.is_present(FLAG_DEBUG);
|
||||
let emit_timings = matches.is_present(FLAG_TIME);
|
||||
|
||||
|
@ -759,48 +776,6 @@ pub fn build(
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_error_module(
|
||||
mut module: roc_load::LoadedModule,
|
||||
total_time: std::time::Duration,
|
||||
filename: &OsStr,
|
||||
print_run_anyway_hint: bool,
|
||||
) -> io::Result<i32> {
|
||||
debug_assert!(module.total_problems() > 0);
|
||||
|
||||
let problems = roc_build::program::report_problems_typechecked(&mut module);
|
||||
|
||||
problems.print_to_stdout(total_time);
|
||||
|
||||
if print_run_anyway_hint {
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
|
||||
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
print!(" {}", &filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("\x1B[39m");
|
||||
}
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
|
||||
fn handle_loading_problem(problem: LoadingProblem) -> io::Result<i32> {
|
||||
match problem {
|
||||
LoadingProblem::FormattedReport(report) => {
|
||||
print!("{}", report);
|
||||
Ok(1)
|
||||
}
|
||||
_ => {
|
||||
// TODO: tighten up the types here, we should always end up with a
|
||||
// formatted report from load.
|
||||
print!("Failed with error: {:?}", problem);
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||
arena: &Bump,
|
||||
opt_level: OptLevel,
|
||||
|
@ -1322,40 +1297,3 @@ impl std::str::FromStr for Target {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These functions don't end up in the final Roc binary but Windows linker needs a definition inside the crate.
|
||||
// On Windows, there seems to be less dead-code-elimination than on Linux or MacOS, or maybe it's done later.
|
||||
#[cfg(windows)]
|
||||
#[allow(unused_imports)]
|
||||
use windows_roc_platform_functions::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows_roc_platform_functions {
|
||||
use core::ffi::c_void;
|
||||
|
||||
/// # Safety
|
||||
/// The Roc application needs this.
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The Roc application needs this.
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The Roc application needs this.
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_cli::{
|
|||
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DEV,
|
||||
CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST,
|
||||
CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME,
|
||||
GLUE_FILE, ROC_FILE,
|
||||
GLUE_DIR, GLUE_SPEC, ROC_FILE,
|
||||
};
|
||||
use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
|
@ -88,12 +88,13 @@ fn main() -> io::Result<()> {
|
|||
}
|
||||
Some((CMD_GLUE, matches)) => {
|
||||
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap());
|
||||
let output_path = Path::new(matches.value_of_os(GLUE_FILE).unwrap());
|
||||
let output_path = Path::new(matches.value_of_os(GLUE_DIR).unwrap());
|
||||
let spec_path = Path::new(matches.value_of_os(GLUE_SPEC).unwrap());
|
||||
|
||||
if Some("rs") == output_path.extension().and_then(OsStr::to_str) {
|
||||
roc_glue::generate(input_path, output_path)
|
||||
if !output_path.exists() || output_path.is_dir() {
|
||||
roc_glue::generate(input_path, output_path, spec_path)
|
||||
} else {
|
||||
eprintln!("Currently, `roc glue` only supports generating Rust glue files (with the .rs extension). In the future, the plan is to decouple `roc glue` from any particular output format, by having it accept a second .roc file which gets executed as a plugin to generate glue code for any desired language. However, this has not yet been implemented, and for now only .rs is supported.");
|
||||
eprintln!("`roc glue` must be given a directory to output into, because the glue might generate multiple files.");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use std::io::Write;
|
|||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -87,11 +88,19 @@ pub fn build_roc_bin(extra_args: &[&str]) -> PathBuf {
|
|||
roc_binary_path
|
||||
}
|
||||
|
||||
// Since glue is always compiling the same plugin, it can not be run in parallel.
|
||||
// That would lead to a race condition in writing the output shared library.
|
||||
// Thus, all calls to glue in a test are made sequential.
|
||||
// TODO: In the future, look into compiling the shared libary once and then caching it.
|
||||
static GLUE_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
pub fn run_glue<I, S>(args: I) -> Out
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let _guard = GLUE_LOCK.lock().unwrap();
|
||||
|
||||
run_roc_with_stdin(&path_to_roc_binary(), args, &[])
|
||||
}
|
||||
|
||||
|
|
|
@ -305,8 +305,8 @@ where
|
|||
debug_assert_eq!(variant_types.len(), 1);
|
||||
variant_types[0]
|
||||
} else {
|
||||
let data_type = builder.add_union_type(&variant_types)?;
|
||||
let cell_type = builder.add_heap_cell_type();
|
||||
let data_type = builder.add_union_type(&variant_types)?;
|
||||
|
||||
builder.add_tuple_type(&[cell_type, data_type])?
|
||||
};
|
||||
|
@ -1289,6 +1289,11 @@ fn expr_spec<'a>(
|
|||
|
||||
match expr {
|
||||
Literal(literal) => literal_spec(builder, block, literal),
|
||||
NullPointer => {
|
||||
let pointer_type = layout_spec(env, builder, interner, layout)?;
|
||||
|
||||
builder.add_unknown_with(block, &[], pointer_type)
|
||||
}
|
||||
Call(call) => call_spec(builder, interner, env, block, layout, call),
|
||||
Reuse {
|
||||
tag_layout,
|
||||
|
@ -1467,7 +1472,8 @@ fn expr_spec<'a>(
|
|||
|
||||
let _unit = builder.add_update(block, update_mode_var, heap_cell)?;
|
||||
|
||||
with_new_heap_cell(builder, block, union_data)
|
||||
let value = with_new_heap_cell(builder, block, union_data)?;
|
||||
builder.add_make_named(block, MOD_APP, type_name, value)
|
||||
}
|
||||
RuntimeErrorFunction(_) => {
|
||||
let type_id = layout_spec(env, builder, interner, layout)?;
|
||||
|
|
|
@ -17,6 +17,7 @@ use roc_reporting::{
|
|||
report::{RenderTarget, DEFAULT_PALETTE},
|
||||
};
|
||||
use roc_target::TargetInfo;
|
||||
use std::ffi::OsStr;
|
||||
use std::ops::Deref;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
@ -28,6 +29,8 @@ use target_lexicon::Triple;
|
|||
#[cfg(feature = "target-wasm32")]
|
||||
use roc_collections::all::MutSet;
|
||||
|
||||
pub const DEFAULT_ROC_FILENAME: &str = "main.roc";
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct CodeGenTiming {
|
||||
pub code_gen: Duration,
|
||||
|
@ -72,7 +75,7 @@ impl Deref for CodeObject {
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CodeGenBackend {
|
||||
Assembly,
|
||||
Llvm,
|
||||
Llvm(LlvmBackendMode),
|
||||
Wasm,
|
||||
}
|
||||
|
||||
|
@ -95,6 +98,10 @@ pub fn gen_from_mono_module<'a>(
|
|||
preprocessed_host_path: &Path,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
) -> GenFromMono<'a> {
|
||||
let path = roc_file_path;
|
||||
let debug = code_gen_options.emit_debug_info;
|
||||
let opt = code_gen_options.opt_level;
|
||||
|
||||
match code_gen_options.backend {
|
||||
CodeGenBackend::Assembly => gen_from_mono_module_dev(
|
||||
arena,
|
||||
|
@ -103,12 +110,18 @@ pub fn gen_from_mono_module<'a>(
|
|||
preprocessed_host_path,
|
||||
wasm_dev_stack_bytes,
|
||||
),
|
||||
CodeGenBackend::Llvm => {
|
||||
gen_from_mono_module_llvm(arena, loaded, roc_file_path, target, code_gen_options)
|
||||
CodeGenBackend::Llvm(backend_mode) => {
|
||||
gen_from_mono_module_llvm(arena, loaded, path, target, opt, backend_mode, debug)
|
||||
}
|
||||
CodeGenBackend::Wasm => {
|
||||
// emit wasm via the llvm backend
|
||||
gen_from_mono_module_llvm(arena, loaded, roc_file_path, target, code_gen_options)
|
||||
|
||||
let backend_mode = match code_gen_options.opt_level {
|
||||
OptLevel::Development => LlvmBackendMode::BinaryDev,
|
||||
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => LlvmBackendMode::Binary,
|
||||
};
|
||||
|
||||
gen_from_mono_module_llvm(arena, loaded, path, target, opt, backend_mode, debug)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +134,9 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
mut loaded: MonomorphizedModule<'a>,
|
||||
roc_file_path: &Path,
|
||||
target: &target_lexicon::Triple,
|
||||
code_gen_options: CodeGenOptions,
|
||||
opt_level: OptLevel,
|
||||
backend_mode: LlvmBackendMode,
|
||||
emit_debug_info: bool,
|
||||
) -> GenFromMono<'a> {
|
||||
use crate::target::{self, convert_opt_level};
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
|
@ -171,12 +186,6 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let CodeGenOptions {
|
||||
backend: _,
|
||||
opt_level,
|
||||
emit_debug_info,
|
||||
} = code_gen_options;
|
||||
|
||||
let builder = context.create_builder();
|
||||
let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module);
|
||||
let (mpm, _fpm) = roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
@ -191,10 +200,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
interns: loaded.interns,
|
||||
module,
|
||||
target_info,
|
||||
mode: match opt_level {
|
||||
OptLevel::Development => LlvmBackendMode::BinaryDev,
|
||||
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => LlvmBackendMode::Binary,
|
||||
},
|
||||
mode: backend_mode,
|
||||
|
||||
exposed_to_host: loaded
|
||||
.exposed_to_host
|
||||
|
@ -652,6 +658,48 @@ impl<'a> BuildFileError<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_error_module(
|
||||
mut module: roc_load::LoadedModule,
|
||||
total_time: std::time::Duration,
|
||||
filename: &OsStr,
|
||||
print_run_anyway_hint: bool,
|
||||
) -> std::io::Result<i32> {
|
||||
debug_assert!(module.total_problems() > 0);
|
||||
|
||||
let problems = report_problems_typechecked(&mut module);
|
||||
|
||||
problems.print_to_stdout(total_time);
|
||||
|
||||
if print_run_anyway_hint {
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
|
||||
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
print!(" {}", &filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("\x1B[39m");
|
||||
}
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
|
||||
pub fn handle_loading_problem(problem: LoadingProblem) -> std::io::Result<i32> {
|
||||
match problem {
|
||||
LoadingProblem::FormattedReport(report) => {
|
||||
print!("{}", report);
|
||||
Ok(1)
|
||||
}
|
||||
_ => {
|
||||
// TODO: tighten up the types here, we should always end up with a
|
||||
// formatted report from load.
|
||||
print!("Failed with error: {:?}", problem);
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn standard_load_config(
|
||||
target: &Triple,
|
||||
order: BuildOrdering,
|
||||
|
@ -1188,7 +1236,7 @@ pub fn build_str_test<'a>(
|
|||
let triple = target_lexicon::Triple::host();
|
||||
|
||||
let code_gen_options = CodeGenOptions {
|
||||
backend: CodeGenBackend::Llvm,
|
||||
backend: CodeGenBackend::Llvm(LlvmBackendMode::Binary),
|
||||
opt_level: OptLevel::Normal,
|
||||
emit_debug_info: false,
|
||||
};
|
||||
|
|
|
@ -1921,7 +1921,7 @@ pub fn fromUtf8Range(arg: RocList, start: usize, count: usize, update_mode: Upda
|
|||
.problem_code = Utf8ByteProblem.InvalidStartByte,
|
||||
};
|
||||
}
|
||||
const bytes = @ptrCast([*]const u8, arg.bytes)[start..count];
|
||||
const bytes = @ptrCast([*]const u8, arg.bytes)[start .. start + count];
|
||||
|
||||
if (isValidUnicode(bytes)) {
|
||||
// Make a seamless slice of the input.
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
## Roc strings are sequences of text values. This module includes functions for combining strings,
|
||||
## as well as breaking them up into smaller units—most commonly [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster)
|
||||
## (referred to in this module's documentation as "graphemes" rather than "characters" for clarity;
|
||||
## "characters" can mean very different things in different languages).
|
||||
##
|
||||
## This module focuses on graphemes (as opposed to, say, Unicode code points or LATIN-1 bytes)
|
||||
## because graphemes avoid common classes of bugs. Breaking strings up using code points often
|
||||
## leads to bugs around things like emoji, where multiple code points combine to form to a
|
||||
## single rendered glyph. Graphemes avoid these bugs by treating multi-code-point things like
|
||||
## emojis as indivisible units.
|
||||
##
|
||||
## Because graphemes can have variable length (there's no upper limit on how many code points one
|
||||
## grapheme can represent), it takes linear time to count the number of graphemes in a string,
|
||||
## and also linear time to find an individual grapheme within a string by its position (or "index")
|
||||
## among the string's other graphemes. The only way to get constant-time access to these is in a way
|
||||
## that can result in bugs if the string contains multi-code-point things like emojis, which is why
|
||||
## this module does not offer those.
|
||||
##
|
||||
##
|
||||
## ## Working with Unicode strings in Roc
|
||||
##
|
||||
|
@ -107,6 +125,7 @@ interface Str
|
|||
replaceLast,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
walkUtf8,
|
||||
walkUtf8WithIndex,
|
||||
reserve,
|
||||
releaseExcessCapacity,
|
||||
|
@ -151,8 +170,89 @@ isEmpty : Str -> Bool
|
|||
concat : Str, Str -> Str
|
||||
|
||||
## Returns a string of the specified capacity without any content.
|
||||
##
|
||||
## This is a performance optimization tool that's like calling [Str.reserve] on an empty string.
|
||||
## It's useful when you plan to build up a string incrementally, for example by calling [Str.concat] on it:
|
||||
##
|
||||
## ```
|
||||
## greeting = "Hello and welcome to Roc"
|
||||
## subject = "Awesome Programmer"
|
||||
##
|
||||
## # Evaluates to "Hello and welcome to Roc, Awesome Programmer!"
|
||||
## helloWorld =
|
||||
## Str.withCapacity 45
|
||||
## |> Str.concat greeting
|
||||
## |> Str.concat ", "
|
||||
## |> Str.concat subject
|
||||
## |> Str.concat "!"
|
||||
## ```
|
||||
##
|
||||
## In general, if you plan to use [Str.concat] on an empty string, it will be faster to start with
|
||||
## [Str.withCapacity] than with `""`. Even if you don't know the exact capacity of the string, giving [withCapacity]
|
||||
## a higher value than ends up being necessary can help prevent reallocation and copying—at
|
||||
## the cost of using more memory than is necessary.
|
||||
##
|
||||
## For more details on how the performance optimization works, see [Str.reserve].
|
||||
withCapacity : Nat -> Str
|
||||
|
||||
## Increase a string's capacity by at least the given number of additional bytes.
|
||||
##
|
||||
## This can improve the performance of string concatenation operations like [Str.concat] by
|
||||
## allocating extra capacity up front, which can prevent the need for reallocations and copies.
|
||||
## Consider the following example which does not use [Str.reserve]:
|
||||
##
|
||||
## ```
|
||||
## greeting = "Hello and welcome to Roc"
|
||||
## subject = "Awesome Programmer"
|
||||
##
|
||||
## # Evaluates to "Hello and welcome to Roc, Awesome Programmer!"
|
||||
## helloWorld =
|
||||
## greeting
|
||||
## |> Str.concat ", "
|
||||
## |> Str.concat subject
|
||||
## |> Str.concat "!"
|
||||
## ```
|
||||
##
|
||||
## In this example:
|
||||
## 1. We start with `greeting`, which has both a length and capacity of 24 (bytes).
|
||||
## 2. `|> Str.concat ", "` will see that there isn't enough capacity to add 2 more bytes for the `", "`, so it will create a new heap allocation with enough bytes to hold both. (This probably will be more than 7 bytes, because when [Str] functions reallocate, they apply a multiplier to the exact capacity required. This makes it less likely that future realloctions will be needed. The multiplier amount is not specified, because it may change in future releases of Roc, but it will likely be around 1.5 to 2 times the exact capacity required.) Then it will copy the current bytes (`"Hello"`) into the new allocation, and finally concatenate the `", "` into the new allocation. The old allocation will then be deallocated because it's no longer referenced anywhere in the program.
|
||||
## 3. `|> Str.concat subject` will again check if there is enough capacity in the string. If it doesn't find enough capacity once again, it will make a third allocation, copy the existing bytes (`"Hello, "`) into that third allocation, and then deallocate the second allocation because it's already no longer being referenced anywhere else in the program. (It may find enough capacity in this prticular case, because the previous [Str.concat] allocated something like 1.5 to 2 times the necessary capacity in order to anticipate future concatenations like this...but if something longer than `"World"` were being concatenated here, it might still require further reallocation and copying.)
|
||||
## 4. `|> Str.concat "!\n"` will repeat this process once more.
|
||||
##
|
||||
## This process can have significant performance costs due to multiple reallocation of new strings, copying between old strings and new strings, and deallocation of immediately obsolete strings.
|
||||
##
|
||||
## Here's a modified example which uses [Str.reserve] to eliminate the need for all that reallocation, copying, and deallocation.
|
||||
##
|
||||
## ```
|
||||
## helloWorld =
|
||||
## greeting
|
||||
## |> Str.reserve 21
|
||||
## |> Str.concat ", "
|
||||
## |> Str.concat subject
|
||||
## |> Str.concat "!"
|
||||
## ```
|
||||
##
|
||||
## In this example:
|
||||
## 1. We again start with `greeting`, which has both a length and capacity of 24 bytes.
|
||||
## 2. `|> Str.reserve 21` will ensure that there is enough capacity in the string for an additional 21 bytes (to make room for `", "`, `"Awesome Programmer"`, and `"!"`). Since the current capacity is only 24, it will create a new 45-byte (24 + 21) heap allocation and copy the contents of the existing allocation (`greeting`) into it.
|
||||
## 3. `|> Str.concat ", "` will concatenate `, ` to the string. No reallocation, copying, or deallocation will be necessary, because the string already has a capacity of 45 btytes, and `greeting` will only use 24 of them.
|
||||
## 4. `|> Str.concat subject` will concatenate `subject` (`"Awesome Programmer"`) to the string. Again, no reallocation, copying, or deallocation will be necessary.
|
||||
## 5. `|> Str.concat "!\n"` will concatenate `"!\n"` to the string, still without any reallocation, copying, or deallocation.
|
||||
##
|
||||
## Here, [Str.reserve] prevented multiple reallocations, copies, and deallocations during the
|
||||
## [Str.concat] calls. Notice that it did perform a heap allocation before any [Str.concat] calls
|
||||
## were made, which means that using [Str.reserve] is not free! You should only use it if you actually
|
||||
## expect to make use of the extra capacity.
|
||||
##
|
||||
## Ideally, you'd be able to predict exactly how many extra bytes of capacity will be needed, but this
|
||||
## may not always be knowable. When you don't know exactly how many bytes to reserve, you can often get better
|
||||
## performance by choosing a number of bytes that's too high, because a number that's too low could lead to reallocations. There's a limit to
|
||||
## this, of course; if you always give it ten times what it turns out to need, that could prevent
|
||||
## reallocations but will also waste a lot of memory!
|
||||
##
|
||||
## If you plan to use [Str.reserve] on an empty string, it's generally better to use [Str.withCapacity] instead.
|
||||
reserve : Str, Nat -> Str
|
||||
|
||||
## Combines a [List] of strings into a single string, with a separator
|
||||
## string in between each.
|
||||
## ```
|
||||
|
@ -200,7 +300,21 @@ repeat : Str, Nat -> Str
|
|||
## using a single Unicode code point.
|
||||
countGraphemes : Str -> Nat
|
||||
|
||||
## Split a string into its constituent grapheme clusters
|
||||
## Split a string into its constituent graphemes.
|
||||
##
|
||||
## This function breaks a string into its individual [graphemes](https://stackoverflow.com/a/27331885/4200103),
|
||||
## returning them as a list of strings. This is useful for working with text that
|
||||
## contains complex characters, such as emojis.
|
||||
##
|
||||
## Examples:
|
||||
## ```
|
||||
## expect Str.graphemes "Roc" == ["R", "o", "c"]
|
||||
## expect Str.graphemes "नमस्ते" == ["न", "म", "स्", "ते"]
|
||||
## expect Str.graphemes "👩👩👦👦" == ["👩", "👩", "👦", "👦"]
|
||||
## ```
|
||||
##
|
||||
## Note that the "👩👩👦👦" example consists of 4 grapheme clusters, although it visually
|
||||
## appears as a single glyph. This is because it uses an emoji modifier sequence.
|
||||
graphemes : Str -> List Str
|
||||
|
||||
## If the string begins with a [Unicode code point](http://www.unicode.org/glossary/#code_point)
|
||||
|
@ -273,6 +387,12 @@ fromUtf8 = \bytes ->
|
|||
else
|
||||
Err (BadUtf8 result.dProblemCode result.aByteIndex)
|
||||
|
||||
expect (Str.fromUtf8 [82, 111, 99]) == Ok "Roc"
|
||||
expect (Str.fromUtf8 [224, 174, 154, 224, 174, 191]) == Ok "சி"
|
||||
expect (Str.fromUtf8 [240, 159, 144, 166]) == Ok "🐦"
|
||||
expect (Str.fromUtf8 []) == Ok ""
|
||||
expect (Str.fromUtf8 [255]) |> Result.isErr
|
||||
|
||||
## Encode part of a [List] of [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## into a [Str]
|
||||
## ```
|
||||
|
@ -290,6 +410,12 @@ fromUtf8Range = \bytes, config ->
|
|||
else
|
||||
Err OutOfBounds
|
||||
|
||||
expect (Str.fromUtf8Range [72, 105, 80, 103] { start: 0, count: 2 }) == Ok "Hi"
|
||||
expect (Str.fromUtf8Range [233, 185, 143, 224, 174, 154, 224, 174, 191] { start: 3, count: 3 }) == Ok "ச"
|
||||
expect (Str.fromUtf8Range [240, 159, 144, 166] { start: 0, count: 4 }) == Ok "🐦"
|
||||
expect (Str.fromUtf8Range [] { start: 0, count: 0 }) == Ok ""
|
||||
expect (Str.fromUtf8Range [72, 105, 80, 103] { start: 2, count: 3 }) |> Result.isErr
|
||||
|
||||
FromUtf8Result : {
|
||||
aByteIndex : Nat,
|
||||
bString : Str,
|
||||
|
@ -744,8 +870,29 @@ walkUtf8WithIndexHelp = \string, state, step, index, length ->
|
|||
else
|
||||
state
|
||||
|
||||
## Enlarge a string for at least the given number additional bytes.
|
||||
reserve : Str, Nat -> Str
|
||||
## Walks over the `UTF-8` bytes of the given [Str] and calls a function to update
|
||||
## state for each byte.
|
||||
##
|
||||
## ```
|
||||
## result = walkUtf8 "hello, world!" "" (\state, byte -> state ++ String.fromCodePoint byte)
|
||||
## expect result == Ok "hello, world!"
|
||||
## ```
|
||||
walkUtf8 : Str, state, (state, U8 -> state) -> state
|
||||
walkUtf8 = \str, initial, step ->
|
||||
walkUtf8Help str initial step 0 (Str.countUtf8Bytes str)
|
||||
|
||||
walkUtf8Help : Str, state, (state, U8 -> state), Nat, Nat -> state
|
||||
walkUtf8Help = \str, state, step, index, length ->
|
||||
if index < length then
|
||||
byte = Str.getUnsafe str index
|
||||
newState = step state byte
|
||||
|
||||
walkUtf8Help str newState step (index + 1) length
|
||||
else
|
||||
state
|
||||
|
||||
expect (walkUtf8 "ABC" [] List.append) == [65, 66, 67]
|
||||
expect (walkUtf8 "鹏" [] List.append) == [233, 185, 143]
|
||||
|
||||
## Shrink the memory footprint of a str such that it's capacity and length are equal.
|
||||
## Note: This will also convert seamless slices to regular lists.
|
||||
|
|
|
@ -2,4 +2,5 @@ mod pretty_print;
|
|||
|
||||
pub use pretty_print::pretty_print_declarations;
|
||||
pub use pretty_print::pretty_print_def;
|
||||
pub use pretty_print::pretty_write_declarations;
|
||||
pub use pretty_print::Ctx as PPCtx;
|
||||
|
|
|
@ -19,18 +19,45 @@ pub struct Ctx<'a> {
|
|||
|
||||
pub fn pretty_print_declarations(c: &Ctx, declarations: &Declarations) -> String {
|
||||
let f = Arena::new();
|
||||
print_declarations_help(c, &f, declarations)
|
||||
.1
|
||||
.pretty(80)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn pretty_write_declarations(
|
||||
writer: &mut impl std::io::Write,
|
||||
c: &Ctx,
|
||||
declarations: &Declarations,
|
||||
) -> std::io::Result<()> {
|
||||
let f = Arena::new();
|
||||
print_declarations_help(c, &f, declarations)
|
||||
.1
|
||||
.render(80, writer)
|
||||
}
|
||||
|
||||
pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
||||
let f = Arena::new();
|
||||
def(c, &f, d).append(f.hardline()).1.pretty(80).to_string()
|
||||
}
|
||||
|
||||
fn print_declarations_help<'a>(
|
||||
c: &Ctx,
|
||||
f: &'a Arena<'a>,
|
||||
declarations: &'a Declarations,
|
||||
) -> DocBuilder<'a, Arena<'a>> {
|
||||
let mut defs = Vec::with_capacity(declarations.len());
|
||||
for (index, tag) in declarations.iter_bottom_up() {
|
||||
let symbol = declarations.symbols[index].value;
|
||||
let body = &declarations.expressions[index];
|
||||
|
||||
let def = match tag {
|
||||
DeclarationTag::Value => def_symbol_help(c, &f, symbol, &body.value),
|
||||
DeclarationTag::Value => def_symbol_help(c, f, symbol, &body.value),
|
||||
DeclarationTag::Function(f_index)
|
||||
| DeclarationTag::Recursive(f_index)
|
||||
| DeclarationTag::TailRecursive(f_index) => {
|
||||
let function_def = &declarations.function_bodies[f_index.index()].value;
|
||||
toplevel_function(c, &f, symbol, function_def, &body.value)
|
||||
toplevel_function(c, f, symbol, function_def, &body.value)
|
||||
}
|
||||
DeclarationTag::Expectation => todo!(),
|
||||
DeclarationTag::ExpectationFx => todo!(),
|
||||
|
@ -45,14 +72,6 @@ pub fn pretty_print_declarations(c: &Ctx, declarations: &Declarations) -> String
|
|||
}
|
||||
|
||||
f.intersperse(defs, f.hardline().append(f.hardline()))
|
||||
.1
|
||||
.pretty(80)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
||||
let f = Arena::new();
|
||||
def(c, &f, d).append(f.hardline()).1.pretty(80).to_string()
|
||||
}
|
||||
|
||||
macro_rules! maybe_paren {
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_types::{subs::Variable, types::MemberImpl};
|
|||
|
||||
use crate::{
|
||||
abilities::AbilitiesStore,
|
||||
def::{Annotation, Declaration, Def},
|
||||
def::{Annotation, Def},
|
||||
expr::{
|
||||
self, AnnotatedMark, ClosureData, Declarations, Expr, Field, OpaqueWrapFunctionData,
|
||||
StructAccessorData,
|
||||
|
@ -14,19 +14,71 @@ use crate::{
|
|||
pattern::{DestructType, Pattern, RecordDestruct, TupleDestruct},
|
||||
};
|
||||
|
||||
macro_rules! visit_list {
|
||||
($visitor:ident, $walk:ident, $list:expr) => {
|
||||
for elem in $list {
|
||||
$visitor.$walk(elem)
|
||||
pub enum DeclarationInfo<'a> {
|
||||
Value {
|
||||
loc_symbol: Loc<Symbol>,
|
||||
loc_expr: &'a Loc<Expr>,
|
||||
expr_var: Variable,
|
||||
pattern: Pattern,
|
||||
annotation: Option<&'a Annotation>,
|
||||
},
|
||||
Expectation {
|
||||
loc_condition: &'a Loc<Expr>,
|
||||
},
|
||||
Function {
|
||||
loc_symbol: Loc<Symbol>,
|
||||
loc_body: &'a Loc<Expr>,
|
||||
expr_var: Variable,
|
||||
pattern: Pattern,
|
||||
function: &'a Loc<expr::FunctionDef>,
|
||||
},
|
||||
Destructure {
|
||||
loc_pattern: &'a Loc<Pattern>,
|
||||
opt_pattern_var: Option<Variable>,
|
||||
loc_expr: &'a Loc<Expr>,
|
||||
expr_var: Variable,
|
||||
annotation: Option<&'a Annotation>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> DeclarationInfo<'a> {
|
||||
pub fn region(&self) -> Region {
|
||||
use DeclarationInfo::*;
|
||||
match self {
|
||||
Value {
|
||||
loc_symbol,
|
||||
loc_expr,
|
||||
..
|
||||
} => Region::span_across(&loc_symbol.region, &loc_expr.region),
|
||||
Expectation { loc_condition } => loc_condition.region,
|
||||
Function {
|
||||
loc_symbol,
|
||||
function,
|
||||
..
|
||||
} => Region::span_across(&loc_symbol.region, &function.region),
|
||||
Destructure {
|
||||
loc_pattern,
|
||||
loc_expr,
|
||||
..
|
||||
} => Region::span_across(&loc_pattern.region, &loc_expr.region),
|
||||
}
|
||||
}
|
||||
|
||||
fn var(&self) -> Variable {
|
||||
match self {
|
||||
DeclarationInfo::Value { expr_var, .. } => *expr_var,
|
||||
DeclarationInfo::Expectation { .. } => Variable::BOOL,
|
||||
DeclarationInfo::Function { expr_var, .. } => *expr_var,
|
||||
DeclarationInfo::Destructure { expr_var, .. } => *expr_var,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
||||
use crate::expr::DeclarationTag::*;
|
||||
|
||||
for (index, tag) in decls.declarations.iter().enumerate() {
|
||||
match tag {
|
||||
let info = match tag {
|
||||
Value => {
|
||||
let loc_expr = &decls.expressions[index];
|
||||
|
||||
|
@ -40,22 +92,19 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
},
|
||||
None => Pattern::Identifier(loc_symbol.value),
|
||||
};
|
||||
visitor.visit_pattern(&pattern, loc_symbol.region, Some(expr_var));
|
||||
|
||||
visitor.visit_expr(&loc_expr.value, loc_expr.region, expr_var);
|
||||
if let Some(annot) = &decls.annotations[index] {
|
||||
visitor.visit_annotation(annot);
|
||||
DeclarationInfo::Value {
|
||||
loc_symbol,
|
||||
loc_expr,
|
||||
expr_var,
|
||||
pattern,
|
||||
annotation: decls.annotations[index].as_ref(),
|
||||
}
|
||||
}
|
||||
Expectation => {
|
||||
Expectation | ExpectationFx => {
|
||||
let loc_condition = &decls.expressions[index];
|
||||
|
||||
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_condition = &decls.expressions[index];
|
||||
|
||||
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||
DeclarationInfo::Expectation { loc_condition }
|
||||
}
|
||||
Function(function_index)
|
||||
| Recursive(function_index)
|
||||
|
@ -72,16 +121,16 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
},
|
||||
None => Pattern::Identifier(loc_symbol.value),
|
||||
};
|
||||
visitor.visit_pattern(&pattern, loc_symbol.region, Some(expr_var));
|
||||
|
||||
let function_def = &decls.function_bodies[function_index.index() as usize];
|
||||
|
||||
walk_closure_help(
|
||||
visitor,
|
||||
&function_def.value.arguments,
|
||||
DeclarationInfo::Function {
|
||||
loc_symbol,
|
||||
loc_body,
|
||||
function_def.value.return_type,
|
||||
)
|
||||
expr_var,
|
||||
pattern,
|
||||
function: function_def,
|
||||
}
|
||||
}
|
||||
Destructure(destructure_index) => {
|
||||
let destructure = &decls.destructs[destructure_index.index() as usize];
|
||||
|
@ -90,51 +139,83 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
let loc_expr = &decls.expressions[index];
|
||||
let expr_var = decls.variables[index];
|
||||
|
||||
let opt_var = match loc_pattern.value {
|
||||
let opt_pattern_var = match loc_pattern.value {
|
||||
Pattern::Identifier(..) | Pattern::AbilityMemberSpecialization { .. } => {
|
||||
Some(expr_var)
|
||||
}
|
||||
_ => loc_pattern.value.opt_var(),
|
||||
};
|
||||
|
||||
visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_var);
|
||||
visitor.visit_expr(&loc_expr.value, loc_expr.region, expr_var);
|
||||
DeclarationInfo::Destructure {
|
||||
loc_pattern,
|
||||
opt_pattern_var,
|
||||
loc_expr,
|
||||
expr_var,
|
||||
annotation: decls.annotations[index].as_ref(),
|
||||
}
|
||||
}
|
||||
MutualRecursion { .. } => {
|
||||
// The actual declarations involved in the mutual recursion will come next.
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(annot) = &decls.annotations[index] {
|
||||
visitor.visit_decl(info);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_decl<V: Visitor>(visitor: &mut V, decl: DeclarationInfo<'_>) {
|
||||
use DeclarationInfo::*;
|
||||
|
||||
match decl {
|
||||
Value {
|
||||
loc_symbol,
|
||||
loc_expr,
|
||||
expr_var,
|
||||
pattern,
|
||||
annotation,
|
||||
} => {
|
||||
visitor.visit_pattern(&pattern, loc_symbol.region, Some(expr_var));
|
||||
|
||||
visitor.visit_expr(&loc_expr.value, loc_expr.region, expr_var);
|
||||
if let Some(annot) = annotation {
|
||||
visitor.visit_annotation(annot);
|
||||
}
|
||||
}
|
||||
MutualRecursion { .. } => { /* ignore */ }
|
||||
}
|
||||
}
|
||||
Expectation { loc_condition } => {
|
||||
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||
}
|
||||
Function {
|
||||
loc_symbol,
|
||||
loc_body,
|
||||
expr_var,
|
||||
pattern,
|
||||
function,
|
||||
} => {
|
||||
visitor.visit_pattern(&pattern, loc_symbol.region, Some(expr_var));
|
||||
|
||||
fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
||||
match decl {
|
||||
Declaration::Declare(def) => {
|
||||
visitor.visit_def(def);
|
||||
}
|
||||
Declaration::DeclareRec(defs, _cycle_mark) => {
|
||||
visit_list!(visitor, visit_def, defs)
|
||||
walk_closure_help(
|
||||
visitor,
|
||||
&function.value.arguments,
|
||||
loc_body,
|
||||
function.value.return_type,
|
||||
)
|
||||
}
|
||||
Destructure {
|
||||
loc_pattern,
|
||||
opt_pattern_var,
|
||||
loc_expr,
|
||||
expr_var,
|
||||
annotation,
|
||||
} => {
|
||||
visitor.visit_pattern(&loc_pattern.value, loc_pattern.region, opt_pattern_var);
|
||||
visitor.visit_expr(&loc_expr.value, loc_expr.region, expr_var);
|
||||
|
||||
Declaration::Expects(expects) => {
|
||||
let it = expects.regions.iter().zip(expects.conditions.iter());
|
||||
for (region, condition) in it {
|
||||
visitor.visit_expr(condition, *region, Variable::BOOL);
|
||||
}
|
||||
}
|
||||
Declaration::ExpectsFx(expects) => {
|
||||
let it = expects.regions.iter().zip(expects.conditions.iter());
|
||||
for (region, condition) in it {
|
||||
visitor.visit_expr(condition, *region, Variable::BOOL);
|
||||
}
|
||||
}
|
||||
Declaration::Builtin(def) => visitor.visit_def(def),
|
||||
Declaration::InvalidCycle(_cycles) => {
|
||||
// ignore
|
||||
if let Some(annot) = annotation {
|
||||
visitor.visit_annotation(annot);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn walk_def<V: Visitor>(visitor: &mut V, def: &Def) {
|
||||
|
@ -449,7 +530,7 @@ pub trait Visitor: Sized {
|
|||
walk_decls(self, decls);
|
||||
}
|
||||
|
||||
fn visit_decl(&mut self, decl: &Declaration) {
|
||||
fn visit_decl(&mut self, decl: DeclarationInfo<'_>) {
|
||||
if self.should_visit(decl.region()) {
|
||||
walk_decl(self, decl);
|
||||
}
|
||||
|
@ -582,14 +663,24 @@ pub fn find_type_at(region: Region, decls: &Declarations) -> Option<Variable> {
|
|||
visitor.typ
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FoundSymbol {
|
||||
/// Specialization(T, foo1) is the specialization of foo for T.
|
||||
Specialization(Symbol, Symbol),
|
||||
/// AbilityMember(Foo, foo) is the ability member foo of Foo.
|
||||
AbilityMember(Symbol, Symbol),
|
||||
/// Raw symbol, not specialized to anything.
|
||||
Symbol(Symbol),
|
||||
}
|
||||
|
||||
/// Given an ability Foo has foo : ..., returns (T, foo1) if the symbol at the given region is a
|
||||
/// symbol foo1 that specializes foo for T. Otherwise if the symbol is foo but the specialization
|
||||
/// is unknown, (Foo, foo) is returned. Otherwise [None] is returned.
|
||||
pub fn find_ability_member_and_owning_type_at(
|
||||
pub fn find_symbol_at(
|
||||
region: Region,
|
||||
decls: &Declarations,
|
||||
abilities_store: &AbilitiesStore,
|
||||
) -> Option<(Symbol, Symbol)> {
|
||||
) -> Option<FoundSymbol> {
|
||||
let mut visitor = Finder {
|
||||
region,
|
||||
found: None,
|
||||
|
@ -601,7 +692,7 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
struct Finder<'a> {
|
||||
region: Region,
|
||||
abilities_store: &'a AbilitiesStore,
|
||||
found: Option<(Symbol, Symbol)>,
|
||||
found: Option<FoundSymbol>,
|
||||
}
|
||||
|
||||
impl Visitor for Finder<'_> {
|
||||
|
@ -611,16 +702,19 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
|
||||
fn visit_pattern(&mut self, pattern: &Pattern, region: Region, _opt_var: Option<Variable>) {
|
||||
if region == self.region {
|
||||
if let Pattern::AbilityMemberSpecialization {
|
||||
match pattern {
|
||||
Pattern::AbilityMemberSpecialization {
|
||||
ident: spec_symbol,
|
||||
specializes: _,
|
||||
} = pattern
|
||||
{
|
||||
} => {
|
||||
debug_assert!(self.found.is_none());
|
||||
let spec_type =
|
||||
find_specialization_type_of_symbol(*spec_symbol, self.abilities_store)
|
||||
.unwrap();
|
||||
self.found = Some((spec_type, *spec_symbol))
|
||||
self.found = Some(FoundSymbol::Specialization(spec_type, *spec_symbol))
|
||||
}
|
||||
Pattern::Identifier(symbol) => self.found = Some(FoundSymbol::Symbol(*symbol)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -629,7 +723,8 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
|
||||
fn visit_expr(&mut self, expr: &Expr, region: Region, var: Variable) {
|
||||
if region == self.region {
|
||||
if let &Expr::AbilityMember(member_symbol, specialization_id, _var) = expr {
|
||||
match expr {
|
||||
&Expr::AbilityMember(member_symbol, specialization_id, _var) => {
|
||||
debug_assert!(self.found.is_none());
|
||||
self.found = match specialization_id
|
||||
.and_then(|id| self.abilities_store.get_resolved(id))
|
||||
|
@ -640,7 +735,7 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
self.abilities_store,
|
||||
)
|
||||
.unwrap();
|
||||
Some((spec_type, spec_symbol))
|
||||
Some(FoundSymbol::Specialization(spec_type, spec_symbol))
|
||||
}
|
||||
None => {
|
||||
let parent_ability = self
|
||||
|
@ -648,11 +743,14 @@ pub fn find_ability_member_and_owning_type_at(
|
|||
.member_def(member_symbol)
|
||||
.unwrap()
|
||||
.parent_ability;
|
||||
Some((parent_ability, member_symbol))
|
||||
Some(FoundSymbol::AbilityMember(parent_ability, member_symbol))
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
Expr::Var(symbol, _var) => self.found = Some(FoundSymbol::Symbol(*symbol)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
walk_expr(self, expr, var);
|
||||
|
@ -705,3 +803,75 @@ pub fn symbols_introduced_from_pattern(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum FoundDeclaration<'a> {
|
||||
Decl(DeclarationInfo<'a>),
|
||||
Def(&'a Def),
|
||||
}
|
||||
|
||||
impl<'a> FoundDeclaration<'a> {
|
||||
pub fn region(&self) -> Region {
|
||||
match self {
|
||||
FoundDeclaration::Decl(decl) => decl.region(),
|
||||
FoundDeclaration::Def(def) => def.region(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var(&self) -> Variable {
|
||||
match self {
|
||||
FoundDeclaration::Decl(decl) => decl.var(),
|
||||
FoundDeclaration::Def(def) => def.expr_var,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the declaration of `symbol`.
|
||||
pub fn find_declaration(symbol: Symbol, decls: &'_ Declarations) -> Option<FoundDeclaration<'_>> {
|
||||
let mut visitor = Finder {
|
||||
symbol,
|
||||
found: None,
|
||||
};
|
||||
visitor.visit_decls(decls);
|
||||
return visitor.found;
|
||||
|
||||
struct Finder<'a> {
|
||||
symbol: Symbol,
|
||||
found: Option<FoundDeclaration<'a>>,
|
||||
}
|
||||
|
||||
impl Visitor for Finder<'_> {
|
||||
fn should_visit(&mut self, _region: Region) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn visit_decl(&mut self, decl: DeclarationInfo<'_>) {
|
||||
match decl {
|
||||
DeclarationInfo::Value { loc_symbol, .. }
|
||||
| DeclarationInfo::Function { loc_symbol, .. }
|
||||
if loc_symbol.value == self.symbol =>
|
||||
{
|
||||
self.found = Some(FoundDeclaration::Decl(unsafe { std::mem::transmute(decl) }));
|
||||
}
|
||||
DeclarationInfo::Destructure { .. } => {
|
||||
// TODO destructures
|
||||
walk_decl(self, decl);
|
||||
}
|
||||
_ => {
|
||||
walk_decl(self, decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_def(&mut self, def: &Def) {
|
||||
if matches!(def.loc_pattern.value, Pattern::Identifier(s) if s == self.symbol) {
|
||||
debug_assert!(self.found.is_none());
|
||||
// Safety: the def can't escape the passed in `decls`, and the visitor does not
|
||||
// synthesize defs.
|
||||
self.found = Some(FoundDeclaration::Def(unsafe { std::mem::transmute(def) }));
|
||||
return;
|
||||
}
|
||||
|
||||
walk_def(self, def)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,3 +15,4 @@ hashbrown.workspace = true
|
|||
im-rc.workspace = true
|
||||
im.workspace = true
|
||||
wyhash.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
pub mod all;
|
||||
mod reference_matrix;
|
||||
mod small_string_interner;
|
||||
mod small_vec;
|
||||
pub mod soa;
|
||||
mod vec_map;
|
||||
mod vec_set;
|
||||
|
@ -13,5 +14,6 @@ mod vec_set;
|
|||
pub use all::{default_hasher, BumpMap, ImEntry, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
pub use reference_matrix::{ReferenceMatrix, Sccs, TopologicalSort};
|
||||
pub use small_string_interner::SmallStringInterner;
|
||||
pub use small_vec::SmallVec;
|
||||
pub use vec_map::VecMap;
|
||||
pub use vec_set::VecSet;
|
||||
|
|
61
crates/compiler/collections/src/small_vec.rs
Normal file
61
crates/compiler/collections/src/small_vec.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use smallvec::SmallVec as Vec;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SmallVec<T, const N: usize>(Vec<[T; N]>);
|
||||
|
||||
impl<T, const N: usize> SmallVec<T, N> {
|
||||
pub const fn new() -> Self {
|
||||
Self(Vec::new_const())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) {
|
||||
self.0.push(value)
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
self.0.pop()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> std::ops::Deref for SmallVec<T, N> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Debug for SmallVec<T, N>
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Clone for SmallVec<T, N>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IntoIterator for SmallVec<T, N> {
|
||||
type Item = T;
|
||||
type IntoIter = <Vec<[T; N]> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> FromIterator<T> for SmallVec<T, N> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
Self(Vec::from_iter(iter))
|
||||
}
|
||||
}
|
|
@ -1501,6 +1501,7 @@ trait Backend<'a> {
|
|||
self.set_last_seen(*sym, stmt);
|
||||
match expr {
|
||||
Expr::Literal(_) => {}
|
||||
Expr::NullPointer => {}
|
||||
|
||||
Expr::Call(call) => self.scan_ast_call(call, stmt),
|
||||
|
||||
|
|
|
@ -200,6 +200,7 @@ pub enum LlvmBackendMode {
|
|||
BinaryDev,
|
||||
/// Creates a test wrapper around the main roc function to catch and report panics.
|
||||
/// Provides a testing implementation of primitives (roc_alloc, roc_panic, etc)
|
||||
BinaryGlue,
|
||||
GenTest,
|
||||
WasmGenTest,
|
||||
CliTest,
|
||||
|
@ -210,6 +211,7 @@ impl LlvmBackendMode {
|
|||
match self {
|
||||
LlvmBackendMode::Binary => true,
|
||||
LlvmBackendMode::BinaryDev => true,
|
||||
LlvmBackendMode::BinaryGlue => false,
|
||||
LlvmBackendMode::GenTest => false,
|
||||
LlvmBackendMode::WasmGenTest => true,
|
||||
LlvmBackendMode::CliTest => false,
|
||||
|
@ -221,6 +223,7 @@ impl LlvmBackendMode {
|
|||
match self {
|
||||
LlvmBackendMode::Binary => false,
|
||||
LlvmBackendMode::BinaryDev => false,
|
||||
LlvmBackendMode::BinaryGlue => false,
|
||||
LlvmBackendMode::GenTest => true,
|
||||
LlvmBackendMode::WasmGenTest => true,
|
||||
LlvmBackendMode::CliTest => true,
|
||||
|
@ -231,6 +234,7 @@ impl LlvmBackendMode {
|
|||
match self {
|
||||
LlvmBackendMode::Binary => false,
|
||||
LlvmBackendMode::BinaryDev => true,
|
||||
LlvmBackendMode::BinaryGlue => false,
|
||||
LlvmBackendMode::GenTest => false,
|
||||
LlvmBackendMode::WasmGenTest => false,
|
||||
LlvmBackendMode::CliTest => true,
|
||||
|
@ -1078,6 +1082,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
match expr {
|
||||
Literal(literal) => build_exp_literal(env, layout_interner, parent, layout, literal),
|
||||
NullPointer => {
|
||||
let basic_type = basic_type_from_layout(env, layout_interner, layout);
|
||||
|
||||
debug_assert!(basic_type.is_pointer_type());
|
||||
basic_type.into_pointer_type().const_zero().into()
|
||||
}
|
||||
|
||||
Call(call) => build_exp_call(
|
||||
env,
|
||||
|
@ -4116,7 +4126,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {}
|
||||
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev | LlvmBackendMode::BinaryGlue => {}
|
||||
}
|
||||
|
||||
// a generic version that writes the result into a passed *u8 pointer
|
||||
|
@ -4169,7 +4179,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
|||
roc_call_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
|
||||
}
|
||||
|
||||
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {
|
||||
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev | LlvmBackendMode::BinaryGlue => {
|
||||
basic_type_from_layout(env, layout_interner, return_layout)
|
||||
}
|
||||
};
|
||||
|
@ -5250,7 +5260,7 @@ fn build_proc<'a, 'ctx, 'env>(
|
|||
GenTest | WasmGenTest | CliTest => {
|
||||
/* no host, or exposing types is not supported */
|
||||
}
|
||||
Binary | BinaryDev => {
|
||||
Binary | BinaryDev | BinaryGlue => {
|
||||
for (alias_name, hels) in aliases.iter() {
|
||||
let ident_string = proc.name.name().as_str(&env.interns);
|
||||
let fn_name: String = format!("{}_{}", ident_string, hels.id.0);
|
||||
|
|
|
@ -41,6 +41,8 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_alloc").unwrap();
|
||||
fn_val.set_linkage(Linkage::Internal);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let size_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
@ -135,6 +137,8 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
// The type of this function (but not the implementation) should have
|
||||
// already been defined by the builtins, which rely on it.
|
||||
let fn_val = module.get_function("roc_dealloc").unwrap();
|
||||
fn_val.set_linkage(Linkage::Internal);
|
||||
|
||||
let mut params = fn_val.get_param_iter();
|
||||
let ptr_arg = params.next().unwrap();
|
||||
let _alignment_arg = params.next().unwrap();
|
||||
|
@ -197,6 +201,11 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
|||
let mut params = fn_val.get_param_iter();
|
||||
let roc_str_arg = params.next().unwrap();
|
||||
|
||||
// normally, roc_panic is marked as external so it can be provided by the host. But when we
|
||||
// define it here in LLVM IR, we never want it to be linked by the host (that would
|
||||
// overwrite which implementation is used.
|
||||
fn_val.set_linkage(Linkage::Internal);
|
||||
|
||||
let tag_id_arg = params.next().unwrap();
|
||||
|
||||
debug_assert!(params.next().is_none());
|
||||
|
|
|
@ -1041,6 +1041,8 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
match expr {
|
||||
Expr::Literal(lit) => self.expr_literal(lit, storage),
|
||||
|
||||
Expr::NullPointer => self.expr_null_pointer(),
|
||||
|
||||
Expr::Call(roc_mono::ir::Call {
|
||||
call_type,
|
||||
arguments,
|
||||
|
@ -1232,6 +1234,10 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
elements_addr
|
||||
}
|
||||
|
||||
fn expr_null_pointer(&mut self) {
|
||||
self.code_builder.i32_const(0);
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
* Call expressions
|
||||
*******************************************************************/
|
||||
|
|
|
@ -992,7 +992,6 @@ struct State<'a> {
|
|||
/// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>!
|
||||
pub arc_modules: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||
pub arc_shorthands: Arc<Mutex<MutMap<&'a str, ShorthandPath>>>,
|
||||
#[allow(unused)]
|
||||
pub derived_module: SharedDerivedModule,
|
||||
|
||||
pub ident_ids_by_module: SharedIdentIdsByModule,
|
||||
|
|
|
@ -1,7 +1,79 @@
|
|||
use self::Associativity::*;
|
||||
use self::BinOp::*;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
const PRECEDENCES: [(BinOp, u8); 20] = [
|
||||
(Caret, 7),
|
||||
(Star, 6),
|
||||
(Slash, 6),
|
||||
(DoubleSlash, 5),
|
||||
(Percent, 5),
|
||||
(Plus, 4),
|
||||
(Minus, 4),
|
||||
(Pizza, 3),
|
||||
(Equals, 2),
|
||||
(NotEquals, 2),
|
||||
(LessThan, 1),
|
||||
(GreaterThan, 1),
|
||||
(LessThanOrEq, 1),
|
||||
(GreaterThanOrEq, 1),
|
||||
(And, 0),
|
||||
(Or, 0),
|
||||
// These should never come up
|
||||
(Assignment, 255),
|
||||
(IsAliasType, 255),
|
||||
(IsOpaqueType, 255),
|
||||
(Backpassing, 255),
|
||||
];
|
||||
|
||||
const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [
|
||||
(Caret, RightAssociative),
|
||||
(Star, LeftAssociative),
|
||||
(Slash, LeftAssociative),
|
||||
(DoubleSlash, LeftAssociative),
|
||||
(Percent, LeftAssociative),
|
||||
(Plus, LeftAssociative),
|
||||
(Minus, LeftAssociative),
|
||||
(Pizza, LeftAssociative),
|
||||
(Equals, NonAssociative),
|
||||
(NotEquals, NonAssociative),
|
||||
(LessThan, NonAssociative),
|
||||
(GreaterThan, NonAssociative),
|
||||
(LessThanOrEq, NonAssociative),
|
||||
(GreaterThanOrEq, NonAssociative),
|
||||
(And, RightAssociative),
|
||||
(Or, RightAssociative),
|
||||
// These should never come up
|
||||
(Assignment, LeftAssociative),
|
||||
(IsAliasType, LeftAssociative),
|
||||
(IsOpaqueType, LeftAssociative),
|
||||
(Backpassing, LeftAssociative),
|
||||
];
|
||||
|
||||
const DISPLAY_STRINGS: [(BinOp, &str); 20] = [
|
||||
(Caret, "^"),
|
||||
(Star, "*"),
|
||||
(Slash, "/"),
|
||||
(DoubleSlash, "//"),
|
||||
(Percent, "%"),
|
||||
(Plus, "+"),
|
||||
(Minus, "-"),
|
||||
(Pizza, "|>"),
|
||||
(Equals, "=="),
|
||||
(NotEquals, "!="),
|
||||
(LessThan, "<"),
|
||||
(GreaterThan, ">"),
|
||||
(LessThanOrEq, "<="),
|
||||
(GreaterThanOrEq, ">="),
|
||||
(And, "&&"),
|
||||
(Or, "||"),
|
||||
(Assignment, "="),
|
||||
(IsAliasType, ":"),
|
||||
(IsOpaqueType, ":="),
|
||||
(Backpassing, "<-"),
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum CalledVia {
|
||||
/// Calling with space, e.g. (foo bar)
|
||||
|
@ -36,6 +108,7 @@ pub enum BinOp {
|
|||
Percent,
|
||||
Plus,
|
||||
Minus,
|
||||
Pizza,
|
||||
Equals,
|
||||
NotEquals,
|
||||
LessThan,
|
||||
|
@ -44,7 +117,6 @@ pub enum BinOp {
|
|||
GreaterThanOrEq,
|
||||
And,
|
||||
Or,
|
||||
Pizza,
|
||||
Assignment,
|
||||
IsAliasType,
|
||||
IsOpaqueType,
|
||||
|
@ -93,29 +165,27 @@ pub enum Associativity {
|
|||
|
||||
impl BinOp {
|
||||
pub fn associativity(self) -> Associativity {
|
||||
use self::Associativity::*;
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
match self {
|
||||
Pizza | Star | Slash | DoubleSlash | Percent | Plus | Minus => LeftAssociative,
|
||||
And | Or | Caret => RightAssociative,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
|
||||
NonAssociative
|
||||
}
|
||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||
}
|
||||
const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table();
|
||||
|
||||
ASSOCIATIVITY_TABLE[self as usize]
|
||||
}
|
||||
|
||||
fn precedence(self) -> u8 {
|
||||
match self {
|
||||
Caret => 7,
|
||||
Star | Slash | DoubleSlash | Percent => 6,
|
||||
Plus | Minus => 5,
|
||||
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => 4,
|
||||
And => 3,
|
||||
Or => 2,
|
||||
Pizza => 1,
|
||||
Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
|
||||
}
|
||||
// The compiler should never pass any of these to this function!
|
||||
debug_assert_ne!(self, Assignment);
|
||||
debug_assert_ne!(self, IsAliasType);
|
||||
debug_assert_ne!(self, IsOpaqueType);
|
||||
debug_assert_ne!(self, Backpassing);
|
||||
|
||||
const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table();
|
||||
|
||||
PRECEDENCE_TABLE[self as usize]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,29 +203,75 @@ impl Ord for BinOp {
|
|||
|
||||
impl std::fmt::Display for BinOp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let as_str = match self {
|
||||
Caret => "^",
|
||||
Star => "*",
|
||||
Slash => "/",
|
||||
DoubleSlash => "//",
|
||||
Percent => "%",
|
||||
Plus => "+",
|
||||
Minus => "-",
|
||||
Equals => "==",
|
||||
NotEquals => "!=",
|
||||
LessThan => "<",
|
||||
GreaterThan => ">",
|
||||
LessThanOrEq => "<=",
|
||||
GreaterThanOrEq => ">=",
|
||||
And => "&&",
|
||||
Or => "||",
|
||||
Pizza => "|>",
|
||||
Assignment => "=",
|
||||
IsAliasType => ":",
|
||||
IsOpaqueType => ":=",
|
||||
Backpassing => "<-",
|
||||
};
|
||||
debug_assert_ne!(*self, Assignment);
|
||||
debug_assert_ne!(*self, IsAliasType);
|
||||
debug_assert_ne!(*self, IsOpaqueType);
|
||||
debug_assert_ne!(*self, Backpassing);
|
||||
|
||||
write!(f, "{}", as_str)
|
||||
const DISPLAY_TABLE: [&str; 20] = generate_display_table();
|
||||
|
||||
write!(f, "{}", DISPLAY_TABLE[*self as usize])
|
||||
}
|
||||
}
|
||||
|
||||
const fn generate_precedence_table() -> [u8; 20] {
|
||||
let mut table = [0u8; 20];
|
||||
let mut i = 0;
|
||||
|
||||
while i < PRECEDENCES.len() {
|
||||
table[(PRECEDENCES[i].0) as usize] = PRECEDENCES[i].1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
const fn generate_associativity_table() -> [Associativity; 20] {
|
||||
let mut table = [NonAssociative; 20];
|
||||
let mut i = 0;
|
||||
|
||||
while i < ASSOCIATIVITIES.len() {
|
||||
table[(ASSOCIATIVITIES[i].0) as usize] = ASSOCIATIVITIES[i].1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
const fn generate_display_table() -> [&'static str; 20] {
|
||||
let mut table = [""; 20];
|
||||
let mut i = 0;
|
||||
|
||||
while i < DISPLAY_STRINGS.len() {
|
||||
table[(DISPLAY_STRINGS[i].0) as usize] = DISPLAY_STRINGS[i].1;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{BinOp, ASSOCIATIVITIES, DISPLAY_STRINGS, PRECEDENCES};
|
||||
|
||||
fn index_is_binop_u8(iter: impl Iterator<Item = BinOp>, table_name: &'static str) {
|
||||
for (index, op) in iter.enumerate() {
|
||||
assert_eq!(op as usize, index, "{op} was found at index {index} in {table_name}, but it should have been at index {} instead.", op as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_precedences() {
|
||||
index_is_binop_u8(PRECEDENCES.iter().map(|(op, _)| *op), "PRECEDENCES")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_associativities() {
|
||||
index_is_binop_u8(ASSOCIATIVITIES.iter().map(|(op, _)| *op), "ASSOCIATIVITIES")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct_in_display_string() {
|
||||
index_is_binop_u8(DISPLAY_STRINGS.iter().map(|(op, _)| *op), "DISPLAY_STRINGS")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1328,6 +1328,7 @@ define_builtins! {
|
|||
55 STR_GRAPHEMES: "graphemes"
|
||||
56 STR_IS_VALID_SCALAR: "isValidScalar"
|
||||
57 STR_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity"
|
||||
58 STR_WALK_UTF8: "walkUtf8"
|
||||
}
|
||||
6 LIST: "List" => {
|
||||
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias
|
||||
|
|
|
@ -761,7 +761,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
|
||||
Call(call) => self.collect_call(interner, param_map, z, call),
|
||||
|
||||
Literal(_) | RuntimeErrorFunction(_) => {}
|
||||
Literal(_) | NullPointer | RuntimeErrorFunction(_) => {}
|
||||
|
||||
StructAtIndex { structure: x, .. } => {
|
||||
// if the structure (record/tag/array) is owned, the extracted value is
|
||||
|
|
|
@ -390,6 +390,7 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
|||
fn check_expr(&mut self, e: &Expr<'a>) -> Option<InLayout<'a>> {
|
||||
match e {
|
||||
Expr::Literal(_) => None,
|
||||
Expr::NullPointer => None,
|
||||
Expr::Call(call) => self.check_call(call),
|
||||
&Expr::Tag {
|
||||
tag_layout,
|
||||
|
|
|
@ -213,7 +213,7 @@ pub fn occurring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
|||
result.insert(*symbol);
|
||||
}
|
||||
|
||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) | NullPointer => {}
|
||||
|
||||
GetTagId {
|
||||
structure: symbol, ..
|
||||
|
@ -946,7 +946,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
|||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
EmptyArray | Literal(_) | Reset { .. } | RuntimeErrorFunction(_) => {
|
||||
EmptyArray | Literal(_) | Reset { .. } | NullPointer | RuntimeErrorFunction(_) => {
|
||||
// EmptyArray is always stack-allocated function pointers are persistent
|
||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
|
|
@ -1878,6 +1878,7 @@ pub enum Expr<'a> {
|
|||
arguments: &'a [Symbol],
|
||||
},
|
||||
Struct(&'a [Symbol]),
|
||||
NullPointer,
|
||||
|
||||
StructAtIndex {
|
||||
index: u64,
|
||||
|
@ -2016,6 +2017,7 @@ impl<'a> Expr<'a> {
|
|||
.append(alloc.space())
|
||||
.append(alloc.intersperse(it, " "))
|
||||
}
|
||||
NullPointer => alloc.text("NullPointer"),
|
||||
Reuse {
|
||||
symbol,
|
||||
tag_id,
|
||||
|
@ -4963,9 +4965,6 @@ pub fn with_hole<'a>(
|
|||
_ => arena.alloc([record_layout]),
|
||||
};
|
||||
|
||||
debug_assert_eq!(field_layouts.len(), symbols.len());
|
||||
debug_assert_eq!(fields.len(), symbols.len());
|
||||
|
||||
if symbols.len() == 1 {
|
||||
// TODO we can probably special-case this more, skippiing the generation of
|
||||
// UpdateExisting
|
||||
|
@ -7465,6 +7464,8 @@ fn substitute_in_expr<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
NullPointer => None,
|
||||
|
||||
Reuse { .. } | Reset { .. } => unreachable!("reset/reuse have not been introduced yet"),
|
||||
|
||||
Struct(args) => {
|
||||
|
|
|
@ -5,7 +5,7 @@ use bumpalo::collections::Vec;
|
|||
use bumpalo::Bump;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::{default_hasher, FnvMap, MutMap};
|
||||
use roc_collections::VecSet;
|
||||
use roc_collections::{SmallVec, VecSet};
|
||||
use roc_error_macros::{internal_error, todo_abilities};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
|
@ -52,22 +52,22 @@ roc_error_macros::assert_sizeof_default!(LambdaSet, 5 * 8);
|
|||
type LayoutResult<'a> = Result<InLayout<'a>, LayoutProblem>;
|
||||
type RawFunctionLayoutResult<'a> = Result<RawFunctionLayout<'a>, LayoutProblem>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct CacheMeta {
|
||||
/// Does this cache entry include a recursive structure? If so, what's the recursion variable
|
||||
/// of that structure?
|
||||
has_recursive_structure: OptVariable,
|
||||
recursive_structures: SmallVec<Variable, 2>,
|
||||
}
|
||||
|
||||
impl CacheMeta {
|
||||
#[inline(always)]
|
||||
fn to_criteria(self) -> CacheCriteria {
|
||||
fn into_criteria(self) -> CacheCriteria {
|
||||
let CacheMeta {
|
||||
has_recursive_structure,
|
||||
recursive_structures,
|
||||
} = self;
|
||||
CacheCriteria {
|
||||
has_naked_recursion_pointer: false,
|
||||
has_recursive_structure,
|
||||
recursive_structures,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ impl<'a> LayoutCache<'a> {
|
|||
// TODO: it's possible that after unification, roots in earlier cache layers changed...
|
||||
// how often does that happen?
|
||||
if let Some(result) = layer.0.get(&root) {
|
||||
return Some(*result);
|
||||
return Some(result.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -342,24 +342,24 @@ pub struct CacheSnapshot {
|
|||
layer: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct CacheCriteria {
|
||||
/// Whether there is a naked recursion pointer in this layout, that doesn't pass through a
|
||||
/// recursive structure.
|
||||
has_naked_recursion_pointer: bool,
|
||||
/// Whether this layout contains a recursive structure. If `Some`, contains the variable of the
|
||||
/// recursion variable of that structure.
|
||||
has_recursive_structure: OptVariable,
|
||||
/// Recursive structures this layout contains, if any.
|
||||
// Typically at most 1 recursive structure is contained, but there may be more.
|
||||
recursive_structures: SmallVec<Variable, 2>,
|
||||
}
|
||||
|
||||
const CACHEABLE: CacheCriteria = CacheCriteria {
|
||||
has_naked_recursion_pointer: false,
|
||||
has_recursive_structure: OptVariable::NONE,
|
||||
recursive_structures: SmallVec::new(),
|
||||
};
|
||||
|
||||
const NAKED_RECURSION_PTR: CacheCriteria = CacheCriteria {
|
||||
has_naked_recursion_pointer: true,
|
||||
has_recursive_structure: OptVariable::NONE,
|
||||
recursive_structures: SmallVec::new(),
|
||||
};
|
||||
|
||||
impl CacheCriteria {
|
||||
|
@ -371,25 +371,32 @@ impl CacheCriteria {
|
|||
|
||||
/// Makes `self` cacheable iff self and other are cacheable.
|
||||
#[inline(always)]
|
||||
fn and(&mut self, other: Self) {
|
||||
fn and(&mut self, other: Self, subs: &Subs) {
|
||||
self.has_naked_recursion_pointer =
|
||||
self.has_naked_recursion_pointer || other.has_naked_recursion_pointer;
|
||||
// TODO: can these ever conflict?
|
||||
self.has_recursive_structure = self
|
||||
.has_recursive_structure
|
||||
.or(other.has_recursive_structure);
|
||||
|
||||
for &other_rec in other.recursive_structures.iter() {
|
||||
if self
|
||||
.recursive_structures
|
||||
.iter()
|
||||
.any(|rec| subs.equivalent_without_compacting(*rec, other_rec))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.recursive_structures.push(other_rec);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pass_through_recursive_union(&mut self, recursion_var: Variable) {
|
||||
self.has_naked_recursion_pointer = false;
|
||||
self.has_recursive_structure = OptVariable::some(recursion_var);
|
||||
self.recursive_structures.push(recursion_var);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn cache_metadata(&self) -> CacheMeta {
|
||||
CacheMeta {
|
||||
has_recursive_structure: self.has_recursive_structure,
|
||||
recursive_structures: self.recursive_structures.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,9 +411,9 @@ impl<T> Cacheable<T> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn decompose(self, and_with: &mut CacheCriteria) -> T {
|
||||
fn decompose(self, and_with: &mut CacheCriteria, subs: &Subs) -> T {
|
||||
let Self(value, criteria) = self;
|
||||
and_with.and(criteria);
|
||||
and_with.and(criteria, subs);
|
||||
value
|
||||
}
|
||||
|
||||
|
@ -438,10 +445,10 @@ fn cacheable<T>(v: T) -> Cacheable<T> {
|
|||
/// If the layout is not an error, the cache policy is `and`ed with `total_criteria`, and the layout
|
||||
/// is passed back.
|
||||
macro_rules! cached {
|
||||
($expr:expr, $total_criteria:expr) => {
|
||||
($expr:expr, $total_criteria:expr, $subs:expr) => {
|
||||
match $expr {
|
||||
Cacheable(Ok(v), criteria) => {
|
||||
$total_criteria.and(criteria);
|
||||
$total_criteria.and(criteria, $subs);
|
||||
v
|
||||
}
|
||||
Cacheable(Err(v), criteria) => return Cacheable(Err(v), criteria),
|
||||
|
@ -583,17 +590,18 @@ impl<'a> RawFunctionLayout<'a> {
|
|||
|
||||
for index in args.into_iter() {
|
||||
let arg_var = env.subs[index];
|
||||
let layout = cached!(Layout::from_var(env, arg_var), cache_criteria);
|
||||
let layout = cached!(Layout::from_var(env, arg_var), cache_criteria, env.subs);
|
||||
fn_args.push(layout);
|
||||
}
|
||||
|
||||
let ret = cached!(Layout::from_var(env, ret_var), cache_criteria);
|
||||
let ret = cached!(Layout::from_var(env, ret_var), cache_criteria, env.subs);
|
||||
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
|
||||
let lambda_set = cached!(
|
||||
LambdaSet::from_var(env, args, closure_var, ret_var),
|
||||
cache_criteria
|
||||
cache_criteria,
|
||||
env.subs
|
||||
);
|
||||
|
||||
Cacheable(Ok(Self::Function(fn_args, lambda_set, ret)), cache_criteria)
|
||||
|
@ -617,7 +625,7 @@ impl<'a> RawFunctionLayout<'a> {
|
|||
}
|
||||
_ => {
|
||||
let mut criteria = CACHEABLE;
|
||||
let layout = cached!(layout_from_flat_type(env, flat_type), criteria);
|
||||
let layout = cached!(layout_from_flat_type(env, flat_type), criteria, env.subs);
|
||||
Cacheable(Ok(Self::ZeroArgumentThunk(layout)), criteria)
|
||||
}
|
||||
}
|
||||
|
@ -1803,11 +1811,11 @@ impl<'a> LambdaSet<'a> {
|
|||
|
||||
for index in args.into_iter() {
|
||||
let arg_var = env.subs[index];
|
||||
let layout = cached!(Layout::from_var(env, arg_var), cache_criteria);
|
||||
let layout = cached!(Layout::from_var(env, arg_var), cache_criteria, env.subs);
|
||||
fn_args.push(layout);
|
||||
}
|
||||
|
||||
let ret = cached!(Layout::from_var(env, ret_var), cache_criteria);
|
||||
let ret = cached!(Layout::from_var(env, ret_var), cache_criteria, env.subs);
|
||||
|
||||
let fn_args = env.arena.alloc(fn_args.into_bump_slice());
|
||||
|
||||
|
@ -1837,7 +1845,7 @@ impl<'a> LambdaSet<'a> {
|
|||
// We determine cacheability of the lambda set based on the runtime
|
||||
// representation, so here the criteria doesn't matter.
|
||||
let mut criteria = CACHEABLE;
|
||||
let arg = cached!(Layout::from_var(env, *var), criteria);
|
||||
let arg = cached!(Layout::from_var(env, *var), criteria, env.subs);
|
||||
arguments.push(arg);
|
||||
set_captures_have_naked_rec_ptr =
|
||||
set_captures_have_naked_rec_ptr || criteria.has_naked_recursion_pointer;
|
||||
|
@ -1899,7 +1907,7 @@ impl<'a> LambdaSet<'a> {
|
|||
set_with_variables,
|
||||
opt_recursion_var.into_variable(),
|
||||
);
|
||||
cache_criteria.and(criteria);
|
||||
cache_criteria.and(criteria, env.subs);
|
||||
|
||||
let needs_recursive_fixup = NeedsRecursionPointerFixup(
|
||||
opt_recursion_var.is_some() && set_captures_have_naked_rec_ptr,
|
||||
|
@ -2213,11 +2221,11 @@ impl<'a, 'b> Env<'a, 'b> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn can_reuse_cached(&self, var: Variable, cache_metadata: CacheMeta) -> bool {
|
||||
fn can_reuse_cached(&self, var: Variable, cache_metadata: &CacheMeta) -> bool {
|
||||
let CacheMeta {
|
||||
has_recursive_structure,
|
||||
recursive_structures,
|
||||
} = cache_metadata;
|
||||
if let Some(recursive_structure) = has_recursive_structure.into_variable() {
|
||||
for &recursive_structure in recursive_structures.iter() {
|
||||
if self.is_seen(recursive_structure) {
|
||||
// If the cached entry references a recursive structure that we're in the process
|
||||
// of visiting currently, we can't use the cached entry, and instead must
|
||||
|
@ -2255,9 +2263,9 @@ macro_rules! cached_or_impl {
|
|||
// cache HIT
|
||||
inc_stat!($self.cache.$stats, hits);
|
||||
|
||||
if $self.can_reuse_cached($var, metadata) {
|
||||
if $self.can_reuse_cached($var, &metadata) {
|
||||
// Happy path - the cached layout can be reused, return it immediately.
|
||||
return Cacheable(result, metadata.to_criteria());
|
||||
return Cacheable(result, metadata.into_criteria());
|
||||
} else {
|
||||
// Although we have a cached layout, we cannot readily reuse it at this time. We'll
|
||||
// need to recompute the layout, as done below.
|
||||
|
@ -3139,7 +3147,8 @@ fn layout_from_flat_type<'a>(
|
|||
let mut criteria = CACHEABLE;
|
||||
|
||||
let inner_var = args[0];
|
||||
let inner_layout = cached!(Layout::from_var(env, inner_var), criteria);
|
||||
let inner_layout =
|
||||
cached!(Layout::from_var(env, inner_var), criteria, env.subs);
|
||||
let boxed_layout = env.cache.put_in(Layout::Boxed(inner_layout));
|
||||
|
||||
Cacheable(Ok(boxed_layout), criteria)
|
||||
|
@ -3163,7 +3172,8 @@ fn layout_from_flat_type<'a>(
|
|||
|
||||
let lambda_set = cached!(
|
||||
LambdaSet::from_var(env, args, closure_var, ret_var),
|
||||
criteria
|
||||
criteria,
|
||||
env.subs
|
||||
);
|
||||
let lambda_set = lambda_set.full_layout;
|
||||
|
||||
|
@ -3187,7 +3197,8 @@ fn layout_from_flat_type<'a>(
|
|||
RecordField::Required(field_var)
|
||||
| RecordField::Demanded(field_var)
|
||||
| RecordField::RigidRequired(field_var) => {
|
||||
let field_layout = cached!(Layout::from_var(env, field_var), criteria);
|
||||
let field_layout =
|
||||
cached!(Layout::from_var(env, field_var), criteria, env.subs);
|
||||
sortables.push((label, field_layout));
|
||||
}
|
||||
RecordField::Optional(_) | RecordField::RigidOptional(_) => {
|
||||
|
@ -3239,7 +3250,7 @@ fn layout_from_flat_type<'a>(
|
|||
};
|
||||
|
||||
for (index, elem) in it {
|
||||
let elem_layout = cached!(Layout::from_var(env, elem), criteria);
|
||||
let elem_layout = cached!(Layout::from_var(env, elem), criteria, env.subs);
|
||||
sortables.push((index, elem_layout));
|
||||
}
|
||||
|
||||
|
@ -3652,7 +3663,7 @@ where
|
|||
|
||||
for &var in arguments {
|
||||
let Cacheable(result, criteria) = Layout::from_var(env, var);
|
||||
cache_criteria.and(criteria);
|
||||
cache_criteria.and(criteria, env.subs);
|
||||
match result {
|
||||
Ok(layout) => {
|
||||
layouts.push(layout);
|
||||
|
@ -3708,7 +3719,7 @@ where
|
|||
|
||||
for &var in arguments {
|
||||
let Cacheable(result, criteria) = Layout::from_var(env, var);
|
||||
cache_criteria.and(criteria);
|
||||
cache_criteria.and(criteria, env.subs);
|
||||
match result {
|
||||
Ok(layout) => {
|
||||
has_any_arguments = true;
|
||||
|
@ -3830,7 +3841,7 @@ where
|
|||
|
||||
for var in arguments {
|
||||
let Cacheable(result, criteria) = Layout::from_var(env, var);
|
||||
cache_criteria.and(criteria);
|
||||
cache_criteria.and(criteria, env.subs);
|
||||
match result {
|
||||
Ok(layout) => {
|
||||
layouts.push(layout);
|
||||
|
@ -3904,7 +3915,7 @@ where
|
|||
|
||||
for var in arguments {
|
||||
let Cacheable(result, criteria) = Layout::from_var(env, var);
|
||||
cache_criteria.and(criteria);
|
||||
cache_criteria.and(criteria, env.subs);
|
||||
match result {
|
||||
Ok(in_layout) => {
|
||||
has_any_arguments = true;
|
||||
|
@ -4077,7 +4088,8 @@ where
|
|||
|
||||
let mut criteria = CACHEABLE;
|
||||
|
||||
let variant = union_sorted_non_recursive_tags_help(env, tags_vec).decompose(&mut criteria);
|
||||
let variant =
|
||||
union_sorted_non_recursive_tags_help(env, tags_vec).decompose(&mut criteria, env.subs);
|
||||
|
||||
let result = match variant {
|
||||
Never => Layout::VOID,
|
||||
|
@ -4188,10 +4200,11 @@ where
|
|||
// The naked pointer will get fixed-up to loopback to the union below when we
|
||||
// intern the union.
|
||||
tag_layout.push(Layout::NAKED_RECURSIVE_PTR);
|
||||
criteria.and(NAKED_RECURSION_PTR, env.subs);
|
||||
continue;
|
||||
}
|
||||
|
||||
let payload = cached!(Layout::from_var(env, var), criteria);
|
||||
let payload = cached!(Layout::from_var(env, var), criteria, env.subs);
|
||||
tag_layout.push(payload);
|
||||
}
|
||||
|
||||
|
@ -4228,12 +4241,17 @@ where
|
|||
} else {
|
||||
UnionLayout::Recursive(tag_layouts.into_bump_slice())
|
||||
};
|
||||
criteria.pass_through_recursive_union(rec_var);
|
||||
|
||||
let union_layout = env
|
||||
.cache
|
||||
let union_layout = if criteria.has_naked_recursion_pointer {
|
||||
env.cache
|
||||
.interner
|
||||
.insert_recursive(env.arena, Layout::Union(union_layout));
|
||||
.insert_recursive(env.arena, Layout::Union(union_layout))
|
||||
} else {
|
||||
// There are no naked recursion pointers, so we can insert the layout as-is.
|
||||
env.cache.interner.insert(Layout::Union(union_layout))
|
||||
};
|
||||
|
||||
criteria.pass_through_recursive_union(rec_var);
|
||||
|
||||
Cacheable(Ok(union_layout), criteria)
|
||||
}
|
||||
|
@ -4360,7 +4378,7 @@ pub(crate) fn list_layout_from_elem<'a>(
|
|||
} else {
|
||||
// NOTE: cannot re-use Content, because it may be recursive
|
||||
// then some state is not correctly kept, we have to go through from_var
|
||||
cached!(Layout::from_var(env, element_var), criteria)
|
||||
cached!(Layout::from_var(env, element_var), criteria, env.subs)
|
||||
};
|
||||
|
||||
let list_layout = env
|
||||
|
|
|
@ -319,6 +319,7 @@ fn insert_reset<'a>(
|
|||
}
|
||||
|
||||
Literal(_)
|
||||
| NullPointer
|
||||
| Call(_)
|
||||
| Tag { .. }
|
||||
| Struct(_)
|
||||
|
@ -839,6 +840,7 @@ fn has_live_var<'a>(jp_live_vars: &JPLiveVarMap, stmt: &'a Stmt<'a>, needle: Sym
|
|||
fn has_live_var_expr<'a>(expr: &'a Expr<'a>, needle: Symbol) -> bool {
|
||||
match expr {
|
||||
Expr::Literal(_) => false,
|
||||
Expr::NullPointer => false,
|
||||
Expr::Call(call) => has_live_var_call(call, needle),
|
||||
Expr::Array { elems: fields, .. } => {
|
||||
for element in fields.iter() {
|
||||
|
|
|
@ -199,6 +199,10 @@ impl LineColumnRegion {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn includes(&self, lc: LineColumn) -> bool {
|
||||
self.contains(&Self::from_pos(lc))
|
||||
}
|
||||
|
||||
pub const fn from_pos(pos: LineColumn) -> Self {
|
||||
Self {
|
||||
start: pos,
|
||||
|
|
|
@ -35,6 +35,7 @@ roc_problem = { path = "../problem" }
|
|||
roc_reporting = { path = "../../reporting" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
test_solve_helpers = { path = "../test_solve_helpers" }
|
||||
|
||||
bumpalo.workspace = true
|
||||
indoc.workspace = true
|
||||
|
@ -43,3 +44,4 @@ lazy_static.workspace = true
|
|||
pretty_assertions.workspace = true
|
||||
regex.workspace = true
|
||||
tempfile.workspace = true
|
||||
libtest-mimic.workspace = true
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
extern crate bumpalo;
|
||||
|
||||
/// Used in the with_larger_debug_stack() function, for tests that otherwise
|
||||
/// run out of stack space in debug builds (but don't in --release builds)
|
||||
#[allow(dead_code)]
|
||||
const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
|
||||
|
||||
/// Without this, some tests pass in `cargo test --release` but fail without
|
||||
/// the --release flag because they run out of stack space. This increases
|
||||
/// stack size for debug builds only, while leaving the stack space at the default
|
||||
/// amount for release builds.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn with_larger_debug_stack<F>(run_test: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
F: Send,
|
||||
F: 'static,
|
||||
{
|
||||
std::thread::Builder::new()
|
||||
.stack_size(EXPANDED_STACK_SIZE)
|
||||
.spawn(run_test)
|
||||
.expect("Error while spawning expanded dev stack size thread")
|
||||
.join()
|
||||
.expect("Error while joining expanded dev stack size thread")
|
||||
}
|
||||
|
||||
/// In --release builds, don't increase the stack size. Run the test normally.
|
||||
/// This way, we find out if any of our tests are blowing the stack even after
|
||||
/// optimizations in release builds.
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[inline(always)]
|
||||
pub fn with_larger_debug_stack<F>(run_test: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
F: Send,
|
||||
F: 'static,
|
||||
{
|
||||
run_test()
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -4386,3 +4386,77 @@ fn recursive_lambda_set_resolved_only_upon_specialization() {
|
|||
u64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn layout_cache_structure_with_multiple_recursive_structures() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Chain : [
|
||||
End,
|
||||
Link Chain,
|
||||
]
|
||||
|
||||
LinkedList : [Nil, Cons { first : Chain, rest : LinkedList }]
|
||||
|
||||
main =
|
||||
base : LinkedList
|
||||
base = Nil
|
||||
|
||||
walker : LinkedList, Chain -> LinkedList
|
||||
walker = \rest, first -> Cons { first, rest }
|
||||
|
||||
list : List Chain
|
||||
list = []
|
||||
|
||||
r = List.walk list base walker
|
||||
|
||||
if r == base then 11u8 else 22u8
|
||||
"#
|
||||
),
|
||||
11,
|
||||
u8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn reset_recursive_type_wraps_in_named_type() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r###"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main : Str
|
||||
main =
|
||||
newList = mapLinkedList (Cons 1 (Cons 2 (Cons 3 Nil))) (\x -> x + 1)
|
||||
printLinkedList newList Num.toStr
|
||||
|
||||
LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
|
||||
mapLinkedList : LinkedList a, (a -> b) -> LinkedList b
|
||||
mapLinkedList = \linkedList, f -> when linkedList is
|
||||
Nil -> Nil
|
||||
Cons x xs ->
|
||||
s = if Bool.true then "true" else "false"
|
||||
expect s == "true"
|
||||
|
||||
Cons (f x) (mapLinkedList xs f)
|
||||
|
||||
printLinkedList : LinkedList a, (a -> Str) -> Str
|
||||
printLinkedList = \linkedList, f ->
|
||||
when linkedList is
|
||||
Nil -> "Nil"
|
||||
Cons x xs ->
|
||||
strX = f x
|
||||
strXs = printLinkedList xs f
|
||||
"Cons \(strX) (\(strXs))"
|
||||
"###
|
||||
),
|
||||
RocStr::from("Cons 2 (Cons 3 (Cons 4 (Nil)))"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1822,6 +1822,33 @@ fn str_split_overlapping_substring_2() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
fn str_walk_utf8() {
|
||||
#[cfg(not(feature = "gen-llvm-wasm"))]
|
||||
assert_evals_to!(
|
||||
// Reverse the bytes
|
||||
indoc!(
|
||||
r#"
|
||||
Str.walkUtf8 "abcd" [] (\list, byte -> List.prepend list byte)
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[b'd', b'c', b'b', b'a']),
|
||||
RocList<u8>
|
||||
);
|
||||
|
||||
#[cfg(feature = "gen-llvm-wasm")]
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Str.walkUtf8WithIndex "abcd" [] (\list, byte, index -> List.append list (Pair index byte))
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]),
|
||||
RocList<(u32, char)>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
|
||||
fn str_walk_utf8_with_index() {
|
||||
|
|
|
@ -253,6 +253,7 @@ fn create_llvm_module<'a>(
|
|||
let (main_fn_name, main_fn) = match config.mode {
|
||||
LlvmBackendMode::Binary => unreachable!(),
|
||||
LlvmBackendMode::BinaryDev => unreachable!(),
|
||||
LlvmBackendMode::BinaryGlue => unreachable!(),
|
||||
LlvmBackendMode::CliTest => unreachable!(),
|
||||
LlvmBackendMode::WasmGenTest => roc_gen_llvm::llvm::build::build_wasm_test_wrapper(
|
||||
&env,
|
||||
|
|
|
@ -47,8 +47,8 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
ret Num.276;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.268 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.268;
|
||||
let Str.300 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.300;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
ret Test.5;
|
||||
|
|
|
@ -319,31 +319,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.303;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.283 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.283;
|
||||
let Str.315 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.315;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : Str = "bar";
|
||||
|
|
|
@ -196,31 +196,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.284;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.281 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.281;
|
||||
let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.313;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
|
|
@ -204,31 +204,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.284;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.281 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.281;
|
||||
let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.313;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.11 : Str = "foo";
|
||||
|
|
|
@ -57,31 +57,31 @@ procedure Num.127 (#Attr.2):
|
|||
ret Num.276;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.280 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.280;
|
||||
let Str.312 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.312;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.9 : Str = "abc";
|
||||
|
|
|
@ -205,31 +205,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.286;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.281 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.281;
|
||||
let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.313;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : Str = "foo";
|
||||
|
|
|
@ -211,31 +211,31 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.286;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.281 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.281;
|
||||
let Str.313 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.313;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.13 : Str = "foo";
|
||||
|
|
|
@ -45,27 +45,27 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
let Num.275 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
|
||||
procedure Str.27 (Str.97):
|
||||
let Str.266 : [C Int1, C I64] = CallByName Str.70 Str.97;
|
||||
ret Str.266;
|
||||
procedure Str.27 (Str.99):
|
||||
let Str.298 : [C Int1, C I64] = CallByName Str.72 Str.99;
|
||||
ret Str.298;
|
||||
|
||||
procedure Str.47 (#Attr.2):
|
||||
let Str.274 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
ret Str.274;
|
||||
let Str.306 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
ret Str.306;
|
||||
|
||||
procedure Str.70 (Str.232):
|
||||
let Str.233 : {I64, U8} = CallByName Str.47 Str.232;
|
||||
let Str.272 : U8 = StructAtIndex 1 Str.233;
|
||||
let Str.273 : U8 = 0i64;
|
||||
let Str.269 : Int1 = CallByName Bool.11 Str.272 Str.273;
|
||||
if Str.269 then
|
||||
let Str.271 : I64 = StructAtIndex 0 Str.233;
|
||||
let Str.270 : [C Int1, C I64] = TagId(1) Str.271;
|
||||
ret Str.270;
|
||||
procedure Str.72 (Str.244):
|
||||
let Str.245 : {I64, U8} = CallByName Str.47 Str.244;
|
||||
let Str.304 : U8 = StructAtIndex 1 Str.245;
|
||||
let Str.305 : U8 = 0i64;
|
||||
let Str.301 : Int1 = CallByName Bool.11 Str.304 Str.305;
|
||||
if Str.301 then
|
||||
let Str.303 : I64 = StructAtIndex 0 Str.245;
|
||||
let Str.302 : [C Int1, C I64] = TagId(1) Str.303;
|
||||
ret Str.302;
|
||||
else
|
||||
let Str.268 : Int1 = false;
|
||||
let Str.267 : [C Int1, C I64] = TagId(0) Str.268;
|
||||
ret Str.267;
|
||||
let Str.300 : Int1 = false;
|
||||
let Str.299 : [C Int1, C I64] = TagId(0) Str.300;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : Int1 = CallByName Bool.2;
|
||||
|
|
|
@ -759,27 +759,27 @@ procedure Num.77 (#Attr.2, #Attr.3):
|
|||
ret Num.321;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.275 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.275;
|
||||
let Str.307 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.273 : U64 = 0i64;
|
||||
let Str.274 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.273 Str.274;
|
||||
let Str.270 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.270 then
|
||||
let Str.272 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.272;
|
||||
dec Str.78;
|
||||
let Str.271 : [C {U64, U8}, C Str] = TagId(1) Str.272;
|
||||
ret Str.271;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.305 : U64 = 0i64;
|
||||
let Str.306 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.305 Str.306;
|
||||
let Str.302 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.302 then
|
||||
let Str.304 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.304;
|
||||
dec Str.80;
|
||||
let Str.303 : [C {U64, U8}, C Str] = TagId(1) Str.304;
|
||||
ret Str.303;
|
||||
else
|
||||
let Str.268 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.269 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.267 : {U64, U8} = Struct {Str.269, Str.268};
|
||||
let Str.266 : [C {U64, U8}, C Str] = TagId(0) Str.267;
|
||||
ret Str.266;
|
||||
let Str.300 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.301 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.299 : {U64, U8} = Struct {Str.301, Str.300};
|
||||
let Str.298 : [C {U64, U8}, C Str] = TagId(0) Str.299;
|
||||
ret Str.298;
|
||||
|
||||
procedure Test.3 ():
|
||||
let Test.0 : List U8 = Array [82i64, 111i64, 99i64];
|
||||
|
|
13
crates/compiler/test_mono/generated/issue_4759.txt
Normal file
13
crates/compiler/test_mono/generated/issue_4759.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
procedure Test.1 (Test.2):
|
||||
dec Test.2;
|
||||
let Test.7 : Str = "ux";
|
||||
let Test.8 : Str = "uy";
|
||||
let Test.6 : {Str, Str} = Struct {Test.7, Test.8};
|
||||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.10 : Str = "x";
|
||||
let Test.11 : Str = "y";
|
||||
let Test.9 : {Str, Str} = Struct {Test.10, Test.11};
|
||||
let Test.3 : {Str, Str} = CallByName Test.1 Test.9;
|
||||
ret Test.3;
|
|
@ -729,53 +729,53 @@ procedure Num.77 (#Attr.2, #Attr.3):
|
|||
ret Num.321;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.275 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.275;
|
||||
let Str.307 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.307;
|
||||
|
||||
procedure Str.27 (Str.97):
|
||||
let Str.266 : [C {}, C I64] = CallByName Str.70 Str.97;
|
||||
ret Str.266;
|
||||
procedure Str.27 (Str.99):
|
||||
let Str.298 : [C {}, C I64] = CallByName Str.72 Str.99;
|
||||
ret Str.298;
|
||||
|
||||
procedure Str.47 (#Attr.2):
|
||||
let Str.274 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
ret Str.274;
|
||||
let Str.306 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||
ret Str.306;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.289 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.289;
|
||||
let Str.321 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.321;
|
||||
|
||||
procedure Str.70 (Str.232):
|
||||
let Str.233 : {I64, U8} = CallByName Str.47 Str.232;
|
||||
let Str.272 : U8 = StructAtIndex 1 Str.233;
|
||||
let Str.273 : U8 = 0i64;
|
||||
let Str.269 : Int1 = CallByName Bool.11 Str.272 Str.273;
|
||||
if Str.269 then
|
||||
let Str.271 : I64 = StructAtIndex 0 Str.233;
|
||||
let Str.270 : [C {}, C I64] = TagId(1) Str.271;
|
||||
ret Str.270;
|
||||
procedure Str.72 (Str.244):
|
||||
let Str.245 : {I64, U8} = CallByName Str.47 Str.244;
|
||||
let Str.304 : U8 = StructAtIndex 1 Str.245;
|
||||
let Str.305 : U8 = 0i64;
|
||||
let Str.301 : Int1 = CallByName Bool.11 Str.304 Str.305;
|
||||
if Str.301 then
|
||||
let Str.303 : I64 = StructAtIndex 0 Str.245;
|
||||
let Str.302 : [C {}, C I64] = TagId(1) Str.303;
|
||||
ret Str.302;
|
||||
else
|
||||
let Str.268 : {} = Struct {};
|
||||
let Str.267 : [C {}, C I64] = TagId(0) Str.268;
|
||||
ret Str.267;
|
||||
let Str.300 : {} = Struct {};
|
||||
let Str.299 : [C {}, C I64] = TagId(0) Str.300;
|
||||
ret Str.299;
|
||||
|
||||
procedure Str.9 (Str.77):
|
||||
let Str.287 : U64 = 0i64;
|
||||
let Str.288 : U64 = CallByName List.6 Str.77;
|
||||
let Str.78 : {U64, Str, Int1, U8} = CallByName Str.48 Str.77 Str.287 Str.288;
|
||||
let Str.284 : Int1 = StructAtIndex 2 Str.78;
|
||||
if Str.284 then
|
||||
let Str.286 : Str = StructAtIndex 1 Str.78;
|
||||
inc Str.286;
|
||||
dec Str.78;
|
||||
let Str.285 : [C {U64, U8}, C Str] = TagId(1) Str.286;
|
||||
ret Str.285;
|
||||
procedure Str.9 (Str.79):
|
||||
let Str.319 : U64 = 0i64;
|
||||
let Str.320 : U64 = CallByName List.6 Str.79;
|
||||
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.48 Str.79 Str.319 Str.320;
|
||||
let Str.316 : Int1 = StructAtIndex 2 Str.80;
|
||||
if Str.316 then
|
||||
let Str.318 : Str = StructAtIndex 1 Str.80;
|
||||
inc Str.318;
|
||||
dec Str.80;
|
||||
let Str.317 : [C {U64, U8}, C Str] = TagId(1) Str.318;
|
||||
ret Str.317;
|
||||
else
|
||||
let Str.282 : U8 = StructAtIndex 3 Str.78;
|
||||
let Str.283 : U64 = StructAtIndex 0 Str.78;
|
||||
dec Str.78;
|
||||
let Str.281 : {U64, U8} = Struct {Str.283, Str.282};
|
||||
let Str.280 : [C {U64, U8}, C Str] = TagId(0) Str.281;
|
||||
ret Str.280;
|
||||
let Str.314 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.315 : U64 = StructAtIndex 0 Str.80;
|
||||
dec Str.80;
|
||||
let Str.313 : {U64, U8} = Struct {Str.315, Str.314};
|
||||
let Str.312 : [C {U64, U8}, C Str] = TagId(0) Str.313;
|
||||
ret Str.312;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.37 : Str = "-1234";
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
procedure List.139 (List.140, List.141, List.138):
|
||||
let List.513 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName Test.7 List.140 List.141;
|
||||
ret List.513;
|
||||
|
||||
procedure List.18 (List.136, List.137, List.138):
|
||||
let List.494 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.92 List.136 List.137 List.138;
|
||||
ret List.494;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.511 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.511;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.510 : [<rnu>C *self, <null>] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.510;
|
||||
|
||||
procedure List.80 (List.517, List.518, List.519, List.520, List.521):
|
||||
joinpoint List.500 List.433 List.434 List.435 List.436 List.437:
|
||||
let List.502 : Int1 = CallByName Num.22 List.436 List.437;
|
||||
if List.502 then
|
||||
let List.509 : [<rnu>C *self, <null>] = CallByName List.66 List.433 List.436;
|
||||
let List.503 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.139 List.434 List.509 List.435;
|
||||
let List.506 : U64 = 1i64;
|
||||
let List.505 : U64 = CallByName Num.19 List.436 List.506;
|
||||
jump List.500 List.433 List.503 List.435 List.505 List.437;
|
||||
else
|
||||
ret List.434;
|
||||
in
|
||||
jump List.500 List.517 List.518 List.519 List.520 List.521;
|
||||
|
||||
procedure List.92 (List.430, List.431, List.432):
|
||||
let List.498 : U64 = 0i64;
|
||||
let List.499 : U64 = CallByName List.6 List.430;
|
||||
let List.497 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.80 List.430 List.431 List.432 List.498 List.499;
|
||||
ret List.497;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.275 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.275;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.276 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.276;
|
||||
|
||||
procedure Test.7 (Test.11, Test.12):
|
||||
let Test.17 : {[<rnu>C *self, <null>], [<rnu><null>, C {[<rnu>C *self, <null>], *self}]} = Struct {Test.12, Test.11};
|
||||
let Test.16 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = TagId(0) Test.17;
|
||||
ret Test.16;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.6 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = TagId(1) ;
|
||||
let Test.8 : List [<rnu>C *self, <null>] = Array [];
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.9 : [<rnu><null>, C {[<rnu>C *self, <null>], *self}] = CallByName List.18 Test.8 Test.6 Test.15;
|
||||
dec Test.8;
|
||||
ret Test.9;
|
|
@ -27,12 +27,12 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
ret Num.275;
|
||||
|
||||
procedure Str.16 (#Attr.2, #Attr.3):
|
||||
let Str.266 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
||||
ret Str.266;
|
||||
let Str.298 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
||||
ret Str.298;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.267 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.267;
|
||||
let Str.299 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||
|
|
|
@ -28,8 +28,8 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||
ret Num.275;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.267 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.267;
|
||||
let Str.299 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.1 ():
|
||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.267 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.267;
|
||||
let Str.299 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.2 (Test.4):
|
||||
let Test.16 : U8 = GetTagId Test.4;
|
||||
|
|
|
@ -3,8 +3,8 @@ procedure Bool.11 (#Attr.2, #Attr.3):
|
|||
ret Bool.23;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.267 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.267;
|
||||
let Str.299 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.2 (Test.7):
|
||||
let Test.24 : Str = ".trace(\"";
|
||||
|
|
|
@ -3,8 +3,8 @@ procedure Num.20 (#Attr.2, #Attr.3):
|
|||
ret Num.275;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.268 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.268;
|
||||
let Str.300 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.300;
|
||||
|
||||
procedure Test.11 (Test.29, #Attr.12):
|
||||
let Test.10 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
|
|
|
@ -190,8 +190,8 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.286;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.267 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.267;
|
||||
let Str.299 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.299;
|
||||
|
||||
procedure Test.2 (Test.10):
|
||||
let Test.15 : {Str, Str} = CallByName Encode.23 Test.10;
|
||||
|
|
|
@ -330,8 +330,8 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Num.305;
|
||||
|
||||
procedure Str.12 (#Attr.2):
|
||||
let Str.268 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.268;
|
||||
let Str.300 : List U8 = lowlevel StrToUtf8 #Attr.2;
|
||||
ret Str.300;
|
||||
|
||||
procedure Test.2 (Test.11):
|
||||
let Test.18 : {{}, {}} = CallByName Encode.23 Test.11;
|
||||
|
|
|
@ -2845,3 +2845,47 @@ fn compose_recursive_lambda_set_productive_nullable_wrapped() {
|
|||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn issue_4759() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
update { a : { x : "x", y: "y" } }
|
||||
|
||||
update = \state -> { state & a : { x : "ux", y: "uy" } }
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn layout_cache_structure_with_multiple_recursive_structures() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Chain : [
|
||||
End,
|
||||
Link Chain,
|
||||
]
|
||||
|
||||
LinkedList : [Nil, Cons { first : Chain, rest : LinkedList }]
|
||||
|
||||
main =
|
||||
base : LinkedList
|
||||
base = Nil
|
||||
|
||||
walker : LinkedList, Chain -> LinkedList
|
||||
walker = \rest, first -> Cons { first, rest }
|
||||
|
||||
list : List Chain
|
||||
list = []
|
||||
|
||||
r = List.walk list base walker
|
||||
|
||||
r
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
|
31
crates/compiler/test_solve_helpers/Cargo.toml
Normal file
31
crates/compiler/test_solve_helpers/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "test_solve_helpers"
|
||||
description = "Utilities for testing the solver. This should eventually made into the roc_solve crate, but currently, solve tests have multiple harnesses."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
roc_can = { path = "../can" }
|
||||
roc_derive = { path = "../derive" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_packaging = { path = "../../packaging" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_late_solve = { path = "../late_solve" }
|
||||
roc_solve_problem = { path = "../solve_problem" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_types = { path = "../types" }
|
||||
|
||||
bumpalo.workspace = true
|
||||
indoc.workspace = true
|
||||
insta.workspace = true
|
||||
lazy_static.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
regex.workspace = true
|
||||
tempfile.workspace = true
|
599
crates/compiler/test_solve_helpers/src/lib.rs
Normal file
599
crates/compiler/test_solve_helpers/src/lib.rs
Normal file
|
@ -0,0 +1,599 @@
|
|||
use std::{error::Error, io, path::PathBuf};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use roc_can::{
|
||||
abilities::AbilitiesStore,
|
||||
expr::Declarations,
|
||||
module::ExposedByModule,
|
||||
traverse::{find_declaration, find_symbol_at, find_type_at, FoundSymbol},
|
||||
};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_late_solve::AbilitiesView;
|
||||
use roc_load::LoadedModule;
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_problem::can::Problem;
|
||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Region};
|
||||
use roc_reporting::report::{can_problem, type_problem, RocDocAllocator};
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::{
|
||||
pretty_print::{name_and_print_var, DebugPrint},
|
||||
subs::{Subs, Variable},
|
||||
};
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from(indoc::indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports []
|
||||
provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
"#
|
||||
));
|
||||
|
||||
for line in src.lines() {
|
||||
// indent the body!
|
||||
buffer.push_str(" ");
|
||||
buffer.push_str(line);
|
||||
buffer.push('\n');
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
pub fn run_load_and_infer(
|
||||
src: &str,
|
||||
no_promote: bool,
|
||||
) -> Result<(LoadedModule, String), std::io::Error> {
|
||||
use tempfile::tempdir;
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
||||
let module_src;
|
||||
let temp;
|
||||
if src.starts_with("app") || no_promote {
|
||||
// this is already a module
|
||||
module_src = src;
|
||||
} else {
|
||||
// this is an expression, promote it to a module
|
||||
temp = promote_expr_to_module(src);
|
||||
module_src = &temp;
|
||||
}
|
||||
|
||||
let loaded = {
|
||||
let dir = tempdir()?;
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let result = roc_load::load_and_typecheck_str(
|
||||
arena,
|
||||
file_path,
|
||||
module_src,
|
||||
dir.path().to_path_buf(),
|
||||
roc_target::TargetInfo::default_x86_64(),
|
||||
roc_reporting::report::RenderTarget::Generic,
|
||||
RocCacheDir::Disallowed,
|
||||
roc_reporting::report::DEFAULT_PALETTE,
|
||||
);
|
||||
|
||||
dir.close()?;
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
let loaded = loaded.expect("failed to load module");
|
||||
Ok((loaded, module_src.to_string()))
|
||||
}
|
||||
|
||||
pub fn format_problems(
|
||||
src: &str,
|
||||
home: ModuleId,
|
||||
interns: &Interns,
|
||||
can_problems: Vec<Problem>,
|
||||
type_problems: Vec<TypeError>,
|
||||
) -> (String, String) {
|
||||
let filename = PathBuf::from("test.roc");
|
||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||
let lines = LineInfo::new(src);
|
||||
let alloc = RocDocAllocator::new(&src_lines, home, interns);
|
||||
|
||||
let mut can_reports = vec![];
|
||||
let mut type_reports = vec![];
|
||||
|
||||
for problem in can_problems {
|
||||
let report = can_problem(&alloc, &lines, filename.clone(), problem.clone());
|
||||
can_reports.push(report.pretty(&alloc));
|
||||
}
|
||||
|
||||
for problem in type_problems {
|
||||
if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) {
|
||||
type_reports.push(report.pretty(&alloc));
|
||||
}
|
||||
}
|
||||
|
||||
let mut can_reports_buf = String::new();
|
||||
let mut type_reports_buf = String::new();
|
||||
use roc_reporting::report::CiWrite;
|
||||
alloc
|
||||
.stack(can_reports)
|
||||
.1
|
||||
.render_raw(70, &mut CiWrite::new(&mut can_reports_buf))
|
||||
.unwrap();
|
||||
alloc
|
||||
.stack(type_reports)
|
||||
.1
|
||||
.render_raw(70, &mut CiWrite::new(&mut type_reports_buf))
|
||||
.unwrap();
|
||||
|
||||
(can_reports_buf, type_reports_buf)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Queries of the form
|
||||
///
|
||||
/// ```text
|
||||
/// ^^^{(directive),*}?
|
||||
///
|
||||
/// directive :=
|
||||
/// -\d+ # shift the query left by N columns
|
||||
/// inst # instantiate the given generic instance
|
||||
/// ```
|
||||
static ref RE_TYPE_QUERY: Regex =
|
||||
Regex::new(r#"(?P<where>\^+)(?:\{(?P<directives>.*?)\})?"#).unwrap();
|
||||
|
||||
static ref RE_DIRECTIVE : Regex =
|
||||
Regex::new(r#"(?:-(?P<sub>\d+))|(?P<inst>inst)"#).unwrap();
|
||||
}
|
||||
|
||||
/// Markers of nested query lines, that should be skipped.
|
||||
pub const MUTLILINE_MARKER: &str = "│";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeQuery {
|
||||
query_region: Region,
|
||||
/// If true, the query is under a function call, which should be instantiated with the present
|
||||
/// value and have its nested queries printed.
|
||||
instantiate: bool,
|
||||
source: String,
|
||||
comment_column: u32,
|
||||
source_line_column: LineColumn,
|
||||
}
|
||||
|
||||
/// Parse inference queries in a Roc program.
|
||||
/// See [RE_TYPE_QUERY].
|
||||
fn parse_queries(src: &str, line_info: &LineInfo) -> Vec<TypeQuery> {
|
||||
let mut queries = vec![];
|
||||
let mut consecutive_query_lines = 0;
|
||||
for (i, line) in src.lines().enumerate() {
|
||||
// If this is a query line, it should start with a comment somewhere before the query
|
||||
// lines.
|
||||
let comment_column = match line.find('#') {
|
||||
Some(i) => i as _,
|
||||
None => {
|
||||
consecutive_query_lines = 0;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut queries_on_line = RE_TYPE_QUERY.captures_iter(line).into_iter().peekable();
|
||||
|
||||
if queries_on_line.peek().is_none() || line.contains(MUTLILINE_MARKER) {
|
||||
consecutive_query_lines = 0;
|
||||
continue;
|
||||
} else {
|
||||
consecutive_query_lines += 1;
|
||||
}
|
||||
|
||||
for capture in queries_on_line {
|
||||
let source = capture
|
||||
.get(0)
|
||||
.expect("full capture must always exist")
|
||||
.as_str()
|
||||
.to_string();
|
||||
|
||||
let wher = capture.name("where").unwrap();
|
||||
|
||||
let mut subtract_col = 0u32;
|
||||
let mut instantiate = false;
|
||||
|
||||
if let Some(directives) = capture.name("directives") {
|
||||
for directive in directives.as_str().split(',') {
|
||||
let directive = RE_DIRECTIVE
|
||||
.captures(directive)
|
||||
.unwrap_or_else(|| panic!("directive {directive} must match RE_DIRECTIVE"));
|
||||
if let Some(sub) = directive.name("sub") {
|
||||
subtract_col += sub.as_str().parse::<u32>().expect("must be a number");
|
||||
}
|
||||
if directive.name("inst").is_some() {
|
||||
instantiate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (source_start, source_end) = (wher.start() as u32, wher.end() as u32);
|
||||
let (query_start, query_end) = (source_start - subtract_col, source_end - subtract_col);
|
||||
|
||||
let source_line_column = LineColumn {
|
||||
line: i as u32,
|
||||
column: source_start,
|
||||
};
|
||||
|
||||
let query_region = {
|
||||
let last_line = i as u32 - consecutive_query_lines;
|
||||
let query_start_lc = LineColumn {
|
||||
line: last_line,
|
||||
column: query_start,
|
||||
};
|
||||
let query_end_lc = LineColumn {
|
||||
line: last_line,
|
||||
column: query_end,
|
||||
};
|
||||
let query_lc_region = LineColumnRegion::new(query_start_lc, query_end_lc);
|
||||
line_info.convert_line_column_region(query_lc_region)
|
||||
};
|
||||
|
||||
queries.push(TypeQuery {
|
||||
query_region,
|
||||
source,
|
||||
comment_column,
|
||||
source_line_column,
|
||||
instantiate,
|
||||
});
|
||||
}
|
||||
}
|
||||
queries
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct InferOptions {
|
||||
pub print_can_decls: bool,
|
||||
pub print_only_under_alias: bool,
|
||||
pub allow_errors: bool,
|
||||
pub no_promote: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Elaboration {
|
||||
Specialization {
|
||||
specialized_name: String,
|
||||
typ: String,
|
||||
},
|
||||
Source {
|
||||
source: String,
|
||||
typ: String,
|
||||
},
|
||||
Instantiation {
|
||||
typ: String,
|
||||
source: String,
|
||||
offset_line: u32,
|
||||
queries_in_instantiation: InferredQueries,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InferredQuery {
|
||||
pub elaboration: Elaboration,
|
||||
/// Where the comment before the query string was written in the source.
|
||||
pub comment_column: u32,
|
||||
/// Where the query string "^^^" itself was written in the source.
|
||||
pub source_line_column: LineColumn,
|
||||
/// The content of the query string.
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
pub struct Program {
|
||||
home: ModuleId,
|
||||
interns: Interns,
|
||||
declarations: Declarations,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn write_can_decls(&self, writer: &mut impl io::Write) -> io::Result<()> {
|
||||
use roc_can::debug::{pretty_write_declarations, PPCtx};
|
||||
let ctx = PPCtx {
|
||||
home: self.home,
|
||||
interns: &self.interns,
|
||||
print_lambda_names: true,
|
||||
};
|
||||
pretty_write_declarations(writer, &ctx, &self.declarations)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InferredQueries(Vec<InferredQuery>);
|
||||
|
||||
impl InferredQueries {
|
||||
/// Returns all inferred queries, sorted by
|
||||
/// - increasing source line
|
||||
/// - on ties, decreasing source column
|
||||
pub fn into_sorted(self) -> Vec<InferredQuery> {
|
||||
let mut queries = self.0;
|
||||
queries.sort_by(|lc1, lc2| {
|
||||
let line1 = lc1.source_line_column.line;
|
||||
let line2 = lc2.source_line_column.line;
|
||||
let col1 = lc1.source_line_column.column;
|
||||
let col2 = lc2.source_line_column.column;
|
||||
line1.cmp(&line2).then(col2.cmp(&col1))
|
||||
});
|
||||
queries
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InferredProgram {
|
||||
program: Program,
|
||||
inferred_queries: Vec<InferredQuery>,
|
||||
}
|
||||
|
||||
impl InferredProgram {
|
||||
/// Decomposes the program and inferred queries.
|
||||
pub fn decompose(self) -> (InferredQueries, Program) {
|
||||
let Self {
|
||||
program,
|
||||
inferred_queries,
|
||||
} = self;
|
||||
|
||||
(InferredQueries(inferred_queries), program)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries(src: &str, options: InferOptions) -> Result<InferredProgram, Box<dyn Error>> {
|
||||
let (
|
||||
LoadedModule {
|
||||
module_id: home,
|
||||
mut can_problems,
|
||||
mut type_problems,
|
||||
mut declarations_by_id,
|
||||
mut solved,
|
||||
interns,
|
||||
abilities_store,
|
||||
..
|
||||
},
|
||||
src,
|
||||
) = run_load_and_infer(src, options.no_promote)?;
|
||||
|
||||
let declarations = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
||||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
||||
if !options.allow_errors {
|
||||
let (can_problems, type_problems) =
|
||||
format_problems(&src, home, &interns, can_problems, type_problems);
|
||||
|
||||
if !can_problems.is_empty() {
|
||||
return Err(format!("Canonicalization problems: {can_problems}",).into());
|
||||
}
|
||||
if !type_problems.is_empty() {
|
||||
return Err(format!("Type problems: {type_problems}",).into());
|
||||
}
|
||||
}
|
||||
|
||||
let line_info = LineInfo::new(&src);
|
||||
let queries = parse_queries(&src, &line_info);
|
||||
if queries.is_empty() {
|
||||
return Err("No queries provided!".into());
|
||||
}
|
||||
|
||||
let mut inferred_queries = Vec::with_capacity(queries.len());
|
||||
let exposed_by_module = ExposedByModule::default();
|
||||
let arena = Bump::new();
|
||||
|
||||
let mut ctx = QueryCtx {
|
||||
all_queries: &queries,
|
||||
arena: &arena,
|
||||
source: &src,
|
||||
declarations: &declarations,
|
||||
subs,
|
||||
abilities_store: &abilities_store,
|
||||
home,
|
||||
interns: &interns,
|
||||
line_info,
|
||||
derived_module: Default::default(),
|
||||
exposed_by_module,
|
||||
options,
|
||||
};
|
||||
|
||||
for query in queries.iter() {
|
||||
let answer = ctx.answer(query)?;
|
||||
inferred_queries.push(answer);
|
||||
}
|
||||
|
||||
Ok(InferredProgram {
|
||||
program: Program {
|
||||
home,
|
||||
interns,
|
||||
declarations,
|
||||
},
|
||||
inferred_queries,
|
||||
})
|
||||
}
|
||||
|
||||
struct QueryCtx<'a> {
|
||||
all_queries: &'a [TypeQuery],
|
||||
arena: &'a Bump,
|
||||
source: &'a str,
|
||||
declarations: &'a Declarations,
|
||||
subs: &'a mut Subs,
|
||||
abilities_store: &'a AbilitiesStore,
|
||||
home: ModuleId,
|
||||
interns: &'a Interns,
|
||||
line_info: LineInfo,
|
||||
derived_module: SharedDerivedModule,
|
||||
exposed_by_module: ExposedByModule,
|
||||
options: InferOptions,
|
||||
}
|
||||
|
||||
impl<'a> QueryCtx<'a> {
|
||||
fn answer(&mut self, query: &TypeQuery) -> Result<InferredQuery, Box<dyn Error>> {
|
||||
let TypeQuery {
|
||||
query_region,
|
||||
source,
|
||||
comment_column,
|
||||
source_line_column,
|
||||
instantiate,
|
||||
} = query;
|
||||
|
||||
let start = query_region.start().offset;
|
||||
let end = query_region.end().offset;
|
||||
let text = &self.source[start as usize..end as usize];
|
||||
let var = find_type_at(*query_region, self.declarations).ok_or_else(|| {
|
||||
format!(
|
||||
"No type for {:?} ({:?})!",
|
||||
&text,
|
||||
self.line_info.convert_region(*query_region)
|
||||
)
|
||||
})?;
|
||||
|
||||
let snapshot = self.subs.snapshot();
|
||||
|
||||
let type_string = name_and_print_var(
|
||||
var,
|
||||
self.subs,
|
||||
self.home,
|
||||
self.interns,
|
||||
DebugPrint {
|
||||
print_lambda_sets: true,
|
||||
print_only_under_alias: self.options.print_only_under_alias,
|
||||
ignore_polarity: true,
|
||||
print_weakened_vars: true,
|
||||
},
|
||||
);
|
||||
|
||||
let elaboration = if *instantiate {
|
||||
self.infer_instantiated(var, type_string, *query_region, text)?
|
||||
} else {
|
||||
self.infer_direct(type_string, *query_region, text)
|
||||
};
|
||||
self.subs.rollback_to(snapshot);
|
||||
|
||||
Ok(InferredQuery {
|
||||
elaboration,
|
||||
comment_column: *comment_column,
|
||||
source_line_column: *source_line_column,
|
||||
source: source.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_direct(&mut self, typ: String, query_region: Region, text: &str) -> Elaboration {
|
||||
match find_symbol_at(query_region, self.declarations, self.abilities_store) {
|
||||
Some(found_symbol) => match found_symbol {
|
||||
FoundSymbol::Specialization(spec_type, spec_symbol)
|
||||
| FoundSymbol::AbilityMember(spec_type, spec_symbol) => {
|
||||
Elaboration::Specialization {
|
||||
specialized_name: format!(
|
||||
"{}#{}({})",
|
||||
spec_type.as_str(self.interns),
|
||||
text,
|
||||
spec_symbol.ident_id().index(),
|
||||
),
|
||||
typ,
|
||||
}
|
||||
}
|
||||
FoundSymbol::Symbol(symbol) => Elaboration::Source {
|
||||
source: symbol.as_str(self.interns).to_owned(),
|
||||
typ,
|
||||
},
|
||||
},
|
||||
None => Elaboration::Source {
|
||||
source: text.to_owned(),
|
||||
typ,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_instantiated(
|
||||
&mut self,
|
||||
var: Variable,
|
||||
typ: String,
|
||||
query_region: Region,
|
||||
text: &str,
|
||||
) -> Result<Elaboration, Box<dyn Error>> {
|
||||
let symbol = match find_symbol_at(query_region, self.declarations, self.abilities_store) {
|
||||
Some(FoundSymbol::Symbol(symbol) | FoundSymbol::Specialization(_, symbol)) => symbol,
|
||||
_ => return Err(format!("No symbol under {text:?}",).into()),
|
||||
};
|
||||
|
||||
let def = find_declaration(symbol, self.declarations)
|
||||
.ok_or_else(|| format!("No def found for {text:?}"))?;
|
||||
|
||||
let region = def.region();
|
||||
let LineColumnRegion { start, end } = self.line_info.convert_region(region);
|
||||
|
||||
let start_pos = self.line_info.convert_line_column(LineColumn {
|
||||
line: start.line,
|
||||
column: 0,
|
||||
});
|
||||
let end_pos = self.line_info.convert_line_column(LineColumn {
|
||||
line: end.line + 1,
|
||||
column: 0,
|
||||
});
|
||||
let def_region = Region::new(start_pos, end_pos);
|
||||
let def_source = &self.source[start_pos.offset as usize..end_pos.offset as usize];
|
||||
|
||||
roc_late_solve::unify(
|
||||
self.home,
|
||||
self.arena,
|
||||
self.subs,
|
||||
&AbilitiesView::Module(self.abilities_store),
|
||||
&self.derived_module,
|
||||
&self.exposed_by_module,
|
||||
var,
|
||||
def.var(),
|
||||
)
|
||||
.map_err(|_| "does not unify")?;
|
||||
|
||||
let queries_in_instantiation = self
|
||||
.all_queries
|
||||
.iter()
|
||||
.filter(|query| def_region.contains(&query.query_region))
|
||||
.map(|query| self.answer(query))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Elaboration::Instantiation {
|
||||
typ,
|
||||
source: def_source.to_owned(),
|
||||
offset_line: start.line,
|
||||
queries_in_instantiation: InferredQueries(queries_in_instantiation),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer_queries_help(src: &str, expected: impl FnOnce(&str), options: InferOptions) {
|
||||
let InferredProgram {
|
||||
program,
|
||||
inferred_queries,
|
||||
} = infer_queries(src, options).unwrap();
|
||||
|
||||
let mut output_parts = Vec::with_capacity(inferred_queries.len() + 2);
|
||||
|
||||
if options.print_can_decls {
|
||||
use roc_can::debug::{pretty_print_declarations, PPCtx};
|
||||
let ctx = PPCtx {
|
||||
home: program.home,
|
||||
interns: &program.interns,
|
||||
print_lambda_names: true,
|
||||
};
|
||||
let pretty_decls = pretty_print_declarations(&ctx, &program.declarations);
|
||||
output_parts.push(pretty_decls);
|
||||
output_parts.push("\n".to_owned());
|
||||
}
|
||||
|
||||
for InferredQuery { elaboration, .. } in inferred_queries {
|
||||
let output_part = match elaboration {
|
||||
Elaboration::Specialization {
|
||||
specialized_name,
|
||||
typ,
|
||||
} => format!("{specialized_name} : {typ}"),
|
||||
Elaboration::Source { source, typ } => format!("{source} : {typ}"),
|
||||
Elaboration::Instantiation { .. } => panic!("Use uitest instead"),
|
||||
};
|
||||
output_parts.push(output_part);
|
||||
}
|
||||
|
||||
let pretty_output = output_parts.join("\n");
|
||||
|
||||
expected(&pretty_output);
|
||||
}
|
|
@ -4607,18 +4607,9 @@ pub fn gather_tags_unsorted_iter(
|
|||
|
||||
let mut stack = vec![other_fields];
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
let mut seen_head_union = false;
|
||||
|
||||
loop {
|
||||
match subs.get_content_without_compacting(ext.var()) {
|
||||
Structure(TagUnion(sub_fields, sub_ext)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
assert!(!seen_head_union, "extension variable is another tag union, but I expected it to be either open or closed!");
|
||||
seen_head_union = true;
|
||||
}
|
||||
|
||||
stack.push(*sub_fields);
|
||||
|
||||
ext = *sub_ext;
|
||||
|
|
33
crates/compiler/uitest/Cargo.toml
Normal file
33
crates/compiler/uitest/Cargo.toml
Normal file
|
@ -0,0 +1,33 @@
|
|||
[package]
|
||||
name = "uitest"
|
||||
description = "Integration tests for the solver."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[[test]]
|
||||
name = "uitest"
|
||||
path = "src/uitest.rs"
|
||||
harness = false
|
||||
|
||||
[dev-dependencies]
|
||||
roc_builtins = { path = "../builtins" }
|
||||
roc_derive = { path = "../derive", features = ["debug-derived-symbols"] }
|
||||
roc_load = { path = "../load" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
test_solve_helpers = { path = "../test_solve_helpers" }
|
||||
|
||||
bumpalo.workspace = true
|
||||
indoc.workspace = true
|
||||
insta.workspace = true
|
||||
lazy_static.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
regex.workspace = true
|
||||
tempfile.workspace = true
|
||||
libtest-mimic.workspace = true
|
448
crates/compiler/uitest/src/uitest.rs
Normal file
448
crates/compiler/uitest/src/uitest.rs
Normal file
|
@ -0,0 +1,448 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
ffi::OsStr,
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use libtest_mimic::{run, Arguments, Failed, Trial};
|
||||
use regex::Regex;
|
||||
use test_solve_helpers::{
|
||||
infer_queries, Elaboration, InferOptions, InferredProgram, InferredQuery, MUTLILINE_MARKER,
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args = Arguments::from_args();
|
||||
|
||||
let test_files = collect_uitest_files()?;
|
||||
let tests = test_files
|
||||
.into_iter()
|
||||
.map(into_test)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
run(&args, tests).exit()
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref UITEST_PATH: PathBuf = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"))
|
||||
.join("crates")
|
||||
.join("compiler")
|
||||
.join("uitest")
|
||||
.join("tests");
|
||||
|
||||
/// # +opt infer:<opt>
|
||||
static ref RE_OPT_INFER: Regex =
|
||||
Regex::new(r#"# \+opt infer:(?P<opt>.*)"#).unwrap();
|
||||
|
||||
/// # +opt print:<opt>
|
||||
static ref RE_OPT_PRINT: Regex =
|
||||
Regex::new(r#"# \+opt print:(?P<opt>.*)"#).unwrap();
|
||||
}
|
||||
|
||||
fn collect_uitest_files() -> io::Result<Vec<PathBuf>> {
|
||||
let mut tests = Vec::with_capacity(200);
|
||||
|
||||
let mut dirs_to_visit = vec![UITEST_PATH.clone()];
|
||||
while let Some(dir) = dirs_to_visit.pop() {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
let entry_type = entry.file_type()?;
|
||||
if entry_type.is_dir() {
|
||||
dirs_to_visit.push(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if path.extension() == Some(OsStr::new("txt")) {
|
||||
tests.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(tests)
|
||||
}
|
||||
|
||||
fn into_test(path: PathBuf) -> io::Result<Trial> {
|
||||
let name = path
|
||||
.strip_prefix(UITEST_PATH.as_path().parent().unwrap())
|
||||
.expect("collected path does not have uitest prefix")
|
||||
.display()
|
||||
.to_string();
|
||||
|
||||
let trial = Trial::test(name, move || run_test(path));
|
||||
Ok(trial)
|
||||
}
|
||||
|
||||
fn run_test(path: PathBuf) -> Result<(), Failed> {
|
||||
let data = std::fs::read_to_string(&path)?;
|
||||
let TestCase {
|
||||
infer_options,
|
||||
print_options,
|
||||
source,
|
||||
} = TestCase::parse(data)?;
|
||||
|
||||
let inferred_program = infer_queries(&source, infer_options)?;
|
||||
|
||||
{
|
||||
let mut fd = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&path)?;
|
||||
|
||||
assemble_query_output(&mut fd, &source, inferred_program, print_options)?;
|
||||
}
|
||||
|
||||
check_for_changes(&path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const EMIT_HEADER: &str = "# -emit:";
|
||||
|
||||
struct TestCase {
|
||||
infer_options: InferOptions,
|
||||
print_options: PrintOptions,
|
||||
source: String,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrintOptions {
|
||||
can_decls: bool,
|
||||
}
|
||||
|
||||
impl TestCase {
|
||||
fn parse(mut data: String) -> Result<Self, Failed> {
|
||||
// Drop anything following `# -emit:` header lines; that's the output.
|
||||
if let Some(drop_at) = data.find(EMIT_HEADER) {
|
||||
data.truncate(drop_at);
|
||||
data.truncate(data.trim_end().len());
|
||||
}
|
||||
|
||||
Ok(TestCase {
|
||||
infer_options: Self::parse_infer_options(&data)?,
|
||||
print_options: Self::parse_print_options(&data)?,
|
||||
source: data,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_infer_options(data: &str) -> Result<InferOptions, Failed> {
|
||||
let mut infer_opts = InferOptions {
|
||||
no_promote: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let found_infer_opts = RE_OPT_INFER.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"allow_errors" => infer_opts.allow_errors = true,
|
||||
"print_only_under_alias" => infer_opts.print_only_under_alias = true,
|
||||
other => return Err(format!("unknown infer option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(infer_opts)
|
||||
}
|
||||
|
||||
fn parse_print_options(data: &str) -> Result<PrintOptions, Failed> {
|
||||
let mut print_opts = PrintOptions::default();
|
||||
|
||||
let found_infer_opts = RE_OPT_PRINT.captures_iter(data);
|
||||
for infer_opt in found_infer_opts {
|
||||
let opt = infer_opt.name("opt").unwrap().as_str();
|
||||
match opt.trim() {
|
||||
"can_decls" => print_opts.can_decls = true,
|
||||
other => return Err(format!("unknown print option: {other:?}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(print_opts)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_changes(path: &Path) -> Result<(), Failed> {
|
||||
Command::new("git").args(["add", "-N"]).arg(path).output()?;
|
||||
|
||||
let has_changes = Command::new("git")
|
||||
.args(["diff", "--color=always"])
|
||||
.arg(path)
|
||||
.output()?;
|
||||
|
||||
if !has_changes.stdout.is_empty() {
|
||||
return Err(format!(
|
||||
"{}\nOutput has changed. If it looks okay, `git` add the file.",
|
||||
std::str::from_utf8(&has_changes.stdout)?
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Assemble the output for a test, with queries elaborated in-line.
|
||||
fn assemble_query_output(
|
||||
writer: &mut impl io::Write,
|
||||
source: &str,
|
||||
inferred_program: InferredProgram,
|
||||
print_options: PrintOptions,
|
||||
) -> io::Result<()> {
|
||||
// Reverse the queries so that we can pop them off the end as we pass through the lines.
|
||||
let (queries, program) = inferred_program.decompose();
|
||||
let mut sorted_queries = queries.into_sorted();
|
||||
sorted_queries.reverse();
|
||||
|
||||
let mut reflow = Reflow::new_unindented(writer);
|
||||
write_source_with_answers(&mut reflow, source, sorted_queries, 0)?;
|
||||
|
||||
// Finish up with any remaining print options we were asked to provide.
|
||||
let PrintOptions { can_decls } = print_options;
|
||||
if can_decls {
|
||||
writeln!(writer, "\n{EMIT_HEADER}can_decls")?;
|
||||
program.write_can_decls(writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_source_with_answers<W: io::Write>(
|
||||
reflow: &mut Reflow<'_, W>,
|
||||
source: &str,
|
||||
mut sorted_queries: Vec<InferredQuery>,
|
||||
offset_line: usize,
|
||||
) -> io::Result<()> {
|
||||
for (i, line) in source.lines().enumerate() {
|
||||
let i = i + offset_line;
|
||||
|
||||
let mut is_query_line = false;
|
||||
|
||||
// Write all elaborated query lines if applicable.
|
||||
while matches!(
|
||||
sorted_queries.last(),
|
||||
Some(InferredQuery {
|
||||
source_line_column,
|
||||
..
|
||||
}) if source_line_column.line == i as _
|
||||
) {
|
||||
let inferred = sorted_queries.pop().unwrap();
|
||||
|
||||
reflow.scoped(|reflow| reconstruct_comment_line(reflow, inferred))?;
|
||||
|
||||
reflow.write("\n")?;
|
||||
|
||||
is_query_line = true;
|
||||
}
|
||||
|
||||
// If this was previously a multi-line query output line, skip it, since we already wrote
|
||||
// the new output above.
|
||||
if line.contains(MUTLILINE_MARKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, write the Roc source line.
|
||||
if !is_query_line {
|
||||
reflow.write(line.trim_end())?;
|
||||
reflow.write("\n")?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut sorted_queries = sorted_queries.into_iter().peekable();
|
||||
while let Some(sorted_query) = sorted_queries.next() {
|
||||
reflow.scoped(|reflow| reconstruct_comment_line(reflow, sorted_query))?;
|
||||
|
||||
// Only write a newline if we're not yet at the end of the source.
|
||||
// Otherwise, a newline will be written for us after exiting the reconstruction of the
|
||||
// comment line, since this must happen in the reconsutrction of a multi-line query.
|
||||
if sorted_queries.peek().is_some() {
|
||||
reflow.write("\n")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reconstruct_comment_line<W: io::Write>(
|
||||
reflow: &mut Reflow<'_, W>,
|
||||
inferred: InferredQuery,
|
||||
) -> io::Result<()> {
|
||||
let InferredQuery {
|
||||
comment_column,
|
||||
source_line_column,
|
||||
source,
|
||||
elaboration,
|
||||
} = inferred;
|
||||
|
||||
reflow.add_layer(comment_column as _, source_line_column.column as _);
|
||||
reflow.write_and_bump(&format!("{source} "))?;
|
||||
|
||||
match elaboration {
|
||||
Elaboration::Specialization {
|
||||
specialized_name,
|
||||
typ,
|
||||
} => {
|
||||
reflow.write_and_bump(&format!("{specialized_name}: "))?;
|
||||
reflow.write(&typ)
|
||||
}
|
||||
Elaboration::Source { source: _, typ } => reflow.write(&typ),
|
||||
Elaboration::Instantiation {
|
||||
typ,
|
||||
source,
|
||||
offset_line,
|
||||
queries_in_instantiation,
|
||||
} => {
|
||||
reflow.write(&typ)?;
|
||||
|
||||
// Write the source on new line, but at the reflow column the comment is aligned at.
|
||||
reflow.set_content(source_line_column.column as _);
|
||||
reflow.write("\n")?;
|
||||
|
||||
let queries = queries_in_instantiation.into_sorted();
|
||||
|
||||
write_source_with_answers(reflow, source.trim_end(), queries, offset_line as _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Reflow<'a, W: io::Write> {
|
||||
writer: &'a mut W,
|
||||
state: ReflowState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ReflowState {
|
||||
/// true if the first line of the elaboration comment has been written.
|
||||
top_line_written: bool,
|
||||
/// Number of `content columns` prefixes written.
|
||||
/// If this equals the number of content columns, the whole prefix for a line has been written.
|
||||
content_prefixes_written: usize,
|
||||
/// The column at which to insert the comment prefix "#".
|
||||
comment_column: usize,
|
||||
/// The columns at which content occurs.
|
||||
/// If the stack is >1, then
|
||||
/// - at the first content column, the [MUTLILINE_MARKER] may be written as appropriate
|
||||
/// - for subsequent columns, spaces are inserted until the column is reached.
|
||||
content_columns: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<'a, W: io::Write> std::ops::Deref for Reflow<'a, W> {
|
||||
type Target = ReflowState;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: io::Write> std::ops::DerefMut for Reflow<'a, W> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: io::Write> Reflow<'a, W> {
|
||||
fn new_unindented(writer: &'a mut W) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
state: ReflowState {
|
||||
top_line_written: false,
|
||||
content_prefixes_written: 0,
|
||||
comment_column: 0,
|
||||
content_columns: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn scoped<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
let state = self.state.clone();
|
||||
let result = f(self);
|
||||
self.state = state;
|
||||
result
|
||||
}
|
||||
|
||||
fn add_layer(&mut self, comment_column: usize, content_column: usize) {
|
||||
if self.comment_column == 0 {
|
||||
// If the comment column is not yet set, this is the top-level and we should update the
|
||||
// state; otherwise, we already have a comment column, only add to the content-ful
|
||||
// layer.
|
||||
self.comment_column = comment_column;
|
||||
}
|
||||
self.content_columns.push(content_column);
|
||||
}
|
||||
|
||||
fn set_content(&mut self, content_column: usize) {
|
||||
let latest_column = self
|
||||
.content_columns
|
||||
.last_mut()
|
||||
.expect("cannot set content before adding a layer");
|
||||
*latest_column = content_column;
|
||||
}
|
||||
|
||||
fn write(&mut self, content: &str) -> io::Result<()> {
|
||||
for (i, content_line) in content.split('\n').enumerate() {
|
||||
if i > 0 {
|
||||
// new line
|
||||
writeln!(self.writer)?;
|
||||
self.content_prefixes_written = 0;
|
||||
}
|
||||
|
||||
// If the content columns are empty, this is top-level and we
|
||||
// have no prefix to write.
|
||||
if self.content_prefixes_written != self.content_columns.len() {
|
||||
if self.content_prefixes_written == 0 {
|
||||
self.write_n_spaces(self.comment_column)?;
|
||||
write!(self.writer, "#")?;
|
||||
|
||||
// For the first column content - write spaces up to the column, and then if we are
|
||||
// in a multiline context, add the multi-line marker.
|
||||
{
|
||||
self.write_n_spaces(self.content_columns[0] - self.comment_column - 1)?;
|
||||
|
||||
if self.top_line_written {
|
||||
write!(self.writer, "{MUTLILINE_MARKER} ")?;
|
||||
}
|
||||
}
|
||||
|
||||
self.content_prefixes_written = 1;
|
||||
}
|
||||
|
||||
// For all remaining content columns, fill them in with spaces.
|
||||
let remaining_content_columns = self
|
||||
.content_columns
|
||||
.iter()
|
||||
.skip(self.content_prefixes_written);
|
||||
self.write_n_spaces(remaining_content_columns.sum())?;
|
||||
|
||||
self.content_prefixes_written = self.content_columns.len();
|
||||
self.top_line_written = true;
|
||||
}
|
||||
|
||||
write!(self.writer, "{content_line}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_and_bump(&mut self, content: &str) -> io::Result<()> {
|
||||
assert!(
|
||||
content.lines().count() == 1,
|
||||
"cannot bump with multi-line content"
|
||||
);
|
||||
|
||||
self.write(content)?;
|
||||
|
||||
let column = self
|
||||
.content_columns
|
||||
.last_mut()
|
||||
.expect("cannot write_and_bump before adding layer");
|
||||
*column += content.len();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_n_spaces(&mut self, n: usize) -> io::Result<()> {
|
||||
for _ in 0..n {
|
||||
write!(self.writer, " ")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
F a : a | a has Hash
|
||||
|
||||
main : F a -> F a
|
||||
#^^^^{-1} a -[[main(0)]]-> a | a has Hash
|
|
@ -0,0 +1,7 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
F a : a | a has Hash & Eq & Decoding
|
||||
|
||||
main : F a -> F a
|
||||
#^^^^{-1} a -[[main(0)]]-> a | a has Hash & Decoding & Eq
|
|
@ -0,0 +1,8 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
f : x -> x | x has Hash
|
||||
g : x -> x | x has Decoding & Encoding
|
||||
|
||||
main : x -> x | x has Hash & Decoding & Encoding
|
||||
main = \x -> x |> f |> g
|
||||
#^^^^{-1} x -[[main(0)]]-> x | x has Hash & Encoding & Decoding
|
|
@ -0,0 +1,12 @@
|
|||
app "test" provides [top] to "./platform"
|
||||
|
||||
MDict u := (List u) | u has Hash & Eq
|
||||
|
||||
bot : MDict k -> MDict k
|
||||
bot = \@MDict data ->
|
||||
when {} is
|
||||
{} -> @MDict data
|
||||
|
||||
top : MDict v -> MDict v
|
||||
top = \x -> bot x
|
||||
#^^^{-1} MDict v -[[top(0)]]-> MDict v | v has Hash & Eq
|
|
@ -0,0 +1,9 @@
|
|||
app "test" provides [isEqQ] to "./platform"
|
||||
|
||||
Q := [ F (Str -> Str), G ] has [Eq { isEq: isEqQ }]
|
||||
|
||||
isEqQ = \@Q q1, @Q q2 -> when T q1 q2 is
|
||||
#^^^^^{-1} Q, Q -[[isEqQ(0)]]-> Bool
|
||||
T (F _) (F _) -> Bool.true
|
||||
T G G -> Bool.true
|
||||
_ -> Bool.false
|
|
@ -0,0 +1,10 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||
|
||||
isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {})
|
||||
#^^^^^{-1} ({} -[[]]-> Str), ({} -[[]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||
|
||||
main = isEqQ (@Q \{} -> "a") (@Q \{} -> "a")
|
||||
# ^^^^^ ({} -[[6, 7]]-> Str), ({} -[[6, 7]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
44
crates/compiler/uitest/tests/ability/smoke/decoder.txt
Normal file
44
crates/compiler/uitest/tests/ability/smoke/decoder.txt
Normal file
|
@ -0,0 +1,44 @@
|
|||
app "test" provides [myU8] to "./platform"
|
||||
|
||||
MDecodeError : [TooShort, Leftover (List U8)]
|
||||
|
||||
MDecoder val fmt := List U8, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting
|
||||
|
||||
MDecoding has
|
||||
decoder : MDecoder val fmt | val has MDecoding, fmt has MDecoderFormatting
|
||||
|
||||
MDecoderFormatting has
|
||||
u8 : MDecoder U8 fmt | fmt has MDecoderFormatting
|
||||
|
||||
decodeWith : List U8, MDecoder val fmt, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting
|
||||
decodeWith = \lst, (@MDecoder doDecode), fmt -> doDecode lst fmt
|
||||
|
||||
fromBytes : List U8, fmt -> Result val MDecodeError
|
||||
| fmt has MDecoderFormatting, val has MDecoding
|
||||
fromBytes = \lst, fmt ->
|
||||
when decodeWith lst decoder fmt is
|
||||
{ result, rest } ->
|
||||
when result is
|
||||
Ok val -> if List.isEmpty rest then Ok val else Err (Leftover rest)
|
||||
Err e -> Err e
|
||||
|
||||
|
||||
Linear := {} has [MDecoderFormatting {u8}]
|
||||
|
||||
u8 = @MDecoder \lst, @Linear {} ->
|
||||
#^^{-1} Linear#u8(11): MDecoder U8 Linear
|
||||
when List.first lst is
|
||||
Ok n -> { result: Ok n, rest: List.dropFirst lst }
|
||||
Err _ -> { result: Err TooShort, rest: [] }
|
||||
|
||||
MyU8 := U8 has [MDecoding {decoder}]
|
||||
|
||||
decoder = @MDecoder \lst, fmt ->
|
||||
#^^^^^^^{-1} MyU8#decoder(12): MDecoder MyU8 fmt | fmt has MDecoderFormatting
|
||||
when decodeWith lst u8 fmt is
|
||||
{ result, rest } ->
|
||||
{ result: Result.map result (\n -> @MyU8 n), rest }
|
||||
|
||||
myU8 : Result MyU8 _
|
||||
myU8 = fromBytes [15] (@Linear {})
|
||||
#^^^^{-1} Result MyU8 MDecodeError
|
29
crates/compiler/uitest/tests/ability/smoke/encoder.txt
Normal file
29
crates/compiler/uitest/tests/ability/smoke/encoder.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
app "test" provides [myU8Bytes] to "./platform"
|
||||
|
||||
MEncoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||
|
||||
MEncoding has
|
||||
toEncoder : val -> MEncoder fmt | val has MEncoding, fmt has Format
|
||||
|
||||
Format has
|
||||
u8 : U8 -> MEncoder fmt | fmt has Format
|
||||
|
||||
appendWith : List U8, MEncoder fmt, fmt -> List U8 | fmt has Format
|
||||
appendWith = \lst, (@MEncoder doFormat), fmt -> doFormat lst fmt
|
||||
|
||||
toBytes : val, fmt -> List U8 | val has MEncoding, fmt has Format
|
||||
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
|
||||
|
||||
|
||||
Linear := {} has [Format {u8}]
|
||||
|
||||
u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n)
|
||||
#^^{-1} Linear#u8(10): U8 -[[u8(10)]]-> MEncoder Linear
|
||||
|
||||
MyU8 := U8 has [MEncoding {toEncoder}]
|
||||
|
||||
toEncoder = \@MyU8 n -> u8 n
|
||||
#^^^^^^^^^{-1} MyU8#toEncoder(11): MyU8 -[[toEncoder(11)]]-> MEncoder fmt | fmt has Format
|
||||
|
||||
myU8Bytes = toBytes (@MyU8 15) (@Linear {})
|
||||
#^^^^^^^^^{-1} List U8
|
|
@ -0,0 +1,6 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main : Decoder Bool _
|
||||
main = Decode.custom \bytes, fmt ->
|
||||
Decode.decodeWith bytes Decode.decoder fmt
|
||||
# ^^^^^^^^^^^^^^ Decoding#Decode.decoder(4): Decoder Bool fmt | fmt has DecoderFormatting
|
|
@ -0,0 +1,4 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main = Bool.isEq Bool.true Bool.false
|
||||
# ^^^^^^^^^ Eq#Bool.isEq(9): Bool, Bool -[[Bool.structuralEq(11)]]-> Bool
|
|
@ -0,0 +1,5 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
\h -> Hash.hash h Bool.true
|
||||
# ^^^^^^^^^ Hash#Hash.hash(1): a, Bool -[[Hash.hashBool(9)]]-> a | a has Hasher
|
|
@ -0,0 +1,4 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main = Encode.toEncoder Bool.true
|
||||
# ^^^^^^^^^^^^^^^^ Encoding#Encode.toEncoder(2): Bool -[[] + fmt:Encode.bool(17):1]-> Encoder fmt | fmt has EncoderFormatting
|
|
@ -0,0 +1,6 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
n : Num *
|
||||
|
||||
main = n == 1.
|
||||
# ^ Dec
|
|
@ -0,0 +1,9 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
N := U8 has [Decoding]
|
||||
|
||||
main : Decoder N _
|
||||
main = Decode.custom \bytes, fmt ->
|
||||
Decode.decodeWith bytes Decode.decoder fmt
|
||||
# ^^^^^^^^^^^^^^ N#Decode.decoder(3): List U8, fmt -[[7]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting
|
|
@ -0,0 +1,6 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
N := U8 has [Encoding]
|
||||
|
||||
main = Encode.toEncoder (@N 15)
|
||||
# ^^^^^^^^^^^^^^^^ N#Encode.toEncoder(3): N -[[#N_toEncoder(3)]]-> Encoder fmt | fmt has EncoderFormatting
|
|
@ -0,0 +1,8 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Trivial := {} has [Eq {isEq}]
|
||||
|
||||
isEq = \@Trivial {}, @Trivial {} -> Bool.true
|
||||
|
||||
main = Bool.isEq (@Trivial {}) (@Trivial {})
|
||||
# ^^^^^^^^^ Trivial#Bool.isEq(2): Trivial, Trivial -[[isEq(2)]]-> Bool
|
|
@ -0,0 +1,6 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
N := U8 has [Eq]
|
||||
|
||||
main = Bool.isEq (@N 15) (@N 23)
|
||||
# ^^^^^^^^^ N#Bool.isEq(3): N, N -[[#N_isEq(3)]]-> Bool
|
|
@ -0,0 +1,8 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Noop := {} has [Hash {hash}]
|
||||
|
||||
hash = \hasher, @Noop {} -> hasher
|
||||
|
||||
main = \hasher -> hash hasher (@Noop {})
|
||||
#^^^^{-1} hasher -[[main(0)]]-> hasher | hasher has Hasher
|
|
@ -0,0 +1,6 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
N := U8 has [Hash]
|
||||
|
||||
main = \hasher, @N n -> Hash.hash hasher (@N n)
|
||||
# ^^^^^^^^^ N#Hash.hash(3): a, N -[[#N_hash(3)]]-> a | a has Hasher
|
|
@ -0,0 +1,16 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
F has f : a -> (b -> {}) | a has F, b has G
|
||||
G has g : b -> {} | b has G
|
||||
|
||||
Fo := {} has [F {f}]
|
||||
f = \@Fo {} -> g
|
||||
#^{-1} Fo#f(7): Fo -[[f(7)]]-> (b -[[] + b:g(4):1]-> {}) | b has G
|
||||
|
||||
Go := {} has [G {g}]
|
||||
g = \@Go {} -> {}
|
||||
#^{-1} Go#g(8): Go -[[g(8)]]-> {}
|
||||
|
||||
main = (f (@Fo {})) (@Go {})
|
||||
# ^ Fo#f(7): Fo -[[f(7)]]-> (Go -[[g(8)]]-> {})
|
||||
# ^^^^^^^^^^ Go -[[g(8)]]-> {}
|
|
@ -0,0 +1,20 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
F has f : a -> ({} -> b) | a has F, b has G
|
||||
G has g : {} -> b | b has G
|
||||
|
||||
Fo := {} has [F {f}]
|
||||
f = \@Fo {} -> g
|
||||
#^{-1} Fo#f(7): Fo -[[f(7)]]-> ({} -[[] + b:g(4):1]-> b) | b has G
|
||||
|
||||
Go := {} has [G {g}]
|
||||
g = \{} -> @Go {}
|
||||
#^{-1} Go#g(8): {} -[[g(8)]]-> Go
|
||||
|
||||
main =
|
||||
foo = 1
|
||||
@Go it = (f (@Fo {})) {}
|
||||
# ^ Fo#f(7): Fo -[[f(7)]]-> ({} -[[g(8)]]-> Go)
|
||||
# ^^^^^^^^^^ {} -[[g(8)]]-> Go
|
||||
|
||||
{foo, it}
|
|
@ -0,0 +1,22 @@
|
|||
app "test" provides [f] to "./platform"
|
||||
|
||||
J has j : j -> (k -> {}) | j has J, k has K
|
||||
K has k : k -> {} | k has K
|
||||
|
||||
C := {} has [J {j: jC}]
|
||||
jC = \@C _ -> k
|
||||
|
||||
D := {} has [J {j: jD}]
|
||||
jD = \@D _ -> k
|
||||
|
||||
E := {} has [K {k}]
|
||||
k = \@E _ -> {}
|
||||
|
||||
f = \flag, a, c ->
|
||||
it =
|
||||
when flag is
|
||||
A -> j a
|
||||
B -> j a
|
||||
it c
|
||||
# ^ k | k has K
|
||||
# ^^ k -[[] + j:j(2):2]-> {} | j has J, k has K
|
|
@ -0,0 +1,34 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
J has j : j -> (k -> {}) | j has J, k has K
|
||||
K has k : k -> {} | k has K
|
||||
|
||||
C := {} has [J {j: jC}]
|
||||
jC = \@C _ -> k
|
||||
#^^{-1} C -[[jC(8)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
|
||||
|
||||
D := {} has [J {j: jD}]
|
||||
jD = \@D _ -> k
|
||||
#^^{-1} D -[[jD(9)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
|
||||
|
||||
E := {} has [K {k}]
|
||||
k = \@E _ -> {}
|
||||
#^{-1} E#k(10): E -[[k(10)]]-> {}
|
||||
|
||||
f = \flag, a, b ->
|
||||
# ^ j | j has J
|
||||
# ^ j | j has J
|
||||
it =
|
||||
# ^^ k -[[] + j:j(2):2 + j1:j(2):2]-> {} | j has J, j1 has J, k has K
|
||||
when flag is
|
||||
A -> j a
|
||||
# ^ J#j(2): j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + j1:j(2):2]-> {}) | j has J, j1 has J, k has K
|
||||
B -> j b
|
||||
# ^ J#j(2): j -[[] + j:j(2):1]-> (k -[[] + j1:j(2):2 + j:j(2):2]-> {}) | j has J, j1 has J, k has K
|
||||
it
|
||||
# ^^ k -[[] + j:j(2):2 + j1:j(2):2]-> {} | j has J, j1 has J, k has K
|
||||
|
||||
main = (f A (@C {}) (@D {})) (@E {})
|
||||
# ^ [A, B], C, D -[[f(11)]]-> (E -[[k(10)]]-> {})
|
||||
# ^^^^^^^^^^^^^^^^^^^ E -[[k(10)]]-> {}
|
||||
#^^^^{-1} {}
|
|
@ -0,0 +1,45 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
J has j : j -> (k -> {}) | j has J, k has K
|
||||
K has k : k -> {} | k has K
|
||||
|
||||
C := {} has [J {j: jC}]
|
||||
jC = \@C _ -> k
|
||||
#^^{-1} C -[[jC(9)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
|
||||
|
||||
D := {} has [J {j: jD}]
|
||||
jD = \@D _ -> k
|
||||
#^^{-1} D -[[jD(10)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
|
||||
|
||||
E := {} has [K {k: kE}]
|
||||
kE = \@E _ -> {}
|
||||
#^^{-1} E -[[kE(11)]]-> {}
|
||||
|
||||
F := {} has [K {k: kF}]
|
||||
kF = \@F _ -> {}
|
||||
#^^{-1} F -[[kF(12)]]-> {}
|
||||
|
||||
f = \flag, a, b ->
|
||||
# ^ j | j has J
|
||||
# ^ j | j has J
|
||||
it =
|
||||
# ^^ k -[[] + j:j(2):2 + j1:j(2):2]-> {} | j has J, j1 has J, k has K
|
||||
when flag is
|
||||
A -> j a
|
||||
# ^ J#j(2): j -[[] + j:j(2):1]-> (k -[[] + j:j(2):2 + j1:j(2):2]-> {}) | j has J, j1 has J, k has K
|
||||
B -> j b
|
||||
# ^ J#j(2): j -[[] + j:j(2):1]-> (k -[[] + j1:j(2):2 + j:j(2):2]-> {}) | j has J, j1 has J, k has K
|
||||
it
|
||||
# ^^ k -[[] + j:j(2):2 + j1:j(2):2]-> {} | j has J, j1 has J, k has K
|
||||
|
||||
main =
|
||||
#^^^^{-1} {}
|
||||
it = \x ->
|
||||
# ^^ k -[[it(21)]]-> {} | k has K
|
||||
(f A (@C {}) (@D {})) x
|
||||
# ^ [A, B], C, D -[[f(13)]]-> (k -[[] + k:k(4):1]-> {}) | k has K
|
||||
if Bool.true
|
||||
then it (@E {})
|
||||
# ^^ E -[[it(21)]]-> {}
|
||||
else it (@F {})
|
||||
# ^^ F -[[it(21)]]-> {}
|
|
@ -0,0 +1,16 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G
|
||||
G has g : b -> ({} -> {}) | b has G
|
||||
|
||||
Fo := {} has [F {f}]
|
||||
f = \@Fo {}, b -> \{} -> g b
|
||||
#^{-1} Fo#f(7): Fo, b -[[f(7)]]-> ({} -[[13 b]]-> ({} -[[] + b:g(4):2]-> {})) | b has G
|
||||
|
||||
Go := {} has [G {g}]
|
||||
g = \@Go {} -> \{} -> {}
|
||||
#^{-1} Go#g(8): Go -[[g(8)]]-> ({} -[[14]]-> {})
|
||||
|
||||
main =
|
||||
(f (@Fo {}) (@Go {})) {}
|
||||
# ^ Fo#f(7): Fo, Go -[[f(7)]]-> ({} -[[13 Go]]-> ({} -[[14]]-> {}))
|
|
@ -0,0 +1,20 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
F has f : a -> (b -> {}) | a has F, b has G
|
||||
G has g : b -> {} | b has G
|
||||
|
||||
Fo := {} has [F {f}]
|
||||
f = \@Fo {} -> g
|
||||
#^{-1} Fo#f(7): Fo -[[f(7)]]-> (b -[[] + b:g(4):1]-> {}) | b has G
|
||||
|
||||
Go := {} has [G {g}]
|
||||
g = \@Go {} -> {}
|
||||
#^{-1} Go#g(8): Go -[[g(8)]]-> {}
|
||||
|
||||
main =
|
||||
# h should get weakened
|
||||
h = f (@Fo {})
|
||||
# ^ Fo#f(7): Fo -[[f(7)]]-> (Go -[[g(8)]]-> {})
|
||||
# ^ Go -[[g(8)]]-> {}
|
||||
h (@Go {})
|
||||
# ^ Go -[[g(8)]]-> {}
|
|
@ -0,0 +1,19 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
F has f : a -> (b -> {}) | a has F, b has G
|
||||
G has g : b -> {} | b has G
|
||||
|
||||
Fo := {} has [F {f}]
|
||||
f = \@Fo {} -> g
|
||||
#^{-1} Fo#f(7): Fo -[[f(7)]]-> (b -[[] + b:g(4):1]-> {}) | b has G
|
||||
|
||||
Go := {} has [G {g}]
|
||||
g = \@Go {} -> {}
|
||||
#^{-1} Go#g(8): Go -[[g(8)]]-> {}
|
||||
|
||||
main =
|
||||
#^^^^{-1} b -[[] + b:g(4):1]-> {} | b has G
|
||||
h = f (@Fo {})
|
||||
# ^ Fo#f(7): Fo -[[f(7)]]-> (b -[[] + b:g(4):1]-> {}) | b has G
|
||||
# ^ b -[[] + b:g(4):1]-> {} | b has G
|
||||
h
|
|
@ -0,0 +1,5 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
\h -> Hash.hash h 7
|
||||
# ^^^^^^^^^ Hash#Hash.hash(1): a, I64 -[[Hash.hashI64(13)]]-> a | a has Hasher
|
|
@ -0,0 +1,6 @@
|
|||
app "test"
|
||||
imports [Encode.{ toEncoder }]
|
||||
provides [main] to "./platform"
|
||||
|
||||
main = toEncoder { a: "" }
|
||||
# ^^^^^^^^^ Encoding#toEncoder(2): { a : Str } -[[#Derived.toEncoder_{a}(0)]]-> Encoder fmt | fmt has EncoderFormatting
|
|
@ -0,0 +1,9 @@
|
|||
app "test"
|
||||
imports [Encode.{ toEncoder, custom }]
|
||||
provides [main] to "./platform"
|
||||
|
||||
A := {} has [Encoding {toEncoder}]
|
||||
toEncoder = \@A _ -> custom \b, _ -> b
|
||||
|
||||
main = toEncoder { a: @A {} }
|
||||
# ^^^^^^^^^ Encoding#toEncoder(2): { a : A } -[[#Derived.toEncoder_{a}(0)]]-> Encoder fmt | fmt has EncoderFormatting
|
|
@ -0,0 +1,19 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Id1 has id1 : a -> a | a has Id1
|
||||
Id2 has id2 : a -> a | a has Id2
|
||||
|
||||
A := {} has [Id1 {id1}, Id2 {id2}]
|
||||
id1 = \@A {} -> @A {}
|
||||
#^^^{-1} A#id1(6): A -[[id1(6)]]-> A
|
||||
|
||||
id2 = \@A {} -> id1 (@A {})
|
||||
# ^^^ A#id1(6): A -[[id1(6)]]-> A
|
||||
#^^^{-1} A#id2(7): A -[[id2(7)]]-> A
|
||||
|
||||
main =
|
||||
a : A
|
||||
a = id2 (@A {})
|
||||
# ^^^ A#id2(7): A -[[id2(7)]]-> A
|
||||
|
||||
a
|
|
@ -0,0 +1,23 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Id has id : a -> a | a has Id
|
||||
|
||||
A := {} has [Id {id}]
|
||||
id = \@A {} -> @A {}
|
||||
#^^{-1} A#id(4): A -[[id(4)]]-> A
|
||||
|
||||
idNotAbility = \x -> x
|
||||
#^^^^^^^^^^^^{-1} a -[[idNotAbility(5)]]-> a
|
||||
|
||||
main =
|
||||
choice : [T, U]
|
||||
|
||||
# Should not get generalized
|
||||
idChoice =
|
||||
#^^^^^^^^{-1} A -[[id(4), idNotAbility(5)]]-> A
|
||||
when choice is
|
||||
T -> id
|
||||
U -> idNotAbility
|
||||
|
||||
idChoice (@A {})
|
||||
#^^^^^^^^{-1} A -[[id(4), idNotAbility(5)]]-> A
|
|
@ -0,0 +1,20 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Id has id : a -> a | a has Id
|
||||
|
||||
A := {} has [Id {id}]
|
||||
id = \@A {} -> @A {}
|
||||
#^^{-1} A#id(4): A -[[id(4)]]-> A
|
||||
|
||||
main =
|
||||
choice : [T, U]
|
||||
|
||||
# Should not get generalized
|
||||
idChoice =
|
||||
#^^^^^^^^{-1} A -[[id(4)]]-> A
|
||||
when choice is
|
||||
T -> id
|
||||
U -> id
|
||||
|
||||
idChoice (@A {})
|
||||
#^^^^^^^^{-1} A -[[id(4)]]-> A
|
|
@ -0,0 +1,19 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Id has id : a -> a | a has Id
|
||||
|
||||
A := {} has [Id {id}]
|
||||
id = \@A {} -> @A {}
|
||||
#^^{-1} A#id(4): A -[[id(4)]]-> A
|
||||
|
||||
main =
|
||||
alias1 = \x -> id x
|
||||
# ^^ Id#id(2): a -[[] + a:id(2):1]-> a | a has Id
|
||||
alias2 = \x -> alias1 x
|
||||
# ^^^^^^ a -[[alias1(6)]]-> a | a has Id
|
||||
|
||||
a : A
|
||||
a = alias2 (@A {})
|
||||
# ^^^^^^ A -[[alias2(7)]]-> A
|
||||
|
||||
a
|
|
@ -0,0 +1,20 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Id has id : a -> a | a has Id
|
||||
|
||||
A := {} has [Id {id}]
|
||||
id = \@A {} -> @A {}
|
||||
#^^{-1} A#id(4): A -[[id(4)]]-> A
|
||||
|
||||
main =
|
||||
# Both alias1, alias2 should get weakened
|
||||
alias1 = id
|
||||
# ^^ Id#id(2): A -[[id(4)]]-> A
|
||||
alias2 = alias1
|
||||
# ^^^^^^ A -[[id(4)]]-> A
|
||||
|
||||
a : A
|
||||
a = alias2 (@A {})
|
||||
# ^^^^^^ A -[[id(4)]]-> A
|
||||
|
||||
a
|
|
@ -0,0 +1,22 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Bounce has
|
||||
ping : a -> a | a has Bounce
|
||||
pong : a -> a | a has Bounce
|
||||
|
||||
A := {} has [Bounce {ping: pingA, pong: pongA}]
|
||||
|
||||
pingA = \@A {} -> pong (@A {})
|
||||
# ^^^^ A#pong(6): A -[[pongA(6)]]-> A
|
||||
#^^^^^{-1} A -[[pingA(5)]]-> A
|
||||
|
||||
pongA = \@A {} -> ping (@A {})
|
||||
# ^^^^ A#ping(5): A -[[pingA(5)]]-> A
|
||||
#^^^^^{-1} A -[[pongA(6)]]-> A
|
||||
|
||||
main =
|
||||
a : A
|
||||
a = ping (@A {})
|
||||
# ^^^^ A#ping(5): A -[[pingA(5)]]-> A
|
||||
|
||||
a
|
|
@ -0,0 +1,22 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Bounce has
|
||||
ping : a -> a | a has Bounce
|
||||
pong : a -> a | a has Bounce
|
||||
|
||||
A := {} has [Bounce {ping, pong}]
|
||||
|
||||
ping = \@A {} -> pong (@A {})
|
||||
# ^^^^ A#pong(6): A -[[pong(6)]]-> A
|
||||
#^^^^{-1} A#ping(5): A -[[ping(5)]]-> A
|
||||
|
||||
pong = \@A {} -> ping (@A {})
|
||||
# ^^^^ A#ping(5): A -[[ping(5)]]-> A
|
||||
#^^^^{-1} A#pong(6): A -[[pong(6)]]-> A
|
||||
|
||||
main =
|
||||
a : A
|
||||
a = ping (@A {})
|
||||
# ^^^^ A#ping(5): A -[[ping(5)]]-> A
|
||||
|
||||
a
|
|
@ -0,0 +1,17 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
Diverge has diverge : a -> a | a has Diverge
|
||||
|
||||
A := {} has [Diverge {diverge}]
|
||||
|
||||
diverge : A -> A
|
||||
diverge = \@A {} -> diverge (@A {})
|
||||
# ^^^^^^^ A#diverge(4): A -[[diverge(4)]]-> A
|
||||
#^^^^^^^{-1} A#diverge(4): A -[[diverge(4)]]-> A
|
||||
|
||||
main =
|
||||
a : A
|
||||
a = diverge (@A {})
|
||||
# ^^^^^^^ A#diverge(4): A -[[diverge(4)]]-> A
|
||||
|
||||
a
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue