Merge remote-tracking branch 'origin/main' into tutorial-syntax

This commit is contained in:
Luke Boswell 2023-03-01 17:20:17 +11:00
commit 7dd5ae3830
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
62 changed files with 4781 additions and 1647 deletions

View file

@ -17,7 +17,7 @@ jobs:
run: ./ci/write_version.sh run: ./ci/write_version.sh
- name: build release - name: build release
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --release --locked
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower. # target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
- name: get commit SHA - name: get commit SHA

View file

@ -22,7 +22,7 @@ jobs:
# this issue may be caused by using older versions of XCode # this issue may be caused by using older versions of XCode
- name: build release - name: build release
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --release --locked
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower. # target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
- name: get commit SHA - name: get commit SHA

346
Cargo.lock generated
View file

@ -68,28 +68,6 @@ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
] ]
[[package]]
name = "alsa"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
dependencies = [
"alsa-sys",
"bitflags",
"libc",
"nix 0.23.1",
]
[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
"libc",
"pkg-config",
]
[[package]] [[package]]
name = "approx" name = "approx"
version = "0.4.0" version = "0.4.0"
@ -198,25 +176,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bindgen"
version = "0.59.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.2" version = "0.5.2"
@ -415,24 +374,6 @@ name = "cc"
version = "1.0.73" version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -462,17 +403,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "clang-sys"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.34.0" version = "2.34.0"
@ -523,12 +453,6 @@ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
[[package]]
name = "claxon"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
[[package]] [[package]]
name = "cli_utils" name = "cli_utils"
version = "0.0.1" version = "0.0.1"
@ -620,16 +544,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "combine"
version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
dependencies = [
"bytes",
"memchr",
]
[[package]] [[package]]
name = "confy" name = "confy"
version = "0.5.0" version = "0.5.0"
@ -792,50 +706,6 @@ dependencies = [
"objc", "objc",
] ]
[[package]]
name = "coreaudio-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
dependencies = [
"bitflags",
"coreaudio-sys",
]
[[package]]
name = "coreaudio-sys"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6"
dependencies = [
"bindgen",
]
[[package]]
name = "cpal"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
dependencies = [
"alsa",
"core-foundation-sys 0.8.3",
"coreaudio-rs",
"jni",
"js-sys",
"lazy_static",
"libc",
"mach",
"ndk 0.6.0",
"ndk-glue 0.6.2",
"nix 0.23.1",
"oboe",
"parking_lot 0.11.2",
"stdweb",
"thiserror",
"web-sys",
"winapi",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.2" version = "0.2.2"
@ -1471,12 +1341,6 @@ version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "glow" name = "glow"
version = "0.11.2" version = "0.11.2"
@ -1632,12 +1496,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "hound"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.8" version = "0.2.8"
@ -1876,35 +1734,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "jni"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
dependencies = [
"cesu8",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
]
[[package]] [[package]]
name = "jni-sys" name = "jni-sys"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.60" version = "0.3.60"
@ -1946,23 +1781,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lewton"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
dependencies = [
"byteorder",
"ogg",
"tinyvec",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.135" version = "0.2.135"
@ -2032,15 +1850,6 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "mach_object" name = "mach_object"
version = "0.1.17" version = "0.1.17"
@ -2155,26 +1964,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "minimp3"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372"
dependencies = [
"minimp3-sys",
"slice-deque",
"thiserror",
]
[[package]]
name = "minimp3-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.5.3" version = "0.5.3"
@ -2232,20 +2021,7 @@ checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"jni-sys", "jni-sys",
"ndk-sys 0.2.2", "ndk-sys",
"num_enum",
"thiserror",
]
[[package]]
name = "ndk"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
dependencies = [
"bitflags",
"jni-sys",
"ndk-sys 0.3.0",
"num_enum", "num_enum",
"thiserror", "thiserror",
] ]
@ -2265,25 +2041,10 @@ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"ndk 0.5.0", "ndk",
"ndk-context", "ndk-context",
"ndk-macro", "ndk-macro",
"ndk-sys 0.2.2", "ndk-sys",
]
[[package]]
name = "ndk-glue"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f"
dependencies = [
"lazy_static",
"libc",
"log",
"ndk 0.6.0",
"ndk-context",
"ndk-macro",
"ndk-sys 0.3.0",
] ]
[[package]] [[package]]
@ -2305,15 +2066,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
[[package]]
name = "ndk-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
dependencies = [
"jni-sys",
]
[[package]] [[package]]
name = "nibble_vec" name = "nibble_vec"
version = "0.1.0" version = "0.1.0"
@ -2386,17 +2138,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.15"
@ -2498,38 +2239,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "oboe"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1"
dependencies = [
"jni",
"ndk 0.6.0",
"ndk-context",
"num-derive",
"num-traits",
"oboe-sys",
]
[[package]]
name = "oboe-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd"
dependencies = [
"cc",
]
[[package]]
name = "ogg"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.17.0"
@ -2685,12 +2394,6 @@ dependencies = [
"windows-sys 0.36.1", "windows-sys 0.36.1",
] ]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]] [[package]]
name = "peg" name = "peg"
version = "0.8.1" version = "0.8.1"
@ -3570,7 +3273,6 @@ dependencies = [
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"roc_utils", "roc_utils",
"rodio",
"serde", "serde",
"snafu", "snafu",
"tempfile", "tempfile",
@ -4187,19 +3889,6 @@ dependencies = [
"roc_error_macros", "roc_error_macros",
] ]
[[package]]
name = "rodio"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e"
dependencies = [
"claxon",
"cpal",
"hound",
"lewton",
"minimp3",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.21" version = "0.1.21"
@ -4501,12 +4190,6 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.14" version = "0.3.14"
@ -4554,17 +4237,6 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]]
name = "slice-deque"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25"
dependencies = [
"libc",
"mach",
"winapi",
]
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.6" version = "1.0.6"
@ -4682,12 +4354,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stdweb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
[[package]] [[package]]
name = "str-buf" name = "str-buf"
version = "1.0.6" version = "1.0.6"
@ -5792,9 +5458,9 @@ dependencies = [
"libc", "libc",
"log", "log",
"mio", "mio",
"ndk 0.5.0", "ndk",
"ndk-glue 0.5.2", "ndk-glue",
"ndk-sys 0.2.2", "ndk-sys",
"objc", "objc",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"percent-encoding", "percent-encoding",

View file

@ -748,6 +748,7 @@ sqrtChecked = \x ->
else else
Ok (Num.sqrt x) Ok (Num.sqrt x)
## Natural logarithm
log : Frac a -> Frac a log : Frac a -> Frac a
logChecked : Frac a -> Result (Frac a) [LogNeedsPositive] logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]

View file

@ -385,7 +385,15 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
), ),
Crash { .. } => todo!(), Crash { .. } => todo!(),
ZeroArgumentTag { .. } => todo!(), ZeroArgumentTag { .. } => todo!(),
OpaqueRef { .. } => todo!(), OpaqueRef { name, argument, .. } => maybe_paren!(
Free,
p,
|| true,
pp_sym(c, f, *name)
.append(f.space())
.append(expr(c, AppArg, f, &argument.1.value))
.group()
),
Dbg { .. } => todo!(), Dbg { .. } => todo!(),
Expect { .. } => todo!(), Expect { .. } => todo!(),
ExpectFx { .. } => todo!(), ExpectFx { .. } => todo!(),

View file

@ -105,7 +105,7 @@ This is the general procedure I follow with some helpful links:
Also, sometimes it doesn't seem to generate things quite as you expect. Also, sometimes it doesn't seem to generate things quite as you expect.
- [Alternative Online Assembler](http://shell-storm.org/online/Online-Assembler-and-Disassembler/) - - [Alternative Online Assembler](http://shell-storm.org/online/Online-Assembler-and-Disassembler/) -
Like previous but with more architecture options. Like previous but with more architecture options.
- [x86 and amd64 instruction reference](https://www.felixcloutier.com/x86/) - - [x86 and amd64 instruction reference](https://web.archive.org/web/20230221053750/https://www.felixcloutier.com/x86/) -
Great for looking up x86_64 instructions and there bytes. Great for looking up x86_64 instructions and there bytes.
Definitely missing information if you aren't used to reading it. Definitely missing information if you aren't used to reading it.
- [Intel 64 ISA Reference](https://software.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf) - - [Intel 64 ISA Reference](https://software.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf) -

View file

@ -2,10 +2,13 @@ use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
use crate::Relocation; use crate::Relocation;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use packed_struct::prelude::*; use packed_struct::prelude::*;
use roc_builtins::bitcode::FloatWidth;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::layout::{InLayout, STLayoutInterner}; use roc_mono::layout::{InLayout, STLayoutInterner};
use super::CompareOperation;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub enum AArch64GeneralReg { pub enum AArch64GeneralReg {
@ -609,9 +612,31 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
} }
} }
#[inline(always)] #[inline(always)]
fn mov_reg32_base32(_buf: &mut Vec<'_, u8>, _dst: AArch64GeneralReg, _offset: i32) {
todo!()
}
#[inline(always)]
fn mov_reg16_base32(_buf: &mut Vec<'_, u8>, _dst: AArch64GeneralReg, _offset: i32) {
todo!()
}
#[inline(always)]
fn mov_reg8_base32(_buf: &mut Vec<'_, u8>, _dst: AArch64GeneralReg, _offset: i32) {
todo!()
}
#[inline(always)]
fn mov_base32_freg64(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64FloatReg) { fn mov_base32_freg64(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64FloatReg) {
todo!("saving floating point reg to base offset for AArch64"); todo!("saving floating point reg to base offset for AArch64");
} }
#[inline(always)]
fn movesd_mem64_offset32_freg64(
_buf: &mut Vec<'_, u8>,
_ptr: AArch64GeneralReg,
_offset: i32,
_src: AArch64FloatReg,
) {
todo!()
}
#[inline(always)] #[inline(always)]
fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: AArch64GeneralReg) { fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: AArch64GeneralReg) {
if offset < 0 { if offset < 0 {
@ -624,6 +649,19 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
} }
} }
#[inline(always)]
fn mov_base32_reg32(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64GeneralReg) {
todo!()
}
#[inline(always)]
fn mov_base32_reg16(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64GeneralReg) {
todo!()
}
#[inline(always)]
fn mov_base32_reg8(_buf: &mut Vec<'_, u8>, _offset: i32, _src: AArch64GeneralReg) {
todo!()
}
#[inline(always)] #[inline(always)]
fn mov_reg64_mem64_offset32( fn mov_reg64_mem64_offset32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -640,6 +678,41 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
todo!("mem offsets over 32k for AArch64"); todo!("mem offsets over 32k for AArch64");
} }
} }
#[inline(always)]
fn mov_reg32_mem32_offset32(
buf: &mut Vec<'_, u8>,
dst: AArch64GeneralReg,
src: AArch64GeneralReg,
offset: i32,
) {
if offset < 0 {
todo!("negative mem offsets for AArch64");
} else if offset < (0xFFF << 8) {
debug_assert!(offset % 8 == 0);
ldr_reg64_reg64_imm12(buf, dst, src, (offset as u16) >> 3);
} else {
todo!("mem offsets over 32k for AArch64");
}
}
#[inline(always)]
fn mov_reg16_mem16_offset32(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src: AArch64GeneralReg,
_offset: i32,
) {
todo!()
}
#[inline(always)]
fn mov_reg8_mem8_offset32(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src: AArch64GeneralReg,
_offset: i32,
) {
todo!()
}
#[inline(always)] #[inline(always)]
fn mov_mem64_offset32_reg64( fn mov_mem64_offset32_reg64(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -657,6 +730,36 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
} }
} }
#[inline(always)]
fn mov_mem32_offset32_reg32(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_offset: i32,
_src: AArch64GeneralReg,
) {
todo!()
}
#[inline(always)]
fn mov_mem16_offset32_reg16(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_offset: i32,
_src: AArch64GeneralReg,
) {
todo!()
}
#[inline(always)]
fn mov_mem8_offset32_reg8(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_offset: i32,
_src: AArch64GeneralReg,
) {
todo!()
}
#[inline(always)] #[inline(always)]
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) { fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) {
debug_assert!(size <= 8); debug_assert!(size <= 8);
@ -788,6 +891,18 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
todo!("registers unsigned less than for AArch64"); todo!("registers unsigned less than for AArch64");
} }
#[inline(always)]
fn cmp_freg_freg_reg64(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src1: AArch64FloatReg,
_src2: AArch64FloatReg,
_width: FloatWidth,
_operation: CompareOperation,
) {
todo!("registers float comparison for AArch64");
}
#[inline(always)] #[inline(always)]
fn igt_reg64_reg64_reg64( fn igt_reg64_reg64_reg64(
_buf: &mut Vec<'_, u8>, _buf: &mut Vec<'_, u8>,
@ -938,6 +1053,14 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
{ {
todo!("sar for AArch64") todo!("sar for AArch64")
} }
fn sqrt_freg64_freg64(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) {
todo!("sqrt")
}
fn sqrt_freg32_freg32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) {
todo!("sqrt")
}
} }
impl AArch64Assembler {} impl AArch64Assembler {}

View file

@ -2,7 +2,7 @@ use crate::{
single_register_floats, single_register_int_builtins, single_register_integers, Backend, Env, single_register_floats, single_register_int_builtins, single_register_integers, Backend, Env,
Relocation, Relocation,
}; };
use bumpalo::collections::Vec; use bumpalo::collections::{CollectIn, Vec};
use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
@ -113,6 +113,13 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
); );
} }
pub enum CompareOperation {
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
/// Assembler contains calls to the backend assembly generator. /// Assembler contains calls to the backend assembly generator.
/// These calls do not necessarily map directly to a single assembly instruction. /// These calls do not necessarily map directly to a single assembly instruction.
/// They are higher level in cases where an instruction would not be common and shared between multiple architectures. /// They are higher level in cases where an instruction would not be common and shared between multiple architectures.
@ -236,22 +243,67 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
// base32 is similar to stack based instructions but they reference the base/frame pointer. // base32 is similar to stack based instructions but they reference the base/frame pointer.
fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32); fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
fn mov_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn mov_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
fn mov_reg32_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
fn mov_reg16_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
fn mov_reg8_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn mov_base32_reg32(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn mov_base32_reg16(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn mov_base32_reg8(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
// move from memory (a pointer) to register
fn mov_reg64_mem64_offset32( fn mov_reg64_mem64_offset32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
dst: GeneralReg, dst: GeneralReg,
src: GeneralReg, src: GeneralReg,
offset: i32, offset: i32,
); );
fn mov_reg32_mem32_offset32(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src: GeneralReg,
offset: i32,
);
fn mov_reg16_mem16_offset32(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src: GeneralReg,
offset: i32,
);
fn mov_reg8_mem8_offset32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg, offset: i32);
// move from register to memory
fn mov_mem64_offset32_reg64( fn mov_mem64_offset32_reg64(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
dst: GeneralReg, dst: GeneralReg,
offset: i32, offset: i32,
src: GeneralReg, src: GeneralReg,
); );
fn mov_mem32_offset32_reg32(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
offset: i32,
src: GeneralReg,
);
fn mov_mem16_offset32_reg16(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
offset: i32,
src: GeneralReg,
);
fn mov_mem8_offset32_reg8(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, src: GeneralReg);
fn movesd_mem64_offset32_freg64(
buf: &mut Vec<'_, u8>,
ptr: GeneralReg,
offset: i32,
src: FloatReg,
);
/// Sign extends the data at `offset` with `size` as it copies it to `dst` /// Sign extends the data at `offset` with `size` as it copies it to `dst`
/// size must be less than or equal to 8. /// size must be less than or equal to 8.
@ -265,6 +317,9 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg); fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg); fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn sqrt_freg64_freg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg); fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
fn mul_freg32_freg32_freg32( fn mul_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -361,6 +416,15 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
src2: GeneralReg, src2: GeneralReg,
); );
fn cmp_freg_freg_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: FloatReg,
src2: FloatReg,
width: FloatWidth,
operation: CompareOperation,
);
fn igt_reg64_reg64_reg64( fn igt_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
dst: GeneralReg, dst: GeneralReg,
@ -958,6 +1022,30 @@ impl<
} }
} }
fn build_num_sub_checked(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
num_layout: &InLayout<'a>,
return_layout: &InLayout<'a>,
) {
let function_name = match self.interner().get(*num_layout) {
Layout::Builtin(Builtin::Int(width)) => &bitcode::NUM_SUB_CHECKED_INT[width],
Layout::Builtin(Builtin::Float(width)) => &bitcode::NUM_SUB_CHECKED_FLOAT[width],
Layout::Builtin(Builtin::Decimal) => bitcode::DEC_SUB_WITH_OVERFLOW,
x => internal_error!("NumSubChecked is not defined for {:?}", x),
};
self.build_fn_call(
dst,
function_name.to_string(),
&[*src1, *src2],
&[*num_layout, *num_layout],
return_layout,
)
}
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>) { fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>) {
use Builtin::Int; use Builtin::Int;
@ -1112,6 +1200,16 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::eq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::eq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::STR => {
// use a zig call
self.build_fn_call(
dst,
bitcode::STR_EQUAL.to_string(),
&[*src1, *src2],
&[Layout::STR, Layout::STR],
&Layout::BOOL,
)
}
x => todo!("NumEq: layout, {:?}", x), x => todo!("NumEq: layout, {:?}", x),
} }
} }
@ -1128,42 +1226,27 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::STR => {
self.build_fn_call(
dst,
bitcode::STR_EQUAL.to_string(),
&[*src1, *src2],
&[Layout::STR, Layout::STR],
&Layout::BOOL,
);
// negate the result
let tmp = &Symbol::DEV_TMP;
let tmp_reg = self.storage_manager.claim_general_reg(&mut self.buf, tmp);
ASM::mov_reg64_imm64(&mut self.buf, tmp_reg, 164);
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
ASM::neq_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, tmp_reg);
}
x => todo!("NumNeq: layout, {:?}", x), x => todo!("NumNeq: layout, {:?}", x),
} }
} }
fn build_and(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) {
match *arg_layout {
Layout::BOOL => {
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, src1);
let src2_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, src2);
ASM::and_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
x => todo!("And: layout, {:?}", x),
}
}
fn build_or(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>) {
match *arg_layout {
Layout::BOOL => {
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, src1);
let src2_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, src2);
ASM::or_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
x => todo!("Or: layout, {:?}", x),
}
}
fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>) { fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>) {
match *arg_layout { match *arg_layout {
Layout::BOOL => { Layout::BOOL => {
@ -1209,6 +1292,20 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::ult_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::ult_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::Builtin(Builtin::Float(width)) => {
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
ASM::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::LessThan,
);
}
x => todo!("NumLt: layout, {:?}", x), x => todo!("NumLt: layout, {:?}", x),
} }
} }
@ -1241,6 +1338,20 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::ugt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::ugt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::Builtin(Builtin::Float(width)) => {
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
ASM::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::GreaterThan,
);
}
x => todo!("NumGt: layout, {:?}", x), x => todo!("NumGt: layout, {:?}", x),
} }
} }
@ -1321,6 +1432,26 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::lte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::lte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::F64 | Layout::F32 => {
let width = if *arg_layout == Layout::F64 {
FloatWidth::F64
} else {
FloatWidth::F32
};
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
ASM::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::LessThanOrEqual,
);
}
x => todo!("NumLte: layout, {:?}", x), x => todo!("NumLte: layout, {:?}", x),
} }
} }
@ -1343,6 +1474,26 @@ impl<
.load_to_general_reg(&mut self.buf, src2); .load_to_general_reg(&mut self.buf, src2);
ASM::gte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); ASM::gte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
} }
Layout::F64 | Layout::F32 => {
let width = if *arg_layout == Layout::F64 {
FloatWidth::F64
} else {
FloatWidth::F32
};
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
ASM::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::GreaterThanOrEqual,
);
}
x => todo!("NumGte: layout, {:?}", x), x => todo!("NumGte: layout, {:?}", x),
} }
} }
@ -1567,43 +1718,14 @@ impl<
ASM::add_reg64_reg64_reg64(buf, tmp, tmp, list_ptr); ASM::add_reg64_reg64_reg64(buf, tmp, tmp, list_ptr);
let element_ptr = tmp; let element_ptr = tmp;
match *ret_layout { Self::ptr_read(
single_register_integers!() if ret_stack_size == 8 => {
let dst_reg = storage_manager.claim_general_reg(buf, dst);
ASM::mov_reg64_mem64_offset32(buf, dst_reg, element_ptr, 0);
}
single_register_floats!() => {
let dst_reg = storage_manager.claim_float_reg(buf, dst);
ASM::mov_freg64_freg64(buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
}
Layout::STR => {
// the `list_ptr` register is now unused, and we can use it as scratch space
let tmp_reg = list_ptr;
Self::unbox_str_or_list(
buf, buf,
storage_manager, storage_manager,
*dst, self.layout_interner,
element_ptr, element_ptr,
tmp_reg, *ret_layout,
*dst,
); );
}
other => {
//
match self.layout_interner.get(other) {
Layout::Boxed(_) => {
let dst_reg = storage_manager.claim_general_reg(buf, dst);
ASM::mov_reg64_reg64(buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
}
_ => {
todo!(
"cannot load {} from the heap yet",
self.layout_interner.dbg(other)
);
}
}
}
}
}); });
}, },
); );
@ -1871,10 +1993,11 @@ impl<
fn create_array( fn create_array(
&mut self, &mut self,
sym: &Symbol, sym: &Symbol,
element_layout: &InLayout<'a>, element_in_layout: &InLayout<'a>,
elements: &'a [ListLiteralElement<'a>], elements: &[ListLiteralElement<'a>],
) { ) {
let element_width = self.layout_interner.stack_size(*element_layout) as u64; let element_layout = self.layout_interner.get(*element_in_layout);
let element_width = self.layout_interner.stack_size(*element_in_layout) as u64;
// load the total size of the data we want to store (excludes refcount) // load the total size of the data we want to store (excludes refcount)
let data_bytes_symbol = Symbol::DEV_TMP; let data_bytes_symbol = Symbol::DEV_TMP;
@ -1904,54 +2027,34 @@ impl<
.load_to_general_reg(&mut self.buf, &Symbol::DEV_TMP3); .load_to_general_reg(&mut self.buf, &Symbol::DEV_TMP3);
// Copy everything into output array. // Copy everything into output array.
let mut elem_offset = 0; let mut element_offset = 0;
for elem in elements { for elem in elements {
// TODO: this could be a lot faster when loading large lists // TODO: this could be a lot faster when loading large lists
// if we move matching on the element layout to outside this loop. // if we move matching on the element layout to outside this loop.
// It also greatly bloats the code here. // It also greatly bloats the code here.
// Refactor this and switch to one external match. // Refactor this and switch to one external match.
// We also could make loadining indivitual literals much faster // We also could make loadining indivitual literals much faster
let elem_sym = match elem { let element_symbol = match elem {
ListLiteralElement::Symbol(sym) => sym, ListLiteralElement::Symbol(sym) => *sym,
ListLiteralElement::Literal(lit) => { ListLiteralElement::Literal(lit) => {
self.load_literal(&Symbol::DEV_TMP, element_layout, lit); self.load_literal(&Symbol::DEV_TMP, element_in_layout, lit);
&Symbol::DEV_TMP Symbol::DEV_TMP
} }
}; };
// TODO: Expand to all types.
match self.layout_interner.get(*element_layout) { Self::ptr_write(
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64) | Builtin::Bool) => {
let sym_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, elem_sym);
ASM::mov_mem64_offset32_reg64(&mut self.buf, ptr_reg, elem_offset, sym_reg);
}
_ if element_width == 0 => {}
_ if element_width > 8 => {
let (from_offset, size) = self.storage_manager.stack_offset_and_size(elem_sym);
debug_assert!(from_offset % 8 == 0);
debug_assert!(size % 8 == 0);
debug_assert_eq!(size as u64, element_width);
self.storage_manager.with_tmp_general_reg(
&mut self.buf, &mut self.buf,
|_storage_manager, buf, tmp_reg| { &mut self.storage_manager,
for i in (0..size as i32).step_by(8) {
ASM::mov_reg64_base32(buf, tmp_reg, from_offset + i);
ASM::mov_mem64_offset32_reg64(
buf,
ptr_reg, ptr_reg,
elem_offset + i, element_offset,
tmp_reg, element_width,
element_layout,
element_symbol,
); );
}
}, element_offset += element_width as i32;
); if element_symbol == Symbol::DEV_TMP {
} self.free_symbol(&element_symbol);
x => todo!("copying data to list with layout, {:?}", x),
}
elem_offset += element_width as i32;
if elem_sym == &Symbol::DEV_TMP {
self.free_symbol(elem_sym);
} }
} }
@ -2049,38 +2152,15 @@ impl<
let element_width = self.layout_interner.stack_size(element_layout) as u64; let element_width = self.layout_interner.stack_size(element_layout) as u64;
let element_offset = 0; let element_offset = 0;
// TODO: Expand to all types. Self::ptr_write(
match self.layout_interner.get(element_layout) {
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let sym_reg = self
.storage_manager
.load_to_general_reg(&mut self.buf, &value);
ASM::mov_mem64_offset32_reg64(&mut self.buf, ptr_reg, element_offset, sym_reg);
}
_ if element_width == 0 => {}
_ if element_width > 8 => {
let (from_offset, size) = self.storage_manager.stack_offset_and_size(&value);
debug_assert!(from_offset % 8 == 0);
debug_assert!(size % 8 == 0);
debug_assert_eq!(size as u64, element_width);
self.storage_manager.with_tmp_general_reg(
&mut self.buf, &mut self.buf,
|_storage_manager, buf, tmp_reg| { &mut self.storage_manager,
// a crude memcpy
for i in (0..size as i32).step_by(8) {
ASM::mov_reg64_base32(buf, tmp_reg, from_offset + i);
ASM::mov_mem64_offset32_reg64(
buf,
ptr_reg, ptr_reg,
element_offset + i, element_offset,
tmp_reg, element_width,
self.layout_interner.get(element_layout),
value,
); );
}
},
);
}
x => todo!("copying data to list with layout, {:?}", x),
}
if value == Symbol::DEV_TMP { if value == Symbol::DEV_TMP {
self.free_symbol(&value); self.free_symbol(&value);
@ -2098,26 +2178,15 @@ impl<
.storage_manager .storage_manager
.load_to_general_reg(&mut self.buf, &ptr); .load_to_general_reg(&mut self.buf, &ptr);
let ret_stack_size = self.layout_interner.stack_size(element_layout); Self::ptr_read(
match element_layout {
single_register_integers!() if ret_stack_size == 8 => {
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, &dst);
ASM::mov_reg64_mem64_offset32(&mut self.buf, dst_reg, ptr_reg, 0);
}
Layout::STR => {
self.storage_manager.with_tmp_general_reg(
&mut self.buf, &mut self.buf,
|storage_manager, buf, tmp_reg| { &mut self.storage_manager,
Self::unbox_str_or_list(buf, storage_manager, dst, ptr_reg, tmp_reg); self.layout_interner,
}, ptr_reg,
element_layout,
dst,
); );
} }
_ => {
todo!("unboxing of {:?}", self.layout_interner.dbg(element_layout))
}
}
}
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) { fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) {
self.storage_manager.load_union_tag_id( self.storage_manager.load_union_tag_id(
@ -2165,6 +2234,33 @@ impl<
let val = *x; let val = *x;
ASM::mov_reg64_imm64(&mut self.buf, reg, i128::from_ne_bytes(val) as i64); ASM::mov_reg64_imm64(&mut self.buf, reg, i128::from_ne_bytes(val) as i64);
} }
(
Literal::Int(bytes),
Layout::Builtin(Builtin::Int(IntWidth::I128 | IntWidth::U128)),
) => {
self.storage_manager.with_tmp_general_reg(
&mut self.buf,
|storage_manager, buf, reg| {
let base_offset = storage_manager.claim_stack_area(sym, 16);
let mut num_bytes = [0; 8];
num_bytes.copy_from_slice(&bytes[..8]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset, reg);
num_bytes.copy_from_slice(&bytes[8..16]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset + 8, reg);
},
);
}
(Literal::Byte(x), Layout::Builtin(Builtin::Int(IntWidth::U8 | IntWidth::I8))) => {
let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
let val = *x;
ASM::mov_reg64_imm64(&mut self.buf, reg, val as i64);
}
(Literal::Bool(x), Layout::Builtin(Builtin::Bool)) => { (Literal::Bool(x), Layout::Builtin(Builtin::Bool)) => {
let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym); let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
let val = [*x as u8; 16]; let val = [*x as u8; 16];
@ -2180,7 +2276,27 @@ impl<
let val = *x as f32; let val = *x as f32;
ASM::mov_freg32_imm32(&mut self.buf, &mut self.relocs, reg, val); ASM::mov_freg32_imm32(&mut self.buf, &mut self.relocs, reg, val);
} }
(Literal::Str(x), Layout::Builtin(Builtin::Str)) if x.len() < 24 => { (Literal::Decimal(bytes), Layout::Builtin(Builtin::Decimal)) => {
self.storage_manager.with_tmp_general_reg(
&mut self.buf,
|storage_manager, buf, reg| {
let base_offset = storage_manager.claim_stack_area(sym, 16);
let mut num_bytes = [0; 8];
num_bytes.copy_from_slice(&bytes[..8]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset, reg);
num_bytes.copy_from_slice(&bytes[8..16]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset + 8, reg);
},
);
}
(Literal::Str(x), Layout::Builtin(Builtin::Str)) => {
if x.len() < 24 {
// Load small string. // Load small string.
self.storage_manager.with_tmp_general_reg( self.storage_manager.with_tmp_general_reg(
&mut self.buf, &mut self.buf,
@ -2207,6 +2323,17 @@ impl<
ASM::mov_base32_reg64(buf, base_offset + 16, reg); ASM::mov_base32_reg64(buf, base_offset + 16, reg);
}, },
); );
} else {
// load large string (pretend it's a `List U8`). We should move this data into
// the binary eventually because our RC algorithm won't free this value
let elements: Vec<_> = x
.as_bytes()
.iter()
.map(|b| ListLiteralElement::Literal(Literal::Byte(*b)))
.collect_in(self.storage_manager.env.arena);
self.create_array(sym, &Layout::U8, elements.into_bump_slice())
}
} }
x => todo!("loading literal, {:?}", x), x => todo!("loading literal, {:?}", x),
} }
@ -2447,6 +2574,18 @@ impl<
} }
} }
} }
fn build_num_sqrt(&mut self, dst: Symbol, src: Symbol, float_width: FloatWidth) {
let buf = &mut self.buf;
let dst_reg = self.storage_manager.claim_float_reg(buf, &dst);
let src_reg = self.storage_manager.load_to_float_reg(buf, &src);
match float_width {
FloatWidth::F32 => ASM::sqrt_freg32_freg32(buf, dst_reg, src_reg),
FloatWidth::F64 => ASM::sqrt_freg64_freg64(buf, dst_reg, src_reg),
}
}
} }
/// This impl block is for ir related instructions that need backend specific information. /// This impl block is for ir related instructions that need backend specific information.
@ -2494,6 +2633,115 @@ impl<
ASM::mov_base32_reg64(buf, base_offset + 16, tmp_reg); ASM::mov_base32_reg64(buf, base_offset + 16, tmp_reg);
} }
fn ptr_read(
buf: &mut Vec<'a, u8>,
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
layout_interner: &STLayoutInterner<'a>,
ptr_reg: GeneralReg,
element_in_layout: InLayout<'a>,
dst: Symbol,
) {
match layout_interner.get(element_in_layout) {
Layout::Builtin(builtin) => match builtin {
Builtin::Int(int_width) => match int_width {
IntWidth::I128 | IntWidth::U128 => {
// can we treat this as 2 u64's?
todo!()
}
IntWidth::I64 | IntWidth::U64 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
}
IntWidth::I32 | IntWidth::U32 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg32_mem32_offset32(buf, dst_reg, ptr_reg, 0);
}
IntWidth::I16 | IntWidth::U16 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg16_mem16_offset32(buf, dst_reg, ptr_reg, 0);
}
IntWidth::I8 | IntWidth::U8 => {
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, 0);
}
},
Builtin::Float(_) => {
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
ASM::mov_freg64_freg64(buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
}
Builtin::Bool => {
// the same as an 8-bit integer
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, 0);
}
Builtin::Decimal => {
// same as 128-bit integer
}
Builtin::Str | Builtin::List(_) => {
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
Self::unbox_str_or_list(buf, storage_manager, dst, ptr_reg, tmp_reg);
});
}
},
Layout::Boxed(_) => {
// the same as 64-bit integer (for 64-bit targets)
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
}
_ => todo!("unboxing of {:?}", layout_interner.dbg(element_in_layout)),
}
}
fn ptr_write(
buf: &mut Vec<'a, u8>,
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
ptr_reg: GeneralReg,
element_offset: i32,
element_width: u64,
element_layout: Layout<'a>,
value: Symbol,
) {
match element_layout {
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, element_offset, sym_reg);
}
Layout::Builtin(Builtin::Int(IntWidth::I32 | IntWidth::U32)) => {
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
ASM::mov_mem32_offset32_reg32(buf, ptr_reg, element_offset, sym_reg);
}
Layout::Builtin(Builtin::Int(IntWidth::I16 | IntWidth::U16)) => {
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
ASM::mov_mem16_offset32_reg16(buf, ptr_reg, element_offset, sym_reg);
}
Layout::Builtin(Builtin::Int(IntWidth::I8 | IntWidth::U8) | Builtin::Bool) => {
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
ASM::mov_mem8_offset32_reg8(buf, ptr_reg, element_offset, sym_reg);
}
Layout::Builtin(Builtin::Float(FloatWidth::F64 | FloatWidth::F32)) => {
let sym_reg = storage_manager.load_to_float_reg(buf, &value);
ASM::movesd_mem64_offset32_freg64(buf, ptr_reg, element_offset, sym_reg);
}
_ if element_width == 0 => {}
_ if element_width > 8 => {
let (from_offset, size) = storage_manager.stack_offset_and_size(&value);
debug_assert!(from_offset % 8 == 0);
debug_assert!(size % 8 == 0);
debug_assert_eq!(size as u64, element_width);
storage_manager.with_tmp_general_reg(buf, |_storage_manager, buf, tmp_reg| {
// a crude memcpy
for i in (0..size as i32).step_by(8) {
ASM::mov_reg64_base32(buf, tmp_reg, from_offset + i);
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, element_offset + i, tmp_reg);
}
});
}
x => todo!("copying data to list with layout, {:?}", x),
}
}
/// Updates a jump instruction to a new offset and returns the number of bytes written. /// Updates a jump instruction to a new offset and returns the number of bytes written.
fn update_jmp_imm32_offset( fn update_jmp_imm32_offset(
&mut self, &mut self,

View file

@ -9,7 +9,6 @@ use roc_collections::all::{MutMap, MutSet};
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::{ use roc_mono::{
borrow::Ownership,
ir::{JoinPointId, Param}, ir::{JoinPointId, Param},
layout::{ layout::{
Builtin, InLayout, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout, Builtin, InLayout, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout,
@ -315,7 +314,7 @@ impl<
reg: Some(Float(_)), reg: Some(Float(_)),
.. ..
}) => { }) => {
internal_error!("Cannot load floating point symbol into GeneralReg: {}", sym) internal_error!("Cannot load floating point symbol into GeneralReg: {sym:?}")
} }
Stack(Primitive { Stack(Primitive {
reg: None, reg: None,
@ -350,8 +349,10 @@ impl<
self.free_reference(sym); self.free_reference(sym);
reg reg
} }
Stack(Complex { .. }) => { Stack(Complex { size, .. }) => {
internal_error!("Cannot load large values into general registers: {}", sym) internal_error!(
"Cannot load large values (size {size}) into general registers: {sym:?}",
)
} }
NoData => { NoData => {
internal_error!("Cannot load no data into general registers: {}", sym) internal_error!("Cannot load no data into general registers: {}", sym)
@ -448,7 +449,7 @@ impl<
reg: Some(Float(_)), reg: Some(Float(_)),
.. ..
}) => { }) => {
internal_error!("Cannot load floating point symbol into GeneralReg: {}", sym) internal_error!("Cannot load floating point symbol into GeneralReg: {sym:?}",)
} }
Stack(Primitive { Stack(Primitive {
reg: None, reg: None,
@ -458,19 +459,25 @@ impl<
ASM::mov_reg64_base32(buf, reg, *base_offset); ASM::mov_reg64_base32(buf, reg, *base_offset);
} }
Stack(ReferencedPrimitive { Stack(ReferencedPrimitive {
base_offset, size, .. base_offset,
}) if base_offset % 8 == 0 && *size == 8 => { size,
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack. sign_extend,
ASM::mov_reg64_base32(buf, reg, *base_offset); }) => {
debug_assert!(*size <= 8);
if *sign_extend {
ASM::movsx_reg64_base32(buf, reg, *base_offset, *size as u8)
} else {
ASM::movzx_reg64_base32(buf, reg, *base_offset, *size as u8)
} }
Stack(ReferencedPrimitive { .. }) => {
todo!("loading referenced primitives")
} }
Stack(Complex { .. }) => { Stack(Complex { size, .. }) => {
internal_error!("Cannot load large values into general registers: {}", sym) internal_error!(
"Cannot load large values (size {size}) into general registers: {sym:?}",
)
} }
NoData => { NoData => {
internal_error!("Cannot load no data into general registers: {}", sym) internal_error!("Cannot load no data into general registers: {:?}", sym)
} }
} }
} }
@ -553,7 +560,7 @@ impl<
self.allocation_map.insert(*sym, owned_data); self.allocation_map.insert(*sym, owned_data);
self.symbol_storage_map.insert( self.symbol_storage_map.insert(
*sym, *sym,
Stack(if is_primitive(layout) { Stack(if is_primitive(layout_interner, layout) {
ReferencedPrimitive { ReferencedPrimitive {
base_offset: data_offset, base_offset: data_offset,
size, size,
@ -739,16 +746,74 @@ impl<
layout: &InLayout<'a>, layout: &InLayout<'a>,
) { ) {
match layout_interner.get(*layout) { match layout_interner.get(*layout) {
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => { Layout::Builtin(builtin) => match builtin {
Builtin::Int(int_width) => match int_width {
IntWidth::I128 | IntWidth::U128 => {
let (from_offset, size) = self.stack_offset_and_size(sym);
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
IntWidth::I64 | IntWidth::U64 => {
debug_assert_eq!(to_offset % 8, 0); debug_assert_eq!(to_offset % 8, 0);
let reg = self.load_to_general_reg(buf, sym); let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg64(buf, to_offset, reg); ASM::mov_base32_reg64(buf, to_offset, reg);
} }
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => { IntWidth::I32 | IntWidth::U32 => {
debug_assert_eq!(to_offset % 4, 0);
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg32(buf, to_offset, reg);
}
IntWidth::I16 | IntWidth::U16 => {
debug_assert_eq!(to_offset % 2, 0);
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg16(buf, to_offset, reg);
}
IntWidth::I8 | IntWidth::U8 => {
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg8(buf, to_offset, reg);
}
},
Builtin::Float(float_width) => match float_width {
FloatWidth::F64 => {
debug_assert_eq!(to_offset % 8, 0); debug_assert_eq!(to_offset % 8, 0);
let reg = self.load_to_float_reg(buf, sym); let reg = self.load_to_float_reg(buf, sym);
ASM::mov_base32_freg64(buf, to_offset, reg); ASM::mov_base32_freg64(buf, to_offset, reg);
} }
FloatWidth::F32 => todo!(),
},
Builtin::Bool => {
// same as 8-bit integer
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg8(buf, to_offset, reg);
}
Builtin::Decimal => todo!(),
Builtin::Str | Builtin::List(_) => {
let (from_offset, size) = self.stack_offset_and_size(sym);
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
},
Layout::Boxed(_) => {
// like a 64-bit integer
debug_assert_eq!(to_offset % 8, 0);
let reg = self.load_to_general_reg(buf, sym);
ASM::mov_base32_reg64(buf, to_offset, reg);
}
Layout::LambdaSet(lambda_set) => {
// like its runtime representation
self.copy_symbol_to_stack_offset(
layout_interner,
buf,
to_offset,
sym,
&lambda_set.runtime_representation(),
)
}
_ if layout_interner.stack_size(*layout) == 0 => {} _ if layout_interner.stack_size(*layout) == 0 => {}
// TODO: Verify this is always true. // TODO: Verify this is always true.
// The dev backend does not deal with refcounting and does not care about if data is safe to memcpy. // The dev backend does not deal with refcounting and does not care about if data is safe to memcpy.
@ -756,20 +821,64 @@ impl<
// Later, it will be reloaded and stored in refcounted as needed. // Later, it will be reloaded and stored in refcounted as needed.
_ if layout_interner.stack_size(*layout) > 8 => { _ if layout_interner.stack_size(*layout) > 8 => {
let (from_offset, size) = self.stack_offset_and_size(sym); let (from_offset, size) = self.stack_offset_and_size(sym);
debug_assert!(from_offset % 8 == 0); debug_assert_eq!(from_offset % 8, 0);
debug_assert!(size % 8 == 0); debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout)); debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| { self.copy_to_stack_offset(buf, size, from_offset, to_offset)
for i in (0..size as i32).step_by(8) {
ASM::mov_reg64_base32(buf, reg, from_offset + i);
ASM::mov_base32_reg64(buf, to_offset + i, reg);
}
});
} }
x => todo!("copying data to the stack with layout, {:?}", x), x => todo!("copying data to the stack with layout, {:?}", x),
} }
} }
pub fn copy_to_stack_offset(
&mut self,
buf: &mut Vec<'a, u8>,
size: u32,
from_offset: i32,
to_offset: i32,
) {
let mut copied = 0;
let size = size as i32;
self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| {
if size - copied >= 8 {
for _ in (0..(size - copied)).step_by(8) {
ASM::mov_reg64_base32(buf, reg, from_offset + copied);
ASM::mov_base32_reg64(buf, to_offset + copied, reg);
copied += 8;
}
}
if size - copied >= 4 {
for _ in (0..(size - copied)).step_by(4) {
ASM::mov_reg32_base32(buf, reg, from_offset + copied);
ASM::mov_base32_reg32(buf, to_offset + copied, reg);
copied += 4;
}
}
if size - copied >= 2 {
for _ in (0..(size - copied)).step_by(2) {
ASM::mov_reg16_base32(buf, reg, from_offset + copied);
ASM::mov_base32_reg16(buf, to_offset + copied, reg);
copied += 2;
}
}
if size - copied >= 1 {
for _ in (0..(size - copied)).step_by(1) {
ASM::mov_reg8_base32(buf, reg, from_offset + copied);
ASM::mov_base32_reg8(buf, to_offset + copied, reg);
copied += 1;
}
}
});
}
#[allow(dead_code)] #[allow(dead_code)]
/// Ensures that a register is free. If it is not free, data will be moved to make it free. /// Ensures that a register is free. If it is not free, data will be moved to make it free.
pub fn ensure_reg_free( pub fn ensure_reg_free(
@ -1008,15 +1117,10 @@ impl<
param_storage.reserve(params.len()); param_storage.reserve(params.len());
for Param { for Param {
symbol, symbol,
ownership, ownership: _,
layout, layout,
} in params } in params
{ {
if *ownership == Ownership::Borrowed {
// These probably need to be passed by pointer/reference?
// Otherwise, we probably need to copy back to the param at the end of the joinpoint.
todo!("joinpoints with borrowed parameters");
}
// Claim a location for every join point parameter to be loaded at. // Claim a location for every join point parameter to be loaded at.
// Put everything on the stack for simplicity. // Put everything on the stack for simplicity.
match *layout { match *layout {
@ -1331,6 +1435,15 @@ impl<
} }
} }
fn is_primitive(layout: InLayout<'_>) -> bool { fn is_primitive(layout_interner: &mut STLayoutInterner<'_>, layout: InLayout<'_>) -> bool {
matches!(layout, single_register_layouts!()) match layout {
single_register_layouts!() => true,
_ => match layout_interner.get(layout) {
Layout::Boxed(_) => true,
Layout::LambdaSet(lambda_set) => {
is_primitive(layout_interner, lambda_set.runtime_representation())
}
_ => false,
},
}
} }

File diff suppressed because it is too large Load diff

View file

@ -447,6 +447,9 @@ trait Backend<'a> {
LowLevel::NumAddChecked => { LowLevel::NumAddChecked => {
self.build_num_add_checked(sym, &args[0], &args[1], &arg_layouts[0], ret_layout) self.build_num_add_checked(sym, &args[0], &args[1], &arg_layouts[0], ret_layout)
} }
LowLevel::NumSubChecked => {
self.build_num_sub_checked(sym, &args[0], &args[1], &arg_layouts[0], ret_layout)
}
LowLevel::NumAcos => self.build_fn_call( LowLevel::NumAcos => self.build_fn_call(
sym, sym,
bitcode::NUM_ACOS[FloatWidth::F64].to_string(), bitcode::NUM_ACOS[FloatWidth::F64].to_string(),
@ -551,6 +554,27 @@ trait Backend<'a> {
); );
self.build_num_sub_wrap(sym, &args[0], &args[1], ret_layout) self.build_num_sub_wrap(sym, &args[0], &args[1], ret_layout)
} }
LowLevel::NumSubSaturated => match self.interner().get(*ret_layout) {
Layout::Builtin(Builtin::Int(int_width)) => self.build_fn_call(
sym,
bitcode::NUM_SUB_SATURATED_INT[int_width].to_string(),
args,
arg_layouts,
ret_layout,
),
Layout::Builtin(Builtin::Float(FloatWidth::F32)) => {
self.build_num_sub(sym, &args[0], &args[1], ret_layout)
}
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
// saturated sub is just normal sub
self.build_num_sub(sym, &args[0], &args[1], ret_layout)
}
Layout::Builtin(Builtin::Decimal) => {
// self.load_args_and_call_zig(backend, bitcode::DEC_SUB_SATURATED)
todo!()
}
_ => internal_error!("invalid return type"),
},
LowLevel::NumBitwiseAnd => { LowLevel::NumBitwiseAnd => {
if let Layout::Builtin(Builtin::Int(int_width)) = self.interner().get(*ret_layout) { if let Layout::Builtin(Builtin::Int(int_width)) = self.interner().get(*ret_layout) {
self.build_int_bitwise_and(sym, &args[0], &args[1], int_width) self.build_int_bitwise_and(sym, &args[0], &args[1], int_width)
@ -572,6 +596,20 @@ trait Backend<'a> {
internal_error!("bitwise xor on a non-integer") internal_error!("bitwise xor on a non-integer")
} }
} }
LowLevel::And => {
if let Layout::Builtin(Builtin::Bool) = self.interner().get(*ret_layout) {
self.build_int_bitwise_and(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise and on a non-integer")
}
}
LowLevel::Or => {
if let Layout::Builtin(Builtin::Bool) = self.interner().get(*ret_layout) {
self.build_int_bitwise_or(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise or on a non-integer")
}
}
LowLevel::NumShiftLeftBy => { LowLevel::NumShiftLeftBy => {
if let Layout::Builtin(Builtin::Int(int_width)) = self.interner().get(*ret_layout) { if let Layout::Builtin(Builtin::Int(int_width)) = self.interner().get(*ret_layout) {
self.build_int_shift_left(sym, &args[0], &args[1], int_width) self.build_int_shift_left(sym, &args[0], &args[1], int_width)
@ -623,32 +661,6 @@ trait Backend<'a> {
); );
self.build_neq(sym, &args[0], &args[1], &arg_layouts[0]) self.build_neq(sym, &args[0], &args[1], &arg_layouts[0])
} }
LowLevel::And => {
debug_assert_eq!(2, args.len(), "And: expected to have exactly two argument");
debug_assert_eq!(
arg_layouts[0], arg_layouts[1],
"And: expected all arguments of to have the same layout"
);
debug_assert_eq!(
Layout::BOOL,
*ret_layout,
"And: expected to have return layout of type Bool"
);
self.build_and(sym, &args[0], &args[1], &arg_layouts[0])
}
LowLevel::Or => {
debug_assert_eq!(2, args.len(), "Or: expected to have exactly two argument");
debug_assert_eq!(
arg_layouts[0], arg_layouts[1],
"Or: expected all arguments of to have the same layout"
);
debug_assert_eq!(
Layout::BOOL,
*ret_layout,
"Or: expected to have return layout of type Bool"
);
self.build_or(sym, &args[0], &args[1], &arg_layouts[0])
}
LowLevel::Not => { LowLevel::Not => {
debug_assert_eq!(1, args.len(), "Not: expected to have exactly one argument"); debug_assert_eq!(1, args.len(), "Not: expected to have exactly one argument");
debug_assert_eq!( debug_assert_eq!(
@ -739,6 +751,30 @@ trait Backend<'a> {
); );
self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0]) self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0])
} }
LowLevel::NumLogUnchecked => {
let float_width = match arg_layouts[0] {
Layout::F64 => FloatWidth::F64,
Layout::F32 => FloatWidth::F32,
_ => unreachable!("invalid layout for sqrt"),
};
self.build_fn_call(
sym,
bitcode::NUM_LOG[float_width].to_string(),
args,
arg_layouts,
ret_layout,
)
}
LowLevel::NumSqrtUnchecked => {
let float_width = match arg_layouts[0] {
Layout::F64 => FloatWidth::F64,
Layout::F32 => FloatWidth::F32,
_ => unreachable!("invalid layout for sqrt"),
};
self.build_num_sqrt(*sym, args[0], float_width);
}
LowLevel::NumRound => self.build_fn_call( LowLevel::NumRound => self.build_fn_call(
sym, sym,
bitcode::NUM_ROUND_F64[IntWidth::I64].to_string(), bitcode::NUM_ROUND_F64[IntWidth::I64].to_string(),
@ -819,6 +855,13 @@ trait Backend<'a> {
arg_layouts, arg_layouts,
ret_layout, ret_layout,
), ),
LowLevel::StrJoinWith => self.build_fn_call(
sym,
bitcode::STR_JOIN_WITH.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrSplit => self.build_fn_call( LowLevel::StrSplit => self.build_fn_call(
sym, sym,
bitcode::STR_SPLIT.to_string(), bitcode::STR_SPLIT.to_string(),
@ -840,6 +883,13 @@ trait Backend<'a> {
arg_layouts, arg_layouts,
ret_layout, ret_layout,
), ),
LowLevel::StrAppendScalar => self.build_fn_call(
sym,
bitcode::STR_APPEND_SCALAR.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrEndsWith => self.build_fn_call( LowLevel::StrEndsWith => self.build_fn_call(
sym, sym,
bitcode::STR_ENDS_WITH.to_string(), bitcode::STR_ENDS_WITH.to_string(),
@ -854,6 +904,122 @@ trait Backend<'a> {
arg_layouts, arg_layouts,
ret_layout, ret_layout,
), ),
LowLevel::StrSubstringUnsafe => self.build_fn_call(
sym,
bitcode::STR_SUBSTRING_UNSAFE.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrToUtf8 => self.build_fn_call(
sym,
bitcode::STR_TO_UTF8.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrCountUtf8Bytes => self.build_fn_call(
sym,
bitcode::STR_COUNT_UTF8_BYTES.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrFromUtf8Range => self.build_fn_call(
sym,
bitcode::STR_FROM_UTF8_RANGE.to_string(),
args,
arg_layouts,
ret_layout,
),
// LowLevel::StrToUtf8 => self.build_fn_call(
// sym,
// bitcode::STR_TO_UTF8.to_string(),
// args,
// arg_layouts,
// ret_layout,
// ),
LowLevel::StrRepeat => self.build_fn_call(
sym,
bitcode::STR_REPEAT.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrTrim => self.build_fn_call(
sym,
bitcode::STR_TRIM.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrTrimLeft => self.build_fn_call(
sym,
bitcode::STR_TRIM_LEFT.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrTrimRight => self.build_fn_call(
sym,
bitcode::STR_TRIM_RIGHT.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrReserve => self.build_fn_call(
sym,
bitcode::STR_RESERVE.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrWithCapacity => self.build_fn_call(
sym,
bitcode::STR_WITH_CAPACITY.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrToScalars => self.build_fn_call(
sym,
bitcode::STR_TO_SCALARS.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrGetUnsafe => self.build_fn_call(
sym,
bitcode::STR_GET_UNSAFE.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrGetScalarUnsafe => self.build_fn_call(
sym,
bitcode::STR_GET_SCALAR_UNSAFE.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrToNum => {
let number_layout = match self.interner().get(*ret_layout) {
Layout::Struct { field_layouts, .. } => field_layouts[0], // TODO: why is it sometimes a struct?
_ => unreachable!(),
};
// match on the return layout to figure out which zig builtin we need
let intrinsic = match self.interner().get(number_layout) {
Layout::Builtin(Builtin::Int(int_width)) => &bitcode::STR_TO_INT[int_width],
Layout::Builtin(Builtin::Float(float_width)) => {
&bitcode::STR_TO_FLOAT[float_width]
}
Layout::Builtin(Builtin::Decimal) => bitcode::DEC_FROM_STR,
_ => unreachable!(),
};
self.build_fn_call(sym, intrinsic.to_string(), args, arg_layouts, ret_layout)
}
LowLevel::PtrCast => { LowLevel::PtrCast => {
debug_assert_eq!( debug_assert_eq!(
1, 1,
@ -920,13 +1086,6 @@ trait Backend<'a> {
self.load_literal_symbols(args); self.load_literal_symbols(args);
self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout) self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout)
} }
Symbol::NUM_ADD_CHECKED => {
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
let fn_name = self.symbol_to_string(func_sym, layout_id);
// Now that the arguments are needed, load them if they are literals.
self.load_literal_symbols(args);
self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout)
}
Symbol::BOOL_TRUE => { Symbol::BOOL_TRUE => {
let bool_layout = Layout::BOOL; let bool_layout = Layout::BOOL;
self.load_literal(&Symbol::DEV_TMP, &bool_layout, &Literal::Bool(true)); self.load_literal(&Symbol::DEV_TMP, &bool_layout, &Literal::Bool(true));
@ -939,7 +1098,24 @@ trait Backend<'a> {
self.return_symbol(&Symbol::DEV_TMP, &bool_layout); self.return_symbol(&Symbol::DEV_TMP, &bool_layout);
self.free_symbol(&Symbol::DEV_TMP) self.free_symbol(&Symbol::DEV_TMP)
} }
_ => todo!("the function, {:?}", func_sym), Symbol::STR_IS_VALID_SCALAR => {
// just call the function
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
let fn_name = self.symbol_to_string(func_sym, layout_id);
// Now that the arguments are needed, load them if they are literals.
self.load_literal_symbols(args);
self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout)
}
other => {
eprintln!("maybe {other:?} should have a custom implementation?");
// just call the function
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
let fn_name = self.symbol_to_string(func_sym, layout_id);
// Now that the arguments are needed, load them if they are literals.
self.load_literal_symbols(args);
self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout)
}
} }
} }
@ -970,6 +1146,16 @@ trait Backend<'a> {
return_layout: &InLayout<'a>, return_layout: &InLayout<'a>,
); );
/// build_num_sub_checked stores the sum of src1 and src2 into dst.
fn build_num_sub_checked(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
num_layout: &InLayout<'a>,
return_layout: &InLayout<'a>,
);
/// build_num_mul stores `src1 * src2` into dst. /// build_num_mul stores `src1 * src2` into dst.
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>); fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>);
@ -1051,12 +1237,6 @@ trait Backend<'a> {
/// build_neq stores the result of `src1 != src2` into dst. /// build_neq stores the result of `src1 != src2` into dst.
fn build_neq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>); fn build_neq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>);
/// build_and stores the result of `src1 && src2` into dst.
fn build_and(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>);
/// build_or stores the result of `src1 || src2` into dst.
fn build_or(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &InLayout<'a>);
/// build_not stores the result of `!src` into dst. /// build_not stores the result of `!src` into dst.
fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>); fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>);
@ -1105,6 +1285,9 @@ trait Backend<'a> {
arg_layout: &InLayout<'a>, arg_layout: &InLayout<'a>,
); );
/// build_sqrt stores the result of `sqrt(src)` into dst.
fn build_num_sqrt(&mut self, dst: Symbol, src: Symbol, float_width: FloatWidth);
/// build_list_len returns the length of a list. /// build_list_len returns the length of a list.
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol); fn build_list_len(&mut self, dst: &Symbol, list: &Symbol);

View file

@ -1319,6 +1319,7 @@ define_builtins! {
53 STR_WITH_CAPACITY: "withCapacity" 53 STR_WITH_CAPACITY: "withCapacity"
54 STR_WITH_PREFIX: "withPrefix" 54 STR_WITH_PREFIX: "withPrefix"
55 STR_GRAPHEMES: "graphemes" 55 STR_GRAPHEMES: "graphemes"
56 STR_IS_VALID_SCALAR: "isValidScalar"
} }
6 LIST: "List" => { 6 LIST: "List" => {
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias 0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias
@ -1401,6 +1402,7 @@ define_builtins! {
77 LIST_COUNT_IF: "countIf" 77 LIST_COUNT_IF: "countIf"
78 LIST_WALK_FROM: "walkFrom" 78 LIST_WALK_FROM: "walkFrom"
79 LIST_WALK_FROM_UNTIL: "walkFromUntil" 79 LIST_WALK_FROM_UNTIL: "walkFromUntil"
80 LIST_ITER_HELP: "iterHelp"
} }
7 RESULT: "Result" => { 7 RESULT: "Result" => {
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias 0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias

View file

@ -100,8 +100,14 @@ pub fn infer_borrow<'a>(
// host-exposed functions must always own their arguments. // host-exposed functions must always own their arguments.
let is_host_exposed = host_exposed_procs.contains(&key.0); let is_host_exposed = host_exposed_procs.contains(&key.0);
let param_offset = param_map.get_param_offset(key.0, key.1); let param_offset = param_map.get_param_offset(interner, key.0, key.1);
env.collect_proc(&mut param_map, proc, param_offset, is_host_exposed); env.collect_proc(
interner,
&mut param_map,
proc,
param_offset,
is_host_exposed,
);
} }
if !env.modified { if !env.modified {
@ -167,6 +173,7 @@ impl<'a> DeclarationToIndex<'a> {
fn get_param_offset( fn get_param_offset(
&self, &self,
interner: &STLayoutInterner<'a>,
needle_symbol: Symbol, needle_symbol: Symbol,
needle_layout: ProcLayout<'a>, needle_layout: ProcLayout<'a>,
) -> ParamOffset { ) -> ParamOffset {
@ -181,12 +188,14 @@ impl<'a> DeclarationToIndex<'a> {
.elements .elements
.iter() .iter()
.filter_map(|(Declaration { symbol, layout }, _)| { .filter_map(|(Declaration { symbol, layout }, _)| {
(*symbol == needle_symbol).then_some(layout) (*symbol == needle_symbol)
.then_some(layout)
.map(|l| l.dbg_deep(interner))
}) })
.collect::<std::vec::Vec<_>>(); .collect::<std::vec::Vec<_>>();
unreachable!( unreachable!(
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex\nHowever {} similar layouts were found:\n{:#?}", "symbol/layout {:?} {:#?} combo must be in DeclarationToIndex\nHowever {} similar layouts were found:\n{:#?}",
needle_symbol, needle_layout, similar.len(), similar needle_symbol, needle_layout.dbg_deep(interner), similar.len(), similar,
) )
} }
} }
@ -206,13 +215,24 @@ pub struct ParamMap<'a> {
} }
impl<'a> ParamMap<'a> { impl<'a> ParamMap<'a> {
pub fn get_param_offset(&self, symbol: Symbol, layout: ProcLayout<'a>) -> ParamOffset { pub fn get_param_offset(
self.declaration_to_index.get_param_offset(symbol, layout) &self,
interner: &STLayoutInterner<'a>,
symbol: Symbol,
layout: ProcLayout<'a>,
) -> ParamOffset {
self.declaration_to_index
.get_param_offset(interner, symbol, layout)
} }
pub fn get_symbol(&self, symbol: Symbol, layout: ProcLayout<'a>) -> Option<&[Param<'a>]> { pub fn get_symbol(
&self,
interner: &STLayoutInterner<'a>,
symbol: Symbol,
layout: ProcLayout<'a>,
) -> Option<&[Param<'a>]> {
// let index: usize = self.declaration_to_index[&(symbol, layout)].into(); // let index: usize = self.declaration_to_index[&(symbol, layout)].into();
let index: usize = self.get_param_offset(symbol, layout).into(); let index: usize = self.get_param_offset(interner, symbol, layout).into();
self.declarations.get(index..index + layout.arguments.len()) self.declarations.get(index..index + layout.arguments.len())
} }
@ -292,7 +312,7 @@ impl<'a> ParamMap<'a> {
return; return;
} }
let index: usize = self.get_param_offset(key.0, key.1).into(); let index: usize = self.get_param_offset(interner, key.0, key.1).into();
for (i, param) in Self::init_borrow_args(arena, interner, proc.args) for (i, param) in Self::init_borrow_args(arena, interner, proc.args)
.iter() .iter()
@ -312,7 +332,7 @@ impl<'a> ParamMap<'a> {
proc: &Proc<'a>, proc: &Proc<'a>,
key: (Symbol, ProcLayout<'a>), key: (Symbol, ProcLayout<'a>),
) { ) {
let index: usize = self.get_param_offset(key.0, key.1).into(); let index: usize = self.get_param_offset(interner, key.0, key.1).into();
for (i, param) in Self::init_borrow_args_always_owned(arena, proc.args) for (i, param) in Self::init_borrow_args_always_owned(arena, proc.args)
.iter() .iter()
@ -534,7 +554,13 @@ impl<'a> BorrowInfState<'a> {
/// ///
/// and determines whether z and which of the symbols used in e /// and determines whether z and which of the symbols used in e
/// must be taken as owned parameters /// must be taken as owned parameters
fn collect_call(&mut self, param_map: &mut ParamMap<'a>, z: Symbol, e: &crate::ir::Call<'a>) { fn collect_call(
&mut self,
interner: &STLayoutInterner<'a>,
param_map: &mut ParamMap<'a>,
z: Symbol,
e: &crate::ir::Call<'a>,
) {
use crate::ir::CallType::*; use crate::ir::CallType::*;
let crate::ir::Call { let crate::ir::Call {
@ -553,7 +579,7 @@ impl<'a> BorrowInfState<'a> {
// get the borrow signature of the applied function // get the borrow signature of the applied function
let ps = param_map let ps = param_map
.get_symbol(name.name(), top_level) .get_symbol(interner, name.name(), top_level)
.expect("function is defined"); .expect("function is defined");
// the return value will be owned // the return value will be owned
@ -595,8 +621,11 @@ impl<'a> BorrowInfState<'a> {
niche: passed_function.name.niche(), niche: passed_function.name.niche(),
}; };
let function_ps = let function_ps = match param_map.get_symbol(
match param_map.get_symbol(passed_function.name.name(), closure_layout) { interner,
passed_function.name.name(),
closure_layout,
) {
Some(function_ps) => function_ps, Some(function_ps) => function_ps,
None => unreachable!(), None => unreachable!(),
}; };
@ -671,7 +700,13 @@ impl<'a> BorrowInfState<'a> {
} }
} }
fn collect_expr(&mut self, param_map: &mut ParamMap<'a>, z: Symbol, e: &Expr<'a>) { fn collect_expr(
&mut self,
interner: &STLayoutInterner<'a>,
param_map: &mut ParamMap<'a>,
z: Symbol,
e: &Expr<'a>,
) {
use Expr::*; use Expr::*;
match e { match e {
@ -724,7 +759,7 @@ impl<'a> BorrowInfState<'a> {
self.own_var(z); self.own_var(z);
} }
Call(call) => self.collect_call(param_map, z, call), Call(call) => self.collect_call(interner, param_map, z, call),
Literal(_) | RuntimeErrorFunction(_) => {} Literal(_) | RuntimeErrorFunction(_) => {}
@ -757,6 +792,7 @@ impl<'a> BorrowInfState<'a> {
#[allow(clippy::many_single_char_names)] #[allow(clippy::many_single_char_names)]
fn preserve_tail_call( fn preserve_tail_call(
&mut self, &mut self,
interner: &STLayoutInterner<'a>,
param_map: &mut ParamMap<'a>, param_map: &mut ParamMap<'a>,
x: Symbol, x: Symbol,
v: &Expr<'a>, v: &Expr<'a>,
@ -782,7 +818,7 @@ impl<'a> BorrowInfState<'a> {
if self.current_proc == g.name() && x == *z { if self.current_proc == g.name() && x == *z {
// anonymous functions (for which the ps may not be known) // anonymous functions (for which the ps may not be known)
// can never be tail-recursive, so this is fine // can never be tail-recursive, so this is fine
if let Some(ps) = param_map.get_symbol(g.name(), top_level) { if let Some(ps) = param_map.get_symbol(interner, g.name(), top_level) {
self.own_params_using_args(ys, ps) self.own_params_using_args(ys, ps)
} }
} }
@ -801,7 +837,12 @@ impl<'a> BorrowInfState<'a> {
} }
} }
fn collect_stmt(&mut self, param_map: &mut ParamMap<'a>, stmt: &Stmt<'a>) { fn collect_stmt(
&mut self,
interner: &STLayoutInterner<'a>,
param_map: &mut ParamMap<'a>,
stmt: &Stmt<'a>,
) {
use Stmt::*; use Stmt::*;
match stmt { match stmt {
@ -813,11 +854,11 @@ impl<'a> BorrowInfState<'a> {
} => { } => {
let old = self.param_set.clone(); let old = self.param_set.clone();
self.update_param_set(ys); self.update_param_set(ys);
self.collect_stmt(param_map, v); self.collect_stmt(interner, param_map, v);
self.param_set = old; self.param_set = old;
self.update_param_map_join_point(param_map, *j); self.update_param_map_join_point(param_map, *j);
self.collect_stmt(param_map, b); self.collect_stmt(interner, param_map, b);
} }
Let(x, v, _, mut b) => { Let(x, v, _, mut b) => {
@ -830,17 +871,17 @@ impl<'a> BorrowInfState<'a> {
stack.push((*symbol, expr)); stack.push((*symbol, expr));
} }
self.collect_stmt(param_map, b); self.collect_stmt(interner, param_map, b);
let mut it = stack.into_iter().rev(); let mut it = stack.into_iter().rev();
// collect the final expr, and see if we need to preserve a tail call // collect the final expr, and see if we need to preserve a tail call
let (x, v) = it.next().unwrap(); let (x, v) = it.next().unwrap();
self.collect_expr(param_map, x, v); self.collect_expr(interner, param_map, x, v);
self.preserve_tail_call(param_map, x, v, b); self.preserve_tail_call(interner, param_map, x, v, b);
for (x, v) in it { for (x, v) in it {
self.collect_expr(param_map, x, v); self.collect_expr(interner, param_map, x, v);
} }
} }
@ -859,21 +900,21 @@ impl<'a> BorrowInfState<'a> {
.. ..
} => { } => {
for (_, _, b) in branches.iter() { for (_, _, b) in branches.iter() {
self.collect_stmt(param_map, b); self.collect_stmt(interner, param_map, b);
} }
self.collect_stmt(param_map, default_branch.1); self.collect_stmt(interner, param_map, default_branch.1);
} }
Dbg { remainder, .. } => { Dbg { remainder, .. } => {
self.collect_stmt(param_map, remainder); self.collect_stmt(interner, param_map, remainder);
} }
Expect { remainder, .. } => { Expect { remainder, .. } => {
self.collect_stmt(param_map, remainder); self.collect_stmt(interner, param_map, remainder);
} }
ExpectFx { remainder, .. } => { ExpectFx { remainder, .. } => {
self.collect_stmt(param_map, remainder); self.collect_stmt(interner, param_map, remainder);
} }
Refcounting(_, _) => unreachable!("these have not been introduced yet"), Refcounting(_, _) => unreachable!("these have not been introduced yet"),
@ -891,6 +932,7 @@ impl<'a> BorrowInfState<'a> {
fn collect_proc( fn collect_proc(
&mut self, &mut self,
interner: &STLayoutInterner<'a>,
param_map: &mut ParamMap<'a>, param_map: &mut ParamMap<'a>,
proc: &Proc<'a>, proc: &Proc<'a>,
param_offset: ParamOffset, param_offset: ParamOffset,
@ -912,7 +954,7 @@ impl<'a> BorrowInfState<'a> {
owned_entry.extend(params.iter().map(|p| p.symbol)); owned_entry.extend(params.iter().map(|p| p.symbol));
} }
self.collect_stmt(param_map, &proc.body); self.collect_stmt(interner, param_map, &proc.body);
self.update_param_map_declaration(param_map, param_offset, proc.args.len()); self.update_param_map_declaration(param_map, param_offset, proc.args.len());
self.param_set = old; self.param_set = old;

View file

@ -605,7 +605,7 @@ impl<'a, 'i> Context<'a, 'i> {
// get the borrow signature // get the borrow signature
let ps = self let ps = self
.param_map .param_map
.get_symbol(name.name(), top_level) .get_symbol(self.layout_interner, name.name(), top_level)
.expect("function is defined"); .expect("function is defined");
let v = Expr::Call(crate::ir::Call { let v = Expr::Call(crate::ir::Call {
@ -653,10 +653,11 @@ impl<'a, 'i> Context<'a, 'i> {
niche: passed_function.name.niche(), niche: passed_function.name.niche(),
}; };
let function_ps = match self let function_ps = match self.param_map.get_symbol(
.param_map self.layout_interner,
.get_symbol(passed_function.name.name(), function_layout) passed_function.name.name(),
{ function_layout,
) {
Some(function_ps) => function_ps, Some(function_ps) => function_ps,
None => unreachable!(), None => unreachable!(),
}; };
@ -1510,19 +1511,28 @@ pub fn visit_procs<'a, 'i>(
}; };
for (key, proc) in procs.iter_mut() { for (key, proc) in procs.iter_mut() {
visit_proc(arena, &mut codegen, param_map, &ctx, proc, key.1); visit_proc(
arena,
layout_interner,
&mut codegen,
param_map,
&ctx,
proc,
key.1,
);
} }
} }
fn visit_proc<'a, 'i>( fn visit_proc<'a, 'i>(
arena: &'a Bump, arena: &'a Bump,
interner: &STLayoutInterner<'a>,
codegen: &mut CodegenTools<'i>, codegen: &mut CodegenTools<'i>,
param_map: &'a ParamMap<'a>, param_map: &'a ParamMap<'a>,
ctx: &Context<'a, 'i>, ctx: &Context<'a, 'i>,
proc: &mut Proc<'a>, proc: &mut Proc<'a>,
layout: ProcLayout<'a>, layout: ProcLayout<'a>,
) { ) {
let params = match param_map.get_symbol(proc.name.name(), layout) { let params = match param_map.get_symbol(interner, proc.name.name(), layout) {
Some(slice) => slice, Some(slice) => slice,
None => Vec::from_iter_in( None => Vec::from_iter_in(
proc.args.iter().cloned().map(|(layout, symbol)| Param { proc.args.iter().cloned().map(|(layout, symbol)| Param {

View file

@ -4038,6 +4038,33 @@ impl<'a> ProcLayout<'a> {
} }
} }
} }
pub fn dbg_deep<'r, I: LayoutInterner<'a>>(&self, interner: &'r I) -> DbgProcLayout<'a, 'r, I> {
DbgProcLayout {
layout: *self,
interner,
}
}
}
pub struct DbgProcLayout<'a, 'r, I: LayoutInterner<'a>> {
layout: ProcLayout<'a>,
interner: &'r I,
}
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgProcLayout<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ProcLayout {
arguments,
result,
niche,
} = self.layout;
f.debug_struct("ProcLayout")
.field("arguments", &self.interner.dbg_deep_iter(arguments))
.field("result", &self.interner.dbg_deep(result))
.field("niche", &niche.dbg_deep(self.interner))
.finish()
}
} }
fn specialize_naked_symbol<'a>( fn specialize_naked_symbol<'a>(

View file

@ -1313,6 +1313,14 @@ impl<'a> Niche<'a> {
]), ]),
} }
} }
pub fn dbg_deep<'r, I: LayoutInterner<'a>>(
&'r self,
interner: &'r I,
) -> crate::layout::intern::dbg::DbgFields<'a, 'r, I> {
let NichePriv::Captures(caps) = &self.0;
interner.dbg_deep_iter(caps)
}
} }
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]

View file

@ -365,6 +365,10 @@ pub trait LayoutInterner<'a>: Sized {
fn dbg_deep<'r>(&'r self, layout: InLayout<'a>) -> dbg::Dbg<'a, 'r, Self> { fn dbg_deep<'r>(&'r self, layout: InLayout<'a>) -> dbg::Dbg<'a, 'r, Self> {
dbg::Dbg(self, layout) dbg::Dbg(self, layout)
} }
fn dbg_deep_iter<'r>(&'r self, layouts: &'a [InLayout<'a>]) -> dbg::DbgFields<'a, 'r, Self> {
dbg::DbgFields(self, layouts)
}
} }
/// An interned layout. /// An interned layout.
@ -1274,7 +1278,7 @@ mod equiv {
} }
} }
mod dbg { pub mod dbg {
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use crate::layout::{Builtin, LambdaSet, Layout, UnionLayout}; use crate::layout::{Builtin, LambdaSet, Layout, UnionLayout};
@ -1311,7 +1315,7 @@ mod dbg {
} }
} }
struct DbgFields<'a, 'r, I: LayoutInterner<'a>>(&'r I, &'a [InLayout<'a>]); pub struct DbgFields<'a, 'r, I: LayoutInterner<'a>>(pub &'r I, pub &'a [InLayout<'a>]);
impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgFields<'a, 'r, I> { impl<'a, 'r, I: LayoutInterner<'a>> std::fmt::Debug for DbgFields<'a, 'r, I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline; use crate::ast::CommentOrNewline;
use crate::ast::Spaceable; use crate::ast::Spaceable;
use crate::parser::Progress;
use crate::parser::SpaceProblem; use crate::parser::SpaceProblem;
use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*}; use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*};
use crate::state::State; use crate::state::State;
@ -7,6 +8,7 @@ use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::Loc; use roc_region::all::Loc;
use roc_region::all::Position; use roc_region::all::Position;
use roc_region::all::Region;
pub fn space0_around_ee<'a, P, S, E>( pub fn space0_around_ee<'a, P, S, E>(
parser: P, parser: P,
@ -386,9 +388,42 @@ pub fn spaces<'a, E>() -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
where where
E: 'a + SpaceProblem, E: 'a + SpaceProblem,
{ {
move |arena, mut state: State<'a>, _min_indent: u32| { move |arena, state: State<'a>, _min_indent: u32| {
let mut newlines = Vec::new_in(arena); let mut newlines = Vec::new_in(arena);
match consume_spaces(state, |_, space, _| newlines.push(space)) {
Ok((progress, state)) => Ok((progress, newlines.into_bump_slice(), state)),
Err((progress, err)) => Err((progress, err)),
}
}
}
pub fn loc_spaces<'a, E>() -> impl Parser<'a, &'a [Loc<CommentOrNewline<'a>>], E>
where
E: 'a + SpaceProblem,
{
move |arena, state: State<'a>, _min_indent: u32| {
let mut newlines = Vec::new_in(arena);
match consume_spaces(state, |start, space, end| {
newlines.push(Loc::at(Region::between(start, end), space))
}) {
Ok((progress, state)) => Ok((progress, newlines.into_bump_slice(), state)),
Err((progress, err)) => Err((progress, err)),
}
}
}
fn consume_spaces<'a, E, F>(
mut state: State<'a>,
mut on_space: F,
) -> Result<(Progress, State<'a>), (Progress, E)>
where
E: 'a + SpaceProblem,
F: FnMut(Position, CommentOrNewline<'a>, Position),
{
let mut progress = NoProgress; let mut progress = NoProgress;
let mut found_newline = false;
loop { loop {
let whitespace = fast_eat_whitespace(state.bytes()); let whitespace = fast_eat_whitespace(state.bytes());
if whitespace > 0 { if whitespace > 0 {
@ -396,6 +431,8 @@ where
progress = MadeProgress; progress = MadeProgress;
} }
let start = state.pos();
match state.bytes().first() { match state.bytes().first() {
Some(b'#') => { Some(b'#') => {
state.advance_mut(1); state.advance_mut(1);
@ -424,8 +461,9 @@ where
} else { } else {
CommentOrNewline::LineComment(text) CommentOrNewline::LineComment(text)
}; };
newlines.push(comment);
state.advance_mut(len); state.advance_mut(len);
on_space(start, comment, state.pos());
found_newline = true;
if begins_with_crlf(state.bytes()) { if begins_with_crlf(state.bytes()) {
state.advance_mut(1); state.advance_mut(1);
@ -438,23 +476,22 @@ where
} }
Some(b'\r') => { Some(b'\r') => {
if state.bytes().get(1) == Some(&b'\n') { if state.bytes().get(1) == Some(&b'\n') {
newlines.push(CommentOrNewline::Newline);
state.advance_mut(1); state.advance_mut(1);
state = state.advance_newline(); state = state.advance_newline();
on_space(start, CommentOrNewline::Newline, state.pos());
found_newline = true;
progress = MadeProgress; progress = MadeProgress;
} else { } else {
return Err(( return Err((
progress, progress,
E::space_problem( E::space_problem(BadInputError::HasMisplacedCarriageReturn, state.pos()),
BadInputError::HasMisplacedCarriageReturn,
state.pos(),
),
)); ));
} }
} }
Some(b'\n') => { Some(b'\n') => {
newlines.push(CommentOrNewline::Newline);
state = state.advance_newline(); state = state.advance_newline();
on_space(start, CommentOrNewline::Newline, state.pos());
found_newline = true;
progress = MadeProgress; progress = MadeProgress;
} }
Some(b'\t') => { Some(b'\t') => {
@ -470,7 +507,7 @@ where
)); ));
} }
_ => { _ => {
if !newlines.is_empty() { if found_newline {
state = state.mark_current_indent(); state = state.mark_current_indent();
} }
break; break;
@ -478,6 +515,5 @@ where
} }
} }
Ok((progress, newlines.into_bump_slice(), state)) Ok((progress, state))
}
} }

View file

@ -0,0 +1,565 @@
use encode_unicode::CharExt;
use std::collections::HashSet;
use bumpalo::Bump;
use roc_region::all::{Loc, Region};
use crate::{
ast::CommentOrNewline,
blankspace::loc_spaces,
keyword::KEYWORDS,
number_literal::positive_number_literal,
parser::{EExpr, ParseResult, Parser},
state::State,
string_literal::{parse_str_like_literal, StrLikeLiteral},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Token {
LineComment,
DocComment,
Error,
SingleQuote,
String,
UnicodeEscape,
EscapedChar,
Interpolated,
Keyword,
UpperIdent,
LowerIdent,
Number,
Other,
Minus,
Plus,
Colon,
Bar,
Equals,
GreaterThan,
LessThan,
Comma,
Backslash,
Brace,
Bracket,
Paren,
Arrow,
Pipe,
Backpass,
}
pub fn highlight(text: &str) -> Vec<Loc<Token>> {
let mut tokens = Vec::new();
let state = State::new(text.as_bytes());
let arena = Bump::new();
let header_keywords = HEADER_KEYWORDS.iter().copied().collect::<HashSet<_>>();
let body_keywords = KEYWORDS.iter().copied().collect::<HashSet<_>>();
if let Ok((_prog, _, new_state)) = crate::module::header().parse(&arena, state.clone(), 0) {
let inner_state =
State::new(text[..state.bytes().len() - new_state.bytes().len()].as_bytes());
highlight_inner(&arena, inner_state, &mut tokens, &header_keywords);
highlight_inner(&arena, new_state, &mut tokens, &body_keywords);
} else {
highlight_inner(&arena, state, &mut tokens, &body_keywords);
}
tokens = combine_tokens(tokens);
tokens
}
fn combine_tokens(locations: Vec<Loc<Token>>) -> Vec<Loc<Token>> {
let mut tokens: Vec<Loc<Token>> = Vec::new();
let mut previous_location: Option<Loc<Token>> = None;
for location in locations {
match location.value {
// Catch tokens which may combine for a different meaning
Token::LessThan => match previous_location {
Some(prev) => {
tokens.push(prev);
tokens.push(location);
previous_location = None;
}
None => {
previous_location = Some(location);
}
},
Token::Bar => match previous_location {
Some(prev) => {
tokens.push(prev);
tokens.push(location);
previous_location = None;
}
None => {
previous_location = Some(location);
}
},
// Combination tokens
Token::GreaterThan => {
match previous_location {
Some(prev) => {
match prev.value {
Token::Minus => {
// arrow operator "->"
tokens.push(Loc::at(
Region::between(prev.region.start(), location.region.end()),
Token::Arrow,
));
previous_location = None;
}
Token::Bar => {
// pipe operator "|>"
tokens.push(Loc::at(
Region::between(prev.region.start(), location.region.end()),
Token::Pipe,
));
previous_location = None;
}
_ => {
tokens.push(prev);
tokens.push(location);
previous_location = None;
}
}
}
_ => {
tokens.push(location);
}
}
}
Token::Minus => {
match previous_location {
Some(prev) => {
match prev.value {
Token::LessThan => {
// backpass operator "<-"
tokens.push(Loc::at(
Region::between(prev.region.start(), location.region.end()),
Token::Backpass,
));
previous_location = None;
}
_ => {
tokens.push(prev);
tokens.push(location);
previous_location = None;
}
}
}
None => {
previous_location = Some(location);
}
}
}
_ => {
tokens.push(location);
}
}
}
tokens
}
fn highlight_inner<'a>(
arena: &'a Bump,
mut state: State<'a>,
tokens: &mut Vec<Loc<Token>>,
keywords: &HashSet<&str>,
) {
loop {
let start = state.pos();
if let Ok((b, _width)) = char::from_utf8_slice_start(state.bytes()) {
match b {
' ' | '\n' | '\t' | '\r' | '#' => {
let res: ParseResult<'a, _, EExpr<'a>> =
loc_spaces().parse(arena, state.clone(), 0);
if let Ok((_, spaces, new_state)) = res {
state = new_state;
for space in spaces {
let token = match space.value {
CommentOrNewline::Newline => {
continue;
}
CommentOrNewline::LineComment(_) => Token::LineComment,
CommentOrNewline::DocComment(_) => Token::DocComment,
};
tokens.push(Loc::at(space.region, token));
}
} else {
fast_forward_to(&mut state, tokens, start, |c| c == b'\n');
}
}
'"' | '\'' => {
if let Ok((_, item, new_state)) =
parse_str_like_literal().parse(arena, state.clone(), 0)
{
state = new_state;
match item {
StrLikeLiteral::SingleQuote(_) => {
tokens.push(Loc::at(
Region::between(start, state.pos()),
Token::SingleQuote,
));
}
StrLikeLiteral::Str(_) => {
tokens.push(Loc::at(
Region::between(start, state.pos()),
Token::String,
));
}
}
} else {
fast_forward_to(&mut state, tokens, start, |c| c == b'\n');
}
}
c if c.is_alphabetic() => {
let buffer = state.bytes();
let mut chomped = 0;
let is_upper = c.is_uppercase();
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if ch.is_alphabetic() || ch.is_ascii_digit() {
chomped += width;
} else {
// we're done
break;
}
}
let ident = std::str::from_utf8(&buffer[..chomped]).unwrap();
state.advance_mut(chomped);
if keywords.contains(ident) {
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Keyword));
} else {
tokens.push(Loc::at(
Region::between(start, state.pos()),
if is_upper {
Token::UpperIdent
} else {
Token::LowerIdent
},
));
}
}
'0'..='9' => {
if let Ok((_, _item, new_state)) =
positive_number_literal().parse(arena, state.clone(), 0)
{
state = new_state;
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Number));
} else {
fast_forward_to(&mut state, tokens, start, |b| !b.is_ascii_digit());
}
}
':' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Colon));
}
'|' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Bar));
}
'-' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Minus));
}
'+' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Plus));
}
'=' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Equals));
}
'>' => {
state.advance_mut(1);
tokens.push(Loc::at(
Region::between(start, state.pos()),
Token::GreaterThan,
));
}
'<' => {
state.advance_mut(1);
tokens.push(Loc::at(
Region::between(start, state.pos()),
Token::LessThan,
));
}
',' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Comma));
}
'\\' => {
state.advance_mut(1);
tokens.push(Loc::at(
Region::between(start, state.pos()),
Token::Backslash,
));
}
'{' | '}' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Brace));
}
'[' | ']' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Bracket));
}
'(' | ')' => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Paren));
}
_ => {
state.advance_mut(1);
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Other));
}
}
} else {
break;
}
}
}
fn fast_forward_to(
state: &mut State,
tokens: &mut Vec<Loc<Token>>,
start: roc_region::all::Position,
end: impl Fn(u8) -> bool,
) {
while let Some(b) = state.bytes().first() {
if end(*b) {
break;
}
state.advance_mut(1);
}
tokens.push(Loc::at(Region::between(start, state.pos()), Token::Error));
}
pub const HEADER_KEYWORDS: [&str; 14] = [
"interface",
"app",
"package",
"platform",
"hosted",
"exposes",
"imports",
"with",
"generates",
"package",
"packages",
"requires",
"provides",
"to",
];
#[cfg(test)]
mod tests {
use roc_region::all::Position;
use super::*;
#[test]
fn test_highlight_comments() {
let text = "# a\n#b\n#c";
let tokens = highlight(text);
assert_eq!(
tokens,
vec![
Loc::at(
Region::between(Position::new(0), Position::new(3)),
Token::LineComment
),
Loc::at(
Region::between(Position::new(4), Position::new(6)),
Token::LineComment
),
Loc::at(
Region::between(Position::new(7), Position::new(9)),
Token::LineComment
),
]
);
}
#[test]
fn test_highlight_doc_comments() {
let text = "## a\n##b\n##c";
let tokens = highlight(text);
assert_eq!(
tokens,
vec![
Loc::at(
Region::between(Position::new(0), Position::new(4)),
Token::DocComment
),
// the next two are line comments because there's not a space at the beginning
Loc::at(
Region::between(Position::new(5), Position::new(8)),
Token::LineComment
),
Loc::at(
Region::between(Position::new(9), Position::new(12)),
Token::LineComment
),
]
);
}
#[test]
fn test_highlight_strings() {
let text = r#""a""#;
let tokens = highlight(text);
assert_eq!(
tokens,
vec![Loc::at(
Region::between(Position::new(0), Position::new(3)),
Token::String
)]
);
}
#[test]
fn test_highlight_single_quotes() {
let text = r#"'a'"#;
let tokens = highlight(text);
assert_eq!(
tokens,
vec![Loc::at(
Region::between(Position::new(0), Position::new(3)),
Token::SingleQuote
)]
);
}
#[test]
fn test_highlight_header() {
let text = r#"app "test-app" provides [] to "./blah""#;
let tokens = highlight(text);
assert_eq!(
tokens,
vec![
Loc::at(
Region::between(Position::new(0), Position::new(3)),
Token::Keyword
),
Loc::at(
Region::between(Position::new(4), Position::new(14)),
Token::String
),
Loc::at(
Region::between(Position::new(15), Position::new(23)),
Token::Keyword
),
Loc::at(
Region::between(Position::new(24), Position::new(25)),
Token::Bracket
),
Loc::at(
Region::between(Position::new(25), Position::new(26)),
Token::Bracket
),
Loc::at(
Region::between(Position::new(27), Position::new(29)),
Token::Keyword
),
Loc::at(
Region::between(Position::new(30), Position::new(38)),
Token::String
),
]
);
}
#[test]
fn test_highlight_numbers() {
let text = "123.0 123 123. 123.0e10 123e10 123e-10 0x123";
let tokens = highlight(text);
assert_eq!(
tokens,
vec![
Loc::at(
Region::between(Position::new(0), Position::new(5)),
Token::Number
),
Loc::at(
Region::between(Position::new(6), Position::new(9)),
Token::Number
),
Loc::at(
Region::between(Position::new(10), Position::new(14)),
Token::Number
),
Loc::at(
Region::between(Position::new(15), Position::new(23)),
Token::Number
),
Loc::at(
Region::between(Position::new(24), Position::new(30)),
Token::Number
),
Loc::at(
Region::between(Position::new(31), Position::new(38)),
Token::Number
),
Loc::at(
Region::between(Position::new(39), Position::new(44)),
Token::Number
),
]
);
}
#[test]
fn test_combine_tokens() {
let input: Vec<Loc<Token>> = vec![
// arrow operator "->"
Loc::at(
Region::between(Position::new(0), Position::new(5)),
Token::Minus,
),
Loc::at(
Region::between(Position::new(6), Position::new(7)),
Token::GreaterThan,
),
// pipe operator "|>"
Loc::at(
Region::between(Position::new(8), Position::new(9)),
Token::Bar,
),
Loc::at(
Region::between(Position::new(10), Position::new(11)),
Token::GreaterThan,
),
// backpass operator "<-"
Loc::at(
Region::between(Position::new(12), Position::new(13)),
Token::LessThan,
),
Loc::at(
Region::between(Position::new(14), Position::new(15)),
Token::Minus,
),
];
let actual = combine_tokens(input);
let expected = vec![
Loc::at(
Region::between(Position::new(0), Position::new(7)),
Token::Arrow,
),
Loc::at(
Region::between(Position::new(8), Position::new(11)),
Token::Pipe,
),
Loc::at(
Region::between(Position::new(12), Position::new(15)),
Token::Backpass,
),
];
assert_eq!(actual, expected);
}
}

View file

@ -10,6 +10,7 @@ pub mod ast;
pub mod blankspace; pub mod blankspace;
pub mod expr; pub mod expr;
pub mod header; pub mod header;
pub mod highlight;
pub mod ident; pub mod ident;
pub mod keyword; pub mod keyword;
pub mod module; pub mod module;

View file

@ -129,6 +129,10 @@ impl Position {
offset: self.offset - count as u32, offset: self.offset - count as u32,
} }
} }
pub fn byte_offset(&self) -> usize {
self.offset as usize
}
} }
impl Debug for Position { impl Debug for Position {
@ -322,6 +326,10 @@ impl<T> Loc<T> {
value: transform(self.value), value: transform(self.value),
} }
} }
pub fn byte_range(&self) -> std::ops::Range<usize> {
self.region.start.byte_offset()..self.region.end.byte_offset()
}
} }
impl<T> fmt::Debug for Loc<T> impl<T> fmt::Debug for Loc<T>

View file

@ -787,6 +787,59 @@ fn encode_derived_record_with_many_types() {
) )
} }
#[test]
#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))]
fn encode_derived_generic_record_with_different_field_types() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode, Json]
provides [main] to "./platform"
Q a b := {a: a, b: b} has [Encoding]
q = @Q {a: 10u32, b: "fieldb"}
main =
result = Str.fromUtf8 (Encode.toBytes q Json.toUtf8)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from(r#"{"a":10,"b":"fieldb"}"#),
RocStr
)
}
#[test]
#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))]
fn encode_derived_generic_tag_with_different_field_types() {
assert_evals_to!(
indoc!(
r#"
app "test"
imports [Encode, Json]
provides [main] to "./platform"
Q a b := [A a, B b] has [Encoding]
q : Q Str U32
q = @Q (B 67)
main =
result = Str.fromUtf8 (Encode.toBytes q Json.toUtf8)
when result is
Ok s -> s
_ -> "<bad>"
"#
),
RocStr::from(r#"{"B":[67]}"#),
RocStr
)
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn decode_use_stdlib() { fn decode_use_stdlib() {

View file

@ -1859,13 +1859,11 @@ fn first_int_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.first [12, 9, 6, 3] is List.first [12, 9, 6, 3]
Ok val -> val
Err _ -> -1
"# "#
), ),
12, RocResult::ok(12),
i64 RocResult<i64, ()>
); );
} }
@ -1889,45 +1887,42 @@ fn first_wildcard_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.first [] is List.last [] |> Result.map (\_ -> 0i64)
Ok _ -> 5
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn first_empty_list() { fn first_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.first [] is list : List I64
Ok val -> val list = []
Err _ -> -1
List.first list
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn last_int_list() { fn last_int_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.last [12, 9, 6, 3] is List.last [12, 9, 6, 3]
Ok val -> val
Err _ -> -1
"# "#
), ),
3, RocResult::ok(3),
i64 RocResult<i64, ()>
); );
} }
@ -1937,13 +1932,11 @@ fn last_wildcard_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.last [] is List.last [] |> Result.map (\_ -> 0i64)
Ok _ -> 5
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
@ -1953,13 +1946,14 @@ fn last_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.last [] is list : List I64
Ok val -> val list = []
Err _ -> -1
List.last list
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
@ -1969,29 +1963,32 @@ fn get_empty_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get [] 0 is list : List I64
Ok val -> val list = []
Err _ -> -1
List.get list 0
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn get_wildcard_empty_list() { fn get_wildcard_empty_list() {
// NOTE: by default, the return type is `Result [] [NotFound]`, which is actually represented
// as just `[NotFound]`. Casting that to `RocResult<(), ()>` is invalid! But accepting any `()`
// would make the test pointless. Therefore, we must explicitly change the type on the roc side
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get [] 0 is List.get [] 0
Ok _ -> 5 |> Result.map (\_ -> {})
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<(), ()>
); );
} }
@ -2010,39 +2007,35 @@ fn get_str_list_ok() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn get_int_list_ok() { fn get_int_list_ok() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get [12, 9, 6] 1 is List.get [12, 9, 6] 1
Ok val -> val
Err _ -> -1
"# "#
), ),
9, RocResult::ok(9),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn get_int_list_oob() { fn get_int_list_oob() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get [12, 9, 6] 1000 is List.get [12, 9, 6] 1000
Ok val -> val
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn replace_unique_int_list() { fn replace_unique_int_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2057,7 +2050,7 @@ fn replace_unique_int_list() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn replace_unique_int_list_out_of_bounds() { fn replace_unique_int_list_out_of_bounds() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2072,7 +2065,7 @@ fn replace_unique_int_list_out_of_bounds() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn replace_unique_int_list_get_old_value() { fn replace_unique_int_list_get_old_value() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2087,7 +2080,7 @@ fn replace_unique_int_list_get_old_value() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn replace_unique_get_large_value() { fn replace_unique_get_large_value() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2138,13 +2131,11 @@ fn get_set_unique_int_list_i64() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get (List.set [12, 9, 7, 3] 1 42) 1 is List.get (List.set [12, 9, 7, 3] 1 42) 1
Ok val -> val
Err _ -> -1
"# "#
), ),
42, RocResult::ok(42),
i64 RocResult<i64, ()>
); );
} }
@ -2154,13 +2145,11 @@ fn get_set_unique_int_list_i8() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.get (List.set [12, 9, 7, 3] 1 42i8) 1 is List.get (List.set [12, 9, 7, 3] 1 42i8) 1
Ok val -> val
Err _ -> -1i8
"# "#
), ),
42, RocResult::ok(42),
i8 RocResult<i8, ()>
); );
} }
@ -2175,7 +2164,7 @@ fn set_unique_int_list() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn set_unique_list_oob() { fn set_unique_list_oob() {
assert_evals_to!( assert_evals_to!(
"List.set [3, 17, 4.1] 1337 9.25", "List.set [3, 17, 4.1] 1337 9.25",
@ -2240,20 +2229,18 @@ fn set_shared_list_oob() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn get_unique_int_list() { fn get_unique_int_list() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
unique = [2, 4] unique = [2, 4]
when List.get unique 1 is List.get unique 1
Ok num -> num
Err _ -> -1
"# "#
), ),
4, RocResult::ok(4),
i64 RocResult<i64, ()>
); );
} }
@ -2275,7 +2262,7 @@ fn gen_wrap_len() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_wrap_first() { fn gen_wrap_first() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2292,7 +2279,7 @@ fn gen_wrap_first() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_duplicate() { fn gen_duplicate() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2605,7 +2592,7 @@ fn list_literal_increment_decrement() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn list_pass_to_function() { fn list_pass_to_function() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2625,7 +2612,7 @@ fn list_pass_to_function() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn list_pass_to_set() { fn list_pass_to_set() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2713,24 +2700,22 @@ fn list_min() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.min [] is List.min []
Ok val -> val |> Result.map (\_ -> {})
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<(), ()>
); );
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.min [3, 1, 2] is List.min [3, 1, 2]
Ok val -> val
Err _ -> -1
"# "#
), ),
1, RocResult::ok(1),
i64 RocResult<i64, ()>
); );
} }
@ -2740,24 +2725,22 @@ fn list_max() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.max [] is List.max []
Ok val -> val |> Result.map (\_ -> {})
Err _ -> -1
"# "#
), ),
-1, RocResult::err(()),
i64 RocResult<(), ()>
); );
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when List.max [3, 1, 2] is List.max [3, 1, 2]
Ok val -> val
Err _ -> -1
"# "#
), ),
3, RocResult::ok(3),
i64 RocResult<i64, ()>
); );
} }

View file

@ -31,7 +31,7 @@ fn nat_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn i128_signed_int_alias() { fn i128_signed_int_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -115,7 +115,7 @@ fn i8_signed_int_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn i128_hex_int_alias() { fn i128_hex_int_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -196,7 +196,7 @@ fn i8_hex_int_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn u128_signed_int_alias() { fn u128_signed_int_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -277,7 +277,7 @@ fn u8_signed_int_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn u128_hex_int_alias() { fn u128_hex_int_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -418,7 +418,7 @@ fn character_literal_new_line() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn dec_float_alias() { fn dec_float_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -451,7 +451,7 @@ fn f64_float_alias() {
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f32_float_alias() { fn f32_float_alias() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -468,112 +468,51 @@ fn f32_float_alias() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_sqrt() { fn f64_sqrt_100() {
assert_evals_to!( assert_evals_to!("Num.sqrt 100", 10.0, f64);
indoc!( }
r#"
when Num.sqrtChecked 100 is #[test]
Ok val -> val #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
Err _ -> -1 fn f64_sqrt_checked_0() {
"# assert_evals_to!("Num.sqrt 0", 0.0, f64);
),
10.0,
f64
);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log() { fn f64_sqrt_checked_positive() {
assert_evals_to!( assert_evals_to!("Num.sqrtChecked 100", RocResult::ok(10.0), RocResult<f64, ()>);
indoc!(
r#"
Num.log 7.38905609893
"#
),
1.999999999999912,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_one() {
assert_evals_to!(
indoc!(
r#"
when Num.logChecked 1 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_sqrt_zero() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrtChecked 0 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_sqrt_checked_negative() { fn f64_sqrt_checked_negative() {
assert_evals_to!( assert_evals_to!("Num.sqrtChecked -1f64", RocResult::err(()), RocResult<f64, ()>);
indoc!( }
r#"
when Num.sqrtChecked -1 is #[test]
Err _ -> 42 #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
Ok val -> val fn f64_log() {
"# assert_evals_to!("Num.log 7.38905609893", 1.999999999999912, f64);
), }
42.0,
f64 #[test]
); #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_one() {
assert_evals_to!("Num.logChecked 1", RocResult::ok(0.0), RocResult<f64, ()>);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_zero() { fn f64_log_checked_zero() {
assert_evals_to!( assert_evals_to!("Num.logChecked 0", RocResult::err(()), RocResult<f64, ()>);
indoc!(
r#"
when Num.logChecked 0 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_negative() { fn f64_log_negative() {
assert_evals_to!( assert_evals_to!("Num.log -1", true, f64, |f: f64| f.is_nan());
indoc!(
r#"
Num.log -1
"#
),
true,
f64,
|f: f64| f.is_nan()
);
} }
#[test] #[test]
@ -890,16 +829,20 @@ fn gen_int_neq() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_int_less_than() { fn int_less_than() {
assert_evals_to!( assert_evals_to!("4 < 5", true, bool);
indoc!( }
r#"
4 < 5 #[test]
"# #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
), fn float_less_than() {
true, assert_evals_to!("4.0 < 5.0", true, bool);
bool }
);
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn float_greater_than() {
assert_evals_to!("5.0 > 4.0", true, bool);
} }
#[test] #[test]

View file

@ -3307,10 +3307,46 @@ fn box_str() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_num() { fn box_and_unbox_u64() {
assert_evals_to!("Box.unbox (Box.box (123u64))", 123, u64) assert_evals_to!("Box.unbox (Box.box (123u64))", 123, u64)
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_u32() {
assert_evals_to!("Box.unbox (Box.box (123u32))", 123, u32)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_u16() {
assert_evals_to!("Box.unbox (Box.box (123u16))", 123, u16)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_u8() {
assert_evals_to!("Box.unbox (Box.box (123u8))", 123, u8)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_bool() {
assert_evals_to!("Box.unbox (Box.box (Bool.true))", true, bool)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_f64() {
assert_evals_to!("Box.unbox (Box.box (123.0f64))", 123.0, f64)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn box_and_unbox_f32() {
assert_evals_to!("Box.unbox (Box.box (123.0f32))", 123.0, f32)
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn box_and_unbox_record() { fn box_and_unbox_record() {

View file

@ -360,7 +360,7 @@ fn i64_record1_literal() {
// ); // );
// } // }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn bool_literal() { fn bool_literal() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1111,3 +1111,22 @@ fn toplevel_accessor_fn_thunk() {
u8 u8
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn pass_record_of_u8s() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
ra = \_ -> 1u8
main =
ra { a: 1u8, b: 0u8 }
"#
),
true,
bool
)
}

View file

@ -227,7 +227,7 @@ fn is_err() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_ok() { fn roc_result_ok_i64() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -242,6 +242,26 @@ fn roc_result_ok() {
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn roc_result_ok_f64() {
// NOTE: the dev backend does not currently use float registers when returning a more
// complex type, but the rust side does expect it to. Hence this test fails with gen-dev
assert_evals_to!(
indoc!(
r#"
result : Result F64 {}
result = Ok 42.0
result
"#
),
RocResult::ok(42.0),
RocResult<f64, ()>
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_err() { fn roc_result_err() {

View file

@ -16,7 +16,7 @@ use indoc::indoc;
use roc_std::{RocList, RocResult, RocStr}; use roc_std::{RocList, RocResult, RocStr};
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_empty_delimiter() { fn str_split_empty_delimiter() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -46,7 +46,7 @@ fn str_split_empty_delimiter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_bigger_delimiter_small_str() { fn str_split_bigger_delimiter_small_str() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -110,7 +110,7 @@ fn str_split_small_str_bigger_delimiter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_big_str_small_delimiter() { fn str_split_big_str_small_delimiter() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -154,7 +154,7 @@ fn str_split_small_str_small_delimiter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_bigger_delimiter_big_strs() { fn str_split_bigger_delimiter_big_strs() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -198,7 +198,7 @@ fn str_split_minimal_example() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_small_str_big_delimiter() { fn str_split_small_str_big_delimiter() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -227,7 +227,7 @@ fn str_split_small_str_big_delimiter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_small_str_20_char_delimiter() { fn str_split_small_str_20_char_delimiter() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -243,7 +243,7 @@ fn str_split_small_str_20_char_delimiter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_concat_big_to_big() { fn str_concat_big_to_big() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -402,7 +402,7 @@ fn small_str_concat_empty_second_arg() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn small_str_concat_small_to_big() { fn small_str_concat_small_to_big() {
assert_evals_to!( assert_evals_to!(
r#"Str.concat "abc" " this is longer than 15 chars""#, r#"Str.concat "abc" " this is longer than 15 chars""#,
@ -530,7 +530,7 @@ fn str_count_graphemes_three_js() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_count_graphemes_big_str() { fn str_count_graphemes_big_str() {
assert_evals_to!( assert_evals_to!(
r#"Str.countGraphemes "6🤔å🤔e¥🤔çppkd🙃1jdal🦯asdfa∆ltråø˚waia8918.,🏅jjc""#, r#"Str.countGraphemes "6🤔å🤔e¥🤔çppkd🙃1jdal🦯asdfa∆ltråø˚waia8918.,🏅jjc""#,
@ -540,7 +540,7 @@ fn str_count_graphemes_big_str() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_starts_with_same_big_str() { fn str_starts_with_same_big_str() {
assert_evals_to!( assert_evals_to!(
r#"Str.startsWith "123456789123456789" "123456789123456789""#, r#"Str.startsWith "123456789123456789" "123456789123456789""#,
@ -550,7 +550,7 @@ fn str_starts_with_same_big_str() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_starts_with_different_big_str() { fn str_starts_with_different_big_str() {
assert_evals_to!( assert_evals_to!(
r#"Str.startsWith "12345678912345678910" "123456789123456789""#, r#"Str.startsWith "12345678912345678910" "123456789123456789""#,
@ -560,24 +560,24 @@ fn str_starts_with_different_big_str() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_starts_with_same_small_str() { fn str_starts_with_same_small_str() {
assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool); assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_starts_with_different_small_str() { fn str_starts_with_different_small_str() {
assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool); assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_starts_with_false_small_str() { fn str_starts_with_false_small_str() {
assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool); assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_single_ascii() { fn str_from_utf8_pass_single_ascii() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -593,7 +593,7 @@ fn str_from_utf8_pass_single_ascii() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_many_ascii() { fn str_from_utf8_pass_many_ascii() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -609,7 +609,7 @@ fn str_from_utf8_pass_many_ascii() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_single_unicode() { fn str_from_utf8_pass_single_unicode() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -625,7 +625,7 @@ fn str_from_utf8_pass_single_unicode() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_many_unicode() { fn str_from_utf8_pass_many_unicode() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -641,7 +641,7 @@ fn str_from_utf8_pass_many_unicode() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_single_grapheme() { fn str_from_utf8_pass_single_grapheme() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -657,7 +657,7 @@ fn str_from_utf8_pass_single_grapheme() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_many_grapheme() { fn str_from_utf8_pass_many_grapheme() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -673,7 +673,7 @@ fn str_from_utf8_pass_many_grapheme() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_pass_all() { fn str_from_utf8_pass_all() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -689,7 +689,7 @@ fn str_from_utf8_pass_all() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_invalid_start_byte() { fn str_from_utf8_fail_invalid_start_byte() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -709,7 +709,7 @@ fn str_from_utf8_fail_invalid_start_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_unexpected_end_of_sequence() { fn str_from_utf8_fail_unexpected_end_of_sequence() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -729,7 +729,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_expected_continuation() { fn str_from_utf8_fail_expected_continuation() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -749,7 +749,7 @@ fn str_from_utf8_fail_expected_continuation() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_overlong_encoding() { fn str_from_utf8_fail_overlong_encoding() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -769,7 +769,7 @@ fn str_from_utf8_fail_overlong_encoding() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_codepoint_too_large() { fn str_from_utf8_fail_codepoint_too_large() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -789,7 +789,7 @@ fn str_from_utf8_fail_codepoint_too_large() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_fail_surrogate_half() { fn str_from_utf8_fail_surrogate_half() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -809,7 +809,7 @@ fn str_from_utf8_fail_surrogate_half() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_equality() { fn str_equality() {
assert_evals_to!(r#""a" == "a""#, true, bool); assert_evals_to!(r#""a" == "a""#, true, bool);
assert_evals_to!( assert_evals_to!(
@ -865,7 +865,7 @@ fn nested_recursive_literal() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_join_comma_small() { fn str_join_comma_small() {
assert_evals_to!( assert_evals_to!(
r#"Str.joinWith ["1", "2"] ", " "#, r#"Str.joinWith ["1", "2"] ", " "#,
@ -875,7 +875,7 @@ fn str_join_comma_small() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_join_comma_big() { fn str_join_comma_big() {
assert_evals_to!( assert_evals_to!(
r#"Str.joinWith ["10000000", "2000000", "30000000"] ", " "#, r#"Str.joinWith ["10000000", "2000000", "30000000"] ", " "#,
@ -885,13 +885,13 @@ fn str_join_comma_big() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_join_comma_single() { fn str_join_comma_single() {
assert_evals_to!(r#"Str.joinWith ["1"] ", " "#, RocStr::from("1"), RocStr); assert_evals_to!(r#"Str.joinWith ["1"] ", " "#, RocStr::from("1"), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_to_utf8() { fn str_to_utf8() {
assert_evals_to!( assert_evals_to!(
r#"Str.toUtf8 "hello""#, r#"Str.toUtf8 "hello""#,
@ -909,7 +909,7 @@ fn str_to_utf8() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range() { fn str_from_utf8_range() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -926,7 +926,7 @@ fn str_from_utf8_range() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_slice() { fn str_from_utf8_range_slice() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -943,7 +943,7 @@ fn str_from_utf8_range_slice() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_slice_not_end() { fn str_from_utf8_range_slice_not_end() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -960,7 +960,7 @@ fn str_from_utf8_range_slice_not_end() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_order_does_not_matter() { fn str_from_utf8_range_order_does_not_matter() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -977,7 +977,7 @@ fn str_from_utf8_range_order_does_not_matter() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_out_of_bounds_start_value() { fn str_from_utf8_range_out_of_bounds_start_value() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -995,7 +995,7 @@ fn str_from_utf8_range_out_of_bounds_start_value() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_count_too_high() { fn str_from_utf8_range_count_too_high() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1013,7 +1013,7 @@ fn str_from_utf8_range_count_too_high() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_from_utf8_range_count_too_high_for_start() { fn str_from_utf8_range_count_too_high_for_start() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1031,7 +1031,7 @@ fn str_from_utf8_range_count_too_high_for_start() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_repeat_small_stays_small() { fn str_repeat_small_stays_small() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.repeat "Roc" 3"#), indoc!(r#"Str.repeat "Roc" 3"#),
@ -1041,7 +1041,7 @@ fn str_repeat_small_stays_small() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_repeat_small_becomes_big() { fn str_repeat_small_becomes_big() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.repeat "less than 23 characters" 2"#), indoc!(r#"Str.repeat "less than 23 characters" 2"#),
@ -1051,7 +1051,7 @@ fn str_repeat_small_becomes_big() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_repeat_big() { fn str_repeat_big() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.repeat "more than 23 characters now" 2"#), indoc!(r#"Str.repeat "more than 23 characters now" 2"#),
@ -1061,27 +1061,26 @@ fn str_repeat_big() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_repeat_empty_string() { fn str_repeat_empty_string() {
let a = indoc!(r#"Str.repeat "" 3"#); let a = indoc!(r#"Str.repeat "" 3"#);
let b = RocStr::from(""); assert_evals_to!(a, RocStr::from(""), RocStr);
assert_evals_to!(a, b, RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_repeat_zero_times() { fn str_repeat_zero_times() {
assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr); assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_empty_string() { fn str_trim_empty_string() {
assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr); assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_null_byte() { fn str_trim_null_byte() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trim (Str.reserve "\u(0000)" 40)"#), indoc!(r#"Str.trim (Str.reserve "\u(0000)" 40)"#),
@ -1091,13 +1090,13 @@ fn str_trim_null_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_small_blank_string() { fn str_trim_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr); assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_small_to_small() { fn str_trim_small_to_small() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trim " hello world ""#), indoc!(r#"Str.trim " hello world ""#),
@ -1107,7 +1106,7 @@ fn str_trim_small_to_small() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_large_to_large_unique() { fn str_trim_large_to_large_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#), indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#),
@ -1117,7 +1116,7 @@ fn str_trim_large_to_large_unique() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_large_to_small_unique() { fn str_trim_large_to_small_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trim (Str.concat " " "hello world ")"#), indoc!(r#"Str.trim (Str.concat " " "hello world ")"#),
@ -1184,13 +1183,13 @@ fn str_trim_small_to_small_shared() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_small_blank_string() { fn str_trim_left_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr); assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_small_to_small() { fn str_trim_left_small_to_small() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimLeft " hello world ""#), indoc!(r#"Str.trimLeft " hello world ""#),
@ -1200,7 +1199,7 @@ fn str_trim_left_small_to_small() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_large_to_large_unique() { fn str_trim_left_large_to_large_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello world from a large string ")"#), indoc!(r#"Str.trimLeft (Str.concat " " "hello world from a large string ")"#),
@ -1210,7 +1209,7 @@ fn str_trim_left_large_to_large_unique() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_left_large_to_small_unique() { fn str_trim_left_large_to_small_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimLeft (Str.concat " " "hello world ")"#), indoc!(r#"Str.trimLeft (Str.concat " " "hello world ")"#),
@ -1277,13 +1276,13 @@ fn str_trim_left_small_to_small_shared() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_small_blank_string() { fn str_trim_right_small_blank_string() {
assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr); assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr);
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_small_to_small() { fn str_trim_right_small_to_small() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimRight " hello world ""#), indoc!(r#"Str.trimRight " hello world ""#),
@ -1293,7 +1292,7 @@ fn str_trim_right_small_to_small() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_large_to_large_unique() { fn str_trim_right_large_to_large_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#), indoc!(r#"Str.trimRight (Str.concat " hello world from a large string" " ")"#),
@ -1303,7 +1302,7 @@ fn str_trim_right_large_to_large_unique() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_trim_right_large_to_small_unique() { fn str_trim_right_large_to_small_unique() {
assert_evals_to!( assert_evals_to!(
indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#), indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#),
@ -1370,9 +1369,17 @@ fn str_trim_right_small_to_small_shared() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_to_nat() { fn str_to_nat() {
assert_evals_to!(r#"Str.toNat "1" |> Result.withDefault 0"#, 1, usize); assert_evals_to!(
indoc!(
r#"
Str.toNat "1"
"#
),
RocResult::ok(1),
RocResult<usize, ()>
);
} }
#[test] #[test]
@ -1381,14 +1388,11 @@ fn str_to_i128() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toI128 "1" is Str.toI128 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
i128 RocResult<i128, ()>
); );
} }
@ -1398,41 +1402,39 @@ fn str_to_u128() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toU128 "1" is Str.toU128 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
u128 RocResult<u128, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_to_i64() { fn str_to_i64() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toI64 "1" is Str.toI64 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
i64 RocResult<i64, ()>
); );
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_to_u64() { fn str_to_u64() {
assert_evals_to!( assert_evals_to!(
r#"Str.toU64 "1""#, indoc!(
RocResult::ok(1u64), r#"
RocResult<u64, u8> Str.toU64 "1"
"#
),
RocResult::ok(1),
RocResult<u64, ()>
); );
} }
@ -1442,14 +1444,11 @@ fn str_to_i32() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toI32 "1" is Str.toI32 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
i32 RocResult<i32, ()>
); );
} }
@ -1457,9 +1456,13 @@ fn str_to_i32() {
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn str_to_u32() { fn str_to_u32() {
assert_evals_to!( assert_evals_to!(
r#"Str.toU32 "1""#, indoc!(
RocResult::ok(1u32), r#"
RocResult<u32, u8> Str.toU32 "1"
"#
),
RocResult::ok(1),
RocResult<u32, ()>
); );
} }
@ -1469,14 +1472,11 @@ fn str_to_i16() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toI16 "1" is Str.toI16 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
i16 RocResult<i16, ()>
); );
} }
@ -1486,14 +1486,11 @@ fn str_to_u16() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toU16 "1" is Str.toU16 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
u16 RocResult<u16, ()>
); );
} }
@ -1503,14 +1500,11 @@ fn str_to_i8() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toI8 "1" is Str.toI8 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
i8 RocResult<i8, ()>
); );
} }
@ -1520,14 +1514,11 @@ fn str_to_u8() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
when Str.toU8 "1" is Str.toU8 "1"
Ok n -> n
Err _ -> 0
"# "#
), ),
1, RocResult::ok(1),
u8 RocResult<u8, ()>
); );
} }
@ -1585,7 +1576,7 @@ fn str_to_dec() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn issue_2811() { fn issue_2811() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1601,7 +1592,7 @@ fn issue_2811() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn to_scalar_1_byte() { fn to_scalar_1_byte() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1625,7 +1616,7 @@ fn to_scalar_1_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn to_scalar_2_byte() { fn to_scalar_2_byte() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1649,7 +1640,7 @@ fn to_scalar_2_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn to_scalar_3_byte() { fn to_scalar_3_byte() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1673,7 +1664,7 @@ fn to_scalar_3_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn to_scalar_4_byte() { fn to_scalar_4_byte() {
// from https://design215.com/toolbox/utf8-4byte-characters.php // from https://design215.com/toolbox/utf8-4byte-characters.php
assert_evals_to!( assert_evals_to!(
@ -1698,7 +1689,7 @@ fn to_scalar_4_byte() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_first_one_char() { fn str_split_first_one_char() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1714,7 +1705,7 @@ fn str_split_first_one_char() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_first_multiple_chars() { fn str_split_first_multiple_chars() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1728,7 +1719,7 @@ fn str_split_first_multiple_chars() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_first_entire_input() { fn str_split_first_entire_input() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1742,7 +1733,7 @@ fn str_split_first_entire_input() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_first_not_found() { fn str_split_first_not_found() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1756,7 +1747,7 @@ fn str_split_first_not_found() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_last_one_char() { fn str_split_last_one_char() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1770,7 +1761,7 @@ fn str_split_last_one_char() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_last_multiple_chars() { fn str_split_last_multiple_chars() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1784,7 +1775,7 @@ fn str_split_last_multiple_chars() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_last_entire_input() { fn str_split_last_entire_input() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1798,12 +1789,12 @@ fn str_split_last_entire_input() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_last_not_found() { fn str_split_last_not_found() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
Str.splitFirst "foo" "bar" Str.splitLast "foo" "bar"
"# "#
), ),
RocResult::err(()), RocResult::err(()),
@ -1812,7 +1803,7 @@ fn str_split_last_not_found() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_overlapping_substring_1() { fn str_split_overlapping_substring_1() {
assert_evals_to!( assert_evals_to!(
r#"Str.split "aaa" "aa""#, r#"Str.split "aaa" "aa""#,
@ -1822,7 +1813,7 @@ fn str_split_overlapping_substring_1() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_split_overlapping_substring_2() { fn str_split_overlapping_substring_2() {
assert_evals_to!( assert_evals_to!(
r#"Str.split "aaaa" "aa""#, r#"Str.split "aaaa" "aa""#,
@ -1832,7 +1823,7 @@ fn str_split_overlapping_substring_2() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_walk_utf8_with_index() { fn str_walk_utf8_with_index() {
#[cfg(not(feature = "gen-llvm-wasm"))] #[cfg(not(feature = "gen-llvm-wasm"))]
assert_evals_to!( assert_evals_to!(
@ -1872,7 +1863,7 @@ fn str_append_scalar() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev"))]
fn str_walk_scalars() { fn str_walk_scalars() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1951,7 +1942,7 @@ fn when_on_strings() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn with_capacity() { fn with_capacity() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1965,7 +1956,7 @@ fn with_capacity() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn with_capacity_concat() { fn with_capacity_concat() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -1979,7 +1970,7 @@ fn with_capacity_concat() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn str_with_prefix() { fn str_with_prefix() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2003,7 +1994,7 @@ fn str_with_prefix() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn destructure_pattern_assigned_from_thunk_opaque() { fn destructure_pattern_assigned_from_thunk_opaque() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -2025,7 +2016,7 @@ fn destructure_pattern_assigned_from_thunk_opaque() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn destructure_pattern_assigned_from_thunk_tag() { fn destructure_pattern_assigned_from_thunk_tag() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(

View file

@ -3,7 +3,7 @@ procedure Bool.11 (#Attr.2, #Attr.3):
ret Bool.24; ret Bool.24;
procedure List.26 (List.152, List.153, List.154): procedure List.26 (List.152, List.153, List.154):
let List.493 : [C U64, C U64] = CallByName List.90 List.152 List.153 List.154; let List.493 : [C U64, C U64] = CallByName List.91 List.152 List.153 List.154;
let List.496 : U8 = 1i64; let List.496 : U8 = 1i64;
let List.497 : U8 = GetTagId List.493; let List.497 : U8 = GetTagId List.493;
let List.498 : Int1 = lowlevel Eq List.496 List.497; let List.498 : Int1 = lowlevel Eq List.496 List.497;
@ -53,13 +53,7 @@ procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
ret List.484; ret List.484;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.528, List.529, List.530, List.531, List.532):
let List.500 : U64 = 0i64;
let List.501 : U64 = CallByName List.6 List.426;
let List.499 : [C U64, C U64] = CallByName List.91 List.426 List.427 List.428 List.500 List.501;
ret List.499;
procedure List.91 (List.528, List.529, List.530, List.531, List.532):
joinpoint List.502 List.429 List.430 List.431 List.432 List.433: joinpoint List.502 List.429 List.430 List.431 List.432 List.433:
let List.504 : Int1 = CallByName Num.22 List.432 List.433; let List.504 : Int1 = CallByName Num.22 List.432 List.433;
if List.504 then if List.504 then
@ -83,6 +77,12 @@ procedure List.91 (List.528, List.529, List.530, List.531, List.532):
in in
jump List.502 List.528 List.529 List.530 List.531 List.532; jump List.502 List.528 List.529 List.530 List.531 List.532;
procedure List.91 (List.426, List.427, List.428):
let List.500 : U64 = 0i64;
let List.501 : U64 = CallByName List.6 List.426;
let List.499 : [C U64, C U64] = CallByName List.80 List.426 List.427 List.428 List.500 List.501;
ret List.499;
procedure Num.19 (#Attr.2, #Attr.3): procedure Num.19 (#Attr.2, #Attr.3):
let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.258; ret Num.258;

View file

@ -21,12 +21,12 @@ procedure Dict.4 (Dict.497):
procedure List.11 (List.114, List.115): procedure List.11 (List.114, List.115):
let List.479 : List I8 = CallByName List.68 List.115; let List.479 : List I8 = CallByName List.68 List.115;
let List.478 : List I8 = CallByName List.80 List.114 List.115 List.479; let List.478 : List I8 = CallByName List.81 List.114 List.115 List.479;
ret List.478; ret List.478;
procedure List.11 (List.114, List.115): procedure List.11 (List.114, List.115):
let List.491 : List U64 = CallByName List.68 List.115; let List.491 : List U64 = CallByName List.68 List.115;
let List.490 : List U64 = CallByName List.80 List.114 List.115 List.491; let List.490 : List U64 = CallByName List.81 List.114 List.115 List.491;
ret List.490; ret List.490;
procedure List.68 (#Attr.2): procedure List.68 (#Attr.2):
@ -45,7 +45,7 @@ procedure List.71 (#Attr.2, #Attr.3):
let List.498 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.498 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.498; ret List.498;
procedure List.80 (List.502, List.503, List.504): procedure List.81 (List.502, List.503, List.504):
joinpoint List.480 List.116 List.117 List.118: joinpoint List.480 List.116 List.117 List.118:
let List.488 : U64 = 0i64; let List.488 : U64 = 0i64;
let List.482 : Int1 = CallByName Num.24 List.117 List.488; let List.482 : Int1 = CallByName Num.24 List.117 List.488;
@ -59,7 +59,7 @@ procedure List.80 (List.502, List.503, List.504):
in in
jump List.480 List.502 List.503 List.504; jump List.480 List.502 List.503 List.504;
procedure List.80 (List.510, List.511, List.512): procedure List.81 (List.510, List.511, List.512):
joinpoint List.492 List.116 List.117 List.118: joinpoint List.492 List.116 List.117 List.118:
let List.500 : U64 = 0i64; let List.500 : U64 = 0i64;
let List.494 : Int1 = CallByName Num.24 List.117 List.500; let List.494 : Int1 = CallByName Num.24 List.117 List.500;

View file

@ -213,11 +213,11 @@ procedure List.138 (List.139, List.140, List.137):
ret List.592; ret List.592;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.500 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.500 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.500; ret List.500;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.573 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.573 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.573; ret List.573;
procedure List.4 (List.106, List.107): procedure List.4 (List.106, List.107):
@ -258,19 +258,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.594 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.594 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.594; ret List.594;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.531, List.532, List.533, List.534, List.535):
let List.504 : U64 = 0i64;
let List.505 : U64 = CallByName List.6 List.426;
let List.503 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.504 List.505;
ret List.503;
procedure List.90 (List.426, List.427, List.428):
let List.577 : U64 = 0i64;
let List.578 : U64 = CallByName List.6 List.426;
let List.576 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.577 List.578;
ret List.576;
procedure List.91 (List.531, List.532, List.533, List.534, List.535):
joinpoint List.506 List.429 List.430 List.431 List.432 List.433: joinpoint List.506 List.429 List.430 List.431 List.432 List.433:
let List.508 : Int1 = CallByName Num.22 List.432 List.433; let List.508 : Int1 = CallByName Num.22 List.432 List.433;
if List.508 then if List.508 then
@ -284,7 +272,7 @@ procedure List.91 (List.531, List.532, List.533, List.534, List.535):
in in
jump List.506 List.531 List.532 List.533 List.534 List.535; jump List.506 List.531 List.532 List.533 List.534 List.535;
procedure List.91 (List.605, List.606, List.607, List.608, List.609): procedure List.80 (List.605, List.606, List.607, List.608, List.609):
joinpoint List.579 List.429 List.430 List.431 List.432 List.433: joinpoint List.579 List.429 List.430 List.431 List.432 List.433:
let List.581 : Int1 = CallByName Num.22 List.432 List.433; let List.581 : Int1 = CallByName Num.22 List.432 List.433;
if List.581 then if List.581 then
@ -298,6 +286,18 @@ procedure List.91 (List.605, List.606, List.607, List.608, List.609):
in in
jump List.579 List.605 List.606 List.607 List.608 List.609; jump List.579 List.605 List.606 List.607 List.608 List.609;
procedure List.91 (List.426, List.427, List.428):
let List.504 : U64 = 0i64;
let List.505 : U64 = CallByName List.6 List.426;
let List.503 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.504 List.505;
ret List.503;
procedure List.91 (List.426, List.427, List.428):
let List.577 : U64 = 0i64;
let List.578 : U64 = CallByName List.6 List.426;
let List.576 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.577 List.578;
ret List.576;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.282 : U8 = lowlevel NumIntCast #Attr.2; let Num.282 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.282; ret Num.282;

View file

@ -122,7 +122,7 @@ procedure List.138 (List.139, List.140, List.137):
ret List.525; ret List.525;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.506 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.506 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.506; ret List.506;
procedure List.4 (List.106, List.107): procedure List.4 (List.106, List.107):
@ -155,13 +155,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.527; ret List.527;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.538, List.539, List.540, List.541, List.542):
let List.510 : U64 = 0i64;
let List.511 : U64 = CallByName List.6 List.426;
let List.509 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.510 List.511;
ret List.509;
procedure List.91 (List.538, List.539, List.540, List.541, List.542):
joinpoint List.512 List.429 List.430 List.431 List.432 List.433: joinpoint List.512 List.429 List.430 List.431 List.432 List.433:
let List.514 : Int1 = CallByName Num.22 List.432 List.433; let List.514 : Int1 = CallByName Num.22 List.432 List.433;
if List.514 then if List.514 then
@ -175,6 +169,12 @@ procedure List.91 (List.538, List.539, List.540, List.541, List.542):
in in
jump List.512 List.538 List.539 List.540 List.541 List.542; jump List.512 List.538 List.539 List.540 List.541 List.542;
procedure List.91 (List.426, List.427, List.428):
let List.510 : U64 = 0i64;
let List.511 : U64 = CallByName List.6 List.426;
let List.509 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.510 List.511;
ret List.509;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.263 : U8 = lowlevel NumIntCast #Attr.2; let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.263; ret Num.263;

View file

@ -130,7 +130,7 @@ procedure List.138 (List.139, List.140, List.137):
ret List.525; ret List.525;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.506 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.506 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.506; ret List.506;
procedure List.4 (List.106, List.107): procedure List.4 (List.106, List.107):
@ -163,13 +163,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.527; ret List.527;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.538, List.539, List.540, List.541, List.542):
let List.510 : U64 = 0i64;
let List.511 : U64 = CallByName List.6 List.426;
let List.509 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.510 List.511;
ret List.509;
procedure List.91 (List.538, List.539, List.540, List.541, List.542):
joinpoint List.512 List.429 List.430 List.431 List.432 List.433: joinpoint List.512 List.429 List.430 List.431 List.432 List.433:
let List.514 : Int1 = CallByName Num.22 List.432 List.433; let List.514 : Int1 = CallByName Num.22 List.432 List.433;
if List.514 then if List.514 then
@ -183,6 +177,12 @@ procedure List.91 (List.538, List.539, List.540, List.541, List.542):
in in
jump List.512 List.538 List.539 List.540 List.541 List.542; jump List.512 List.538 List.539 List.540 List.541 List.542;
procedure List.91 (List.426, List.427, List.428):
let List.510 : U64 = 0i64;
let List.511 : U64 = CallByName List.6 List.426;
let List.509 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.510 List.511;
ret List.509;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.263 : U8 = lowlevel NumIntCast #Attr.2; let Num.263 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.263; ret Num.263;

View file

@ -131,7 +131,7 @@ procedure List.138 (List.139, List.140, List.137):
ret List.531; ret List.531;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.512 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.512 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.512; ret List.512;
procedure List.4 (List.106, List.107): procedure List.4 (List.106, List.107):
@ -164,13 +164,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.534; ret List.534;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.544, List.545, List.546, List.547, List.548):
let List.516 : U64 = 0i64;
let List.517 : U64 = CallByName List.6 List.426;
let List.515 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.516 List.517;
ret List.515;
procedure List.91 (List.544, List.545, List.546, List.547, List.548):
joinpoint List.518 List.429 List.430 List.431 List.432 List.433: joinpoint List.518 List.429 List.430 List.431 List.432 List.433:
let List.520 : Int1 = CallByName Num.22 List.432 List.433; let List.520 : Int1 = CallByName Num.22 List.432 List.433;
if List.520 then if List.520 then
@ -184,6 +178,12 @@ procedure List.91 (List.544, List.545, List.546, List.547, List.548):
in in
jump List.518 List.544 List.545 List.546 List.547 List.548; jump List.518 List.544 List.545 List.546 List.547 List.548;
procedure List.91 (List.426, List.427, List.428):
let List.516 : U64 = 0i64;
let List.517 : U64 = CallByName List.6 List.426;
let List.515 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.516 List.517;
ret List.515;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.265 : U8 = lowlevel NumIntCast #Attr.2; let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.265; ret Num.265;

View file

@ -137,7 +137,7 @@ procedure List.138 (List.139, List.140, List.137):
ret List.531; ret List.531;
procedure List.18 (List.135, List.136, List.137): procedure List.18 (List.135, List.136, List.137):
let List.512 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137; let List.512 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.512; ret List.512;
procedure List.4 (List.106, List.107): procedure List.4 (List.106, List.107):
@ -170,13 +170,7 @@ procedure List.8 (#Attr.2, #Attr.3):
let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.534; ret List.534;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.544, List.545, List.546, List.547, List.548):
let List.516 : U64 = 0i64;
let List.517 : U64 = CallByName List.6 List.426;
let List.515 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.516 List.517;
ret List.515;
procedure List.91 (List.544, List.545, List.546, List.547, List.548):
joinpoint List.518 List.429 List.430 List.431 List.432 List.433: joinpoint List.518 List.429 List.430 List.431 List.432 List.433:
let List.520 : Int1 = CallByName Num.22 List.432 List.433; let List.520 : Int1 = CallByName Num.22 List.432 List.433;
if List.520 then if List.520 then
@ -190,6 +184,12 @@ procedure List.91 (List.544, List.545, List.546, List.547, List.548):
in in
jump List.518 List.544 List.545 List.546 List.547 List.548; jump List.518 List.544 List.545 List.546 List.547 List.548;
procedure List.91 (List.426, List.427, List.428):
let List.516 : U64 = 0i64;
let List.517 : U64 = CallByName List.6 List.426;
let List.515 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.516 List.517;
ret List.515;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.265 : U8 = lowlevel NumIntCast #Attr.2; let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.265; ret Num.265;

View file

@ -0,0 +1,235 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName Test.5 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.126 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.118 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102;
ret Encode.118;
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {Str, Str} = CallByName Test.2 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.396 : {} = Struct {};
ret Json.396;
procedure Json.126 (Json.127, Json.399, #Attr.12):
let Json.125 : List Str = StructAtIndex 1 #Attr.12;
inc Json.125;
let Json.124 : Str = StructAtIndex 0 #Attr.12;
inc Json.124;
dec #Attr.12;
let Json.437 : I64 = 123i64;
let Json.436 : U8 = CallByName Num.125 Json.437;
let Json.433 : List U8 = CallByName List.4 Json.127 Json.436;
let Json.435 : I64 = 34i64;
let Json.434 : U8 = CallByName Num.125 Json.435;
let Json.431 : List U8 = CallByName List.4 Json.433 Json.434;
let Json.432 : List U8 = CallByName Str.12 Json.124;
let Json.428 : List U8 = CallByName List.8 Json.431 Json.432;
let Json.430 : I64 = 34i64;
let Json.429 : U8 = CallByName Num.125 Json.430;
let Json.425 : List U8 = CallByName List.4 Json.428 Json.429;
let Json.427 : I64 = 58i64;
let Json.426 : U8 = CallByName Num.125 Json.427;
let Json.422 : List U8 = CallByName List.4 Json.425 Json.426;
let Json.424 : I64 = 91i64;
let Json.423 : U8 = CallByName Num.125 Json.424;
let Json.129 : List U8 = CallByName List.4 Json.422 Json.423;
let Json.421 : U64 = CallByName List.6 Json.125;
let Json.409 : {List U8, U64} = Struct {Json.129, Json.421};
let Json.410 : {} = Struct {};
let Json.408 : {List U8, U64} = CallByName List.18 Json.125 Json.409 Json.410;
dec Json.125;
let Json.131 : List U8 = StructAtIndex 0 Json.408;
inc Json.131;
dec Json.408;
let Json.407 : I64 = 93i64;
let Json.406 : U8 = CallByName Num.125 Json.407;
let Json.403 : List U8 = CallByName List.4 Json.131 Json.406;
let Json.405 : I64 = 125i64;
let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.402 : List U8 = CallByName List.4 Json.403 Json.404;
ret Json.402;
procedure Json.128 (Json.401, Json.134):
let Json.132 : List U8 = StructAtIndex 0 Json.401;
inc Json.132;
let Json.133 : U64 = StructAtIndex 1 Json.401;
dec Json.401;
let Json.420 : {} = Struct {};
let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.420;
joinpoint Json.415 Json.136:
let Json.413 : U64 = 1i64;
let Json.412 : U64 = CallByName Num.20 Json.133 Json.413;
let Json.411 : {List U8, U64} = Struct {Json.136, Json.412};
ret Json.411;
in
let Json.419 : U64 = 1i64;
let Json.416 : Int1 = CallByName Num.24 Json.133 Json.419;
if Json.416 then
let Json.418 : I64 = 44i64;
let Json.417 : U8 = CallByName Num.125 Json.418;
let Json.414 : List U8 = CallByName List.4 Json.135 Json.417;
jump Json.415 Json.414;
else
jump Json.415 Json.135;
procedure Json.18 (Json.95):
let Json.453 : Str = CallByName Encode.22 Json.95;
ret Json.453;
procedure Json.21 (Json.124, Json.125):
let Json.439 : {Str, List Str} = Struct {Json.124, Json.125};
let Json.438 : {Str, List Str} = CallByName Encode.22 Json.439;
ret Json.438;
procedure Json.96 (Json.97, Json.443, Json.95):
let Json.452 : I64 = 34i64;
let Json.451 : U8 = CallByName Num.125 Json.452;
let Json.449 : List U8 = CallByName List.4 Json.97 Json.451;
let Json.450 : List U8 = CallByName Str.12 Json.95;
let Json.446 : List U8 = CallByName List.8 Json.449 Json.450;
let Json.448 : I64 = 34i64;
let Json.447 : U8 = CallByName Num.125 Json.448;
let Json.445 : List U8 = CallByName List.4 Json.446 Json.447;
ret Json.445;
procedure List.138 (List.139, List.140, List.137):
let List.529 : {List U8, U64} = CallByName Json.128 List.139 List.140;
ret List.529;
procedure List.18 (List.135, List.136, List.137):
let List.510 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.510;
procedure List.4 (List.106, List.107):
let List.509 : U64 = 1i64;
let List.508 : List U8 = CallByName List.70 List.106 List.509;
let List.507 : List U8 = CallByName List.71 List.508 List.107;
ret List.507;
procedure List.6 (#Attr.2):
let List.530 : U64 = lowlevel ListLen #Attr.2;
ret List.530;
procedure List.66 (#Attr.2, #Attr.3):
let List.526 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.526;
procedure List.70 (#Attr.2, #Attr.3):
let List.482 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.482;
procedure List.71 (#Attr.2, #Attr.3):
let List.480 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.480;
procedure List.8 (#Attr.2, #Attr.3):
let List.532 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.532;
procedure List.80 (List.542, List.543, List.544, List.545, List.546):
joinpoint List.516 List.429 List.430 List.431 List.432 List.433:
let List.518 : Int1 = CallByName Num.22 List.432 List.433;
if List.518 then
let List.525 : Str = CallByName List.66 List.429 List.432;
let List.519 : {List U8, U64} = CallByName List.138 List.430 List.525 List.431;
let List.522 : U64 = 1i64;
let List.521 : U64 = CallByName Num.19 List.432 List.522;
jump List.516 List.429 List.519 List.431 List.521 List.433;
else
ret List.430;
in
jump List.516 List.542 List.543 List.544 List.545 List.546;
procedure List.91 (List.426, List.427, List.428):
let List.514 : U64 = 0i64;
let List.515 : U64 = CallByName List.6 List.426;
let List.513 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.514 List.515;
ret List.513;
procedure Num.125 (#Attr.2):
let Num.265 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.265;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.268 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.268;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.266 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.266;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.269 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.269;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.267 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.267;
procedure Str.12 (#Attr.2):
let Str.266 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.266;
procedure Test.2 (Test.10):
let Test.15 : {Str, Str} = CallByName Encode.22 Test.10;
ret Test.15;
procedure Test.3 ():
let Test.9 : Str = "";
inc Test.9;
let Test.14 : {Str, Str} = Struct {Test.9, Test.9};
ret Test.14;
procedure Test.5 (Test.6, Test.7, Test.4):
joinpoint Test.20 Test.8:
let Test.18 : List U8 = CallByName Encode.23 Test.6 Test.8 Test.7;
ret Test.18;
in
let Test.25 : Int1 = CallByName Bool.2;
if Test.25 then
let Test.26 : Str = "A";
let Test.29 : Str = StructAtIndex 0 Test.4;
inc Test.29;
dec Test.4;
let Test.28 : Str = CallByName Json.18 Test.29;
let Test.27 : List Str = Array [Test.28];
let Test.19 : {Str, List Str} = CallByName Json.21 Test.26 Test.27;
jump Test.20 Test.19;
else
let Test.21 : Str = "B";
let Test.24 : Str = StructAtIndex 1 Test.4;
inc Test.24;
dec Test.4;
let Test.23 : Str = CallByName Json.18 Test.24;
let Test.22 : List Str = Array [Test.23];
let Test.19 : {Str, List Str} = CallByName Json.21 Test.21 Test.22;
jump Test.20 Test.19;
procedure Test.0 ():
let Test.12 : {Str, Str} = CallByName Test.3;
let Test.13 : {} = CallByName Json.1;
let Test.11 : List U8 = CallByName Encode.25 Test.12 Test.13;
ret Test.11;

View file

@ -0,0 +1,82 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
procedure Test.12 (Test.52):
let Test.75 : Int1 = false;
ret Test.75;
procedure Test.13 (Test.51):
let Test.83 : Int1 = true;
ret Test.83;
procedure Test.14 (Test.50):
ret Test.50;
procedure Test.15 (Test.49):
let Test.74 : {} = Struct {};
let Test.73 : Int1 = CallByName Test.12 Test.74;
ret Test.73;
procedure Test.16 (Test.48):
let Test.82 : {} = Struct {};
let Test.81 : Int1 = CallByName Test.13 Test.82;
ret Test.81;
procedure Test.17 (Test.47):
ret Test.47;
procedure Test.35 (Test.36, Test.76):
inc Test.36;
ret Test.36;
procedure Test.37 (Test.38, Test.84):
inc Test.38;
ret Test.38;
procedure Test.40 (Test.41, Test.65, Test.39):
let Test.68 : {} = Struct {};
joinpoint Test.69 Test.67:
ret Test.67;
in
switch Test.39:
case 0:
let Test.70 : List U8 = CallByName Test.35 Test.41 Test.68;
jump Test.69 Test.70;
default:
let Test.71 : List U8 = CallByName Test.37 Test.41 Test.68;
jump Test.69 Test.71;
procedure Test.43 (Test.44, Test.42):
joinpoint Test.62 Test.60:
let Test.59 : List U8 = Array [];
let Test.58 : List U8 = CallByName Test.40 Test.59 Test.44 Test.60;
dec Test.59;
ret Test.58;
in
let Test.78 : Int1 = CallByName Bool.2;
if Test.78 then
let Test.80 : Str = StructAtIndex 0 Test.42;
inc Test.80;
dec Test.42;
let Test.79 : Int1 = CallByName Test.16 Test.80;
dec Test.80;
let Test.61 : Int1 = CallByName Test.14 Test.79;
jump Test.62 Test.61;
else
let Test.72 : U8 = StructAtIndex 1 Test.42;
dec Test.42;
let Test.63 : Int1 = CallByName Test.15 Test.72;
let Test.61 : Int1 = CallByName Test.14 Test.63;
jump Test.62 Test.61;
procedure Test.0 ():
let Test.86 : Str = "";
let Test.87 : U8 = 7i64;
let Test.55 : {Str, U8} = Struct {Test.86, Test.87};
let Test.46 : {Str, U8} = CallByName Test.17 Test.55;
let Test.54 : {} = Struct {};
let Test.53 : List U8 = CallByName Test.43 Test.54 Test.46;
ret Test.53;

View file

@ -0,0 +1,374 @@
procedure #Derived.0 (#Derived.1):
let #Derived_gen.10 : [C {}, C {}] = TagId(0) #Derived.1;
let #Derived_gen.9 : [C {}, C {}] = CallByName Encode.22 #Derived_gen.10;
ret #Derived_gen.9;
procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12):
let #Derived.1 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
joinpoint #Derived_gen.14 #Derived_gen.13:
let #Derived_gen.12 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.13 #Derived.4;
ret #Derived_gen.12;
in
let #Derived_gen.16 : Str = "A";
let #Derived_gen.17 : List [] = Array [];
let #Derived_gen.15 : {Str, List []} = CallByName Json.21 #Derived_gen.16 #Derived_gen.17;
jump #Derived_gen.14 #Derived_gen.15;
procedure #Derived.5 (#Derived.6):
let #Derived_gen.1 : [C {}, C {}] = TagId(1) #Derived.6;
let #Derived_gen.0 : [C {}, C {}] = CallByName Encode.22 #Derived_gen.1;
ret #Derived_gen.0;
procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12):
let #Derived.6 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12;
joinpoint #Derived_gen.5 #Derived_gen.4:
let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.4 #Derived.9;
ret #Derived_gen.3;
in
let #Derived_gen.7 : Str = "B";
let #Derived_gen.8 : List [] = Array [];
let #Derived_gen.6 : {Str, List []} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8;
jump #Derived_gen.5 #Derived_gen.6;
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.22 (Encode.93):
ret Encode.93;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.106 : List U8 = CallByName Test.5 Encode.94 Encode.96 Encode.102;
ret Encode.106;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.113 : List U8 = CallByName Json.126 Encode.94 Encode.96 Encode.102;
ret Encode.113;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.117 : U8 = GetTagId Encode.102;
joinpoint Encode.118 Encode.116:
ret Encode.116;
in
switch Encode.117:
case 0:
let Encode.119 : List U8 = CallByName #Derived.2 Encode.94 Encode.96 Encode.102;
jump Encode.118 Encode.119;
default:
let Encode.120 : List U8 = CallByName #Derived.7 Encode.94 Encode.96 Encode.102;
jump Encode.118 Encode.120;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.132 : List U8 = CallByName Json.126 Encode.94 Encode.96 Encode.102;
ret Encode.132;
procedure Encode.23 (Encode.94, Encode.102, Encode.96):
let Encode.136 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
Crash Encode.136
procedure Encode.25 (Encode.100, Encode.101):
let Encode.104 : List U8 = Array [];
let Encode.105 : {{}, {}} = CallByName Test.2 Encode.100;
let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101;
ret Encode.103;
procedure Json.1 ():
let Json.396 : {} = Struct {};
ret Json.396;
procedure Json.126 (Json.127, Json.399, #Attr.12):
let Json.125 : List [C {}, C {}] = StructAtIndex 1 #Attr.12;
inc Json.125;
let Json.124 : Str = StructAtIndex 0 #Attr.12;
inc Json.124;
dec #Attr.12;
let Json.437 : I64 = 123i64;
let Json.436 : U8 = CallByName Num.125 Json.437;
let Json.433 : List U8 = CallByName List.4 Json.127 Json.436;
let Json.435 : I64 = 34i64;
let Json.434 : U8 = CallByName Num.125 Json.435;
let Json.431 : List U8 = CallByName List.4 Json.433 Json.434;
let Json.432 : List U8 = CallByName Str.12 Json.124;
let Json.428 : List U8 = CallByName List.8 Json.431 Json.432;
let Json.430 : I64 = 34i64;
let Json.429 : U8 = CallByName Num.125 Json.430;
let Json.425 : List U8 = CallByName List.4 Json.428 Json.429;
let Json.427 : I64 = 58i64;
let Json.426 : U8 = CallByName Num.125 Json.427;
let Json.422 : List U8 = CallByName List.4 Json.425 Json.426;
let Json.424 : I64 = 91i64;
let Json.423 : U8 = CallByName Num.125 Json.424;
let Json.129 : List U8 = CallByName List.4 Json.422 Json.423;
let Json.421 : U64 = CallByName List.6 Json.125;
let Json.409 : {List U8, U64} = Struct {Json.129, Json.421};
let Json.410 : {} = Struct {};
let Json.408 : {List U8, U64} = CallByName List.18 Json.125 Json.409 Json.410;
dec Json.125;
let Json.131 : List U8 = StructAtIndex 0 Json.408;
inc Json.131;
dec Json.408;
let Json.407 : I64 = 93i64;
let Json.406 : U8 = CallByName Num.125 Json.407;
let Json.403 : List U8 = CallByName List.4 Json.131 Json.406;
let Json.405 : I64 = 125i64;
let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.402 : List U8 = CallByName List.4 Json.403 Json.404;
ret Json.402;
procedure Json.126 (Json.127, Json.399, #Attr.12):
let Json.125 : List [] = StructAtIndex 1 #Attr.12;
inc Json.125;
let Json.124 : Str = StructAtIndex 0 #Attr.12;
inc Json.124;
dec #Attr.12;
let Json.487 : I64 = 123i64;
let Json.486 : U8 = CallByName Num.125 Json.487;
let Json.483 : List U8 = CallByName List.4 Json.127 Json.486;
let Json.485 : I64 = 34i64;
let Json.484 : U8 = CallByName Num.125 Json.485;
let Json.481 : List U8 = CallByName List.4 Json.483 Json.484;
let Json.482 : List U8 = CallByName Str.12 Json.124;
let Json.478 : List U8 = CallByName List.8 Json.481 Json.482;
let Json.480 : I64 = 34i64;
let Json.479 : U8 = CallByName Num.125 Json.480;
let Json.475 : List U8 = CallByName List.4 Json.478 Json.479;
let Json.477 : I64 = 58i64;
let Json.476 : U8 = CallByName Num.125 Json.477;
let Json.472 : List U8 = CallByName List.4 Json.475 Json.476;
let Json.474 : I64 = 91i64;
let Json.473 : U8 = CallByName Num.125 Json.474;
let Json.129 : List U8 = CallByName List.4 Json.472 Json.473;
let Json.471 : U64 = CallByName List.6 Json.125;
let Json.459 : {List U8, U64} = Struct {Json.129, Json.471};
let Json.460 : {} = Struct {};
let Json.458 : {List U8, U64} = CallByName List.18 Json.125 Json.459 Json.460;
dec Json.125;
let Json.131 : List U8 = StructAtIndex 0 Json.458;
inc Json.131;
dec Json.458;
let Json.457 : I64 = 93i64;
let Json.456 : U8 = CallByName Num.125 Json.457;
let Json.453 : List U8 = CallByName List.4 Json.131 Json.456;
let Json.455 : I64 = 125i64;
let Json.454 : U8 = CallByName Num.125 Json.455;
let Json.452 : List U8 = CallByName List.4 Json.453 Json.454;
ret Json.452;
procedure Json.128 (Json.401, Json.134):
let Json.132 : List U8 = StructAtIndex 0 Json.401;
inc Json.132;
let Json.133 : U64 = StructAtIndex 1 Json.401;
dec Json.401;
let Json.420 : {} = Struct {};
let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.420;
joinpoint Json.415 Json.136:
let Json.413 : U64 = 1i64;
let Json.412 : U64 = CallByName Num.20 Json.133 Json.413;
let Json.411 : {List U8, U64} = Struct {Json.136, Json.412};
ret Json.411;
in
let Json.419 : U64 = 1i64;
let Json.416 : Int1 = CallByName Num.24 Json.133 Json.419;
if Json.416 then
let Json.418 : I64 = 44i64;
let Json.417 : U8 = CallByName Num.125 Json.418;
let Json.414 : List U8 = CallByName List.4 Json.135 Json.417;
jump Json.415 Json.414;
else
jump Json.415 Json.135;
procedure Json.128 (Json.401, Json.134):
let Json.132 : List U8 = StructAtIndex 0 Json.401;
inc Json.132;
let Json.133 : U64 = StructAtIndex 1 Json.401;
dec Json.401;
let Json.470 : {} = Struct {};
let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.470;
dec Json.132;
joinpoint Json.465 Json.136:
let Json.463 : U64 = 1i64;
let Json.462 : U64 = CallByName Num.20 Json.133 Json.463;
let Json.461 : {List U8, U64} = Struct {Json.136, Json.462};
ret Json.461;
in
let Json.469 : U64 = 1i64;
let Json.466 : Int1 = CallByName Num.24 Json.133 Json.469;
if Json.466 then
let Json.468 : I64 = 44i64;
let Json.467 : U8 = CallByName Num.125 Json.468;
let Json.464 : List U8 = CallByName List.4 Json.135 Json.467;
jump Json.465 Json.464;
else
jump Json.465 Json.135;
procedure Json.21 (Json.124, Json.125):
let Json.439 : {Str, List [C {}, C {}]} = Struct {Json.124, Json.125};
let Json.438 : {Str, List [C {}, C {}]} = CallByName Encode.22 Json.439;
ret Json.438;
procedure Json.21 (Json.124, Json.125):
let Json.489 : {Str, List []} = Struct {Json.124, Json.125};
let Json.488 : {Str, List []} = CallByName Encode.22 Json.489;
ret Json.488;
procedure List.138 (List.139, List.140, List.137):
let List.523 : {List U8, U64} = CallByName Json.128 List.139 List.140;
ret List.523;
procedure List.138 (List.139, List.140, List.137):
let List.596 : {List U8, U64} = CallByName Json.128 List.139 List.140;
ret List.596;
procedure List.18 (List.135, List.136, List.137):
let List.504 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.504;
procedure List.18 (List.135, List.136, List.137):
let List.577 : {List U8, U64} = CallByName List.91 List.135 List.136 List.137;
ret List.577;
procedure List.4 (List.106, List.107):
let List.576 : U64 = 1i64;
let List.575 : List U8 = CallByName List.70 List.106 List.576;
let List.574 : List U8 = CallByName List.71 List.575 List.107;
ret List.574;
procedure List.6 (#Attr.2):
let List.524 : U64 = lowlevel ListLen #Attr.2;
ret List.524;
procedure List.6 (#Attr.2):
let List.597 : U64 = lowlevel ListLen #Attr.2;
ret List.597;
procedure List.66 (#Attr.2, #Attr.3):
let List.520 : [C {}, C {}] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.520;
procedure List.66 (#Attr.2, #Attr.3):
let List.593 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.593;
procedure List.70 (#Attr.2, #Attr.3):
let List.555 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.555;
procedure List.71 (#Attr.2, #Attr.3):
let List.553 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.553;
procedure List.8 (#Attr.2, #Attr.3):
let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.598;
procedure List.80 (List.535, List.536, List.537, List.538, List.539):
joinpoint List.510 List.429 List.430 List.431 List.432 List.433:
let List.512 : Int1 = CallByName Num.22 List.432 List.433;
if List.512 then
let List.519 : [C {}, C {}] = CallByName List.66 List.429 List.432;
let List.513 : {List U8, U64} = CallByName List.138 List.430 List.519 List.431;
let List.516 : U64 = 1i64;
let List.515 : U64 = CallByName Num.19 List.432 List.516;
jump List.510 List.429 List.513 List.431 List.515 List.433;
else
ret List.430;
in
jump List.510 List.535 List.536 List.537 List.538 List.539;
procedure List.80 (List.608, List.609, List.610, List.611, List.612):
joinpoint List.583 List.429 List.430 List.431 List.432 List.433:
let List.585 : Int1 = CallByName Num.22 List.432 List.433;
if List.585 then
let List.592 : [] = CallByName List.66 List.429 List.432;
let List.586 : {List U8, U64} = CallByName List.138 List.430 List.592 List.431;
let List.589 : U64 = 1i64;
let List.588 : U64 = CallByName Num.19 List.432 List.589;
jump List.583 List.429 List.586 List.431 List.588 List.433;
else
ret List.430;
in
jump List.583 List.608 List.609 List.610 List.611 List.612;
procedure List.91 (List.426, List.427, List.428):
let List.508 : U64 = 0i64;
let List.509 : U64 = CallByName List.6 List.426;
let List.507 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.508 List.509;
ret List.507;
procedure List.91 (List.426, List.427, List.428):
let List.581 : U64 = 0i64;
let List.582 : U64 = CallByName List.6 List.426;
let List.580 : {List U8, U64} = CallByName List.80 List.426 List.427 List.428 List.581 List.582;
ret List.580;
procedure Num.125 (#Attr.2):
let Num.284 : U8 = lowlevel NumIntCast #Attr.2;
ret Num.284;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.287 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.287;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.285 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.285;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.288 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.288;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.286 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.286;
procedure Str.12 (#Attr.2):
let Str.267 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.267;
procedure Test.2 (Test.11):
let Test.18 : {{}, {}} = CallByName Encode.22 Test.11;
ret Test.18;
procedure Test.3 ():
let Test.16 : {} = Struct {};
let Test.17 : {} = Struct {};
let Test.15 : {{}, {}} = Struct {Test.16, Test.17};
ret Test.15;
procedure Test.5 (Test.6, Test.7, Test.4):
joinpoint Test.23 Test.8:
let Test.21 : List U8 = CallByName Encode.23 Test.6 Test.8 Test.7;
ret Test.21;
in
let Test.28 : Int1 = CallByName Bool.2;
if Test.28 then
let Test.29 : Str = "A";
let Test.32 : {} = StructAtIndex 0 Test.4;
let Test.31 : [C {}, C {}] = CallByName #Derived.0 Test.32;
let Test.30 : List [C {}, C {}] = Array [Test.31];
let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.21 Test.29 Test.30;
jump Test.23 Test.22;
else
let Test.24 : Str = "B";
let Test.27 : {} = StructAtIndex 1 Test.4;
let Test.26 : [C {}, C {}] = CallByName #Derived.5 Test.27;
let Test.25 : List [C {}, C {}] = Array [Test.26];
let Test.22 : {Str, List [C {}, C {}]} = CallByName Json.21 Test.24 Test.25;
jump Test.23 Test.22;
procedure Test.0 ():
let Test.13 : {{}, {}} = CallByName Test.3;
let Test.14 : {} = CallByName Json.1;
let Test.12 : List U8 = CallByName Encode.25 Test.13 Test.14;
ret Test.12;

View file

@ -3,7 +3,7 @@ procedure Bool.11 (#Attr.2, #Attr.3):
ret Bool.24; ret Bool.24;
procedure List.26 (List.152, List.153, List.154): procedure List.26 (List.152, List.153, List.154):
let List.493 : [C U64, C U64] = CallByName List.90 List.152 List.153 List.154; let List.493 : [C U64, C U64] = CallByName List.91 List.152 List.153 List.154;
let List.496 : U8 = 1i64; let List.496 : U8 = 1i64;
let List.497 : U8 = GetTagId List.493; let List.497 : U8 = GetTagId List.493;
let List.498 : Int1 = lowlevel Eq List.496 List.497; let List.498 : Int1 = lowlevel Eq List.496 List.497;
@ -53,13 +53,7 @@ procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
ret List.484; ret List.484;
procedure List.90 (List.426, List.427, List.428): procedure List.80 (List.528, List.529, List.530, List.531, List.532):
let List.500 : U64 = 0i64;
let List.501 : U64 = CallByName List.6 List.426;
let List.499 : [C U64, C U64] = CallByName List.91 List.426 List.427 List.428 List.500 List.501;
ret List.499;
procedure List.91 (List.528, List.529, List.530, List.531, List.532):
joinpoint List.502 List.429 List.430 List.431 List.432 List.433: joinpoint List.502 List.429 List.430 List.431 List.432 List.433:
let List.504 : Int1 = CallByName Num.22 List.432 List.433; let List.504 : Int1 = CallByName Num.22 List.432 List.433;
if List.504 then if List.504 then
@ -83,6 +77,12 @@ procedure List.91 (List.528, List.529, List.530, List.531, List.532):
in in
jump List.502 List.528 List.529 List.530 List.531 List.532; jump List.502 List.528 List.529 List.530 List.531 List.532;
procedure List.91 (List.426, List.427, List.428):
let List.500 : U64 = 0i64;
let List.501 : U64 = CallByName List.6 List.426;
let List.499 : [C U64, C U64] = CallByName List.80 List.426 List.427 List.428 List.500 List.501;
ret List.499;
procedure Num.19 (#Attr.2, #Attr.3): procedure Num.19 (#Attr.2, #Attr.3):
let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
ret Num.258; ret Num.258;

View file

@ -2571,3 +2571,155 @@ fn recursive_lambda_set_has_nested_non_recursive_lambda_sets_issue_5026() {
"# "#
) )
} }
#[mono_test]
fn unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification() {
// This is a regression test for the ambient lambda set specialization algorithm.
//
// In the program below, monomorphization of `toEncoderQ` with the `Q` in `main` induces the
// resolution of `t.a` and `t.b`, and the unification of their pending unspecialization lambda
// sets, when `t.a` and `t.b` have been resolved to concrete types, but before the
// specialization procedure steps in to resolve the lambda sets concretely. That's because
// monomorphization unifies the general type of `toEncoderQ` with the concrete type, forcing
// concretization of `t`, but the specialization procedure runs only after the unification is
// complete.
//
// In this case, it's imperative that the unspecialized lambda sets of `toEncoder t.a` and
// `toEncoder t.b` wind up in the same lambda set, that is in
//
// tag : @MEncoder (Bytes, Linear -[[] + @MU8:toEncoder:1 + @MStr:toEncoder+1] -> Bytes)
// -[lTag]->
// @MEncoder (Bytes, Linear -[[Linear:lTag:3 { @MEncoder (Bytes, Linear -[[] + @MU8:toEncoder:1 + @MStr:toEncoder:1] -> Bytes) }]] -> Bytes)
//
// rather than forcing the lambda set inside to `tag` to become disjoint, as e.g.
//
// tag : @MEncoder (Bytes, Linear -[[] + @MU8:toEncoder:1 + @MStr:toEncoder+1] -> Bytes)
// -[lTag]->
// @MEncoder (Bytes, Linear -[[
// Linear:lTag:3 { @MEncoder (Bytes, Linear -[[] + @MU8:toEncoder:1] -> Bytes) },
// Linear:lTag:3 { @MEncoder (Bytes, Linear -[[] + @MStr:toEncoder:1] -> Bytes) },
// ]] -> Bytes)
indoc!(
r#"
app "test" provides [main] 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 : {} -> MEncoder fmt | fmt has Format
str : {} -> MEncoder fmt | fmt has Format
tag : MEncoder fmt -> MEncoder fmt | fmt has Format
Linear := {} has [Format {u8: lU8, str: lStr, tag: lTag}]
MU8 := U8 has [MEncoding {toEncoder: toEncoderU8}]
MStr := Str has [MEncoding {toEncoder: toEncoderStr}]
Q a b := { a: a, b: b }
lU8 = \{} -> @MEncoder (\lst, @Linear {} -> lst)
lStr = \{} -> @MEncoder (\lst, @Linear {} -> lst)
lTag = \@MEncoder doFormat -> @MEncoder (\lst, @Linear {} ->
doFormat lst (@Linear {})
)
toEncoderU8 = \@MU8 _ -> u8 {}
toEncoderStr = \@MStr _ -> str {}
toEncoderQ =
\@Q t -> \fmt ->
@MEncoder doit = if Bool.true
then tag (toEncoder t.a)
else tag (toEncoder t.b)
doit [] fmt
main =
fmt = toEncoderQ (@Q {a : @MStr "", b: @MU8 7})
fmt (@Linear {})
"#
)
}
#[mono_test]
fn unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable()
{
// This is a regression test for the ambient lambda set specialization algorithm.
//
// The principle of the test is equivalent to that of `unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification`.
//
// However, this test requires a larger reproduction because it is negative behavior is only
// visible in the presence of builtin ability usage (in this case, `Encoding` and
// `EncoderFormatting`).
//
// In this test, the payload types `[A]*` and `[B]*` of the encoded type `Q` are unifiable in
// their unspecialized lambda set representations under `toEncoderQ`; however, they must not
// be, because they in fact represent to different specializations of needed encoders. In
// particular, the lambda set `[[] + [A]:toEncoder:1 + [B]:toEncoder:1]` must be preserved,
// rather than collapsing to `[[] + [A, B]:toEncoder:1]`.
indoc!(
r#"
app "test" imports [Json] provides [main] to "./platform"
Q a b := { a: a, b: b } has [Encoding {toEncoder: toEncoderQ}]
toEncoderQ =
\@Q t -> Encode.custom \bytes, fmt ->
f = if Bool.true
then Encode.tag "A" [Encode.toEncoder t.a]
else Encode.tag "B" [Encode.toEncoder t.b]
Encode.appendWith bytes f fmt
accessor = @Q {a : A, b: B}
main =
Encode.toBytes accessor Json.toUtf8
"#
)
}
#[mono_test]
fn unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types() {
// This is a regression test for the ambient lambda set specialization algorithm.
//
// The principle of the test is equivalent to that of `unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification`.
//
// However, this test requires a larger reproduction because it is negative behavior is only
// visible in the presence of builtin ability usage (in this case, `Encoding` and
// `EncoderFormatting`).
//
// In this test, the payload types `Str` and `Str` of the encoded type `Q` are unifiable in
// their unspecialized lambda set representations under `toEncoderQ`, and moreoever they are
// equivalent specializations, since they both come from the same root variable `x`. In as
// such, the lambda set `[[] + Str:toEncoder:1]` should be produced during compaction, rather
// than staying as the expanded `[[] + Str:toEncoder:1 + Str:toEncoder:1]` after the types of
// `t.a` and `t.b` are filled in.
indoc!(
r#"
app "test" imports [Json] provides [main] to "./platform"
Q a b := { a: a, b: b } has [Encoding {toEncoder: toEncoderQ}]
toEncoderQ =
\@Q t -> Encode.custom \bytes, fmt ->
f = if Bool.true
then Encode.tag "A" [Encode.toEncoder t.a]
else Encode.tag "B" [Encode.toEncoder t.b]
Encode.appendWith bytes f fmt
accessor =
x = ""
@Q {a : x, b: x}
main =
Encode.toBytes accessor Json.toUtf8
"#
)
}

View file

@ -568,6 +568,15 @@ mod test_snapshots {
Err(err) => Err(format!("{:?}", err)), Err(err) => Err(format!("{:?}", err)),
}; };
if expect == TestExpectation::Pass {
let tokens = roc_parse::highlight::highlight(&source);
for token in tokens {
if token.value == roc_parse::highlight::Token::Error {
panic!("Found an error highlight token in the input: {:?}", token);
}
}
}
let actual_result = let actual_result =
if expect == TestExpectation::Pass || expect == TestExpectation::Malformed { if expect == TestExpectation::Pass || expect == TestExpectation::Malformed {
result.expect("The source code for this test did not successfully parse!") result.expect("The source code for this test did not successfully parse!")

View file

@ -1568,8 +1568,10 @@ fn unspecialized_lambda_set_sorter(subs: &Subs, uls1: Uls, uls2: Uls) -> std::cm
} }
(FlexAbleVar(..), _) => Greater, (FlexAbleVar(..), _) => Greater,
(_, FlexAbleVar(..)) => Less, (_, FlexAbleVar(..)) => Less,
// For everything else, the order is irrelevant // For everything else, sort by the root key
(_, _) => Less, (_, _) => subs
.get_root_key_without_compacting(var1)
.cmp(&subs.get_root_key_without_compacting(var2)),
} }
} }
ord => ord, ord => ord,
@ -1731,6 +1733,8 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
let kept = uls_left.next().unwrap(); let kept = uls_left.next().unwrap();
merged_uls.push(*kept); merged_uls.push(*kept);
} else { } else {
// CASE: disjoint_flex_specializations
//
// ... a1 ... // ... a1 ...
// ... b1 ... // ... b1 ...
// => ... a1, b1 ... // => ... a1, b1 ...
@ -1800,20 +1804,47 @@ fn unify_unspecialized_lambdas<M: MetaCollector>(
let _dropped = uls_left.next().unwrap(); let _dropped = uls_left.next().unwrap();
} }
(_, _) => { (_, _) => {
// ... {foo: _} ... if env.subs.equivalent_without_compacting(var_l, var_r) {
// ... {foo: _} ... // ... a1 ...
// => ... {foo: _} ... // ... b1=a1 ...
// => ... a1 ...
// //
// Unify them, then advance one. // Keep the one on the left, drop the one on the right. Then progress
// (the choice is arbitrary, so we choose the left) // both, because the next variable on the left must be disjoint from
// the current on the right (resp. next variable on the right vs.
// current left) - if they aren't, then the invariant was broken.
//
// Then progress both, because the invariant tells us they must be
// disjoint, and if there were any concrete variables, they would have
// appeared earlier.
let _dropped = uls_right.next().unwrap();
let kept = uls_left.next().unwrap();
merged_uls.push(*kept);
let outcome = unify_pool(env, pool, var_l, var_r, mode); debug_assert!(uls_right
if !outcome.mismatches.is_empty() { .peek()
return Err(outcome); .map(|r| env.subs.equivalent_without_compacting(var_l, r.0))
.unwrap_or(true));
debug_assert!(uls_left
.peek()
.map(|l| env.subs.equivalent_without_compacting(l.0, var_r))
.unwrap_or(true));
} else {
// Even if these two variables unify, since they are not equivalent,
// they correspond to different specializations! As such we must not
// merge them.
//
// Instead, keep both, but do so by adding and advancing the side with
// the lower root. See CASE disjoint_flex_specializations for
// reasoning.
if env.subs.get_root_key(var_l) < env.subs.get_root_key(var_r) {
let kept = uls_left.next().unwrap();
merged_uls.push(*kept);
} else {
let kept = uls_right.next().unwrap();
merged_uls.push(*kept);
}
} }
whole_outcome.union(outcome);
let _dropped = uls_left.next().unwrap();
} }
} }
} }

View file

@ -14,7 +14,6 @@ normal = ["confy"]
[features] [features]
default = [] default = []
with_sound = ["rodio"]
[dependencies] [dependencies]
roc_ast = { path = "../ast" } roc_ast = { path = "../ast" }
@ -57,7 +56,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [
], default-features = false } ], default-features = false }
serde = { version = "1.0.144", features = ["derive"] } serde = { version = "1.0.144", features = ["derive"] }
nonempty = "0.8.0" nonempty = "0.8.0"
rodio = { version = "0.15.0", optional = true } # to play sounds
threadpool = "1.8.1" threadpool = "1.8.1"
fs_extra.workspace = true fs_extra.workspace = true

View file

@ -8,7 +8,5 @@ mod mvc;
mod render_ast; mod render_ast;
mod render_debug; mod render_debug;
mod resources; mod resources;
#[cfg(feature = "with_sound")]
mod sound;
mod theme; mod theme;
mod util; mod util;

View file

@ -21,8 +21,6 @@ use crate::editor::mvc::string_update::start_new_string;
use crate::editor::mvc::string_update::update_small_string; use crate::editor::mvc::string_update::update_small_string;
use crate::editor::mvc::string_update::update_string; use crate::editor::mvc::string_update::update_string;
use crate::editor::mvc::tld_value_update::{start_new_tld_value, update_tld_val_name}; use crate::editor::mvc::tld_value_update::{start_new_tld_value, update_tld_val_name};
#[cfg(feature = "with_sound")]
use crate::editor::sound::play_sound;
use crate::ui::text::caret_w_select::CaretWSelect; use crate::ui::text::caret_w_select::CaretWSelect;
use crate::ui::text::lines::MoveCaretFun; use crate::ui::text::lines::MoveCaretFun;
use crate::ui::text::selection::validate_raw_sel; use crate::ui::text::selection::validate_raw_sel;
@ -546,12 +544,6 @@ impl<'a> EdModel<'a> {
self.show_debug_view = !self.show_debug_view; self.show_debug_view = !self.show_debug_view;
self.dirty = true; self.dirty = true;
} }
F12 => {
#[cfg(feature = "with_sound")]
_sound_thread_pool.execute(move || {
play_sound("./editor/src/editor/resources/sounds/bell_sound.mp3");
});
}
_ => (), _ => (),
} }

View file

@ -1,45 +0,0 @@
use rodio::{Decoder, OutputStream, Sink};
use std::fs::File;
use std::io::BufReader;
pub(crate) fn play_sound(sound_path_str: &str) {
let out_stream_res = OutputStream::try_default();
match out_stream_res {
Ok((_, out_stream_handle)) => match Sink::try_new(&out_stream_handle) {
Ok(sink) => match File::open(sound_path_str) {
Ok(file) => {
let reader = BufReader::new(file);
match Decoder::new(reader) {
Ok(decoder) => {
sink.append(decoder);
sink.sleep_until_end();
}
Err(e) => {
println!("Failed to create Decoder from BufReader from sound file at {}. Error message: {:?}", sound_path_str, e);
}
}
}
Err(e) => {
println!(
"Failed to open sound file at {}. Error message: {}",
sound_path_str, e
);
}
},
Err(e) => {
println!(
"Failed to create Sink to play sound. Error message: {:?}",
e
);
}
},
Err(e) => {
println!(
"Failed to create OutputStream to play sound. Error message: {:?}",
e
);
}
}
}

View file

@ -65,7 +65,6 @@ rustPlatform.buildRustPackage {
cargo cargo
makeWrapper # necessary for postBuild wrapProgram makeWrapper # necessary for postBuild wrapProgram
] ++ lib.optionals pkgs.stdenv.isLinux [ ] ++ lib.optionals pkgs.stdenv.isLinux [
alsa-lib
valgrind valgrind
vulkan-headers vulkan-headers
vulkan-loader vulkan-loader

View file

@ -39,7 +39,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [
serde = { version = "1.0.130", features = ["derive"] } serde = { version = "1.0.130", features = ["derive"] }
nonempty = "0.7.0" nonempty = "0.7.0"
fs_extra = "1.2.0" fs_extra = "1.2.0"
rodio = { version = "0.14.0", optional = true } # to play sounds
threadpool = "1.8.1" threadpool = "1.8.1"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
@ -50,7 +49,6 @@ normal = ["confy"]
[features] [features]
default = [] default = []
with_sound = ["rodio"]
[dependencies.bytemuck] [dependencies.bytemuck]
version = "1.7.2" version = "1.7.2"

View file

@ -40,7 +40,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [
serde = { version = "1.0.130", features = ["derive"] } serde = { version = "1.0.130", features = ["derive"] }
nonempty = "0.7.0" nonempty = "0.7.0"
fs_extra = "1.2.0" fs_extra = "1.2.0"
rodio = { version = "0.14.0", optional = true } # to play sounds
threadpool = "1.8.1" threadpool = "1.8.1"
[package.metadata.cargo-udeps.ignore] [package.metadata.cargo-udeps.ignore]
@ -51,7 +50,6 @@ normal = ["confy"]
[features] [features]
default = [] default = []
with_sound = ["rodio"]
[dependencies.bytemuck] [dependencies.bytemuck]
version = "1.7.2" version = "1.7.2"

View file

@ -1,58 +0,0 @@
app "parse-movies-csv"
packages { pf: "platform/main.roc" }
imports [Parser.Core.{ Parser, map, keep }, Parser.Str.{ RawStr }, Parser.CSV.{ CSV, record, field, string, nat }]
provides [main] to pf
input : Str
input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\""
main : Str
main =
when Parser.CSV.parseStr movieInfoParser input is
Ok movies ->
moviesString =
movies
|> List.map movieInfoExplanation
|> Str.joinWith ("\n")
nMovies = List.len movies |> Num.toStr
"\(nMovies) movies were found:\n\n\(moviesString)\n\nParse success!\n"
Err problem ->
when problem is
ParsingFailure failure ->
"Parsing failure: \(failure)\n"
ParsingIncomplete leftover ->
leftoverStr = leftover |> List.map Parser.Str.strFromRaw |> List.map (\val -> "\"\(val)\"") |> Str.joinWith ", "
"Parsing incomplete. Following leftover fields while parsing a record: \(leftoverStr)\n"
SyntaxError error ->
"Parsing failure. Syntax error in the CSV: \(error)"
MovieInfo := { title : Str, releaseYear : Nat, actors : List Str }
movieInfoParser =
record (\title -> \releaseYear -> \actors -> @MovieInfo { title, releaseYear, actors })
|> keep (field string)
|> keep (field nat)
|> keep (field actorsParser)
actorsParser =
string
|> map (\val -> Str.split val ",")
movieInfoExplanation = \@MovieInfo { title, releaseYear, actors } ->
enumeratedActors = enumerate actors
releaseYearStr = Num.toStr releaseYear
"The movie '\(title)' was released in \(releaseYearStr) and stars \(enumeratedActors)"
enumerate : List Str -> Str
enumerate = \elements ->
{ before: inits, others: last } = List.split elements (List.len elements - 1)
last
|> List.prepend (inits |> Str.joinWith ", ")
|> Str.joinWith " and "

View file

@ -1,125 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const str = @import("str");
const RocStr = str.RocStr;
const testing = std.testing;
const expectEqual = testing.expectEqual;
const expect = testing.expect;
extern fn roc__mainForHost_1_exposed_generic(*RocStr) void;
pub fn main() u8 {
const stdout = std.io.getStdOut().writer();
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
// stdout the result
stdout.print("{s}", .{callresult.asSlice()}) catch unreachable;
callresult.deinit();
return 0;
}
comptime {
// This is a workaround for https://github.com/ziglang/zig/issues/8218
// which is only necessary on macOS.
//
// Once that issue is fixed, we can undo the changes in
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
// -fcompiler-rt in link.rs instead of doing this. Note that this
// workaround is present in many host.zig files, so make sure to undo
// it everywhere!
if (builtin.os.tag == .macos) {
_ = @import("compiler_rt");
}
}
const Align = 2 * @alignOf(usize);
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void;
extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void;
extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void;
const DEBUG: bool = false;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque {
if (DEBUG) {
var ptr = malloc(size);
const stdout = std.io.getStdOut().writer();
stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable;
return ptr;
} else {
return malloc(size);
}
}
export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable;
}
return realloc(@alignCast(Align, @ptrCast([*]u8, c_ptr)), new_size);
}
export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void {
if (DEBUG) {
const stdout = std.io.getStdOut().writer();
stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable;
}
free(@alignCast(Align, @ptrCast([*]u8, c_ptr)));
}
export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer();
const msg = @ptrCast([*:0]const u8, c_ptr);
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
std.process.exit(0);
}
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size);
}
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_getppid_windows_stub() callconv(.C) c_int {
return 0;
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
if (builtin.os.tag == .windows) {
@export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong });
}
}

View file

@ -1,9 +0,0 @@
platform "hello-world"
requires {} { main : Str }
exposes []
packages {}
imports []
provides [mainForHost]
mainForHost : Str
mainForHost = main

View file

@ -9,7 +9,7 @@ To run, `cd` into this directory and run this in your terminal:
If `roc` is on your PATH: If `roc` is on your PATH:
```bash ```bash
roc run static-site.roc input/ output/ roc run static-site.roc -- input/ output/
``` ```
If not, and you're building Roc from source: If not, and you're building Roc from source:

View file

@ -44,24 +44,92 @@ ingeniis in pugna quadripedis glandes superos. Tanta quam, illo es prole est
telis **unus verba** quisquis iuvenci annis. Nec velox sed sacra gaudia vacuos, telis **unus verba** quisquis iuvenci annis. Nec velox sed sacra gaudia vacuos,
Herculei undae calcata inmeriti quercus ignes parabant iam. Herculei undae calcata inmeriti quercus ignes parabant iam.
digitize(undoDhcp(card_record, cad_flash_dot)); ### Example Table
supercomputer(2 - load_type, yobibyteTraceroute - installHibernate, 1);
burnPci.pop_wrap.usbEmulation(hostESmm, processor_impression(4, lanNntp),
-5);
map_camera -= 73;
if (53) {
dacRootkitDrive(publicActivex.bmpNumWhite.wins_pci_firmware(scroll_cell,
4, tShortcut));
horse_algorithm_eide -= 51;
flatbed_blob(flat);
} else {
surge.pci -= open_flash_dv(4, 4, usbStation);
led.memory_fsb.matrixBinaryUrl(umlEngineOsd.agp_thick_thin.t(58));
kindle_cookie(formulaLedVpn, digital_meme);
}
Foret inpendere, haec ipse ossa, dolentes das Caystro miscuit iunctoque | Tables | Are | Cool |
spoliantis illae, ex! Bello istis nunc Aegides? Animo caelestia melior, | :------------ | :-----------: | ----: |
furoribus optat maior invecta quid harenis [est](http://example.org) sollemnia modo | col 3 is | right-aligned | $1600 |
Phineu. Suarum pectora. Relinquam in labore Medusae sororem Herculis [simillima | col 2 is | centered | $12 |
corpora](http://example.org) plus regi ignibus, totum domus! | zebra stripes | are neat | $1 |
### Example Code Blocks
```sh
# This isn't fenced roc code so its not formatted
# Use a fence like ```roc to format code blocks
```
```roc
## This is a documentation comment
# This is a comment
app "static-site"
packages { pf: "platform/main.roc" }
imports [
pf.Html.{ html, head, body, div, text, a, ul, li, link, meta },
pf.Html.Attributes.{ httpEquiv, content, href, rel, lang, class, title },
]
provides [transformFileContent] to pf
NavLink : {
# this is another comment
url : Str,
title : Str,
text : Str,
}
navLinks : List NavLink
navLinks = [
{ url: "apple.html", title: "Exempli Gratia Pagina Pomi", text: "Apple" },
{ url: "banana.html", title: "Exempli Gratia Pagina Musa", text: "Banana" },
{ url: "cherry.html", title: "Exempli Pagina Cerasus", text: "Cherry" },
]
transformFileContent : Str, Str -> Str
transformFileContent = \currentUrl, htmlContent ->
List.findFirst navLinks (\{ url } -> url == currentUrl)
|> Result.map (\currentNavLink -> view currentNavLink htmlContent)
|> Result.map Html.render
|> Result.withDefault ""
view : NavLink, Str -> Html.Node
view = \currentNavLink, htmlContent ->
html [lang "en"] [
head [] [
meta [httpEquiv "content-type", content "text/html; charset=utf-8"] [],
Html.title [] [text currentNavLink.title],
link [rel "stylesheet", href "style.css"] [],
],
body [] [
div [class "main"] [
div [class "navbar"] [
viewNavbar currentNavLink,
],
div [class "article"] [
# For now `text` is not escaped so we can use it to insert HTML
# We'll probably want something more explicit in the long term though!
text htmlContent,
],
],
],
]
viewNavbar : NavLink -> Html.Node
viewNavbar = \currentNavLink ->
ul
[]
(List.map navLinks \nl -> viewNavLink (nl == currentNavLink) nl)
viewNavLink : Bool, NavLink -> Html.Node
viewNavLink = \isCurrent, navlink ->
if isCurrent then
li [class "nav-link nav-link--current"] [
text navlink.text,
]
else
li [class "nav-link"] [
a
[href navlink.url, title navlink.title]
[text navlink.text],
]
```

View file

@ -39,7 +39,102 @@
color: #444; color: #444;
} }
.article pre { .article pre {
background-color: #222; background-color: rgb(241, 241, 241);
color: yellow; color: rgb(27, 27, 27);
padding: 16px; padding: 16px;
} }
pre {
white-space: pre-wrap;
}
samp .ann {
/* type annotation - purple in the repl */
color: #f384fd;
}
samp .autovar .comment {
/* automatic variable names in the repl, e.g. # val1 */
color: #338545;
}
samp .kw {
/* language keywords, e.g. `if`*/
color: #004cc2;
}
samp .arrow {
/* operators, e.g. `+` */
color: #0600c2;
}
samp .pipe {
/* operators, e.g. `+` */
color: #0600c2;
}
samp .op {
/* operators, e.g. `+` */
color: #0600c2;
}
samp .assign {
color: #48fd00;
}
samp .paren {
color: #ff0000;
}
samp .comma {
color: #ff00fb;
}
samp .colon {
color: #9b0098;
}
samp .number {
/* number literals */
color: #9669ff;
}
samp .str {
/* string literals */
color: #1dbf00;
}
samp .str-esc, samp .str-interp {
/* escapes inside string literals, e.g. \t */
color: #3474db;
}
samp .dim {
opacity: 0.55;
}
samp .comment {
color: #005a13;
}
table {
table-layout: fixed;
width: 100%;
border-collapse: collapse;
border: 3px solid rgb(161, 64, 0);
}
tbody tr:nth-child(even) {
background-color: #c6f4ff;
}
th {
background-color: #ffabab;
}
th,
td {
padding: 2px;
}

View file

@ -2,27 +2,194 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "addr2line"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.2" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "backtrace"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitmaps"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
dependencies = [
"typenum",
]
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
"bumpalo",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "host" name = "host"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"libc", "libc",
"pulldown-cmark", "pulldown-cmark",
"roc_parse",
"roc_region",
"roc_std", "roc_std",
] ]
[[package]]
name = "im"
version = "15.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9"
dependencies = [
"bitmaps",
"rand_core",
"rand_xoshiro",
"sized-chunks",
"typenum",
"version_check",
]
[[package]]
name = "im-rc"
version = "15.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
dependencies = [
"bitmaps",
"rand_core",
"rand_xoshiro",
"sized-chunks",
"typenum",
"version_check",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.132" version = "0.2.132"
@ -35,6 +202,39 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "proc-macro2"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]] [[package]]
name = "pulldown-cmark" name = "pulldown-cmark"
version = "0.9.2" version = "0.9.2"
@ -46,6 +246,88 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_xoshiro"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
dependencies = [
"rand_core",
]
[[package]]
name = "roc_collections"
version = "0.0.1"
dependencies = [
"bitvec",
"bumpalo",
"fnv",
"hashbrown",
"im",
"im-rc",
"wyhash",
]
[[package]]
name = "roc_error_macros"
version = "0.0.1"
[[package]]
name = "roc_ident"
version = "0.0.1"
[[package]]
name = "roc_module"
version = "0.0.1"
dependencies = [
"bumpalo",
"roc_collections",
"roc_error_macros",
"roc_ident",
"roc_region",
"snafu",
"static_assertions",
]
[[package]]
name = "roc_parse"
version = "0.0.1"
dependencies = [
"bumpalo",
"encode_unicode",
"roc_collections",
"roc_module",
"roc_region",
]
[[package]]
name = "roc_region"
version = "0.0.1"
dependencies = [
"static_assertions",
]
[[package]] [[package]]
name = "roc_std" name = "roc_std"
version = "0.0.1" version = "0.0.1"
@ -54,12 +336,74 @@ dependencies = [
"static_assertions", "static_assertions",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "sized-chunks"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
dependencies = [
"bitmaps",
"typenum",
]
[[package]]
name = "snafu"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045"
dependencies = [
"backtrace",
"doc-comment",
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"
@ -69,8 +413,38 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wyhash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf6e163c25e3fac820b4b453185ea2dea3b6a3e0a721d4d23d75bd33734c295"
dependencies = [
"rand_core",
]
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

View file

@ -18,7 +18,11 @@ path = "src/main.rs"
[dependencies] [dependencies]
roc_std = { path = "../../../crates/roc_std" } roc_std = { path = "../../../crates/roc_std" }
roc_region = { path = "../../../crates/compiler/region" }
roc_parse = { path = "../../../crates/compiler/parse" }
libc = "0.2" libc = "0.2"
html-escape = "0.2"
# Default features include building a binary that we don't need # Default features include building a binary that we don't need
pulldown-cmark = { version = "0.9.2", default-features = false } pulldown-cmark = { version = "0.9.2", default-features = false }

View file

@ -0,0 +1,77 @@
use roc_parse::highlight::Token;
use roc_region::all::Loc;
pub fn highlight_roc_code(code: &str) -> String {
let locations: Vec<Loc<Token>> = roc_parse::highlight::highlight(code);
let mut buf: Vec<String> = Vec::new();
let mut offset = 0;
for location in locations {
let current_text = &code[offset..location.byte_range().end];
match location.value {
Token::LineComment | Token::DocComment => {
buf = push_html_span(buf, current_text, "comment");
}
Token::SingleQuote
| Token::String
| Token::UnicodeEscape
| Token::EscapedChar
| Token::Interpolated => {
buf = push_html_span(buf, current_text, "str");
}
Token::Keyword => {
buf = push_html_span(buf, current_text, "kw");
}
Token::Number => {
buf = push_html_span(buf, current_text, "number");
}
Token::Pipe => {
buf = push_html_span(buf, current_text, "pipe");
}
Token::Arrow => {
buf = push_html_span(buf, current_text, "arrow");
}
Token::Backpass => {
buf = push_html_span(buf, current_text, "arrow");
}
Token::Comma => {
buf = push_html_span(buf, current_text, "comma");
}
Token::Colon | Token::Backslash => {
buf = push_html_span(buf, current_text, "colon");
}
Token::GreaterThan | Token::Minus | Token::LessThan | Token::Plus | Token::Equals => {
buf = push_html_span(buf, current_text, "op");
}
Token::Brace | Token::Bracket | Token::Paren => {
buf = push_html_span(buf, current_text, "paren");
}
_ => {
buf = push_html(buf, current_text);
}
}
offset = location.byte_range().end;
}
format!("<pre><samp>{}</samp></pre>", buf.join(""))
}
fn push_html_span(mut buf: Vec<String>, curr: &str, class: &str) -> Vec<String> {
// html escape strings from source code
let escaped = html_escape::encode_text(curr);
buf.push(format!("<span class=\"{}\">{}</span>", class, escaped));
buf
}
fn push_html(mut buf: Vec<String>, curr: &str) -> Vec<String> {
// html escape strings from source code
let escaped = html_escape::encode_text(curr);
buf.push(format!("{}", escaped));
buf
}

View file

@ -8,6 +8,8 @@ use std::fs;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
mod highlight;
extern "C" { extern "C" {
#[link_name = "roc__transformFileContentForHost_1_exposed"] #[link_name = "roc__transformFileContentForHost_1_exposed"]
fn roc_transformFileContentForHost(relPath: &RocStr, content: &RocStr) -> RocStr; fn roc_transformFileContentForHost(relPath: &RocStr, content: &RocStr) -> RocStr;
@ -202,7 +204,54 @@ fn process_file(input_dir: &Path, output_dir: &Path, input_file: &Path) -> Resul
options.remove(Options::ENABLE_SMART_PUNCTUATION); options.remove(Options::ENABLE_SMART_PUNCTUATION);
let parser = Parser::new_ext(&content_md, options); let parser = Parser::new_ext(&content_md, options);
html::push_html(&mut content_html, parser);
// We'll build a new vector of events since we can only consume the parser once
let mut parser_with_highlighting = Vec::new();
// As we go along, we'll want to highlight code in bundles, not lines
let mut to_highlight = String::new();
// And track a little bit of state
let mut in_code_block = false;
let mut is_roc_code = false;
for event in parser {
match event {
pulldown_cmark::Event::Start(pulldown_cmark::Tag::CodeBlock(cbk)) => {
in_code_block = true;
is_roc_code = is_roc_code_block(&cbk);
}
pulldown_cmark::Event::End(pulldown_cmark::Tag::CodeBlock(_)) => {
if in_code_block {
// Format the whole multi-line code block as HTML all at once
let highlighted_html: String;
if is_roc_code {
highlighted_html = crate::highlight::highlight_roc_code(&to_highlight)
} else {
highlighted_html = format!("<pre><samp>{}</pre></samp>", &to_highlight)
}
// And put it into the vector
parser_with_highlighting.push(pulldown_cmark::Event::Html(
pulldown_cmark::CowStr::from(highlighted_html),
));
to_highlight = String::new();
in_code_block = false;
}
}
pulldown_cmark::Event::Text(t) => {
if in_code_block {
// If we're in a code block, build up the string of text
to_highlight.push_str(&t);
} else {
parser_with_highlighting.push(pulldown_cmark::Event::Text(t))
}
}
e => {
parser_with_highlighting.push(e);
}
}
}
html::push_html(&mut content_html, parser_with_highlighting.into_iter());
let roc_relpath = RocStr::from(output_relpath.to_str().unwrap()); let roc_relpath = RocStr::from(output_relpath.to_str().unwrap());
let roc_content_html = RocStr::from(content_html.as_str()); let roc_content_html = RocStr::from(content_html.as_str());
@ -240,3 +289,16 @@ pub fn strip_windows_prefix(path_buf: PathBuf) -> std::path::PathBuf {
std::path::Path::new(path_str.trim_start_matches(r"\\?\")).to_path_buf() std::path::Path::new(path_str.trim_start_matches(r"\\?\")).to_path_buf()
} }
fn is_roc_code_block(cbk: &pulldown_cmark::CodeBlockKind) -> bool {
match cbk {
pulldown_cmark::CodeBlockKind::Indented => false,
pulldown_cmark::CodeBlockKind::Fenced(cow_str) => {
if cow_str.contains("roc") {
true
} else {
false
}
}
}
}

View file

@ -44,7 +44,6 @@
xorg.libXrandr xorg.libXrandr
xorg.libXi xorg.libXi
xorg.libxcb xorg.libxcb
alsa-lib
]; ];
darwinInputs = with pkgs; darwinInputs = with pkgs;

View file

@ -629,7 +629,7 @@ We can also give `List.map` a named function, instead of an anonymous one:
<samp>List.map <span class="brace">[</span><span class="number">1</span><span class="comma">,</span> <span class="number">2</span><span class="comma">,</span> <span class="number">3</span><span class="brace">]</span> Num.isOdd</samp> <samp>List.map <span class="brace">[</span><span class="number">1</span><span class="comma">,</span> <span class="number">2</span><span class="comma">,</span> <span class="number">3</span><span class="brace">]</span> Num.isOdd</samp>
This `Num.isOdd` function returns `Bool.true` if it's given an odd number, and `Bool.false` otherwise. So `Num.isOdd 5` returns true and `Num.isOdd 2` returns false. This `Num.isOdd` function returns `Bool.true` if it's given an odd number, and `Bool.false` otherwise. So `Num.isOdd 5` returns `Bool.true` and `Num.isOdd 2` returns `Bool.false`.
As such, calling `List.map [1, 2, 3] Num.isOdd` returns a new list of `[Bool.true, Bool.false, Bool.true]`. As such, calling `List.map [1, 2, 3] Num.isOdd` returns a new list of `[Bool.true, Bool.false, Bool.true]`.
@ -694,7 +694,7 @@ These two versions compile to the same thing. As a convenience, Roc lets you spe
### [`List.any` and `List.all`](#list-any-and-list-all) {#list-any-and-list-all} ### [`List.any` and `List.all`](#list-any-and-list-all) {#list-any-and-list-all}
There are several functions that work like `List.map`, they walk through each element of a list and do something with it. Another is `List.any`, which returns `Bool.true` if calling the given function on any element in the list returns `true`: There are several functions that work like `List.map`, they walk through each element of a list and do something with it. Another is `List.any`, which returns `Bool.true` if calling the given function on any element in the list returns `Bool.true`:
<pre><samp>List.any [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] Num.isOdd <pre><samp>List.any [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] Num.isOdd
<span class="comment"># returns `Bool.true` because 1 and 3 are odd</span> <span class="comment"># returns `Bool.true` because 1 and 3 are odd</span>
@ -704,7 +704,7 @@ There are several functions that work like `List.map`, they walk through each el
<span class="comment"># returns `Bool.false` because none of these is negative</span> <span class="comment"># returns `Bool.false` because none of these is negative</span>
</samp></pre> </samp></pre>
There's also `List.all` which only returns `true` if all the elements in the list pass the test: There's also `List.all` which only returns `Bool.true` if all the elements in the list pass the test:
<pre><samp>List.all [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] Num.isOdd <pre><samp>List.all [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] Num.isOdd
<span class="comment"># returns `Bool.<span class="hljs-literal">false</span>` because 2 is not odd</span> <span class="comment"># returns `Bool.<span class="hljs-literal">false</span>` because 2 is not odd</span>
@ -722,7 +722,7 @@ You can also drop elements from a list. One way is `List.dropAt` - for example:
<span class="comment"># drops the element at offset 1 ("Lee") and returns ["Sam", "Ari"]</span> <span class="comment"># drops the element at offset 1 ("Lee") and returns ["Sam", "Ari"]</span>
</samp></pre> </samp></pre>
Another way is to use `List.keepIf`, which passes each of the list's elements to the given function, and then keeps them only if that function returns `true`. Another way is to use `List.keepIf`, which passes each of the list's elements to the given function, and then keeps them only if that function returns `Bool.true`.
<pre><samp>List.keepIf [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>] Num.isEven <pre><samp>List.keepIf [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>] Num.isEven
<span class="comment"># returns [2, 4]</span> <span class="comment"># returns [2, 4]</span>
@ -1279,7 +1279,7 @@ You can write automated tests for your Roc code like so:
<span class="kw">expect</span> pluralize <span class="str">"cactus"</span> <span class="str">"cacti"</span> <span class="number">2</span> <span class="op">==</span> <span class="str">"2 cacti"</span> <span class="kw">expect</span> pluralize <span class="str">"cactus"</span> <span class="str">"cacti"</span> <span class="number">2</span> <span class="op">==</span> <span class="str">"2 cacti"</span>
</samp></pre> </samp></pre>
If you put this in a file named `main.roc` and run `roc test`, Roc will execute the two `expect` expressions (that is, the two `pluralize` calls) and report any that returned `false`. If you put this in a file named `main.roc` and run `roc test`, Roc will execute the two `expect` expressions (that is, the two `pluralize` calls) and report any that returned `Bool.false`.
If a test fails, it will not show the actual value that differs from the expected value. To show the actual value, you can write the expect like this: If a test fails, it will not show the actual value that differs from the expected value. To show the actual value, you can write the expect like this:

View file

@ -323,6 +323,10 @@ p, aside, li, footer {
font-size: inherit; font-size: inherit;
} }
code {
white-space: normal;
}
#tutorial-toc-toggle-label, #tutorial-toc-toggle-label,
#close-tutorial-toc { #close-tutorial-toc {
display: block; display: block;