diff --git a/.github/workflows/nightly_linux_x86_64.yml b/.github/workflows/nightly_linux_x86_64.yml index 571517d7d6..9f127eb3ab 100644 --- a/.github/workflows/nightly_linux_x86_64.yml +++ b/.github/workflows/nightly_linux_x86_64.yml @@ -17,7 +17,7 @@ jobs: run: ./ci/write_version.sh - 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. - name: get commit SHA diff --git a/.github/workflows/nightly_macos_x86_64.yml b/.github/workflows/nightly_macos_x86_64.yml index b9b6245b56..1af3be4317 100644 --- a/.github/workflows/nightly_macos_x86_64.yml +++ b/.github/workflows/nightly_macos_x86_64.yml @@ -22,7 +22,7 @@ jobs: # this issue may be caused by using older versions of XCode - 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. - name: get commit SHA diff --git a/Cargo.lock b/Cargo.lock index ec06ea7ab1..ea3c42bfe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,28 +68,6 @@ dependencies = [ "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]] name = "approx" version = "0.4.0" @@ -198,25 +176,6 @@ dependencies = [ "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]] name = "bit-set" version = "0.5.2" @@ -415,24 +374,6 @@ name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "cfg-if" @@ -462,17 +403,6 @@ dependencies = [ "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]] name = "clap" version = "2.34.0" @@ -523,12 +453,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "claxon" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" - [[package]] name = "cli_utils" version = "0.0.1" @@ -620,16 +544,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "combine" -version = "4.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" -dependencies = [ - "bytes", - "memchr", -] - [[package]] name = "confy" version = "0.5.0" @@ -792,50 +706,6 @@ dependencies = [ "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]] name = "cpufeatures" version = "0.2.2" @@ -1471,12 +1341,6 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - [[package]] name = "glow" version = "0.11.2" @@ -1632,12 +1496,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "hound" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" - [[package]] name = "http" version = "0.2.8" @@ -1876,35 +1734,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.60" @@ -1946,23 +1781,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "libc" version = "0.2.135" @@ -2032,15 +1850,6 @@ dependencies = [ "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]] name = "mach_object" version = "0.1.17" @@ -2155,26 +1964,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "miniz_oxide" version = "0.5.3" @@ -2232,20 +2021,7 @@ checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" dependencies = [ "bitflags", "jni-sys", - "ndk-sys 0.2.2", - "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", + "ndk-sys", "num_enum", "thiserror", ] @@ -2265,25 +2041,10 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk 0.5.0", + "ndk", "ndk-context", "ndk-macro", - "ndk-sys 0.2.2", -] - -[[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", + "ndk-sys", ] [[package]] @@ -2305,15 +2066,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "nibble_vec" version = "0.1.0" @@ -2386,17 +2138,6 @@ dependencies = [ "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]] name = "num-traits" version = "0.2.15" @@ -2498,38 +2239,6 @@ dependencies = [ "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]] name = "once_cell" version = "1.17.0" @@ -2685,12 +2394,6 @@ dependencies = [ "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]] name = "peg" version = "0.8.1" @@ -3570,7 +3273,6 @@ dependencies = [ "roc_types", "roc_unify", "roc_utils", - "rodio", "serde", "snafu", "tempfile", @@ -4187,19 +3889,6 @@ dependencies = [ "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]] name = "rustc-demangle" version = "0.1.21" @@ -4501,12 +4190,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - [[package]] name = "signal-hook" version = "0.3.14" @@ -4554,17 +4237,6 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "slotmap" version = "1.0.6" @@ -4682,12 +4354,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" - [[package]] name = "str-buf" version = "1.0.6" @@ -5792,9 +5458,9 @@ dependencies = [ "libc", "log", "mio", - "ndk 0.5.0", - "ndk-glue 0.5.2", - "ndk-sys 0.2.2", + "ndk", + "ndk-glue", + "ndk-sys", "objc", "parking_lot 0.11.2", "percent-encoding", diff --git a/crates/compiler/builtins/roc/Num.roc b/crates/compiler/builtins/roc/Num.roc index eef9cf4741..6157934eca 100644 --- a/crates/compiler/builtins/roc/Num.roc +++ b/crates/compiler/builtins/roc/Num.roc @@ -748,6 +748,7 @@ sqrtChecked = \x -> else Ok (Num.sqrt x) +## Natural logarithm log : Frac a -> Frac a logChecked : Frac a -> Result (Frac a) [LogNeedsPositive] diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index fa981db290..361cdf0b70 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -385,7 +385,15 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, ), Crash { .. } => 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!(), Expect { .. } => todo!(), ExpectFx { .. } => todo!(), diff --git a/crates/compiler/gen_dev/README.md b/crates/compiler/gen_dev/README.md index 15cffb0bc4..01b4bdd78f 100644 --- a/crates/compiler/gen_dev/README.md +++ b/crates/compiler/gen_dev/README.md @@ -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. - [Alternative Online Assembler](http://shell-storm.org/online/Online-Assembler-and-Disassembler/) - 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. 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) - diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index 01ebac5c90..67f312b624 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -2,10 +2,13 @@ use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait}; use crate::Relocation; use bumpalo::collections::Vec; use packed_struct::prelude::*; +use roc_builtins::bitcode::FloatWidth; use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::{InLayout, STLayoutInterner}; +use super::CompareOperation; + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] #[allow(dead_code)] pub enum AArch64GeneralReg { @@ -609,9 +612,31 @@ impl Assembler for AArch64Assembler { } } #[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) { 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)] fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: AArch64GeneralReg) { if offset < 0 { @@ -624,6 +649,19 @@ impl Assembler 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)] fn mov_reg64_mem64_offset32( buf: &mut Vec<'_, u8>, @@ -640,6 +678,41 @@ impl Assembler for AArch64Assembler { 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)] fn mov_mem64_offset32_reg64( buf: &mut Vec<'_, u8>, @@ -657,6 +730,36 @@ impl Assembler 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)] fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) { debug_assert!(size <= 8); @@ -788,6 +891,18 @@ impl Assembler for AArch64Assembler { 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)] fn igt_reg64_reg64_reg64( _buf: &mut Vec<'_, u8>, @@ -938,6 +1053,14 @@ impl Assembler for AArch64Assembler { { 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 {} diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index aa1184d684..81e8f6bc7a 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -2,7 +2,7 @@ use crate::{ single_register_floats, single_register_int_builtins, single_register_integers, Backend, Env, Relocation, }; -use bumpalo::collections::Vec; +use bumpalo::collections::{CollectIn, Vec}; use roc_builtins::bitcode::{self, FloatWidth, IntWidth}; use roc_collections::all::MutMap; use roc_error_macros::internal_error; @@ -113,6 +113,13 @@ pub trait CallConv: Sized + Copy { // 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_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( buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg, 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( buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, 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` /// size must be less than or equal to 8. @@ -265,6 +317,9 @@ pub trait Assembler: Sized + Copy { 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 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 mul_freg32_freg32_freg32( buf: &mut Vec<'_, u8>, @@ -361,6 +416,15 @@ pub trait Assembler: Sized + Copy { 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( buf: &mut Vec<'_, u8>, 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>) { use Builtin::Int; @@ -1112,6 +1200,16 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } @@ -1128,42 +1226,27 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } - 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>) { match *arg_layout { Layout::BOOL => { @@ -1209,6 +1292,20 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } @@ -1241,6 +1338,20 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } @@ -1321,6 +1432,26 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } @@ -1343,6 +1474,26 @@ impl< .load_to_general_reg(&mut self.buf, src2); 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), } } @@ -1567,43 +1718,14 @@ impl< ASM::add_reg64_reg64_reg64(buf, tmp, tmp, list_ptr); let element_ptr = tmp; - match *ret_layout { - 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, - storage_manager, - *dst, - element_ptr, - tmp_reg, - ); - } - 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) - ); - } - } - } - } + Self::ptr_read( + buf, + storage_manager, + self.layout_interner, + element_ptr, + *ret_layout, + *dst, + ); }); }, ); @@ -1871,10 +1993,11 @@ impl< fn create_array( &mut self, sym: &Symbol, - element_layout: &InLayout<'a>, - elements: &'a [ListLiteralElement<'a>], + element_in_layout: &InLayout<'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) let data_bytes_symbol = Symbol::DEV_TMP; @@ -1904,54 +2027,34 @@ impl< .load_to_general_reg(&mut self.buf, &Symbol::DEV_TMP3); // Copy everything into output array. - let mut elem_offset = 0; + let mut element_offset = 0; for elem in elements { // TODO: this could be a lot faster when loading large lists // if we move matching on the element layout to outside this loop. // It also greatly bloats the code here. // Refactor this and switch to one external match. // We also could make loadining indivitual literals much faster - let elem_sym = match elem { - ListLiteralElement::Symbol(sym) => sym, + let element_symbol = match elem { + ListLiteralElement::Symbol(sym) => *sym, ListLiteralElement::Literal(lit) => { - self.load_literal(&Symbol::DEV_TMP, element_layout, lit); - &Symbol::DEV_TMP + self.load_literal(&Symbol::DEV_TMP, element_in_layout, lit); + Symbol::DEV_TMP } }; - // TODO: Expand to all types. - match self.layout_interner.get(*element_layout) { - 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, - |_storage_manager, buf, tmp_reg| { - 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, - elem_offset + i, - tmp_reg, - ); - } - }, - ); - } - 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); + + Self::ptr_write( + &mut self.buf, + &mut self.storage_manager, + ptr_reg, + element_offset, + element_width, + element_layout, + element_symbol, + ); + + element_offset += element_width as i32; + if element_symbol == Symbol::DEV_TMP { + self.free_symbol(&element_symbol); } } @@ -2049,38 +2152,15 @@ impl< let element_width = self.layout_interner.stack_size(element_layout) as u64; let element_offset = 0; - // TODO: Expand to all types. - 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, - |_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), - } + Self::ptr_write( + &mut self.buf, + &mut self.storage_manager, + ptr_reg, + element_offset, + element_width, + self.layout_interner.get(element_layout), + value, + ); if value == Symbol::DEV_TMP { self.free_symbol(&value); @@ -2098,25 +2178,14 @@ impl< .storage_manager .load_to_general_reg(&mut self.buf, &ptr); - let ret_stack_size = self.layout_interner.stack_size(element_layout); - - 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, - |storage_manager, buf, tmp_reg| { - Self::unbox_str_or_list(buf, storage_manager, dst, ptr_reg, tmp_reg); - }, - ); - } - _ => { - todo!("unboxing of {:?}", self.layout_interner.dbg(element_layout)) - } - } + Self::ptr_read( + &mut self.buf, + &mut self.storage_manager, + self.layout_interner, + ptr_reg, + element_layout, + dst, + ); } fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) { @@ -2165,6 +2234,33 @@ impl< let val = *x; 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)) => { let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym); let val = [*x as u8; 16]; @@ -2180,15 +2276,11 @@ impl< let val = *x as f32; ASM::mov_freg32_imm32(&mut self.buf, &mut self.relocs, reg, val); } - (Literal::Str(x), Layout::Builtin(Builtin::Str)) if x.len() < 24 => { - // Load small string. + (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, 24); - let mut bytes = [0; 24]; - bytes[..x.len()].copy_from_slice(x.as_bytes()); - bytes[23] = (x.len() as u8) | 0b1000_0000; + let base_offset = storage_manager.claim_stack_area(sym, 16); let mut num_bytes = [0; 8]; num_bytes.copy_from_slice(&bytes[..8]); @@ -2200,14 +2292,49 @@ impl< let num = i64::from_ne_bytes(num_bytes); ASM::mov_reg64_imm64(buf, reg, num); ASM::mov_base32_reg64(buf, base_offset + 8, reg); - - num_bytes.copy_from_slice(&bytes[16..]); - let num = i64::from_ne_bytes(num_bytes); - ASM::mov_reg64_imm64(buf, reg, num); - ASM::mov_base32_reg64(buf, base_offset + 16, reg); }, ); } + (Literal::Str(x), Layout::Builtin(Builtin::Str)) => { + if x.len() < 24 { + // Load small string. + self.storage_manager.with_tmp_general_reg( + &mut self.buf, + |storage_manager, buf, reg| { + let base_offset = storage_manager.claim_stack_area(sym, 24); + let mut bytes = [0; 24]; + bytes[..x.len()].copy_from_slice(x.as_bytes()); + bytes[23] = (x.len() as u8) | 0b1000_0000; + + 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); + + num_bytes.copy_from_slice(&bytes[16..]); + let num = i64::from_ne_bytes(num_bytes); + ASM::mov_reg64_imm64(buf, reg, num); + 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), } } @@ -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. @@ -2494,6 +2633,115 @@ impl< 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. fn update_jmp_imm32_offset( &mut self, diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 6c72b462a2..7e2eb7d4b4 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -9,7 +9,6 @@ use roc_collections::all::{MutMap, MutSet}; use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::{ - borrow::Ownership, ir::{JoinPointId, Param}, layout::{ Builtin, InLayout, Layout, LayoutInterner, STLayoutInterner, TagIdIntType, UnionLayout, @@ -315,7 +314,7 @@ impl< 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 { reg: None, @@ -350,8 +349,10 @@ impl< self.free_reference(sym); reg } - Stack(Complex { .. }) => { - internal_error!("Cannot load large values into general registers: {}", sym) + Stack(Complex { size, .. }) => { + internal_error!( + "Cannot load large values (size {size}) into general registers: {sym:?}", + ) } NoData => { internal_error!("Cannot load no data into general registers: {}", sym) @@ -448,7 +449,7 @@ impl< 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 { reg: None, @@ -458,19 +459,25 @@ impl< ASM::mov_reg64_base32(buf, reg, *base_offset); } Stack(ReferencedPrimitive { - base_offset, size, .. - }) if base_offset % 8 == 0 && *size == 8 => { - // The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack. - ASM::mov_reg64_base32(buf, reg, *base_offset); + base_offset, + size, + sign_extend, + }) => { + 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 { .. }) => { - internal_error!("Cannot load large values into general registers: {}", sym) + Stack(Complex { size, .. }) => { + internal_error!( + "Cannot load large values (size {size}) into general registers: {sym:?}", + ) } 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.symbol_storage_map.insert( *sym, - Stack(if is_primitive(layout) { + Stack(if is_primitive(layout_interner, layout) { ReferencedPrimitive { base_offset: data_offset, size, @@ -739,15 +746,73 @@ impl< layout: &InLayout<'a>, ) { 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); + let reg = self.load_to_general_reg(buf, sym); + ASM::mov_base32_reg64(buf, to_offset, reg); + } + 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); + let reg = self.load_to_float_reg(buf, sym); + 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::Builtin(Builtin::Float(FloatWidth::F64)) => { - debug_assert_eq!(to_offset % 8, 0); - let reg = self.load_to_float_reg(buf, sym); - ASM::mov_base32_freg64(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 => {} // TODO: Verify this is always true. @@ -756,20 +821,64 @@ impl< // Later, it will be reloaded and stored in refcounted as needed. _ if layout_interner.stack_size(*layout) > 8 => { let (from_offset, size) = self.stack_offset_and_size(sym); - debug_assert!(from_offset % 8 == 0); - debug_assert!(size % 8 == 0); + debug_assert_eq!(from_offset % 8, 0); + debug_assert_eq!(size % 8, 0); debug_assert_eq!(size, layout_interner.stack_size(*layout)); - self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| { - 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); - } - }); + self.copy_to_stack_offset(buf, size, from_offset, to_offset) } 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)] /// Ensures that a register is free. If it is not free, data will be moved to make it free. pub fn ensure_reg_free( @@ -1008,15 +1117,10 @@ impl< param_storage.reserve(params.len()); for Param { symbol, - ownership, + ownership: _, layout, } 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. // Put everything on the stack for simplicity. match *layout { @@ -1331,6 +1435,15 @@ impl< } } -fn is_primitive(layout: InLayout<'_>) -> bool { - matches!(layout, single_register_layouts!()) +fn is_primitive(layout_interner: &mut STLayoutInterner<'_>, layout: InLayout<'_>) -> bool { + 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, + }, + } } diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index c2c865f057..0f3902e951 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -4,10 +4,13 @@ use crate::{ single_register_layouts, Relocation, }; use bumpalo::collections::Vec; +use roc_builtins::bitcode::FloatWidth; use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_mono::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner}; +use super::CompareOperation; + // Not sure exactly how I want to represent registers. // If we want max speed, we would likely make them structs that impl the same trait to avoid ifs. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] @@ -261,59 +264,22 @@ impl CallConv for X86_64Syste args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, ) { - let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 16; // 16 is the size of the pushed return address and base pointer. - let mut general_i = 0; - let mut float_i = 0; - if X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout) { - storage_manager.ret_pointer_arg(Self::GENERAL_PARAM_REGS[0]); - general_i += 1; + let returns_via_pointer = + X86_64SystemV::returns_via_arg_pointer(layout_interner, ret_layout); + + let mut state = X64_64SystemVLoadArgs { + general_i: usize::from(returns_via_pointer), + float_i: 0, + // 16 is the size of the pushed return address and base pointer. + argument_offset: X86_64SystemV::SHADOW_SPACE_SIZE as i32 + 16, + }; + + if returns_via_pointer { + storage_manager.ret_pointer_arg(X86_64SystemV::GENERAL_PARAM_REGS[0]); } + for (in_layout, sym) in args.iter() { - let stack_size = layout_interner.stack_size(*in_layout); - match *in_layout { - single_register_integers!() => { - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]); - general_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - single_register_floats!() => { - if float_i < Self::FLOAT_PARAM_REGS.len() { - storage_manager.float_reg_arg(sym, Self::FLOAT_PARAM_REGS[float_i]); - float_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - _ if stack_size == 0 => { - storage_manager.no_data_arg(sym); - } - _ if stack_size > 16 => { - // TODO: Double check this. - storage_manager.complex_stack_arg(sym, arg_offset, stack_size); - arg_offset += stack_size as i32; - } - other => match layout_interner.get(other) { - Layout::Boxed(_) => { - // boxed layouts are pointers, which we treat as 64-bit integers - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager - .general_reg_arg(sym, Self::GENERAL_PARAM_REGS[general_i]); - general_i += 1; - } else { - storage_manager.primitive_stack_arg(sym, arg_offset); - arg_offset += 8; - } - } - _ => { - todo!("Loading args with layout {:?}", layout_interner.dbg(other)); - } - }, - } + state.load_arg(storage_manager, layout_interner, *sym, *in_layout); } } @@ -334,9 +300,8 @@ impl CallConv for X86_64Syste arg_layouts: &[InLayout<'a>], ret_layout: &InLayout<'a>, ) { - let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32; let mut general_i = 0; - let mut float_i = 0; + if Self::returns_via_arg_pointer(layout_interner, ret_layout) { // Save space on the stack for the result we will be return. let base_offset = @@ -352,110 +317,17 @@ impl CallConv for X86_64Syste ); } - for (sym, layout) in args.iter().zip(arg_layouts.iter()) { - match *layout { - single_register_integers!() => { - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_PARAM_REGS[general_i], - ); - general_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_reg64( - buf, - tmp_stack_offset, - Self::GENERAL_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - single_register_floats!() => { - if float_i < Self::FLOAT_PARAM_REGS.len() { - storage_manager.load_to_specified_float_reg( - buf, - sym, - Self::FLOAT_PARAM_REGS[float_i], - ); - float_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_float_reg( - buf, - sym, - Self::FLOAT_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_freg64( - buf, - tmp_stack_offset, - Self::FLOAT_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - x if layout_interner.stack_size(x) == 0 => {} - x if layout_interner.stack_size(x) > 16 => { - // TODO: Double check this. - // Just copy onto the stack. - // Use return reg as buffer because it will be empty right now. - let (base_offset, size) = storage_manager.stack_offset_and_size(sym); - debug_assert_eq!(base_offset % 8, 0); - for i in (0..size as i32).step_by(8) { - X86_64Assembler::mov_reg64_base32( - buf, - Self::GENERAL_RETURN_REGS[0], - base_offset + i, - ); - X86_64Assembler::mov_stack32_reg64( - buf, - tmp_stack_offset + i, - Self::GENERAL_RETURN_REGS[0], - ); - } - tmp_stack_offset += size as i32; - } - other => { - // look at the layout in more detail - match layout_interner.get(other) { - Layout::Boxed(_) => { - // treat boxed like a 64-bit integer - if general_i < Self::GENERAL_PARAM_REGS.len() { - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_PARAM_REGS[general_i], - ); - general_i += 1; - } else { - // Copy to stack using return reg as buffer. - storage_manager.load_to_specified_general_reg( - buf, - sym, - Self::GENERAL_RETURN_REGS[0], - ); - X86_64Assembler::mov_stack32_reg64( - buf, - tmp_stack_offset, - Self::GENERAL_RETURN_REGS[0], - ); - tmp_stack_offset += 8; - } - } - _ => { - todo!("calling with arg type, {:?}", layout_interner.dbg(other)); - } - } - } - } + let mut state = X64_64SystemVStoreArgs { + general_i, + float_i: 0, + tmp_stack_offset: Self::SHADOW_SPACE_SIZE as i32, + }; + + for (sym, in_layout) in args.iter().zip(arg_layouts.iter()) { + state.store_arg(buf, storage_manager, layout_interner, *sym, *in_layout); } - storage_manager.update_fn_call_stack_size(tmp_stack_offset as u32); + + storage_manager.update_fn_call_stack_size(state.tmp_stack_offset as u32); } fn return_complex_symbol<'a, 'r>( @@ -562,6 +434,225 @@ impl CallConv for X86_64Syste } } +struct X64_64SystemVStoreArgs { + general_i: usize, + float_i: usize, + tmp_stack_offset: i32, +} + +impl X64_64SystemVStoreArgs { + const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = X86_64SystemV::GENERAL_PARAM_REGS; + const GENERAL_RETURN_REGS: &'static [X86_64GeneralReg] = X86_64SystemV::GENERAL_RETURN_REGS; + + const FLOAT_PARAM_REGS: &'static [X86_64FloatReg] = X86_64SystemV::FLOAT_PARAM_REGS; + const FLOAT_RETURN_REGS: &'static [X86_64FloatReg] = X86_64SystemV::FLOAT_RETURN_REGS; + + fn store_arg<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + match in_layout { + single_register_integers!() => self.store_arg_general(buf, storage_manager, sym), + single_register_floats!() => self.store_arg_float(buf, storage_manager, sym), + x if layout_interner.stack_size(x) == 0 => {} + x if layout_interner.stack_size(x) > 16 => { + // TODO: Double check this. + // Just copy onto the stack. + // Use return reg as buffer because it will be empty right now. + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + debug_assert_eq!(base_offset % 8, 0); + for i in (0..size as i32).step_by(8) { + X86_64Assembler::mov_reg64_base32( + buf, + Self::GENERAL_RETURN_REGS[0], + base_offset + i, + ); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset + i, + Self::GENERAL_RETURN_REGS[0], + ); + } + self.tmp_stack_offset += size as i32; + } + other => { + // look at the layout in more detail + match layout_interner.get(other) { + Layout::Boxed(_) => { + // treat boxed like a 64-bit integer + self.store_arg_general(buf, storage_manager, sym) + } + Layout::LambdaSet(lambda_set) => self.store_arg( + buf, + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + Layout::Struct { .. } => { + // for now, just also store this on the stack + let (base_offset, size) = storage_manager.stack_offset_and_size(&sym); + debug_assert_eq!(base_offset % 8, 0); + for i in (0..size as i32).step_by(8) { + X86_64Assembler::mov_reg64_base32( + buf, + Self::GENERAL_RETURN_REGS[0], + base_offset + i, + ); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset + i, + Self::GENERAL_RETURN_REGS[0], + ); + } + self.tmp_stack_offset += size as i32; + } + _ => { + todo!("calling with arg type, {:?}", layout_interner.dbg(other)); + } + } + } + } + } + + fn store_arg_general<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < Self::GENERAL_PARAM_REGS.len() { + storage_manager.load_to_specified_general_reg( + buf, + &sym, + Self::GENERAL_PARAM_REGS[self.general_i], + ); + self.general_i += 1; + } else { + // Copy to stack using return reg as buffer. + storage_manager.load_to_specified_general_reg(buf, &sym, Self::GENERAL_RETURN_REGS[0]); + X86_64Assembler::mov_stack32_reg64( + buf, + self.tmp_stack_offset, + Self::GENERAL_RETURN_REGS[0], + ); + self.tmp_stack_offset += 8; + } + } + + fn store_arg_float<'a, 'r>( + &mut self, + buf: &mut Vec<'a, u8>, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.float_i < Self::FLOAT_PARAM_REGS.len() { + storage_manager.load_to_specified_float_reg( + buf, + &sym, + Self::FLOAT_PARAM_REGS[self.float_i], + ); + self.float_i += 1; + } else { + // Copy to stack using return reg as buffer. + storage_manager.load_to_specified_float_reg(buf, &sym, Self::FLOAT_RETURN_REGS[0]); + X86_64Assembler::mov_stack32_freg64( + buf, + self.tmp_stack_offset, + Self::FLOAT_RETURN_REGS[0], + ); + self.tmp_stack_offset += 8; + } + } +} + +struct X64_64SystemVLoadArgs { + general_i: usize, + float_i: usize, + argument_offset: i32, +} + +type X86_64StorageManager<'a, 'r, CallConv> = + StorageManager<'a, 'r, X86_64GeneralReg, X86_64FloatReg, X86_64Assembler, CallConv>; + +impl X64_64SystemVLoadArgs { + fn load_arg<'a, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + layout_interner: &mut STLayoutInterner<'a>, + sym: Symbol, + in_layout: InLayout<'a>, + ) { + let stack_size = layout_interner.stack_size(in_layout); + match in_layout { + single_register_integers!() => self.load_arg_general(storage_manager, sym), + single_register_floats!() => self.load_arg_float(storage_manager, sym), + _ if stack_size == 0 => { + storage_manager.no_data_arg(&sym); + } + _ if stack_size > 16 => { + // TODO: Double check this. + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + other => match layout_interner.get(other) { + Layout::Boxed(_) => { + // boxed layouts are pointers, which we treat as 64-bit integers + self.load_arg_general(storage_manager, sym) + } + Layout::LambdaSet(lambda_set) => self.load_arg( + storage_manager, + layout_interner, + sym, + lambda_set.runtime_representation(), + ), + Layout::Struct { .. } => { + // for now, just also store this on the stack + storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size); + self.argument_offset += stack_size as i32; + } + _ => { + todo!("Loading args with layout {:?}", layout_interner.dbg(other)); + } + }, + } + } + + fn load_arg_general<'a, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() { + let reg = X86_64SystemV::GENERAL_PARAM_REGS[self.general_i]; + storage_manager.general_reg_arg(&sym, reg); + self.general_i += 1; + } else { + storage_manager.primitive_stack_arg(&sym, self.argument_offset); + self.argument_offset += 8; + } + } + + fn load_arg_float<'a, 'r>( + &mut self, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64SystemV>, + sym: Symbol, + ) { + if self.general_i < X86_64SystemV::GENERAL_PARAM_REGS.len() { + let reg = X86_64SystemV::FLOAT_PARAM_REGS[self.general_i]; + storage_manager.float_reg_arg(&sym, reg); + self.float_i += 1; + } else { + storage_manager.primitive_stack_arg(&sym, self.argument_offset); + self.argument_offset += 8; + } + } +} + impl X86_64SystemV { fn returns_via_arg_pointer<'a>( interner: &STLayoutInterner<'a>, @@ -705,14 +796,7 @@ impl CallConv for X86_64Windo #[inline(always)] fn load_args<'a, 'r>( _buf: &mut Vec<'a, u8>, - storage_manager: &mut StorageManager< - 'a, - 'r, - X86_64GeneralReg, - X86_64FloatReg, - X86_64Assembler, - X86_64WindowsFastcall, - >, + storage_manager: &mut X86_64StorageManager<'a, 'r, X86_64WindowsFastcall>, layout_interner: &mut STLayoutInterner<'a>, args: &'a [(InLayout<'a>, Symbol)], ret_layout: &InLayout<'a>, @@ -1271,18 +1355,55 @@ impl Assembler for X86_64Assembler { fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: i32) { movsd_freg64_base64_offset32(buf, dst, X86_64GeneralReg::RBP, offset) } + #[inline(always)] fn mov_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32) { mov_reg64_base64_offset32(buf, dst, X86_64GeneralReg::RBP, offset) } + #[inline(always)] + fn mov_reg32_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32) { + mov_reg32_base32_offset32(buf, dst, X86_64GeneralReg::RBP, offset) + } + #[inline(always)] + fn mov_reg16_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32) { + mov_reg16_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset) + } + #[inline(always)] + fn mov_reg8_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32) { + mov_reg8_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset) + } + #[inline(always)] fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64FloatReg) { movsd_base64_offset32_freg64(buf, X86_64GeneralReg::RBP, offset, src) } + + #[inline(always)] + fn movesd_mem64_offset32_freg64( + buf: &mut Vec<'_, u8>, + ptr: X86_64GeneralReg, + offset: i32, + src: X86_64FloatReg, + ) { + movsd_base64_offset32_freg64(buf, ptr, offset, src) + } + #[inline(always)] fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64GeneralReg) { mov_base64_offset32_reg64(buf, X86_64GeneralReg::RBP, offset, src) } + #[inline(always)] + fn mov_base32_reg32(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64GeneralReg) { + mov_base32_offset32_reg32(buf, X86_64GeneralReg::RBP, offset, src) + } + #[inline(always)] + fn mov_base32_reg16(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64GeneralReg) { + mov_base16_offset32_reg16(buf, X86_64GeneralReg::RBP, offset, src) + } + #[inline(always)] + fn mov_base32_reg8(buf: &mut Vec<'_, u8>, offset: i32, src: X86_64GeneralReg) { + mov_base8_offset32_reg8(buf, X86_64GeneralReg::RBP, offset, src) + } #[inline(always)] fn mov_reg64_mem64_offset32( @@ -1293,6 +1414,32 @@ impl Assembler for X86_64Assembler { ) { mov_reg64_base64_offset32(buf, dst, src, offset) } + #[inline(always)] + fn mov_reg32_mem32_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, + offset: i32, + ) { + mov_reg32_base32_offset32(buf, dst, src, offset) + } + fn mov_reg16_mem16_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, + offset: i32, + ) { + mov_reg16_base16_offset32(buf, dst, src, offset) + } + fn mov_reg8_mem8_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src: X86_64GeneralReg, + offset: i32, + ) { + mov_reg8_base8_offset32(buf, dst, src, offset) + } + #[inline(always)] fn mov_mem64_offset32_reg64( buf: &mut Vec<'_, u8>, @@ -1303,12 +1450,44 @@ impl Assembler for X86_64Assembler { mov_base64_offset32_reg64(buf, dst, offset, src) } + #[inline(always)] + fn mov_mem32_offset32_reg32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, + ) { + mov_base32_offset32_reg32(buf, dst, offset, src) + } + + #[inline(always)] + fn mov_mem16_offset32_reg16( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, + ) { + mov_base16_offset32_reg16(buf, dst, offset, src) + } + + #[inline(always)] + fn mov_mem8_offset32_reg8( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, + ) { + mov_base8_offset32_reg8(buf, dst, offset, src) + } + #[inline(always)] fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) { debug_assert!(size <= 8); match size { 8 => Self::mov_reg64_base32(buf, dst, offset), - 4 | 2 | 1 => todo!("sign extending {size} byte values"), + 4 => movsx_reg64_base32_offset32(buf, dst, X86_64GeneralReg::RBP, offset), + 2 => movsx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset), + 1 => movsx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset), _ => internal_error!("Invalid size for sign extension: {size}"), } } @@ -1317,7 +1496,12 @@ impl Assembler for X86_64Assembler { debug_assert!(size <= 8); match size { 8 => Self::mov_reg64_base32(buf, dst, offset), - 4 | 2 => todo!("zero extending {size} byte values"), + 4 => { + // The Intel documentation (3.4.1.1 General-Purpose Registers in 64-Bit Mode in manual Basic Architecture)) + // 32-bit operands generate a 32-bit result, zero-extended to a 64-bit result in the destination general-purpose register. + Self::mov_reg64_base32(buf, dst, offset) + } + 2 => movzx_reg64_base16_offset32(buf, dst, X86_64GeneralReg::RBP, offset), 1 => movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset), _ => internal_error!("Invalid size for zero extension: {size}"), } @@ -1411,6 +1595,33 @@ impl Assembler for X86_64Assembler { setb_reg64(buf, dst); } + #[inline(always)] + fn cmp_freg_freg_reg64( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + src1: X86_64FloatReg, + src2: X86_64FloatReg, + width: FloatWidth, + operation: CompareOperation, + ) { + use CompareOperation::*; + + let (arg1, arg2) = match operation { + LessThan | LessThanOrEqual => (src1, src2), + GreaterThan | GreaterThanOrEqual => (src2, src1), + }; + + match width { + FloatWidth::F32 => cmp_freg32_freg32(buf, arg2, arg1), + FloatWidth::F64 => cmp_freg64_freg64(buf, arg2, arg1), + } + + match operation { + LessThan | GreaterThan => seta_reg64(buf, dst), + LessThanOrEqual | GreaterThanOrEqual => setae_reg64(buf, dst), + }; + } + #[inline(always)] fn igt_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, @@ -1534,6 +1745,14 @@ impl Assembler for X86_64Assembler { { shift_reg64_reg64_reg64(buf, storage_manager, sar_reg64_reg64, dst, src1, src2) } + + fn sqrt_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + sqrtsd_freg64_freg64(buf, dst, src) + } + + fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + sqrtss_freg32_freg32(buf, dst, src) + } } fn shift_reg64_reg64_reg64<'a, 'r, ASM, CC>( @@ -1585,12 +1804,16 @@ impl X86_64Assembler { push_reg64(buf, reg); } } + +const GRP_4: u8 = 0x66; + const REX: u8 = 0x40; // see https://wiki.osdev.org/X86-64_Instruction_Encoding#Encoding /// If set, 64-bit operand size is used const REX_PREFIX_W: u8 = 0b1000; /// Extension to the MODRM.reg +/// Permits access to additional registers const REX_PREFIX_R: u8 = 0b0100; #[allow(unused)] /// Extension to the SIB.index field @@ -1598,7 +1821,7 @@ const REX_PREFIX_X: u8 = 0b0010; /// Extension to the MODRM.rm const REX_PREFIX_B: u8 = 0b0001; -/// Wide REX +/// Wide REX (64-bit) const REX_W: u8 = REX | REX_PREFIX_W; #[inline(always)] @@ -1900,6 +2123,90 @@ fn cmp_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Gene binop_reg64_reg64(0x39, buf, dst, src); } +#[inline(always)] +fn cmp_freg64_freg64(buf: &mut Vec<'_, u8>, src1: X86_64FloatReg, src2: X86_64FloatReg) { + let src1_high = src1 as u8 > 7; + let src1_mod = src1 as u8 % 8; + + let src2_high = src2 as u8 > 7; + let src2_mod = src2 as u8 % 8; + + if src1_high || src2_high { + buf.extend([ + 0x66, + 0x40 | ((src1_high as u8) << 2) | (src2_high as u8), + 0x0F, + 0x2E, + 0xC0 | (src1_mod << 3) | (src2_mod), + ]) + } else { + buf.extend([0x66, 0x0F, 0x2E, 0xC0 | (src1_mod << 3) | (src2_mod)]) + } +} + +#[inline(always)] +fn cmp_freg32_freg32(buf: &mut Vec<'_, u8>, src1: X86_64FloatReg, src2: X86_64FloatReg) { + let src1_high = src1 as u8 > 7; + let src1_mod = src1 as u8 % 8; + + let src2_high = src2 as u8 > 7; + let src2_mod = src2 as u8 % 8; + + if src1_high || src2_high { + buf.extend([ + 0x65, + 0x40 | ((src1_high as u8) << 2) | (src2_high as u8), + 0x0F, + 0x2E, + 0xC0 | (src1_mod << 3) | (src2_mod), + ]) + } else { + buf.extend([0x65, 0x0F, 0x2E, 0xC0 | (src1_mod << 3) | (src2_mod)]) + } +} + +#[inline(always)] +fn sqrtsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + + let src_high = src as u8 > 7; + let src_mod = src as u8 % 8; + + if dst_high || src_high { + buf.extend([ + 0xF2, + 0x40 | ((dst_high as u8) << 2) | (src_high as u8), + 0x0F, + 0x51, + 0xC0 | (dst_mod << 3) | (src_mod), + ]) + } else { + buf.extend([0xF2, 0x0F, 0x51, 0xC0 | (dst_mod << 3) | (src_mod)]) + } +} + +#[inline(always)] +fn sqrtss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + + let src_high = src as u8 > 7; + let src_mod = src as u8 % 8; + + if dst_high || src_high { + buf.extend([ + 0xF3, + 0x40 | ((dst_high as u8) << 2) | (src_high as u8), + 0x0F, + 0x51, + 0xC0 | (dst_mod << 3) | (src_mod), + ]) + } else { + buf.extend([0xF3, 0x0F, 0x51, 0xC0 | (dst_mod << 3) | (src_mod)]) + } +} + /// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result. #[allow(dead_code)] #[inline(always)] @@ -2049,6 +2356,117 @@ fn mov_base64_offset32_reg64( buf.extend(offset.to_le_bytes()); } +/// `MOV r/m32,r32` -> Move r32 to r/m32, where m32 references a base + offset. +#[inline(always)] +fn mov_base32_offset32_reg32( + buf: &mut Vec<'_, u8>, + base: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, +) { + let rex = add_rm_extension(base, REX); + let rex = add_reg_extension(src, rex); + let src_mod = (src as u8 % 8) << 3; + let base_mod = base as u8 % 8; + buf.reserve(8); + buf.extend([rex, 0x89, 0x80 | src_mod | base_mod]); + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + +/// `MOV r/m16,r16` -> Move r16 to r/m16, where m16 references a base + offset. +#[inline(always)] +fn mov_base16_offset32_reg16( + buf: &mut Vec<'_, u8>, + base: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, +) { + let rex = add_rm_extension(base, REX); + let rex = add_reg_extension(src, rex); + let src_mod = (src as u8 % 8) << 3; + let base_mod = base as u8 % 8; + buf.reserve(8); + buf.extend([GRP_4, rex, 0x89, 0x80 | src_mod | base_mod]); + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + +/// `MOV r/m8,r8` -> Move r8 to r/m8, where m8 references a base + offset. +#[inline(always)] +fn mov_base8_offset32_reg8( + buf: &mut Vec<'_, u8>, + base: X86_64GeneralReg, + offset: i32, + src: X86_64GeneralReg, +) { + let rex = add_rm_extension(base, REX); + let rex = add_reg_extension(src, rex); + let src_mod = (src as u8 % 8) << 3; + let base_mod = base as u8 % 8; + buf.reserve(8); + buf.extend([rex, 0x88, 0x80 | src_mod | base_mod]); + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + +enum RegisterWidth { + W8, + W16, + W32, + W64, +} + +#[inline(always)] +fn mov_reg_base_offset32( + buf: &mut Vec<'_, u8>, + register_width: RegisterWidth, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + use RegisterWidth::*; + + let rex = match register_width { + W64 => REX_W, + _ => REX, + }; + + let rex = add_rm_extension(base, rex); + let rex = add_reg_extension(dst, rex); + + let dst_mod = (dst as u8 % 8) << 3; + let base_mod = base as u8 % 8; + let operands = 0x80 | dst_mod | base_mod; + + buf.reserve(8); + + let instruction = match register_width { + W8 => 0x8A, + W16 | W32 | W64 => 0x8B, + }; + + match register_width { + W16 => buf.extend([GRP_4, rex, instruction, operands]), + _ => buf.extend([rex, instruction, operands]), + }; + + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + /// `MOV r64,r/m64` -> Move r/m64 to r64, where m64 references a base + offset. #[inline(always)] fn mov_reg64_base64_offset32( @@ -2056,13 +2474,116 @@ fn mov_reg64_base64_offset32( dst: X86_64GeneralReg, base: X86_64GeneralReg, offset: i32, +) { + mov_reg_base_offset32(buf, RegisterWidth::W64, dst, base, offset) +} + +/// `MOV r/m32,r32` -> Move r32 to r/m32. +#[inline(always)] +fn mov_reg32_base32_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + mov_reg_base_offset32(buf, RegisterWidth::W32, dst, base, offset) +} + +/// `MOV r/m16,r16` -> Move r16 to r/m16. +#[inline(always)] +fn mov_reg16_base16_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + mov_reg_base_offset32(buf, RegisterWidth::W16, dst, base, offset) +} + +/// `MOV r/m8,r8` -> Move r8 to r/m8. +#[inline(always)] +fn mov_reg8_base8_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + mov_reg_base_offset32(buf, RegisterWidth::W8, dst, base, offset) +} + +#[inline(always)] +fn movsx_reg64_base_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, + opcode: &[u8], ) { let rex = add_rm_extension(base, REX_W); let rex = add_reg_extension(dst, rex); let dst_mod = (dst as u8 % 8) << 3; let base_mod = base as u8 % 8; - buf.reserve(8); - buf.extend([rex, 0x8B, 0x80 | dst_mod | base_mod]); + buf.reserve(9); + + // our output is a 64-bit value, so rex is always needed + buf.push(rex); + buf.extend(opcode); + buf.push(0x80 | dst_mod | base_mod); + + // Using RSP or R12 requires a secondary index byte. + if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { + buf.push(0x24); + } + buf.extend(offset.to_le_bytes()); +} + +/// `MOVSX r64,r/m32` -> Move r/m32 with sign extention to r64, where m32 references a base + offset. +#[inline(always)] +fn movsx_reg64_base32_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + movsx_reg64_base_offset32(buf, dst, base, offset, &[0x63]) +} + +/// `MOVSX r64,r/m16` -> Move r/m16 with sign extention to r64, where m16 references a base + offset. +#[inline(always)] +fn movsx_reg64_base16_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + movsx_reg64_base_offset32(buf, dst, base, offset, &[0x0F, 0xBF]) +} + +/// `MOVSX r64,r/m8` -> Move r/m8 with sign extention to r64, where m8 references a base + offset. +#[inline(always)] +fn movsx_reg64_base8_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + movsx_reg64_base_offset32(buf, dst, base, offset, &[0x0F, 0xBE]) +} + +#[inline(always)] +fn movzx_reg64_base_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, + opcode: u8, +) { + let rex = add_rm_extension(base, REX_W); + let rex = add_reg_extension(dst, rex); + let dst_mod = (dst as u8 % 8) << 3; + let base_mod = base as u8 % 8; + buf.reserve(9); + buf.extend([rex, 0x0F, opcode, 0x80 | dst_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); @@ -2078,17 +2599,18 @@ fn movzx_reg64_base8_offset32( base: X86_64GeneralReg, offset: i32, ) { - let rex = add_rm_extension(base, REX_W); - let rex = add_reg_extension(dst, rex); - let dst_mod = (dst as u8 % 8) << 3; - let base_mod = base as u8 % 8; - buf.reserve(9); - buf.extend([rex, 0x0F, 0xB6, 0x80 | dst_mod | base_mod]); - // Using RSP or R12 requires a secondary index byte. - if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { - buf.push(0x24); - } - buf.extend(offset.to_le_bytes()); + movzx_reg64_base_offset32(buf, dst, base, offset, 0xB6) +} + +/// `MOVZX r64,r/m16` -> Move r/m16 with zero extention to r64, where m16 references a base + offset. +#[inline(always)] +fn movzx_reg64_base16_offset32( + buf: &mut Vec<'_, u8>, + dst: X86_64GeneralReg, + base: X86_64GeneralReg, + offset: i32, +) { + movzx_reg64_base_offset32(buf, dst, base, offset, 0xB7) } /// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register. @@ -2357,6 +2879,12 @@ fn seta_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { set_reg64_help(0x97, buf, reg); } +/// `SETAE r/m64` -> Set byte if above or equal (CF=0). +#[inline(always)] +fn setae_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { + set_reg64_help(0x93, buf, reg); +} + /// `SETLE r/m64` -> Set byte if less or equal (ZF=1 or SF≠ OF). #[inline(always)] fn setle_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { @@ -2431,6 +2959,50 @@ mod tests { use capstone::prelude::*; impl X86_64GeneralReg { + #[allow(dead_code)] + fn low_32bits_string(&self) -> &str { + match self { + X86_64GeneralReg::RAX => "eax", + X86_64GeneralReg::RBX => "ebx", + X86_64GeneralReg::RCX => "ecx", + X86_64GeneralReg::RDX => "edx", + X86_64GeneralReg::RBP => "ebp", + X86_64GeneralReg::RSP => "esp", + X86_64GeneralReg::RDI => "edi", + X86_64GeneralReg::RSI => "esi", + X86_64GeneralReg::R8 => "r8d", + X86_64GeneralReg::R9 => "r9d", + X86_64GeneralReg::R10 => "r10d", + X86_64GeneralReg::R11 => "r11d", + X86_64GeneralReg::R12 => "r12d", + X86_64GeneralReg::R13 => "r13d", + X86_64GeneralReg::R14 => "r14d", + X86_64GeneralReg::R15 => "r15d", + } + } + + #[allow(dead_code)] + fn low_16bits_string(&self) -> &str { + match self { + X86_64GeneralReg::RAX => "ax", + X86_64GeneralReg::RBX => "bx", + X86_64GeneralReg::RCX => "cx", + X86_64GeneralReg::RDX => "dx", + X86_64GeneralReg::RBP => "bp", + X86_64GeneralReg::RSP => "sp", + X86_64GeneralReg::RDI => "di", + X86_64GeneralReg::RSI => "si", + X86_64GeneralReg::R8 => "r8w", + X86_64GeneralReg::R9 => "r9w", + X86_64GeneralReg::R10 => "r10w", + X86_64GeneralReg::R11 => "r11w", + X86_64GeneralReg::R12 => "r12w", + X86_64GeneralReg::R13 => "r13w", + X86_64GeneralReg::R14 => "r14w", + X86_64GeneralReg::R15 => "r15w", + } + } + #[allow(dead_code)] fn low_8bits_string(&self) -> &str { match self { @@ -2810,6 +3382,54 @@ mod tests { ); } + #[test] + fn test_mov_reg32_base32_offset32() { + disassembler_test!( + mov_reg32_base32_offset32, + |reg1, reg2, imm| format!( + "mov {}, dword ptr [{} + 0x{:x}]", + X86_64GeneralReg::low_32bits_string(®1), + reg2, + imm + ), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + + #[test] + fn test_mov_reg16_base16_offset32() { + disassembler_test!( + mov_reg16_base16_offset32, + |reg1, reg2, imm| format!( + "mov {}, word ptr [{} + 0x{:x}]", + X86_64GeneralReg::low_16bits_string(®1), + reg2, + imm + ), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + + #[test] + fn test_mov_reg8_base8_offset32() { + disassembler_test!( + mov_reg8_base8_offset32, + |reg1, reg2, imm| format!( + "mov {}, byte ptr [{} + 0x{:x}]", + X86_64GeneralReg::low_8bits_string(®1), + reg2, + imm + ), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + #[test] fn test_mov_base64_offset32_reg64() { disassembler_test!( @@ -2821,6 +3441,98 @@ mod tests { ); } + #[test] + fn test_mov_base32_offset32_reg32() { + disassembler_test!( + mov_base32_offset32_reg32, + |reg1, imm, reg2| format!( + "mov dword ptr [{} + 0x{:x}], {}", + reg1, + imm, + X86_64GeneralReg::low_32bits_string(®2), + ), + ALL_GENERAL_REGS, + [TEST_I32], + ALL_GENERAL_REGS + ); + } + + #[test] + fn test_mov_base16_offset32_reg16() { + disassembler_test!( + mov_base16_offset32_reg16, + |reg1, imm, reg2| format!( + "mov word ptr [{} + 0x{:x}], {}", + reg1, + imm, + X86_64GeneralReg::low_16bits_string(®2), + ), + ALL_GENERAL_REGS, + [TEST_I32], + ALL_GENERAL_REGS + ); + } + + #[test] + fn test_mov_base8_offset32_reg8() { + disassembler_test!( + mov_base8_offset32_reg8, + |reg1, imm, reg2| format!( + "mov byte ptr [{} + 0x{:x}], {}", + reg1, + imm, + X86_64GeneralReg::low_8bits_string(®2), + ), + ALL_GENERAL_REGS, + [TEST_I32], + ALL_GENERAL_REGS + ); + } + + #[test] + fn test_movsx_reg64_base32_offset32() { + disassembler_test!( + movsx_reg64_base32_offset32, + |reg1, reg2, imm| format!("movsxd {}, dword ptr [{} + 0x{:x}]", reg1, reg2, imm), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + + #[test] + fn test_movsx_reg64_base16_offset32() { + disassembler_test!( + movsx_reg64_base16_offset32, + |reg1, reg2, imm| format!("movsx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + + #[test] + fn test_movsx_reg64_base8_offset32() { + disassembler_test!( + movsx_reg64_base8_offset32, + |reg1, reg2, imm| format!("movsx {}, byte ptr [{} + 0x{:x}]", reg1, reg2, imm), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + + #[test] + fn test_movzx_reg64_base16_offset32() { + disassembler_test!( + movzx_reg64_base16_offset32, + |reg1, reg2, imm| format!("movzx {}, word ptr [{} + 0x{:x}]", reg1, reg2, imm), + ALL_GENERAL_REGS, + ALL_GENERAL_REGS, + [TEST_I32] + ); + } + #[test] fn test_movzx_reg64_base8_offset32() { disassembler_test!( @@ -2939,4 +3651,24 @@ mod tests { fn test_push_reg64() { disassembler_test!(push_reg64, |reg| format!("push {}", reg), ALL_GENERAL_REGS); } + + #[test] + fn test_sqrt_freg64_freg64() { + disassembler_test!( + sqrtsd_freg64_freg64, + |dst, src| format!("sqrtsd {dst}, {src}"), + ALL_FLOAT_REGS, + ALL_FLOAT_REGS + ); + } + + #[test] + fn test_sqrt_freg32_freg32() { + disassembler_test!( + sqrtss_freg32_freg32, + |dst, src| format!("sqrtss {dst}, {src}"), + ALL_FLOAT_REGS, + ALL_FLOAT_REGS + ); + } } diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index de0e7734e4..1aba223c90 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -447,6 +447,9 @@ trait Backend<'a> { LowLevel::NumAddChecked => { 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( sym, 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) } + 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 => { 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) @@ -572,6 +596,20 @@ trait Backend<'a> { 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 => { 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) @@ -623,32 +661,6 @@ trait Backend<'a> { ); 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 => { debug_assert_eq!(1, args.len(), "Not: expected to have exactly one argument"); debug_assert_eq!( @@ -739,6 +751,30 @@ trait Backend<'a> { ); 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( sym, bitcode::NUM_ROUND_F64[IntWidth::I64].to_string(), @@ -819,6 +855,13 @@ trait Backend<'a> { arg_layouts, 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( sym, bitcode::STR_SPLIT.to_string(), @@ -840,6 +883,13 @@ trait Backend<'a> { arg_layouts, 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( sym, bitcode::STR_ENDS_WITH.to_string(), @@ -854,6 +904,122 @@ trait Backend<'a> { arg_layouts, 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 => { debug_assert_eq!( 1, @@ -920,13 +1086,6 @@ trait Backend<'a> { self.load_literal_symbols(args); 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 => { let bool_layout = Layout::BOOL; 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.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>, ); + /// 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. 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. 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. fn build_not(&mut self, dst: &Symbol, src: &Symbol, arg_layout: &InLayout<'a>); @@ -1105,6 +1285,9 @@ trait Backend<'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. fn build_list_len(&mut self, dst: &Symbol, list: &Symbol); diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 4ab92ec50c..3a93cbfac6 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1319,6 +1319,7 @@ define_builtins! { 53 STR_WITH_CAPACITY: "withCapacity" 54 STR_WITH_PREFIX: "withPrefix" 55 STR_GRAPHEMES: "graphemes" + 56 STR_IS_VALID_SCALAR: "isValidScalar" } 6 LIST: "List" => { 0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias @@ -1401,6 +1402,7 @@ define_builtins! { 77 LIST_COUNT_IF: "countIf" 78 LIST_WALK_FROM: "walkFrom" 79 LIST_WALK_FROM_UNTIL: "walkFromUntil" + 80 LIST_ITER_HELP: "iterHelp" } 7 RESULT: "Result" => { 0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index 3d31172173..ae88dbecce 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -100,8 +100,14 @@ pub fn infer_borrow<'a>( // host-exposed functions must always own their arguments. let is_host_exposed = host_exposed_procs.contains(&key.0); - let param_offset = param_map.get_param_offset(key.0, key.1); - env.collect_proc(&mut param_map, proc, param_offset, is_host_exposed); + let param_offset = param_map.get_param_offset(interner, key.0, key.1); + env.collect_proc( + interner, + &mut param_map, + proc, + param_offset, + is_host_exposed, + ); } if !env.modified { @@ -167,6 +173,7 @@ impl<'a> DeclarationToIndex<'a> { fn get_param_offset( &self, + interner: &STLayoutInterner<'a>, needle_symbol: Symbol, needle_layout: ProcLayout<'a>, ) -> ParamOffset { @@ -181,12 +188,14 @@ impl<'a> DeclarationToIndex<'a> { .elements .iter() .filter_map(|(Declaration { symbol, layout }, _)| { - (*symbol == needle_symbol).then_some(layout) + (*symbol == needle_symbol) + .then_some(layout) + .map(|l| l.dbg_deep(interner)) }) .collect::>(); unreachable!( "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> { - pub fn get_param_offset(&self, symbol: Symbol, layout: ProcLayout<'a>) -> ParamOffset { - self.declaration_to_index.get_param_offset(symbol, layout) + pub fn get_param_offset( + &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.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()) } @@ -292,7 +312,7 @@ impl<'a> ParamMap<'a> { 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) .iter() @@ -312,7 +332,7 @@ impl<'a> ParamMap<'a> { proc: &Proc<'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) .iter() @@ -534,7 +554,13 @@ impl<'a> BorrowInfState<'a> { /// /// and determines whether z and which of the symbols used in e /// 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::*; let crate::ir::Call { @@ -553,7 +579,7 @@ impl<'a> BorrowInfState<'a> { // get the borrow signature of the applied function let ps = param_map - .get_symbol(name.name(), top_level) + .get_symbol(interner, name.name(), top_level) .expect("function is defined"); // the return value will be owned @@ -595,11 +621,14 @@ impl<'a> BorrowInfState<'a> { niche: passed_function.name.niche(), }; - let function_ps = - match param_map.get_symbol(passed_function.name.name(), closure_layout) { - Some(function_ps) => function_ps, - None => unreachable!(), - }; + let function_ps = match param_map.get_symbol( + interner, + passed_function.name.name(), + closure_layout, + ) { + Some(function_ps) => function_ps, + None => unreachable!(), + }; match op { ListMap { xs } => { @@ -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::*; match e { @@ -724,7 +759,7 @@ impl<'a> BorrowInfState<'a> { 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(_) => {} @@ -757,6 +792,7 @@ impl<'a> BorrowInfState<'a> { #[allow(clippy::many_single_char_names)] fn preserve_tail_call( &mut self, + interner: &STLayoutInterner<'a>, param_map: &mut ParamMap<'a>, x: Symbol, v: &Expr<'a>, @@ -782,7 +818,7 @@ impl<'a> BorrowInfState<'a> { if self.current_proc == g.name() && x == *z { // anonymous functions (for which the ps may not be known) // 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) } } @@ -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::*; match stmt { @@ -813,11 +854,11 @@ impl<'a> BorrowInfState<'a> { } => { let old = self.param_set.clone(); self.update_param_set(ys); - self.collect_stmt(param_map, v); + self.collect_stmt(interner, param_map, v); self.param_set = old; 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) => { @@ -830,17 +871,17 @@ impl<'a> BorrowInfState<'a> { stack.push((*symbol, expr)); } - self.collect_stmt(param_map, b); + self.collect_stmt(interner, param_map, b); let mut it = stack.into_iter().rev(); // collect the final expr, and see if we need to preserve a tail call let (x, v) = it.next().unwrap(); - self.collect_expr(param_map, x, v); - self.preserve_tail_call(param_map, x, v, b); + self.collect_expr(interner, param_map, x, v); + self.preserve_tail_call(interner, param_map, x, v, b); 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() { - 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, .. } => { - self.collect_stmt(param_map, remainder); + self.collect_stmt(interner, param_map, remainder); } Expect { remainder, .. } => { - self.collect_stmt(param_map, remainder); + self.collect_stmt(interner, param_map, remainder); } ExpectFx { remainder, .. } => { - self.collect_stmt(param_map, remainder); + self.collect_stmt(interner, param_map, remainder); } Refcounting(_, _) => unreachable!("these have not been introduced yet"), @@ -891,6 +932,7 @@ impl<'a> BorrowInfState<'a> { fn collect_proc( &mut self, + interner: &STLayoutInterner<'a>, param_map: &mut ParamMap<'a>, proc: &Proc<'a>, param_offset: ParamOffset, @@ -912,7 +954,7 @@ impl<'a> BorrowInfState<'a> { 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.param_set = old; diff --git a/crates/compiler/mono/src/inc_dec.rs b/crates/compiler/mono/src/inc_dec.rs index eab6ec8acf..d4c5a5abc7 100644 --- a/crates/compiler/mono/src/inc_dec.rs +++ b/crates/compiler/mono/src/inc_dec.rs @@ -605,7 +605,7 @@ impl<'a, 'i> Context<'a, 'i> { // get the borrow signature let ps = self .param_map - .get_symbol(name.name(), top_level) + .get_symbol(self.layout_interner, name.name(), top_level) .expect("function is defined"); let v = Expr::Call(crate::ir::Call { @@ -653,10 +653,11 @@ impl<'a, 'i> Context<'a, 'i> { niche: passed_function.name.niche(), }; - let function_ps = match self - .param_map - .get_symbol(passed_function.name.name(), function_layout) - { + let function_ps = match self.param_map.get_symbol( + self.layout_interner, + passed_function.name.name(), + function_layout, + ) { Some(function_ps) => function_ps, None => unreachable!(), }; @@ -1510,19 +1511,28 @@ pub fn visit_procs<'a, 'i>( }; 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>( arena: &'a Bump, + interner: &STLayoutInterner<'a>, codegen: &mut CodegenTools<'i>, param_map: &'a ParamMap<'a>, ctx: &Context<'a, 'i>, proc: &mut Proc<'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, None => Vec::from_iter_in( proc.args.iter().cloned().map(|(layout, symbol)| Param { diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 971d13abf4..ee2c14293a 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -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>( diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index 48d8e523d2..159bd58c31 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -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)] diff --git a/crates/compiler/mono/src/layout/intern.rs b/crates/compiler/mono/src/layout/intern.rs index 8687f010e9..158a47dd74 100644 --- a/crates/compiler/mono/src/layout/intern.rs +++ b/crates/compiler/mono/src/layout/intern.rs @@ -365,6 +365,10 @@ pub trait LayoutInterner<'a>: Sized { fn dbg_deep<'r>(&'r self, layout: InLayout<'a>) -> dbg::Dbg<'a, 'r, Self> { 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. @@ -1274,7 +1278,7 @@ mod equiv { } } -mod dbg { +pub mod dbg { use roc_module::symbol::Symbol; 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> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/compiler/parse/src/blankspace.rs b/crates/compiler/parse/src/blankspace.rs index 3f9cc06d3d..b0dbb7bb35 100644 --- a/crates/compiler/parse/src/blankspace.rs +++ b/crates/compiler/parse/src/blankspace.rs @@ -1,5 +1,6 @@ use crate::ast::CommentOrNewline; use crate::ast::Spaceable; +use crate::parser::Progress; use crate::parser::SpaceProblem; use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*}; use crate::state::State; @@ -7,6 +8,7 @@ use bumpalo::collections::vec::Vec; use bumpalo::Bump; use roc_region::all::Loc; use roc_region::all::Position; +use roc_region::all::Region; pub fn space0_around_ee<'a, P, S, E>( parser: P, @@ -386,98 +388,132 @@ pub fn spaces<'a, E>() -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> where 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 progress = NoProgress; - loop { - let whitespace = fast_eat_whitespace(state.bytes()); - if whitespace > 0 { - state.advance_mut(whitespace); - progress = MadeProgress; - } - match state.bytes().first() { - Some(b'#') => { - state.advance_mut(1); - - let is_doc_comment = state.bytes().first() == Some(&b'#') - && (state.bytes().get(1) == Some(&b' ') - || state.bytes().get(1) == Some(&b'\n') - || begins_with_crlf(&state.bytes()[1..]) - || Option::is_none(&state.bytes().get(1))); - - if is_doc_comment { - state.advance_mut(1); - if state.bytes().first() == Some(&b' ') { - state.advance_mut(1); - } - } - - let len = fast_eat_until_control_character(state.bytes()); - - // We already checked that the string is valid UTF-8 - debug_assert!(std::str::from_utf8(&state.bytes()[..len]).is_ok()); - let text = unsafe { std::str::from_utf8_unchecked(&state.bytes()[..len]) }; - - let comment = if is_doc_comment { - CommentOrNewline::DocComment(text) - } else { - CommentOrNewline::LineComment(text) - }; - newlines.push(comment); - state.advance_mut(len); - - if begins_with_crlf(state.bytes()) { - state.advance_mut(1); - state = state.advance_newline(); - } else if state.bytes().first() == Some(&b'\n') { - state = state.advance_newline(); - } - - progress = MadeProgress; - } - Some(b'\r') => { - if state.bytes().get(1) == Some(&b'\n') { - newlines.push(CommentOrNewline::Newline); - state.advance_mut(1); - state = state.advance_newline(); - progress = MadeProgress; - } else { - return Err(( - progress, - E::space_problem( - BadInputError::HasMisplacedCarriageReturn, - state.pos(), - ), - )); - } - } - Some(b'\n') => { - newlines.push(CommentOrNewline::Newline); - state = state.advance_newline(); - progress = MadeProgress; - } - Some(b'\t') => { - return Err(( - progress, - E::space_problem(BadInputError::HasTab, state.pos()), - )); - } - Some(x) if *x < b' ' => { - return Err(( - progress, - E::space_problem(BadInputError::HasAsciiControl, state.pos()), - )); - } - _ => { - if !newlines.is_empty() { - state = state.mark_current_indent(); - } - break; - } - } + match consume_spaces(state, |_, space, _| newlines.push(space)) { + Ok((progress, state)) => Ok((progress, newlines.into_bump_slice(), state)), + Err((progress, err)) => Err((progress, err)), } - - Ok((progress, newlines.into_bump_slice(), state)) } } + +pub fn loc_spaces<'a, E>() -> impl Parser<'a, &'a [Loc>], 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 found_newline = false; + loop { + let whitespace = fast_eat_whitespace(state.bytes()); + if whitespace > 0 { + state.advance_mut(whitespace); + progress = MadeProgress; + } + + let start = state.pos(); + + match state.bytes().first() { + Some(b'#') => { + state.advance_mut(1); + + let is_doc_comment = state.bytes().first() == Some(&b'#') + && (state.bytes().get(1) == Some(&b' ') + || state.bytes().get(1) == Some(&b'\n') + || begins_with_crlf(&state.bytes()[1..]) + || Option::is_none(&state.bytes().get(1))); + + if is_doc_comment { + state.advance_mut(1); + if state.bytes().first() == Some(&b' ') { + state.advance_mut(1); + } + } + + let len = fast_eat_until_control_character(state.bytes()); + + // We already checked that the string is valid UTF-8 + debug_assert!(std::str::from_utf8(&state.bytes()[..len]).is_ok()); + let text = unsafe { std::str::from_utf8_unchecked(&state.bytes()[..len]) }; + + let comment = if is_doc_comment { + CommentOrNewline::DocComment(text) + } else { + CommentOrNewline::LineComment(text) + }; + state.advance_mut(len); + on_space(start, comment, state.pos()); + found_newline = true; + + if begins_with_crlf(state.bytes()) { + state.advance_mut(1); + state = state.advance_newline(); + } else if state.bytes().first() == Some(&b'\n') { + state = state.advance_newline(); + } + + progress = MadeProgress; + } + Some(b'\r') => { + if state.bytes().get(1) == Some(&b'\n') { + state.advance_mut(1); + state = state.advance_newline(); + on_space(start, CommentOrNewline::Newline, state.pos()); + found_newline = true; + progress = MadeProgress; + } else { + return Err(( + progress, + E::space_problem(BadInputError::HasMisplacedCarriageReturn, state.pos()), + )); + } + } + Some(b'\n') => { + state = state.advance_newline(); + on_space(start, CommentOrNewline::Newline, state.pos()); + found_newline = true; + progress = MadeProgress; + } + Some(b'\t') => { + return Err(( + progress, + E::space_problem(BadInputError::HasTab, state.pos()), + )); + } + Some(x) if *x < b' ' => { + return Err(( + progress, + E::space_problem(BadInputError::HasAsciiControl, state.pos()), + )); + } + _ => { + if found_newline { + state = state.mark_current_indent(); + } + break; + } + } + } + + Ok((progress, state)) +} diff --git a/crates/compiler/parse/src/highlight.rs b/crates/compiler/parse/src/highlight.rs new file mode 100644 index 0000000000..53bd5c04c7 --- /dev/null +++ b/crates/compiler/parse/src/highlight.rs @@ -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> { + let mut tokens = Vec::new(); + let state = State::new(text.as_bytes()); + + let arena = Bump::new(); + + let header_keywords = HEADER_KEYWORDS.iter().copied().collect::>(); + let body_keywords = KEYWORDS.iter().copied().collect::>(); + + 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>) -> Vec> { + let mut tokens: Vec> = Vec::new(); + let mut previous_location: Option> = 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>, + 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>, + 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> = 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); + } +} diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index dbdfe30ff4..41deda54f6 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -10,6 +10,7 @@ pub mod ast; pub mod blankspace; pub mod expr; pub mod header; +pub mod highlight; pub mod ident; pub mod keyword; pub mod module; diff --git a/crates/compiler/region/src/all.rs b/crates/compiler/region/src/all.rs index 457810cdf9..fb83fe2b37 100644 --- a/crates/compiler/region/src/all.rs +++ b/crates/compiler/region/src/all.rs @@ -129,6 +129,10 @@ impl Position { offset: self.offset - count as u32, } } + + pub fn byte_offset(&self) -> usize { + self.offset as usize + } } impl Debug for Position { @@ -322,6 +326,10 @@ impl Loc { value: transform(self.value), } } + + pub fn byte_range(&self) -> std::ops::Range { + self.region.start.byte_offset()..self.region.end.byte_offset() + } } impl fmt::Debug for Loc diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 996bfb61ef..498293824e 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -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 + _ -> "" + "# + ), + 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 + _ -> "" + "# + ), + RocStr::from(r#"{"B":[67]}"#), + RocStr + ) +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn decode_use_stdlib() { diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index bdbdd19153..064c67b453 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -1859,13 +1859,11 @@ fn first_int_list() { assert_evals_to!( indoc!( r#" - when List.first [12, 9, 6, 3] is - Ok val -> val - Err _ -> -1 + List.first [12, 9, 6, 3] "# ), - 12, - i64 + RocResult::ok(12), + RocResult ); } @@ -1889,45 +1887,42 @@ fn first_wildcard_empty_list() { assert_evals_to!( indoc!( r#" - when List.first [] is - Ok _ -> 5 - Err _ -> -1 + List.last [] |> Result.map (\_ -> 0i64) "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn first_empty_list() { assert_evals_to!( indoc!( r#" - when List.first [] is - Ok val -> val - Err _ -> -1 + list : List I64 + list = [] + + List.first list "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn last_int_list() { assert_evals_to!( indoc!( r#" - when List.last [12, 9, 6, 3] is - Ok val -> val - Err _ -> -1 + List.last [12, 9, 6, 3] "# ), - 3, - i64 + RocResult::ok(3), + RocResult ); } @@ -1937,13 +1932,11 @@ fn last_wildcard_empty_list() { assert_evals_to!( indoc!( r#" - when List.last [] is - Ok _ -> 5 - Err _ -> -1 + List.last [] |> Result.map (\_ -> 0i64) "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } @@ -1953,13 +1946,14 @@ fn last_empty_list() { assert_evals_to!( indoc!( r#" - when List.last [] is - Ok val -> val - Err _ -> -1 + list : List I64 + list = [] + + List.last list "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } @@ -1969,29 +1963,32 @@ fn get_empty_list() { assert_evals_to!( indoc!( r#" - when List.get [] 0 is - Ok val -> val - Err _ -> -1 + list : List I64 + list = [] + + List.get list 0 "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] 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!( indoc!( r#" - when List.get [] 0 is - Ok _ -> 5 - Err _ -> -1 + List.get [] 0 + |> Result.map (\_ -> {}) "# ), - -1, - i64 + RocResult::err(()), + RocResult<(), ()> ); } @@ -2010,39 +2007,35 @@ fn get_str_list_ok() { } #[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() { assert_evals_to!( indoc!( r#" - when List.get [12, 9, 6] 1 is - Ok val -> val - Err _ -> -1 + List.get [12, 9, 6] 1 "# ), - 9, - i64 + RocResult::ok(9), + RocResult ); } #[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() { assert_evals_to!( indoc!( r#" - when List.get [12, 9, 6] 1000 is - Ok val -> val - Err _ -> -1 + List.get [12, 9, 6] 1000 "# ), - -1, - i64 + RocResult::err(()), + RocResult ); } #[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() { assert_evals_to!( indoc!( @@ -2057,7 +2050,7 @@ fn replace_unique_int_list() { } #[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() { assert_evals_to!( indoc!( @@ -2072,7 +2065,7 @@ fn replace_unique_int_list_out_of_bounds() { } #[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() { assert_evals_to!( indoc!( @@ -2087,7 +2080,7 @@ fn replace_unique_int_list_get_old_value() { } #[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() { assert_evals_to!( indoc!( @@ -2138,13 +2131,11 @@ fn get_set_unique_int_list_i64() { assert_evals_to!( indoc!( r#" - when List.get (List.set [12, 9, 7, 3] 1 42) 1 is - Ok val -> val - Err _ -> -1 + List.get (List.set [12, 9, 7, 3] 1 42) 1 "# ), - 42, - i64 + RocResult::ok(42), + RocResult ); } @@ -2154,13 +2145,11 @@ fn get_set_unique_int_list_i8() { assert_evals_to!( indoc!( r#" - when List.get (List.set [12, 9, 7, 3] 1 42i8) 1 is - Ok val -> val - Err _ -> -1i8 + List.get (List.set [12, 9, 7, 3] 1 42i8) 1 "# ), - 42, - i8 + RocResult::ok(42), + RocResult ); } @@ -2175,7 +2164,7 @@ fn set_unique_int_list() { } #[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() { assert_evals_to!( "List.set [3, 17, 4.1] 1337 9.25", @@ -2240,20 +2229,18 @@ fn set_shared_list_oob() { } #[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() { assert_evals_to!( indoc!( r#" unique = [2, 4] - when List.get unique 1 is - Ok num -> num - Err _ -> -1 + List.get unique 1 "# ), - 4, - i64 + RocResult::ok(4), + RocResult ); } @@ -2275,7 +2262,7 @@ fn gen_wrap_len() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn gen_wrap_first() { assert_evals_to!( indoc!( @@ -2292,7 +2279,7 @@ fn gen_wrap_first() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn gen_duplicate() { assert_evals_to!( indoc!( @@ -2605,7 +2592,7 @@ fn list_literal_increment_decrement() { } #[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() { assert_evals_to!( indoc!( @@ -2625,7 +2612,7 @@ fn list_pass_to_function() { } #[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() { assert_evals_to!( indoc!( @@ -2713,24 +2700,22 @@ fn list_min() { assert_evals_to!( indoc!( r#" - when List.min [] is - Ok val -> val - Err _ -> -1 - "# + List.min [] + |> Result.map (\_ -> {}) + "# ), - -1, - i64 + RocResult::err(()), + RocResult<(), ()> ); + assert_evals_to!( indoc!( r#" - when List.min [3, 1, 2] is - Ok val -> val - Err _ -> -1 - "# + List.min [3, 1, 2] + "# ), - 1, - i64 + RocResult::ok(1), + RocResult ); } @@ -2740,24 +2725,22 @@ fn list_max() { assert_evals_to!( indoc!( r#" - when List.max [] is - Ok val -> val - Err _ -> -1 - "# + List.max [] + |> Result.map (\_ -> {}) + "# ), - -1, - i64 + RocResult::err(()), + RocResult<(), ()> ); + assert_evals_to!( indoc!( r#" - when List.max [3, 1, 2] is - Ok val -> val - Err _ -> -1 - "# + List.max [3, 1, 2] + "# ), - 3, - i64 + RocResult::ok(3), + RocResult ); } @@ -3440,7 +3423,7 @@ fn with_capacity() { r#" l : List U64 l = List.withCapacity 10 - + l "# ), diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index afdd1b0118..b0f7fe2b63 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -19,11 +19,11 @@ fn nat_alias() { assert_evals_to!( indoc!( r#" - i : Num.Nat - i = 1 + i : Num.Nat + i = 1 - i - "# + i + "# ), 1, usize @@ -31,16 +31,16 @@ fn nat_alias() { } #[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() { assert_evals_to!( indoc!( r#" - i : I128 - i = 128 + i : I128 + i = 128 - i - "# + i + "# ), 128, i128 @@ -71,11 +71,11 @@ fn i32_signed_int_alias() { assert_evals_to!( indoc!( r#" - i : I32 - i = 32 + i : I32 + i = 32 - i - "# + i + "# ), 32, i32 @@ -115,7 +115,7 @@ fn i8_signed_int_alias() { } #[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() { assert_evals_to!( indoc!( @@ -196,7 +196,7 @@ fn i8_hex_int_alias() { } #[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() { assert_evals_to!( indoc!( @@ -277,7 +277,7 @@ fn u8_signed_int_alias() { } #[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() { assert_evals_to!( indoc!( @@ -418,7 +418,7 @@ fn character_literal_new_line() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn dec_float_alias() { assert_evals_to!( indoc!( @@ -451,7 +451,7 @@ fn f64_float_alias() { ); } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] fn f32_float_alias() { assert_evals_to!( indoc!( @@ -468,112 +468,51 @@ fn f32_float_alias() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -fn f64_sqrt() { - assert_evals_to!( - indoc!( - r#" - when Num.sqrtChecked 100 is - Ok val -> val - Err _ -> -1 - "# - ), - 10.0, - f64 - ); +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn f64_sqrt_100() { + assert_evals_to!("Num.sqrt 100", 10.0, f64); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn f64_sqrt_checked_0() { + assert_evals_to!("Num.sqrt 0", 0.0, f64); } #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -fn f64_log() { - assert_evals_to!( - 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 - ); +fn f64_sqrt_checked_positive() { + assert_evals_to!("Num.sqrtChecked 100", RocResult::ok(10.0), RocResult); } #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn f64_sqrt_checked_negative() { - assert_evals_to!( - indoc!( - r#" - when Num.sqrtChecked -1 is - Err _ -> 42 - Ok val -> val - "# - ), - 42.0, - f64 - ); + assert_evals_to!("Num.sqrtChecked -1f64", RocResult::err(()), RocResult); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn f64_log() { + assert_evals_to!("Num.log 7.38905609893", 1.999999999999912, 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); } #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn f64_log_checked_zero() { - assert_evals_to!( - indoc!( - r#" - when Num.logChecked 0 is - Err _ -> 42 - Ok val -> val - "# - ), - 42.0, - f64 - ); + assert_evals_to!("Num.logChecked 0", RocResult::err(()), RocResult); } #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn f64_log_negative() { - assert_evals_to!( - indoc!( - r#" - Num.log -1 - "# - ), - true, - f64, - |f: f64| f.is_nan() - ); + assert_evals_to!("Num.log -1", true, f64, |f: f64| f.is_nan()); } #[test] @@ -890,16 +829,20 @@ fn gen_int_neq() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn gen_int_less_than() { - assert_evals_to!( - indoc!( - r#" - 4 < 5 - "# - ), - true, - bool - ); +fn int_less_than() { + assert_evals_to!("4 < 5", true, bool); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn float_less_than() { + assert_evals_to!("4.0 < 5.0", true, 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] diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 51c9ed01a2..3c07349438 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -3307,10 +3307,46 @@ fn box_str() { #[test] #[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) } +#[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] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn box_and_unbox_record() { diff --git a/crates/compiler/test_gen/src/gen_records.rs b/crates/compiler/test_gen/src/gen_records.rs index 9f1106e4a9..33f63a6d43 100644 --- a/crates/compiler/test_gen/src/gen_records.rs +++ b/crates/compiler/test_gen/src/gen_records.rs @@ -360,7 +360,7 @@ fn i64_record1_literal() { // ); // } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn bool_literal() { assert_evals_to!( indoc!( @@ -1111,3 +1111,22 @@ fn toplevel_accessor_fn_thunk() { 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 + ) +} diff --git a/crates/compiler/test_gen/src/gen_result.rs b/crates/compiler/test_gen/src/gen_result.rs index 53cc8aaf76..9729fbe9ce 100644 --- a/crates/compiler/test_gen/src/gen_result.rs +++ b/crates/compiler/test_gen/src/gen_result.rs @@ -227,7 +227,7 @@ fn is_err() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn roc_result_ok() { +fn roc_result_ok_i64() { assert_evals_to!( indoc!( 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 + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn roc_result_err() { diff --git a/crates/compiler/test_gen/src/gen_str.rs b/crates/compiler/test_gen/src/gen_str.rs index f540e62c61..4b4b61f41a 100644 --- a/crates/compiler/test_gen/src/gen_str.rs +++ b/crates/compiler/test_gen/src/gen_str.rs @@ -16,7 +16,7 @@ use indoc::indoc; use roc_std::{RocList, RocResult, RocStr}; #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_empty_delimiter() { assert_evals_to!( indoc!( @@ -46,7 +46,7 @@ fn str_split_empty_delimiter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_bigger_delimiter_small_str() { assert_evals_to!( indoc!( @@ -110,7 +110,7 @@ fn str_split_small_str_bigger_delimiter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_big_str_small_delimiter() { assert_evals_to!( indoc!( @@ -154,7 +154,7 @@ fn str_split_small_str_small_delimiter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_bigger_delimiter_big_strs() { assert_evals_to!( indoc!( @@ -198,7 +198,7 @@ fn str_split_minimal_example() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_small_str_big_delimiter() { assert_evals_to!( indoc!( @@ -227,7 +227,7 @@ fn str_split_small_str_big_delimiter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_small_str_20_char_delimiter() { assert_evals_to!( indoc!( @@ -243,7 +243,7 @@ fn str_split_small_str_20_char_delimiter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_concat_big_to_big() { assert_evals_to!( indoc!( @@ -402,7 +402,7 @@ fn small_str_concat_empty_second_arg() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn small_str_concat_small_to_big() { assert_evals_to!( r#"Str.concat "abc" " this is longer than 15 chars""#, @@ -530,7 +530,7 @@ fn str_count_graphemes_three_js() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_count_graphemes_big_str() { assert_evals_to!( r#"Str.countGraphemes "6🤔å🤔e¥🤔çppkd🙃1jdal🦯asdfa∆ltråø˚waia8918.,🏅jjc""#, @@ -540,7 +540,7 @@ fn str_count_graphemes_big_str() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_starts_with_same_big_str() { assert_evals_to!( r#"Str.startsWith "123456789123456789" "123456789123456789""#, @@ -550,7 +550,7 @@ fn str_starts_with_same_big_str() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_starts_with_different_big_str() { assert_evals_to!( r#"Str.startsWith "12345678912345678910" "123456789123456789""#, @@ -560,24 +560,24 @@ fn str_starts_with_different_big_str() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_starts_with_same_small_str() { assert_evals_to!(r#"Str.startsWith "1234" "1234""#, true, bool); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_starts_with_different_small_str() { assert_evals_to!(r#"Str.startsWith "1234" "12""#, true, bool); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_starts_with_false_small_str() { assert_evals_to!(r#"Str.startsWith "1234" "23""#, false, bool); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_single_ascii() { assert_evals_to!( indoc!( @@ -593,7 +593,7 @@ fn str_from_utf8_pass_single_ascii() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_many_ascii() { assert_evals_to!( indoc!( @@ -609,7 +609,7 @@ fn str_from_utf8_pass_many_ascii() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_single_unicode() { assert_evals_to!( indoc!( @@ -625,7 +625,7 @@ fn str_from_utf8_pass_single_unicode() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_many_unicode() { assert_evals_to!( indoc!( @@ -641,7 +641,7 @@ fn str_from_utf8_pass_many_unicode() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_single_grapheme() { assert_evals_to!( indoc!( @@ -657,7 +657,7 @@ fn str_from_utf8_pass_single_grapheme() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_many_grapheme() { assert_evals_to!( indoc!( @@ -673,7 +673,7 @@ fn str_from_utf8_pass_many_grapheme() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_pass_all() { assert_evals_to!( indoc!( @@ -689,7 +689,7 @@ fn str_from_utf8_pass_all() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_invalid_start_byte() { assert_evals_to!( indoc!( @@ -709,7 +709,7 @@ fn str_from_utf8_fail_invalid_start_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_unexpected_end_of_sequence() { assert_evals_to!( indoc!( @@ -729,7 +729,7 @@ fn str_from_utf8_fail_unexpected_end_of_sequence() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_expected_continuation() { assert_evals_to!( indoc!( @@ -749,7 +749,7 @@ fn str_from_utf8_fail_expected_continuation() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_overlong_encoding() { assert_evals_to!( indoc!( @@ -769,7 +769,7 @@ fn str_from_utf8_fail_overlong_encoding() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_codepoint_too_large() { assert_evals_to!( indoc!( @@ -789,7 +789,7 @@ fn str_from_utf8_fail_codepoint_too_large() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_fail_surrogate_half() { assert_evals_to!( indoc!( @@ -809,7 +809,7 @@ fn str_from_utf8_fail_surrogate_half() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_equality() { assert_evals_to!(r#""a" == "a""#, true, bool); assert_evals_to!( @@ -865,7 +865,7 @@ fn nested_recursive_literal() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_join_comma_small() { assert_evals_to!( r#"Str.joinWith ["1", "2"] ", " "#, @@ -875,7 +875,7 @@ fn str_join_comma_small() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_join_comma_big() { assert_evals_to!( r#"Str.joinWith ["10000000", "2000000", "30000000"] ", " "#, @@ -885,13 +885,13 @@ fn str_join_comma_big() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_join_comma_single() { assert_evals_to!(r#"Str.joinWith ["1"] ", " "#, RocStr::from("1"), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_to_utf8() { assert_evals_to!( r#"Str.toUtf8 "hello""#, @@ -909,7 +909,7 @@ fn str_to_utf8() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range() { assert_evals_to!( indoc!( @@ -926,7 +926,7 @@ fn str_from_utf8_range() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_slice() { assert_evals_to!( indoc!( @@ -943,7 +943,7 @@ fn str_from_utf8_range_slice() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_slice_not_end() { assert_evals_to!( indoc!( @@ -960,7 +960,7 @@ fn str_from_utf8_range_slice_not_end() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_order_does_not_matter() { assert_evals_to!( indoc!( @@ -977,7 +977,7 @@ fn str_from_utf8_range_order_does_not_matter() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_out_of_bounds_start_value() { assert_evals_to!( indoc!( @@ -995,7 +995,7 @@ fn str_from_utf8_range_out_of_bounds_start_value() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_count_too_high() { assert_evals_to!( indoc!( @@ -1013,7 +1013,7 @@ fn str_from_utf8_range_count_too_high() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_from_utf8_range_count_too_high_for_start() { assert_evals_to!( indoc!( @@ -1031,7 +1031,7 @@ fn str_from_utf8_range_count_too_high_for_start() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_repeat_small_stays_small() { assert_evals_to!( indoc!(r#"Str.repeat "Roc" 3"#), @@ -1041,7 +1041,7 @@ fn str_repeat_small_stays_small() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_repeat_small_becomes_big() { assert_evals_to!( indoc!(r#"Str.repeat "less than 23 characters" 2"#), @@ -1051,7 +1051,7 @@ fn str_repeat_small_becomes_big() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_repeat_big() { assert_evals_to!( indoc!(r#"Str.repeat "more than 23 characters now" 2"#), @@ -1061,27 +1061,26 @@ fn str_repeat_big() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_repeat_empty_string() { let a = indoc!(r#"Str.repeat "" 3"#); - let b = RocStr::from(""); - assert_evals_to!(a, b, RocStr); + assert_evals_to!(a, RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_repeat_zero_times() { assert_evals_to!(indoc!(r#"Str.repeat "Roc" 0"#), RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_empty_string() { assert_evals_to!(indoc!(r#"Str.trim """#), RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_null_byte() { assert_evals_to!( indoc!(r#"Str.trim (Str.reserve "\u(0000)" 40)"#), @@ -1091,13 +1090,13 @@ fn str_trim_null_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_small_blank_string() { assert_evals_to!(indoc!(r#"Str.trim " ""#), RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_small_to_small() { assert_evals_to!( indoc!(r#"Str.trim " hello world ""#), @@ -1107,7 +1106,7 @@ fn str_trim_small_to_small() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_large_to_large_unique() { assert_evals_to!( indoc!(r#"Str.trim (Str.concat " " "hello world from a large string ")"#), @@ -1117,7 +1116,7 @@ fn str_trim_large_to_large_unique() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_large_to_small_unique() { assert_evals_to!( indoc!(r#"Str.trim (Str.concat " " "hello world ")"#), @@ -1184,13 +1183,13 @@ fn str_trim_small_to_small_shared() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_left_small_blank_string() { assert_evals_to!(indoc!(r#"Str.trimLeft " ""#), RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_left_small_to_small() { assert_evals_to!( indoc!(r#"Str.trimLeft " hello world ""#), @@ -1200,7 +1199,7 @@ fn str_trim_left_small_to_small() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_left_large_to_large_unique() { assert_evals_to!( 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] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_left_large_to_small_unique() { assert_evals_to!( indoc!(r#"Str.trimLeft (Str.concat " " "hello world ")"#), @@ -1277,13 +1276,13 @@ fn str_trim_left_small_to_small_shared() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_right_small_blank_string() { assert_evals_to!(indoc!(r#"Str.trimRight " ""#), RocStr::from(""), RocStr); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_right_small_to_small() { assert_evals_to!( indoc!(r#"Str.trimRight " hello world ""#), @@ -1293,7 +1292,7 @@ fn str_trim_right_small_to_small() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_right_large_to_large_unique() { assert_evals_to!( 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] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_trim_right_large_to_small_unique() { assert_evals_to!( indoc!(r#"Str.trimRight (Str.concat " hello world" " ")"#), @@ -1370,9 +1369,17 @@ fn str_trim_right_small_to_small_shared() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] 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 + ); } #[test] @@ -1381,14 +1388,11 @@ fn str_to_i128() { assert_evals_to!( indoc!( r#" - when Str.toI128 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toI128 "1" + "# ), - 1, - i128 + RocResult::ok(1), + RocResult ); } @@ -1398,41 +1402,39 @@ fn str_to_u128() { assert_evals_to!( indoc!( r#" - when Str.toU128 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toU128 "1" + "# ), - 1, - u128 + RocResult::ok(1), + RocResult ); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_to_i64() { assert_evals_to!( indoc!( r#" - when Str.toI64 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toI64 "1" + "# ), - 1, - i64 + RocResult::ok(1), + RocResult ); } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_to_u64() { assert_evals_to!( - r#"Str.toU64 "1""#, - RocResult::ok(1u64), - RocResult + indoc!( + r#" + Str.toU64 "1" + "# + ), + RocResult::ok(1), + RocResult ); } @@ -1442,14 +1444,11 @@ fn str_to_i32() { assert_evals_to!( indoc!( r#" - when Str.toI32 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toI32 "1" + "# ), - 1, - i32 + RocResult::ok(1), + RocResult ); } @@ -1457,9 +1456,13 @@ fn str_to_i32() { #[cfg(any(feature = "gen-llvm"))] fn str_to_u32() { assert_evals_to!( - r#"Str.toU32 "1""#, - RocResult::ok(1u32), - RocResult + indoc!( + r#" + Str.toU32 "1" + "# + ), + RocResult::ok(1), + RocResult ); } @@ -1469,14 +1472,11 @@ fn str_to_i16() { assert_evals_to!( indoc!( r#" - when Str.toI16 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toI16 "1" + "# ), - 1, - i16 + RocResult::ok(1), + RocResult ); } @@ -1486,14 +1486,11 @@ fn str_to_u16() { assert_evals_to!( indoc!( r#" - when Str.toU16 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toU16 "1" + "# ), - 1, - u16 + RocResult::ok(1), + RocResult ); } @@ -1503,14 +1500,11 @@ fn str_to_i8() { assert_evals_to!( indoc!( r#" - when Str.toI8 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toI8 "1" + "# ), - 1, - i8 + RocResult::ok(1), + RocResult ); } @@ -1520,14 +1514,11 @@ fn str_to_u8() { assert_evals_to!( indoc!( r#" - when Str.toU8 "1" is - Ok n -> n - Err _ -> 0 - - "# + Str.toU8 "1" + "# ), - 1, - u8 + RocResult::ok(1), + RocResult ); } @@ -1585,7 +1576,7 @@ fn str_to_dec() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn issue_2811() { assert_evals_to!( indoc!( @@ -1601,7 +1592,7 @@ fn issue_2811() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn to_scalar_1_byte() { assert_evals_to!( indoc!( @@ -1625,7 +1616,7 @@ fn to_scalar_1_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn to_scalar_2_byte() { assert_evals_to!( indoc!( @@ -1649,7 +1640,7 @@ fn to_scalar_2_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn to_scalar_3_byte() { assert_evals_to!( indoc!( @@ -1673,7 +1664,7 @@ fn to_scalar_3_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn to_scalar_4_byte() { // from https://design215.com/toolbox/utf8-4byte-characters.php assert_evals_to!( @@ -1698,7 +1689,7 @@ fn to_scalar_4_byte() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_first_one_char() { assert_evals_to!( indoc!( @@ -1714,7 +1705,7 @@ fn str_split_first_one_char() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_first_multiple_chars() { assert_evals_to!( indoc!( @@ -1728,7 +1719,7 @@ fn str_split_first_multiple_chars() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_first_entire_input() { assert_evals_to!( indoc!( @@ -1742,7 +1733,7 @@ fn str_split_first_entire_input() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_first_not_found() { assert_evals_to!( indoc!( @@ -1756,7 +1747,7 @@ fn str_split_first_not_found() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_last_one_char() { assert_evals_to!( indoc!( @@ -1770,7 +1761,7 @@ fn str_split_last_one_char() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_last_multiple_chars() { assert_evals_to!( indoc!( @@ -1784,7 +1775,7 @@ fn str_split_last_multiple_chars() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_last_entire_input() { assert_evals_to!( indoc!( @@ -1798,12 +1789,12 @@ fn str_split_last_entire_input() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_last_not_found() { assert_evals_to!( indoc!( r#" - Str.splitFirst "foo" "bar" + Str.splitLast "foo" "bar" "# ), RocResult::err(()), @@ -1812,7 +1803,7 @@ fn str_split_last_not_found() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_overlapping_substring_1() { assert_evals_to!( r#"Str.split "aaa" "aa""#, @@ -1822,7 +1813,7 @@ fn str_split_overlapping_substring_1() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_split_overlapping_substring_2() { assert_evals_to!( r#"Str.split "aaaa" "aa""#, @@ -1832,7 +1823,7 @@ fn str_split_overlapping_substring_2() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_walk_utf8_with_index() { #[cfg(not(feature = "gen-llvm-wasm"))] assert_evals_to!( @@ -1872,7 +1863,7 @@ fn str_append_scalar() { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] fn str_walk_scalars() { assert_evals_to!( indoc!( @@ -1951,7 +1942,7 @@ fn when_on_strings() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn with_capacity() { assert_evals_to!( indoc!( @@ -1965,7 +1956,7 @@ fn with_capacity() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn with_capacity_concat() { assert_evals_to!( indoc!( @@ -1979,7 +1970,7 @@ fn with_capacity_concat() { } #[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn str_with_prefix() { assert_evals_to!( indoc!( @@ -2003,7 +1994,7 @@ fn str_with_prefix() { } #[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() { assert_evals_to!( indoc!( @@ -2025,7 +2016,7 @@ fn destructure_pattern_assigned_from_thunk_opaque() { } #[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() { assert_evals_to!( indoc!( diff --git a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt index 4538c4e459..84ec5fb5b2 100644 --- a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt +++ b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt @@ -3,7 +3,7 @@ procedure Bool.11 (#Attr.2, #Attr.3): ret Bool.24; 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.497 : U8 = GetTagId List.493; 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; ret List.484; -procedure List.90 (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.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): +procedure List.80 (List.528, List.529, List.530, List.531, List.532): joinpoint List.502 List.429 List.430 List.431 List.432 List.433: let List.504 : Int1 = CallByName Num.22 List.432 List.433; if List.504 then @@ -83,6 +77,12 @@ procedure List.91 (List.528, List.529, List.530, List.531, List.532): in 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): let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.258; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index d1c66212c0..d85f844125 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -21,12 +21,12 @@ procedure Dict.4 (Dict.497): procedure List.11 (List.114, 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; procedure List.11 (List.114, 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; 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; 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: let List.488 : U64 = 0i64; 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 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: let List.500 : U64 = 0i64; let List.494 : Int1 = CallByName Num.24 List.117 List.500; diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 0c7233dd44..4a2a6c52ea 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -213,11 +213,11 @@ procedure List.138 (List.139, List.140, List.137): ret List.592; 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; 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; 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; ret List.594; -procedure List.90 (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.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): +procedure List.80 (List.531, List.532, List.533, List.534, List.535): joinpoint List.506 List.429 List.430 List.431 List.432 List.433: let List.508 : Int1 = CallByName Num.22 List.432 List.433; if List.508 then @@ -284,7 +272,7 @@ procedure List.91 (List.531, List.532, List.533, List.534, List.535): in 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: let List.581 : Int1 = CallByName Num.22 List.432 List.433; if List.581 then @@ -298,6 +286,18 @@ procedure List.91 (List.605, List.606, List.607, List.608, List.609): in 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): let Num.282 : U8 = lowlevel NumIntCast #Attr.2; ret Num.282; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt index 02d3f30240..7d79cabf6a 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -122,7 +122,7 @@ procedure List.138 (List.139, List.140, List.137): ret List.525; 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; 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; ret List.527; -procedure List.90 (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.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): +procedure List.80 (List.538, List.539, List.540, List.541, List.542): joinpoint List.512 List.429 List.430 List.431 List.432 List.433: let List.514 : Int1 = CallByName Num.22 List.432 List.433; if List.514 then @@ -175,6 +169,12 @@ procedure List.91 (List.538, List.539, List.540, List.541, List.542): in 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): let Num.263 : U8 = lowlevel NumIntCast #Attr.2; ret Num.263; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt index 829fbcd5a2..5c223a1326 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -130,7 +130,7 @@ procedure List.138 (List.139, List.140, List.137): ret List.525; 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; 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; ret List.527; -procedure List.90 (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.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): +procedure List.80 (List.538, List.539, List.540, List.541, List.542): joinpoint List.512 List.429 List.430 List.431 List.432 List.433: let List.514 : Int1 = CallByName Num.22 List.432 List.433; if List.514 then @@ -183,6 +177,12 @@ procedure List.91 (List.538, List.539, List.540, List.541, List.542): in 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): let Num.263 : U8 = lowlevel NumIntCast #Attr.2; ret Num.263; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index 40580ef311..51bb3b1c8c 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -131,7 +131,7 @@ procedure List.138 (List.139, List.140, List.137): ret List.531; 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; 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; ret List.534; -procedure List.90 (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.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): +procedure List.80 (List.544, List.545, List.546, List.547, List.548): joinpoint List.518 List.429 List.430 List.431 List.432 List.433: let List.520 : Int1 = CallByName Num.22 List.432 List.433; if List.520 then @@ -184,6 +178,12 @@ procedure List.91 (List.544, List.545, List.546, List.547, List.548): in 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): let Num.265 : U8 = lowlevel NumIntCast #Attr.2; ret Num.265; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index 72a865f670..0995768410 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -137,7 +137,7 @@ procedure List.138 (List.139, List.140, List.137): ret List.531; 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; 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; ret List.534; -procedure List.90 (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.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): +procedure List.80 (List.544, List.545, List.546, List.547, List.548): joinpoint List.518 List.429 List.430 List.431 List.432 List.433: let List.520 : Int1 = CallByName Num.22 List.432 List.433; if List.520 then @@ -190,6 +184,12 @@ procedure List.91 (List.544, List.545, List.546, List.547, List.548): in 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): let Num.265 : U8 = lowlevel NumIntCast #Attr.2; ret Num.265; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt new file mode 100644 index 0000000000..18c4b302df --- /dev/null +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -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; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt new file mode 100644 index 0000000000..6781849fa6 --- /dev/null +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification.txt @@ -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; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt new file mode 100644 index 0000000000..c4fcf542bb --- /dev/null +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -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; diff --git a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt index 6720a3c26e..079dd957b6 100644 --- a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt +++ b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt @@ -3,7 +3,7 @@ procedure Bool.11 (#Attr.2, #Attr.3): ret Bool.24; 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.497 : U8 = GetTagId List.493; 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; ret List.484; -procedure List.90 (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.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): +procedure List.80 (List.528, List.529, List.530, List.531, List.532): joinpoint List.502 List.429 List.430 List.431 List.432 List.433: let List.504 : Int1 = CallByName Num.22 List.432 List.433; if List.504 then @@ -83,6 +77,12 @@ procedure List.91 (List.528, List.529, List.530, List.531, List.532): in 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): let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.258; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 0fb8eaab3a..f553ce49d8 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -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 + "# + ) +} diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index afb979f2f3..34d5062c1b 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -568,6 +568,15 @@ mod test_snapshots { 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 = if expect == TestExpectation::Pass || expect == TestExpectation::Malformed { result.expect("The source code for this test did not successfully parse!") diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 77c3317306..c38e9b73ae 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1568,8 +1568,10 @@ fn unspecialized_lambda_set_sorter(subs: &Subs, uls1: Uls, uls2: Uls) -> std::cm } (FlexAbleVar(..), _) => Greater, (_, FlexAbleVar(..)) => Less, - // For everything else, the order is irrelevant - (_, _) => Less, + // For everything else, sort by the root key + (_, _) => subs + .get_root_key_without_compacting(var1) + .cmp(&subs.get_root_key_without_compacting(var2)), } } ord => ord, @@ -1731,6 +1733,8 @@ fn unify_unspecialized_lambdas( let kept = uls_left.next().unwrap(); merged_uls.push(*kept); } else { + // CASE: disjoint_flex_specializations + // // ... a1 ... // ... b1 ... // => ... a1, b1 ... @@ -1800,20 +1804,47 @@ fn unify_unspecialized_lambdas( let _dropped = uls_left.next().unwrap(); } (_, _) => { - // ... {foo: _} ... - // ... {foo: _} ... - // => ... {foo: _} ... - // - // Unify them, then advance one. - // (the choice is arbitrary, so we choose the left) + if env.subs.equivalent_without_compacting(var_l, var_r) { + // ... a1 ... + // ... b1=a1 ... + // => ... a1 ... + // + // Keep the one on the left, drop the one on the right. Then progress + // 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); - if !outcome.mismatches.is_empty() { - return Err(outcome); + debug_assert!(uls_right + .peek() + .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(); } } } diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 95162b1a44..6cff04153d 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -14,7 +14,6 @@ normal = ["confy"] [features] default = [] -with_sound = ["rodio"] [dependencies] roc_ast = { path = "../ast" } @@ -57,7 +56,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [ ], default-features = false } serde = { version = "1.0.144", features = ["derive"] } nonempty = "0.8.0" -rodio = { version = "0.15.0", optional = true } # to play sounds threadpool = "1.8.1" fs_extra.workspace = true diff --git a/crates/editor/src/editor/mod.rs b/crates/editor/src/editor/mod.rs index 23d273400d..5012aa0b4d 100644 --- a/crates/editor/src/editor/mod.rs +++ b/crates/editor/src/editor/mod.rs @@ -8,7 +8,5 @@ mod mvc; mod render_ast; mod render_debug; mod resources; -#[cfg(feature = "with_sound")] -mod sound; mod theme; mod util; diff --git a/crates/editor/src/editor/mvc/ed_update.rs b/crates/editor/src/editor/mvc/ed_update.rs index 507fd98358..89816f142f 100644 --- a/crates/editor/src/editor/mvc/ed_update.rs +++ b/crates/editor/src/editor/mvc/ed_update.rs @@ -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_string; 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::lines::MoveCaretFun; 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.dirty = true; } - F12 => { - #[cfg(feature = "with_sound")] - _sound_thread_pool.execute(move || { - play_sound("./editor/src/editor/resources/sounds/bell_sound.mp3"); - }); - } _ => (), } diff --git a/crates/editor/src/editor/sound.rs b/crates/editor/src/editor/sound.rs deleted file mode 100644 index 76a9994de6..0000000000 --- a/crates/editor/src/editor/sound.rs +++ /dev/null @@ -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 - ); - } - } -} diff --git a/default.nix b/default.nix index 31c84f5bfe..0e4da8457d 100644 --- a/default.nix +++ b/default.nix @@ -65,7 +65,6 @@ rustPlatform.buildRustPackage { cargo makeWrapper # necessary for postBuild wrapProgram ] ++ lib.optionals pkgs.stdenv.isLinux [ - alsa-lib valgrind vulkan-headers vulkan-loader diff --git a/examples/gui/breakout/platform/Cargo.toml b/examples/gui/breakout/platform/Cargo.toml index 5b612df452..10fb6f4b7a 100644 --- a/examples/gui/breakout/platform/Cargo.toml +++ b/examples/gui/breakout/platform/Cargo.toml @@ -39,7 +39,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [ serde = { version = "1.0.130", features = ["derive"] } nonempty = "0.7.0" fs_extra = "1.2.0" -rodio = { version = "0.14.0", optional = true } # to play sounds threadpool = "1.8.1" [package.metadata.cargo-udeps.ignore] @@ -50,7 +49,6 @@ normal = ["confy"] [features] default = [] -with_sound = ["rodio"] [dependencies.bytemuck] version = "1.7.2" diff --git a/examples/gui/platform/Cargo.toml b/examples/gui/platform/Cargo.toml index ec1ec5fad1..76357974f3 100644 --- a/examples/gui/platform/Cargo.toml +++ b/examples/gui/platform/Cargo.toml @@ -40,7 +40,6 @@ confy = { git = 'https://github.com/rust-cli/confy', features = [ serde = { version = "1.0.130", features = ["derive"] } nonempty = "0.7.0" fs_extra = "1.2.0" -rodio = { version = "0.14.0", optional = true } # to play sounds threadpool = "1.8.1" [package.metadata.cargo-udeps.ignore] @@ -51,7 +50,6 @@ normal = ["confy"] [features] default = [] -with_sound = ["rodio"] [dependencies.bytemuck] version = "1.7.2" diff --git a/examples/parser/parse-movies-csv.roc b/examples/parser/parse-movies-csv.roc deleted file mode 100644 index 46dd00b964..0000000000 --- a/examples/parser/parse-movies-csv.roc +++ /dev/null @@ -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 " diff --git a/examples/parser/platform/host.zig b/examples/parser/platform/host.zig deleted file mode 100755 index b84d428034..0000000000 --- a/examples/parser/platform/host.zig +++ /dev/null @@ -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 }); - } -} diff --git a/examples/parser/platform/main.roc b/examples/parser/platform/main.roc deleted file mode 100644 index 175f7070d5..0000000000 --- a/examples/parser/platform/main.roc +++ /dev/null @@ -1,9 +0,0 @@ -platform "hello-world" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [mainForHost] - -mainForHost : Str -mainForHost = main diff --git a/examples/static-site-gen/README.md b/examples/static-site-gen/README.md index af96a62bfa..f8dd72ab56 100644 --- a/examples/static-site-gen/README.md +++ b/examples/static-site-gen/README.md @@ -9,7 +9,7 @@ To run, `cd` into this directory and run this in your terminal: If `roc` is on your PATH: ```bash -roc run static-site.roc input/ output/ +roc run static-site.roc -- input/ output/ ``` If not, and you're building Roc from source: diff --git a/examples/static-site-gen/input/apple.md b/examples/static-site-gen/input/apple.md index f4e28c985d..f5102a8eb7 100644 --- a/examples/static-site-gen/input/apple.md +++ b/examples/static-site-gen/input/apple.md @@ -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, Herculei undae calcata inmeriti quercus ignes parabant iam. - digitize(undoDhcp(card_record, cad_flash_dot)); - 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); - } +### Example Table -Foret inpendere, haec ipse ossa, dolentes das Caystro miscuit iunctoque -spoliantis illae, ex! Bello istis nunc Aegides? Animo caelestia melior, -furoribus optat maior invecta quid harenis [est](http://example.org) sollemnia modo -Phineu. Suarum pectora. Relinquam in labore Medusae sororem Herculis [simillima -corpora](http://example.org) plus regi ignibus, totum domus! +| Tables | Are | Cool | +| :------------ | :-----------: | ----: | +| col 3 is | right-aligned | $1600 | +| col 2 is | centered | $12 | +| 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], + ] +``` \ No newline at end of file diff --git a/examples/static-site-gen/output/style.css b/examples/static-site-gen/output/style.css index cdc5bf0e97..fe77ec0472 100644 --- a/examples/static-site-gen/output/style.css +++ b/examples/static-site-gen/output/style.css @@ -39,7 +39,102 @@ color: #444; } .article pre { - background-color: #222; - color: yellow; + background-color: rgb(241, 241, 241); + color: rgb(27, 27, 27); 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; +} \ No newline at end of file diff --git a/examples/static-site-gen/platform/Cargo.lock b/examples/static-site-gen/platform/Cargo.lock index 0e81055120..2aa6bd2bc5 100644 --- a/examples/static-site-gen/platform/Cargo.lock +++ b/examples/static-site-gen/platform/Cargo.lock @@ -2,27 +2,194 @@ # It is not intended for manual editing. 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]] name = "arrayvec" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "host" version = "0.0.1" dependencies = [ "libc", "pulldown-cmark", + "roc_parse", + "roc_region", "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]] name = "libc" version = "0.2.132" @@ -35,6 +202,39 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "pulldown-cmark" version = "0.9.2" @@ -46,6 +246,88 @@ dependencies = [ "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]] name = "roc_std" version = "0.0.1" @@ -54,12 +336,74 @@ dependencies = [ "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]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "unicase" version = "2.6.0" @@ -69,8 +413,38 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" 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", +] diff --git a/examples/static-site-gen/platform/Cargo.toml b/examples/static-site-gen/platform/Cargo.toml index 9b5becea49..64a9ab497d 100644 --- a/examples/static-site-gen/platform/Cargo.toml +++ b/examples/static-site-gen/platform/Cargo.toml @@ -18,7 +18,11 @@ path = "src/main.rs" [dependencies] roc_std = { path = "../../../crates/roc_std" } +roc_region = { path = "../../../crates/compiler/region" } +roc_parse = { path = "../../../crates/compiler/parse" } libc = "0.2" +html-escape = "0.2" + # Default features include building a binary that we don't need pulldown-cmark = { version = "0.9.2", default-features = false } diff --git a/examples/static-site-gen/platform/src/highlight.rs b/examples/static-site-gen/platform/src/highlight.rs new file mode 100644 index 0000000000..776918e362 --- /dev/null +++ b/examples/static-site-gen/platform/src/highlight.rs @@ -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> = roc_parse::highlight::highlight(code); + let mut buf: Vec = 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!("
{}
", buf.join("")) +} + +fn push_html_span(mut buf: Vec, curr: &str, class: &str) -> Vec { + // html escape strings from source code + let escaped = html_escape::encode_text(curr); + + buf.push(format!("{}", class, escaped)); + + buf +} + +fn push_html(mut buf: Vec, curr: &str) -> Vec { + // html escape strings from source code + let escaped = html_escape::encode_text(curr); + + buf.push(format!("{}", escaped)); + + buf +} diff --git a/examples/static-site-gen/platform/src/lib.rs b/examples/static-site-gen/platform/src/lib.rs index 7db426008e..28f0070eba 100644 --- a/examples/static-site-gen/platform/src/lib.rs +++ b/examples/static-site-gen/platform/src/lib.rs @@ -8,6 +8,8 @@ use std::fs; use std::os::raw::c_char; use std::path::{Path, PathBuf}; +mod highlight; + extern "C" { #[link_name = "roc__transformFileContentForHost_1_exposed"] 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); 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!("
{}
", &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_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() } + +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 + } + } + } +} diff --git a/flake.nix b/flake.nix index f7a59ceb5c..8f201c739e 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,6 @@ xorg.libXrandr xorg.libXi xorg.libxcb - alsa-lib ]; darwinInputs = with pkgs; diff --git a/www/generate_tutorial/src/input/tutorial.md b/www/generate_tutorial/src/input/tutorial.md index 4023012571..a41e76b72b 100644 --- a/www/generate_tutorial/src/input/tutorial.md +++ b/www/generate_tutorial/src/input/tutorial.md @@ -629,7 +629,7 @@ We can also give `List.map` a named function, instead of an anonymous one: List.map [1, 2, 3] Num.isOdd -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]`. @@ -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} -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`:
List.any [1, 2, 3] Num.isOdd
 # returns `Bool.true` because 1 and 3 are odd
@@ -704,7 +704,7 @@ There are several functions that work like `List.map`, they walk through each el
 # returns `Bool.false` because none of these is negative
 
-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:
List.all [1, 2, 3] Num.isOdd
 # returns `Bool.false` because 2 is not odd
@@ -722,7 +722,7 @@ You can also drop elements from a list. One way is `List.dropAt` - for example:
 # drops the element at offset 1 ("Lee") and returns ["Sam", "Ari"]
 
-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`.
List.keepIf [1, 2, 3, 4, 5] Num.isEven
 # returns [2, 4]
@@ -1279,7 +1279,7 @@ You can write automated tests for your Roc code like so:
 expect pluralize "cactus" "cacti" 2 == "2 cacti"
 
-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: diff --git a/www/public/site.css b/www/public/site.css index 393bda01c4..4c4d32dcf9 100644 --- a/www/public/site.css +++ b/www/public/site.css @@ -322,6 +322,10 @@ p, aside, li, footer { h1 code, h2 code, h3 code, h4 code, h5 code { font-size: inherit; } + + code { + white-space: normal; + } #tutorial-toc-toggle-label, #close-tutorial-toc {