diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..909c46012a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "07:00" + timezone: "Europe/Brussels" diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index dbc7005e80..17ab7a890e 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -20,14 +20,14 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: "trunk" + ref: "main" clean: "true" - name: Earthly version run: earthly --version - - name: on trunk; prepare a self-contained benchmark folder - run: ./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=trunk +prep-bench-folder + - name: on main; prepare a self-contained benchmark folder + run: ./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=main +prep-bench-folder - uses: actions/checkout@v2 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 845a8e9425..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,28 +0,0 @@ -on: [pull_request] - -name: CI - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - RUST_BACKTRACE: 1 - -jobs: - build-fmt-clippy-test: - name: fmt, clippy, test --release - runs-on: [self-hosted, i5-4690K] - timeout-minutes: 90 - env: - FORCE_COLOR: 1 - steps: - - uses: actions/checkout@v2 - with: - clean: "true" - - - name: Earthly version - run: earthly --version - - - name: install dependencies, build, run zig tests, rustfmt, clippy, cargo test --release - run: ./ci/safe-earthly.sh +test-all diff --git a/.github/workflows/nightly_linux_x86_64.yml b/.github/workflows/nightly_linux_x86_64.yml index 679f89f6b8..f9389f65ba 100644 --- a/.github/workflows/nightly_linux_x86_64.yml +++ b/.github/workflows/nightly_linux_x86_64.yml @@ -22,7 +22,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions with: - upload_url: https://uploads.github.com/repos/rtfeldman/roc/releases/51880579/assets{?name,label} + upload_url: https://uploads.github.com/repos/roc-lang/roc/releases/51880579/assets{?name,label} release_id: 51880579 asset_path: ./roc_linux_x86_64.tar.gz asset_name: roc_nightly-linux_x86_64-$$.tar.gz # $$ inserts 6 char commit hash and date (YYYY-MM-DD) diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index 79c8ba6ebc..56a6888df9 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -15,23 +15,26 @@ jobs: run: zig version - name: llvm version run: llc --version | grep LLVM - - name: run tests + + - name: run tests run: cargo test --locked --release + - name: write version to file run: ./ci/write_version.sh + - name: build nightly release run: cargo build --locked --release + - name: package release run: ./ci/package_release.sh roc_darwin_apple_silicon.tar.gz - - name: Create pre-release with test_archive.tar.gz - uses: Anton-4/deploy-nightly@1609d8dfe211b078674801113ab7a2ec2938b2a9 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions - with: - upload_url: https://uploads.github.com/repos/rtfeldman/roc/releases/51880579/assets{?name,label} - release_id: 51880579 - asset_path: ./roc_darwin_apple_silicon.tar.gz - asset_name: roc_nightly-macos_apple_silicon-$$.tar.gz # $$ inserts 6 char commit hash and date (YYYY-MM-DD) - asset_content_type: application/gzip - max_releases: 3 + - name: print short commit SHA + run: git rev-parse --short "$GITHUB_SHA" + - name: print date + run: date + - name: Upload artifact Actually uploading to github releases has to be done manually + uses: actions/upload-artifact@v3 + with: + name: roc_nightly-macos_apple_silicon.tar.gz + path: roc_darwin_apple_silicon.tar.gz + retention-days: 4 diff --git a/.github/workflows/nightly_macos_x86_64.yml b/.github/workflows/nightly_macos_x86_64.yml index 577cf1d7e2..1f760c5226 100644 --- a/.github/workflows/nightly_macos_x86_64.yml +++ b/.github/workflows/nightly_macos_x86_64.yml @@ -44,7 +44,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions with: - upload_url: https://uploads.github.com/repos/rtfeldman/roc/releases/51880579/assets{?name,label} + upload_url: https://uploads.github.com/repos/roc-lang/roc/releases/51880579/assets{?name,label} release_id: 51880579 asset_path: ./roc_darwin_x86_64.tar.gz asset_name: roc_nightly-macos_x86_64-$$.tar.gz # $$ inserts 6 char commit hash and date (YYYY-MM-DD) diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 0a1eee7af9..cb1bf5bfab 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -12,7 +12,7 @@ env: jobs: spell-check: name: spell check - runs-on: [self-hosted, linux] + runs-on: [self-hosted, i7-6700K] timeout-minutes: 10 env: FORCE_COLOR: 1 diff --git a/.github/workflows/ubuntu_x86_64.yml b/.github/workflows/ubuntu_x86_64.yml new file mode 100644 index 0000000000..d03ff37597 --- /dev/null +++ b/.github/workflows/ubuntu_x86_64.yml @@ -0,0 +1,51 @@ +on: [pull_request] + +name: CI + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + RUST_BACKTRACE: 1 + +jobs: + test-zig-rust-wasm: + name: test zig, rust, wasm + runs-on: [self-hosted, i5-4690K] + timeout-minutes: 90 + env: + RUSTC_WRAPPER: /home/small-ci-user/.cargo/bin/sccache + steps: + - uses: actions/checkout@v2 + with: + clean: "true" + + - name: zig fmt check, zig tests + run: cd crates/compiler/builtins/bitcode && ./run-tests.sh + + - name: zig wasm tests + run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh + + - name: regular rust tests + run: cargo test --locked --release --features with_sound serde --workspace && sccache --show-stats + + - name: test the dev backend # these tests require an explicit feature flag + run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats + + - name: test gen-wasm single threaded # gen-wasm has some multithreading problems to do with the wasmer runtime + run: cargo test --locked --release --package test_gen --no-default-features --features gen-wasm -- --test-threads=1 && sccache --show-stats + + - name: run `roc test` on Str builtins + run: cargo run --locked --release -- test crates/compiler/builtins/roc/Str.roc && sccache --show-stats + + #TODO pass --locked into the script here as well, this avoids rebuilding dependencies unnecessarily + - name: wasm repl test + run: crates/repl_test/test_wasm.sh && sccache --show-stats + + #TODO i386 (32-bit linux) cli tests + + #TODO verify-no-git-changes + + - name: test website build script + run: REPL_DEBUG=1 bash www/build.sh diff --git a/.github/workflows/www.yml b/.github/workflows/www.yml index d02f905723..73e58c7501 100644 --- a/.github/workflows/www.yml +++ b/.github/workflows/www.yml @@ -1,10 +1,10 @@ name: deploy www.roc-lang.org -# Whenever a commit lands on trunk, deploy the site +# Whenever a commit lands on `main`, deploy the site on: push: branches: - - deploy-www # TODO change to trunk + - deploy-www jobs: deploy: diff --git a/.reuse/dep5 b/.reuse/dep5 index f1dd1329f6..eb10621a4f 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,7 +1,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Roc Upstream-Contact: Richard Feldman -Source: https://github.com/rtfeldman/roc +Source: https://github.com/roc-lang/roc Files: * Copyright: © The Roc Contributors diff --git a/AUTHORS b/AUTHORS index 1e4d2b8be0..a047739fc0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -89,3 +89,10 @@ Christoph Rüßler Ralf Engbers Mostly Void <7rat13@gmail.com> Luis F. Gutierrez +Oskar Hahn +Ross Smyth +David A. Kunz +Paul Young <84700+paulyoung@users.noreply.github.com> +Rod +Marko Vujanic +kilianv diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index ced14f0806..a0cca336b6 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -6,6 +6,9 @@ Installation should be a smooth process, let us now if anything does not work pe We highly recommend Using [nix](https://nixos.org/download.html) to quickly install all dependencies necessary to build roc. +> See issue [#3863](https://github.com/roc-lang/roc/issues/3863) if you encounter "version GLIBC_2.34 not found". +> This error can occur if you ran `cargo build` in the same folder without nix. + ### On Linux x86_64/aarch64 or MacOS aarch64/arm64/x86_64 #### Install diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 54dd29a18f..4da8ae30ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,9 +28,9 @@ Earthly may temporarily use a lot of disk space, up to 90 GB. This disk space is ## Contribution Tips - Create an issue if the purpose of a struct/field/type/function/... is not immediately clear from its name or nearby comments. -- You find good first issues [here](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). +- You find good first issues [here](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). - Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation. -- It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review! +- It's a good idea to open a draft pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Click the button "ready for review" when it's ready. - Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security. - All your commits need to be signed to prevent impersonation: 1. If you have a Yubikey, follow [guide 1](https://dev.to/paulmicheli/using-your-yubikey-to-get-started-with-gpg-3h4k), [guide 2](https://dev.to/paulmicheli/using-your-yubikey-for-signed-git-commits-4l73) and skip the steps below. diff --git a/Cargo.lock b/Cargo.lock index 0eed1fd729..7cc066aa8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,9 +330,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" +checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" dependencies = [ "bytemuck_derive", ] @@ -631,11 +631,11 @@ dependencies = [ [[package]] name = "console" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" dependencies = [ - "encode_unicode", + "encode_unicode 0.3.6", "libc", "once_cell", "terminal_size", @@ -950,9 +950,9 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" dependencies = [ "cfg-if 1.0.0", "crossbeam-channel", @@ -1111,6 +1111,18 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.12.3", + "lock_api", + "parking_lot_core 0.9.3", +] + [[package]] name = "diff" version = "0.1.13" @@ -1138,9 +1150,9 @@ dependencies = [ [[package]] name = "dircpy" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4388680a28717a3ff2b6b60824bf62d67076232d9416c34d17a148dade0e6cd3" +checksum = "d16e8f15af1ed7189d2bf43c7ae5d6fe0a840cd3b1e9c7bf7c08a384a5df441f" dependencies = [ "jwalk", "log", @@ -1261,6 +1273,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "endian-type" version = "0.1.2" @@ -1713,9 +1731,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", "bumpalo", @@ -1805,27 +1823,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", - "hashbrown 0.12.2", + "hashbrown 0.12.3", "serde", ] [[package]] name = "indoc" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" [[package]] name = "inkwell" version = "0.1.0" dependencies = [ - "inkwell 0.1.0 (git+https://github.com/rtfeldman/inkwell?branch=master)", + "inkwell 0.1.0 (git+https://github.com/roc-lang/inkwell?branch=master)", ] [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/rtfeldman/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" +source = "git+https://github.com/roc-lang/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" dependencies = [ "either", "inkwell_internals", @@ -1838,7 +1856,7 @@ dependencies = [ [[package]] name = "inkwell_internals" version = "0.5.0" -source = "git+https://github.com/rtfeldman/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" +source = "git+https://github.com/roc-lang/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" dependencies = [ "proc-macro2", "quote", @@ -1853,16 +1871,16 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca" [[package]] name = "insta" -version = "1.15.0" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4126dd76ebfe2561486a1bd6738a33d2029ffb068a99ac446b7f8c77b2e58dbc" +checksum = "57a9aec10c9a062ef0454fd49ebaefa59239f836d1b30891d9cc2289978dd970" dependencies = [ "console", + "linked-hash-map", "once_cell", "serde", - "serde_json", - "serde_yaml", "similar", + "yaml-rust", ] [[package]] @@ -1944,9 +1962,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" dependencies = [ "wasm-bindgen", ] @@ -2421,9 +2439,9 @@ dependencies = [ [[package]] name = "nonempty" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" +checksum = "09f1f8e5676e1a1f2ee8b21f38238e1243c827531c9435624c7bfb305102cee4" [[package]] name = "num-derive" @@ -2556,7 +2574,7 @@ checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", "flate2", - "hashbrown 0.12.2", + "hashbrown 0.12.3", "indexmap", "memchr", ] @@ -2678,9 +2696,9 @@ dependencies = [ [[package]] name = "palette" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9735f7e1e51a3f740bacd5dc2724b61a7806f23597a8736e679f38ee3435d18" +checksum = "8f9cd68f7112581033f157e56c77ac4a5538ec5836a2e39284e65bd7d7275e49" dependencies = [ "approx 0.5.1", "num-traits", @@ -2690,9 +2708,9 @@ dependencies = [ [[package]] name = "palette_derive" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7799c3053ea8a6d8a1193c7ba42f534e7863cf52e378a7f90406f4a645d33bad" +checksum = "05eedf46a8e7c27f74af0c9cfcdb004ceca158cb1b918c6f68f8d7a549b3e427" dependencies = [ "find-crate", "proc-macro2", @@ -2832,20 +2850,19 @@ dependencies = [ [[package]] name = "phf" -version = "0.9.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ "phf_macros", "phf_shared", - "proc-macro-hack", ] [[package]] name = "phf_generator" -version = "0.9.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", "rand", @@ -2853,13 +2870,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.9.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86" +checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" dependencies = [ "phf_generator", "phf_shared", - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -2867,9 +2883,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.9.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" dependencies = [ "siphasher", ] @@ -3014,9 +3030,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -3287,7 +3303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" dependencies = [ "bytecheck", - "hashbrown 0.12.2", + "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", @@ -3488,7 +3504,7 @@ version = "0.0.1" dependencies = [ "bitvec 1.0.1", "bumpalo", - "hashbrown 0.12.2", + "hashbrown 0.12.3", "im", "im-rc", "wyhash", @@ -3853,7 +3869,7 @@ name = "roc_mono" version = "0.0.1" dependencies = [ "bumpalo", - "hashbrown 0.12.2", + "hashbrown 0.12.3", "roc_builtins", "roc_can", "roc_collections", @@ -3879,7 +3895,7 @@ version = "0.0.1" dependencies = [ "bumpalo", "criterion", - "encode_unicode", + "encode_unicode 1.0.0", "indoc", "pretty_assertions", "quickcheck", @@ -4209,7 +4225,7 @@ checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" [[package]] name = "rustyline" version = "9.1.1" -source = "git+https://github.com/rtfeldman/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" +source = "git+https://github.com/roc-lang/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -4232,7 +4248,7 @@ dependencies = [ [[package]] name = "rustyline-derive" version = "0.6.0" -source = "git+https://github.com/rtfeldman/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" +source = "git+https://github.com/roc-lang/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" dependencies = [ "quote", "syn", @@ -4386,10 +4402,11 @@ dependencies = [ [[package]] name = "serial_test" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eec42e7232e5ca56aa59d63af3c7f991fe71ee6a3ddd2d3480834cf3902b007" +checksum = "92761393ee4dc3ff8f4af487bd58f4307c9329bbedea02cac0089ad9c411e153" dependencies = [ + "dashmap", "futures", "lazy_static", "log", @@ -4399,14 +4416,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b95bb2f4f624565e8fe8140c789af7e2082c0e0561b5a82a1b678baa9703dc" +checksum = "4b6f5d1c3087fb119617cff2966fe3808a80e5eb59a8c1601d5994d66f4346a5" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "rustversion", "syn", ] @@ -5193,9 +5209,9 @@ version = "0.0.1" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5203,13 +5219,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -5218,9 +5234,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5230,9 +5246,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5240,9 +5256,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -5253,9 +5269,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "wasm-encoder" @@ -5268,9 +5284,8 @@ dependencies = [ [[package]] name = "wasm3" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7dde97449e99be474a432bbb0b1ab40b8f7ce3e97aa7ac640e9ecd018bbf88" +version = "0.5.0" +source = "git+https://github.com/roc-lang/wasm3-rs?rev=f0f807d1fc0a50d1d68e5799e54ee62c05af00f5#f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" dependencies = [ "cty", "wasm3-sys", @@ -5278,9 +5293,8 @@ dependencies = [ [[package]] name = "wasm3-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4e5d10bf1ffe7753275d4bbae3dc135dd2d2decd90e615accf9fef8bc52bab" +version = "0.5.0" +source = "git+https://github.com/roc-lang/wasm3-rs?rev=f0f807d1fc0a50d1d68e5799e54ee62c05af00f5#f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" dependencies = [ "cc", "cty", diff --git a/Earthfile b/Earthfile index 701cfb0537..5310740526 100644 --- a/Earthfile +++ b/Earthfile @@ -126,7 +126,7 @@ build-nightly-release: COPY --dir .git LICENSE LEGAL_DETAILS ci ./ # version.txt is used by the CLI: roc --version RUN ./ci/write_version.sh - RUN RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound serde --release + RUN RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release RUN ./ci/package_release.sh roc_linux_x86_64.tar.gz SAVE ARTIFACT ./roc_linux_x86_64.tar.gz AS LOCAL roc_linux_x86_64.tar.gz diff --git a/FAQ.md b/FAQ.md index 607358d858..d996888813 100644 --- a/FAQ.md +++ b/FAQ.md @@ -234,7 +234,7 @@ Culturally, to support HKP is to take a side, and to decline to support it is al Given this, language designers have three options: - Have HKP and have Monad in the standard library. Embrace them and build a culture and ecosystem around them. -- Have HKP and don't have Monad in the standard library. An alternate standard lbirary built around monads will inevitably emerge, and both the community and ecosystem will divide themselves along pro-monad and anti-monad lines. +- Have HKP and don't have Monad in the standard library. An alternate standard library built around monads will inevitably emerge, and both the community and ecosystem will divide themselves along pro-monad and anti-monad lines. - Don't have HKP; build a culture and ecosystem around other things. Considering that these are the only three options, I think the best choice for Roc—not only on a technical @@ -458,7 +458,7 @@ The short explanation for why Roc is released under the [Universal Permissive Li - It's one license, unlike "MIT or Apache2, at your choice" (which is how [Rust addressed the problem](https://internals.rust-lang.org/t/rationale-of-apache-dual-licensing/8952/4) of MIT not having patent protections but Apache2 not being GPLv2 compatible) - It's been approved by OSI, FSF, and Oracle's lawyers, so it has been not only vetted by three giants in the world of software licensing, but also three giants with competing interests - and they all approved it. -There's also [a longer explanation](https://github.com/rtfeldman/roc/issues/1199) with more detail about the motivation and thought process, if you're interested. +There's also [a longer explanation](https://github.com/roc-lang/roc/issues/1199) with more detail about the motivation and thought process, if you're interested. ## Why does Roc use both Rust and Zig? @@ -472,3 +472,7 @@ There were a few reasons for this rewrite. 4. Zig has more tools for working in a memory-unsafe environment, such as reporting memory leaks in tests. These have been helpful in finding bugs that are out of scope for safe Rust. The split of Rust for the compiler and Zig for the standard library has worked well so far, and there are no plans to change it. + +## Why is the website so basic? + +We have a very basic website on purpose, it helps set expectations that roc is a work in progress and not ready yet for a first release. diff --git a/README.md b/README.md index 746539d72c..e71111dc40 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Work in progress! -Roc is not ready for an 0.1 release yet, but we have a [Zulip chat](https://roc.zulipchat.com) where you can learn more about the project. +Roc is not ready for a 0.1 release yet, but we have [a guide](https://github.com/roc-lang/roc/tree/main/getting_started) for installing it, [a tutorial](https://github.com/roc-lang/roc/blob/main/TUTORIAL.md) for using it, and [a Zulip chat](https://roc.zulipchat.com) for asking questions. -If you'd like to get involved in contributing to the language, the Zulip chat is also the best place to get help with [good first issues](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). +If you'd like to get involved in contributing to the language, the Zulip chat is also the best place to get help with [good first issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). # Sponsors diff --git a/TUTORIAL.md b/TUTORIAL.md index 703dea10e3..d675de7c45 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -8,7 +8,7 @@ Enjoy! ## Getting started -Learn how to install roc on your machine [here](https://github.com/rtfeldman/roc#getting-started). +Learn how to install roc on your machine [here](https://github.com/roc-lang/roc/tree/main/getting_started#installation). ## Strings and Numbers @@ -115,7 +115,7 @@ Create a new file called `Hello.roc` and put this inside it: ```coffee app "hello" - packages { pf: "examples/interactive/cli-platform" } + packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout] provides [main] to pf @@ -124,8 +124,8 @@ main = Stdout.line "I'm a Roc application!" > **NOTE:** This assumes you've put Hello.roc in the root directory of the Roc > source code. If you'd like to put it somewhere else, you'll need to replace -> `"examples/interactive/cli-platform"` with the path to the -> `examples/interactive/cli-platform` folder in that source code. In the future, +> `"examples/interactive/cli-platform/main.roc"` with the path to the +> `examples/interactive/cli-platform/main.roc` file in that source code. In the future, > Roc will have the tutorial built in, and this aside will no longer be > necessary! @@ -1255,7 +1255,7 @@ Let's take a closer look at the part of `Hello.roc` above `main`: ```coffee app "hello" - packages { pf: "examples/interactive/cli-platform" } + packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout] provides main to pf ``` @@ -1273,14 +1273,14 @@ without running it by running `roc build Hello.roc`. The remaining lines all involve the *platform* this application is built on: ```coffee -packages { pf: "examples/interactive/cli-platform" } +packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout] provides main to pf ``` -The `packages { pf: "examples/interactive/cli-platform" }` part says two things: +The `packages { pf: "examples/interactive/cli-platform/main.roc" }` part says two things: -- We're going to be using a *package* (that is, a collection of modules) called `"examples/interactive/cli-platform"` +- We're going to be using a *package* (that is, a collection of modules) called `"examples/interactive/cli-platform/main.roc"` - We're going to name that package `pf` so we can refer to it more concisely in the future. The `imports [pf.Stdout]` line says that we want to import the `Stdout` module @@ -1300,18 +1300,18 @@ calling a function named `line` which is exposed by a module named When we write `imports [pf.Stdout]`, it specifies that the `Stdout` module comes from the `pf` package. -Since `pf` was the name we chose for the `examples/interactive/cli-platform` -package (when we wrote `packages { pf: "examples/interactive/cli-platform" }`), +Since `pf` was the name we chose for the `examples/interactive/cli-platform/main.roc` +package (when we wrote `packages { pf: "examples/interactive/cli-platform/main.roc" }`), this `imports` line tells the Roc compiler that when we call `Stdout.line`, it should look for that `line` function in the `Stdout` module of the -`examples/interactive/cli-platform` package. +`examples/interactive/cli-platform/main.roc` package. # Building a Command-Line Interface (CLI) ## Tasks Tasks are technically not part of the Roc language, but they're very common in -platforms. Let's use the CLI platform in `examples/interactive/cli-platform` as an example! +platforms. Let's use the CLI platform in `examples/interactive/cli-platform/main.roc` as an example! In the CLI platform, we have four operations we can do: @@ -1326,7 +1326,7 @@ First, let's do a basic "Hello World" using the tutorial app. ```coffee app "cli-tutorial" - packages { pf: "examples/interactive/cli-platform" } + packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout] provides [main] to pf @@ -1363,7 +1363,7 @@ Let's change `main` to read a line from `stdin`, and then print it back out agai ```swift app "cli-tutorial" - packages { pf: "examples/interactive/cli-platform" } + packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout, pf.Stdin, pf.Task] provides [main] to pf @@ -1413,7 +1413,7 @@ This works, but we can make it a little nicer to read. Let's change it to the fo ```haskell app "cli-tutorial" - packages { pf: "examples/interactive/cli-platform" } + packages { pf: "examples/interactive/cli-platform/main.roc" } imports [pf.Stdout, pf.Stdin, pf.Task.{ await }] provides [main] to pf diff --git a/ci/bench-runner/src/main.rs b/ci/bench-runner/src/main.rs index 6cab1cf003..bb44247ea9 100644 --- a/ci/bench-runner/src/main.rs +++ b/ci/bench-runner/src/main.rs @@ -12,13 +12,13 @@ use std::{ process::{self, Command, Stdio}, }; -const BENCH_FOLDER_TRUNK: &str = "bench-folder-trunk"; +const BENCH_FOLDER_MAIN: &str = "bench-folder-main"; const BENCH_FOLDER_BRANCH: &str = "bench-folder-branch"; fn main() { let optional_args: OptionalArgs = OptionalArgs::parse(); - if Path::new(BENCH_FOLDER_TRUNK).exists() && Path::new(BENCH_FOLDER_BRANCH).exists() { + if Path::new(BENCH_FOLDER_MAIN).exists() && Path::new(BENCH_FOLDER_BRANCH).exists() { delete_old_bench_results(); if optional_args.check_executables_changed { @@ -26,7 +26,7 @@ fn main() { std::env::set_var("BENCH_DRY_RUN", "1"); - do_benchmark("trunk"); + do_benchmark("main"); do_benchmark("branch"); std::env::set_var("BENCH_DRY_RUN", "0"); @@ -49,9 +49,9 @@ fn main() { } } else { eprintln!( - r#"I can't find bench-folder-trunk and bench-folder-branch from the current directory. + r#"I can't find bench-folder-main and bench-folder-branch from the current directory. I should be executed from the repo root. - Use `./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=trunk +prep-bench-folder` to generate bench-folder-trunk. + Use `./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=main +prep-bench-folder` to generate bench-folder-main. Use `./ci/safe-earthly.sh +prep-bench-folder` to generate bench-folder-branch."# ); @@ -77,7 +77,7 @@ fn finish(all_regressed_benches: HashSet, nr_repeat_benchmarks: usize) { // returns all benchmarks that have regressed fn do_all_benches(nr_repeat_benchmarks: usize) -> HashSet { delete_old_bench_results(); - do_benchmark("trunk"); + do_benchmark("main"); let mut all_regressed_benches = do_benchmark("branch"); // if no benches regressed this round, abort early @@ -87,7 +87,7 @@ fn do_all_benches(nr_repeat_benchmarks: usize) -> HashSet { for _ in 1..nr_repeat_benchmarks { delete_old_bench_results(); - do_benchmark("trunk"); + do_benchmark("main"); let regressed_benches = do_benchmark("branch"); // if no benches regressed this round, abort early @@ -229,17 +229,17 @@ fn calc_hashes_for_folder(benches_path_str: &str) -> HashMap { fn check_if_bench_executables_changed() -> bool { let bench_folder_str = "/examples/benchmarks/"; - let trunk_benches_path_str = [BENCH_FOLDER_TRUNK, bench_folder_str].join(""); - let trunk_bench_hashes = calc_hashes_for_folder(&trunk_benches_path_str); + let main_benches_path_str = [BENCH_FOLDER_MAIN, bench_folder_str].join(""); + let main_bench_hashes = calc_hashes_for_folder(&main_benches_path_str); let branch_benches_path_str = [BENCH_FOLDER_BRANCH, bench_folder_str].join(""); let branch_bench_hashes = calc_hashes_for_folder(&branch_benches_path_str); - if trunk_bench_hashes.keys().len() == branch_bench_hashes.keys().len() { - for key in trunk_bench_hashes.keys() { - if let Some(trunk_hash_val) = trunk_bench_hashes.get(key) { + if main_bench_hashes.keys().len() == branch_bench_hashes.keys().len() { + for key in main_bench_hashes.keys() { + if let Some(main_hash_val) = main_bench_hashes.get(key) { if let Some(branch_hash_val) = branch_bench_hashes.get(key) { - if !trunk_hash_val.eq(branch_hash_val) { + if !main_hash_val.eq(branch_hash_val) { return true; } } else { diff --git a/ci/enable-lld.sh b/ci/enable-lld.sh index a70301f8dc..04377329b6 100755 --- a/ci/enable-lld.sh +++ b/ci/enable-lld.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash mkdir -p $HOME/.cargo echo -e "[build]\nrustflags = [\"-C\", \"link-arg=-fuse-ld=lld\", \"-C\", \"target-cpu=native\"]" > $HOME/.cargo/config diff --git a/crates/ast/Cargo.toml b/crates/ast/Cargo.toml index 073b119d06..bc719845dd 100644 --- a/crates/ast/Cargo.toml +++ b/crates/ast/Cargo.toml @@ -29,7 +29,7 @@ ven_graph = { path = "../vendor/pathfinding" } libc = "0.2.106" [dev-dependencies] -indoc = "1.0.3" +indoc = "1.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["memoryapi"]} diff --git a/crates/ast/src/constrain.rs b/crates/ast/src/constrain.rs index 5787c4726c..4bd81416f3 100644 --- a/crates/ast/src/constrain.rs +++ b/crates/ast/src/constrain.rs @@ -2614,7 +2614,7 @@ pub mod test_constrain { #[test] fn inference_var_tag_union_ext() { // TODO: we should really be inferring [Blue, Orange]a -> [Lavender, Peach]a here. - // See https://github.com/rtfeldman/roc/issues/2053 + // See https://github.com/roc-lang/roc/issues/2053 infer_eq( indoc!( r#" diff --git a/crates/ast/src/lang/core/def/def.rs b/crates/ast/src/lang/core/def/def.rs index b07842b45f..d3005ad1f8 100644 --- a/crates/ast/src/lang/core/def/def.rs +++ b/crates/ast/src/lang/core/def/def.rs @@ -943,7 +943,7 @@ pub fn canonicalize_defs<'a>( ) } -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum Declaration { diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 7a0e21a809..455f49dd88 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -3,7 +3,7 @@ name = "roc_cli" version = "0.1.0" authors = ["The Roc Contributors"] license = "UPL-1.0" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" edition = "2021" description = "A CLI for Roc" default-run = "roc" @@ -88,8 +88,8 @@ wasmer = { version = "2.2.1", optional = true, default-features = false, feature wasmer-wasi = "2.2.1" pretty_assertions = "1.0.0" roc_test_utils = { path = "../test_utils" } -indoc = "1.0.3" -serial_test = "0.8.0" +indoc = "1.0.7" +serial_test = "0.9.0" criterion = { git = "https://github.com/Anton-4/criterion.rs"} cli_utils = { path = "../cli_utils" } strum = "0.24.0" diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index 11129068b0..e52271dd21 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -5,7 +5,10 @@ use roc_build::{ }; use roc_builtins::bitcode; use roc_collections::VecMap; -use roc_load::{EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadingProblem, Threading}; +use roc_load::{ + EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule, + LoadingProblem, Threading, +}; use roc_module::symbol::{Interns, ModuleId}; use roc_mono::ir::OptLevel; use roc_reporting::report::RenderTarget; @@ -35,6 +38,23 @@ pub struct BuiltFile { pub interns: Interns, } +pub enum BuildOrdering { + /// Run up through typechecking first; continue building iff that is successful. + BuildIfChecks, + /// Always build the Roc binary, even if there are type errors. + AlwaysBuild, +} + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum BuildFileError<'a> { + LoadingProblem(LoadingProblem<'a>), + ErrorModule { + module: LoadedModule, + total_time: Duration, + }, +} + #[allow(clippy::too_many_arguments)] pub fn build_file<'a>( arena: &'a Bump, @@ -46,29 +66,46 @@ pub fn build_file<'a>( link_type: LinkType, linking_strategy: LinkingStrategy, precompiled: bool, - target_valgrind: bool, threading: Threading, wasm_dev_stack_bytes: Option, -) -> Result> { + order: BuildOrdering, +) -> Result> { let compilation_start = Instant::now(); let target_info = TargetInfo::from(target); // Step 1: compile the app and generate the .o file let subs_by_module = Default::default(); + let exec_mode = match order { + BuildOrdering::BuildIfChecks => ExecutionMode::ExecutableIfCheck, + BuildOrdering::AlwaysBuild => ExecutionMode::Executable, + }; + let load_config = LoadConfig { target_info, // TODO: expose this from CLI? render: RenderTarget::ColorTerminal, threading, - exec_mode: ExecutionMode::Executable, + exec_mode, }; - let loaded = roc_load::load_and_monomorphize( + let load_result = roc_load::load_and_monomorphize( arena, app_module_path.clone(), subs_by_module, load_config, - )?; + ); + let loaded = match load_result { + Ok(loaded) => loaded, + Err(LoadMonomorphizedError::LoadingProblem(problem)) => { + return Err(BuildFileError::LoadingProblem(problem)) + } + Err(LoadMonomorphizedError::ErrorModule(module)) => { + return Err(BuildFileError::ErrorModule { + module, + total_time: compilation_start.elapsed(), + }) + } + }; use target_lexicon::Architecture; let emit_wasm = matches!(target.architecture, Architecture::Wasm32); @@ -152,7 +189,6 @@ pub fn build_file<'a>( target, exposed_values, exposed_closure_types, - target_valgrind, ); // TODO try to move as much of this linking as possible to the precompiled @@ -161,9 +197,7 @@ pub fn build_file<'a>( .prefix("roc_app") .suffix(&format!(".{}", app_extension)) .tempfile() - .map_err(|err| { - todo!("TODO Gracefully handle tempfile creation error {:?}", err); - })?; + .map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?; let app_o_file = app_o_file.path(); let buf = &mut String::with_capacity(1024); @@ -329,12 +363,12 @@ pub fn build_file<'a>( link_type ) .map_err(|_| { - todo!("gracefully handle `ld` failing to spawn."); + todo!("gracefully handle `ld` failing to spawn.") })?; - let exit_status = child.wait().map_err(|_| { - todo!("gracefully handle error after `ld` spawned"); - })?; + let exit_status = child + .wait() + .map_err(|_| todo!("gracefully handle error after `ld` spawned"))?; if exit_status.success() { problems @@ -377,7 +411,6 @@ fn spawn_rebuild_thread( target: &Triple, exported_symbols: Vec, exported_closure_types: Vec, - target_valgrind: bool, ) -> std::thread::JoinHandle { let thread_local_target = target.clone(); std::thread::spawn(move || { @@ -395,7 +428,6 @@ fn spawn_rebuild_thread( &thread_local_target, host_input_path.as_path(), None, - target_valgrind, ); preprocess_host_wasm32(host_dest.as_path(), &preprocessed_host_path); @@ -408,7 +440,6 @@ fn spawn_rebuild_thread( preprocessed_host_path.as_path(), exported_symbols, exported_closure_types, - target_valgrind, ); } LinkingStrategy::Legacy => { @@ -417,7 +448,6 @@ fn spawn_rebuild_thread( &thread_local_target, host_input_path.as_path(), None, - target_valgrind, ); } } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index c3317cfc04..ab7a7e167d 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -31,10 +31,13 @@ pub mod build; mod format; pub use format::format; +use crate::build::{BuildFileError, BuildOrdering}; + const DEFAULT_ROC_FILENAME: &str = "main.roc"; pub const CMD_BUILD: &str = "build"; pub const CMD_RUN: &str = "run"; +pub const CMD_DEV: &str = "dev"; pub const CMD_REPL: &str = "repl"; pub const CMD_EDIT: &str = "edit"; pub const CMD_DOCS: &str = "docs"; @@ -55,7 +58,6 @@ pub const FLAG_TARGET: &str = "target"; pub const FLAG_TIME: &str = "time"; pub const FLAG_LINKER: &str = "linker"; pub const FLAG_PRECOMPILED: &str = "precompiled-host"; -pub const FLAG_VALGRIND: &str = "valgrind"; pub const FLAG_CHECK: &str = "check"; pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb"; pub const ROC_FILE: &str = "ROC_FILE"; @@ -94,11 +96,6 @@ pub fn build_app<'a>() -> Command<'a> { .help("Store LLVM debug information in the generated program.") .required(false); - let flag_valgrind = Arg::new(FLAG_VALGRIND) - .long(FLAG_VALGRIND) - .help("Some assembly instructions are not supported by valgrind, this flag prevents those from being output when building the host.") - .required(false); - let flag_time = Arg::new(FLAG_TIME) .long(FLAG_TIME) .help("Prints detailed compilation time information.") @@ -150,7 +147,6 @@ pub fn build_app<'a>() -> Command<'a> { .arg(flag_time.clone()) .arg(flag_linker.clone()) .arg(flag_precompiled.clone()) - .arg(flag_valgrind.clone()) .arg(flag_wasm_stack_size_kb.clone()) .arg( Arg::new(FLAG_TARGET) @@ -190,7 +186,6 @@ pub fn build_app<'a>() -> Command<'a> { .arg(flag_time.clone()) .arg(flag_linker.clone()) .arg(flag_precompiled.clone()) - .arg(flag_valgrind.clone()) .arg( Arg::new(ROC_FILE) .help("The .roc file for the main module") @@ -213,7 +208,19 @@ pub fn build_app<'a>() -> Command<'a> { .arg(flag_time.clone()) .arg(flag_linker.clone()) .arg(flag_precompiled.clone()) - .arg(flag_valgrind.clone()) + .arg(roc_file_to_run.clone()) + .arg(args_for_app.clone()) + ) + .subcommand(Command::new(CMD_DEV) + .about("`check` a .roc file, and then run it if there were no errors.") + .arg(flag_optimize.clone()) + .arg(flag_max_threads.clone()) + .arg(flag_opt_size.clone()) + .arg(flag_dev.clone()) + .arg(flag_debug.clone()) + .arg(flag_time.clone()) + .arg(flag_linker.clone()) + .arg(flag_precompiled.clone()) .arg(roc_file_to_run.clone()) .arg(args_for_app.clone()) ) @@ -280,7 +287,6 @@ pub fn build_app<'a>() -> Command<'a> { .arg(flag_time) .arg(flag_linker) .arg(flag_precompiled) - .arg(flag_valgrind) .arg(roc_file_to_run.required(false)) .arg(args_for_app); @@ -519,7 +525,10 @@ pub fn build( .and_then(|s| s.parse::().ok()) .map(|x| x * 1024); - let target_valgrind = matches.is_present(FLAG_VALGRIND); + let build_ordering = match config { + BuildAndRunIfNoErrors => BuildOrdering::BuildIfChecks, + _ => BuildOrdering::AlwaysBuild, + }; let res_binary_path = build_file( &arena, &triple, @@ -530,9 +539,9 @@ pub fn build( link_type, linking_strategy, precompiled, - target_valgrind, threading, wasm_dev_stack_bytes, + build_ordering, ); match res_binary_path { @@ -633,55 +642,13 @@ pub fn build( x } BuildAndRunIfNoErrors => { - if problems.errors == 0 { - if problems.warnings > 0 { - println!( - "\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m", - problems.warnings, - if problems.warnings == 1 { - "warning" - } else { - "warnings" - }, - total_time.as_millis(), - "─".repeat(80) - ); - } - - let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default(); - - let mut bytes = std::fs::read(&binary_path).unwrap(); - - let x = roc_run( - arena, - opt_level, - triple, - args, - &mut bytes, - expectations, - interns, - ); - std::mem::forget(bytes); - x - } else { - let mut output = format!( - "\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run", - if problems.errors == 0 { - 32 // green - } else { - 33 // yellow - }, - problems.errors, - if problems.errors == 1 { - "error" - } else { - "errors" - }, - if problems.warnings == 0 { - 32 // green - } else { - 33 // yellow - }, + debug_assert!( + problems.errors == 0, + "if there are errors, they should have been returned as an error variant" + ); + if problems.warnings > 0 { + println!( + "\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m", problems.warnings, if problems.warnings == 1 { "warning" @@ -689,22 +656,74 @@ pub fn build( "warnings" }, total_time.as_millis(), + "─".repeat(80) ); - // If you're running "main.roc" then you can just do `roc run` - // to re-run the program. - if filename != DEFAULT_ROC_FILENAME { - output.push(' '); - output.push_str(&filename.to_string_lossy()); - } - - println!("{}\x1B[39m", output); - - Ok(problems.exit_code()) } + + let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default(); + + let mut bytes = std::fs::read(&binary_path).unwrap(); + + let x = roc_run( + arena, + opt_level, + triple, + args, + &mut bytes, + expectations, + interns, + ); + std::mem::forget(bytes); + x } } } - Err(LoadingProblem::FormattedReport(report)) => { + Err(BuildFileError::ErrorModule { + mut module, + total_time, + }) => { + debug_assert!(module.total_problems() > 0); + + let problems = roc_build::program::report_problems_typechecked(&mut module); + + let mut output = format!( + "\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run", + if problems.errors == 0 { + 32 // green + } else { + 33 // yellow + }, + problems.errors, + if problems.errors == 1 { + "error" + } else { + "errors" + }, + if problems.warnings == 0 { + 32 // green + } else { + 33 // yellow + }, + problems.warnings, + if problems.warnings == 1 { + "warning" + } else { + "warnings" + }, + total_time.as_millis(), + ); + // If you're running "main.roc" then you can just do `roc run` + // to re-run the program. + if filename != DEFAULT_ROC_FILENAME { + output.push(' '); + output.push_str(&filename.to_string_lossy()); + } + + println!("{}\x1B[39m", output); + + Ok(problems.exit_code()) + } + Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => { print!("{}", report); Ok(1) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index d09bd189b4..ac411d1d21 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,9 +1,10 @@ use roc_build::link::LinkType; use roc_cli::build::check_file; use roc_cli::{ - build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DOCS, - CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, DIRECTORY_OR_FILES, - FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE, ROC_FILE, + build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DEV, + CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION, + DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE, + ROC_FILE, }; use roc_docs::generate_docs_html; use roc_error_macros::user_error; @@ -64,6 +65,20 @@ fn main() -> io::Result<()> { Ok(1) } } + Some((CMD_DEV, matches)) => { + if matches.is_present(ROC_FILE) { + build( + matches, + BuildConfig::BuildAndRunIfNoErrors, + Triple::host(), + LinkType::Executable, + ) + } else { + eprintln!("What .roc file do you want to build? Specify it at the end of the `roc run` command."); + + Ok(1) + } + } Some((CMD_GLUE, matches)) => { let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap()); let output_path = Path::new(matches.value_of_os(GLUE_FILE).unwrap()); diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 13bfcd16fa..32d8180e0d 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -25,7 +25,6 @@ mod cli_run { use strum_macros::EnumIter; const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE); - const VALGRIND_FLAG: &str = concatcp!("--", roc_cli::FLAG_VALGRIND); const LINKER_FLAG: &str = concatcp!("--", roc_cli::FLAG_LINKER); const CHECK_FLAG: &str = concatcp!("--", roc_cli::FLAG_CHECK); const PRECOMPILED_HOST: &str = concatcp!("--", roc_cli::FLAG_PRECOMPILED, "=true"); @@ -137,14 +136,17 @@ mod cli_run { expected_ending: &str, use_valgrind: bool, ) { + // valgrind does not yet support avx512 instructions, see #1963. + // we can't enable this only when testing with valgrind because of host re-use between tests + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + if is_x86_feature_detected!("avx512f") { + std::env::set_var("NO_AVX512", "1"); + } + for cli_mode in CliMode::iter() { let flags = { let mut vec = flags.to_vec(); - if use_valgrind { - vec.push(VALGRIND_FLAG); - } - vec.push("--max-threads=1"); vec.into_iter() @@ -295,13 +297,10 @@ mod cli_run { let file_name = example_file(dir_name, example.filename); match example.executable_filename { - "form" => { - // test is skipped until we upgrate to zig 0.9 / llvm 13 - eprintln!("WARNING: skipping testing example {} because the test is broken right now!", example.filename); - return; - } - "hello-gui" | "breakout" => { - // Since these require opening a window, we do `roc build` on them but don't run them. + "form" | "hello-gui" | "breakout" | "ruby" => { + // Since these require things the build system often doesn't have + // (e.g. GUIs open a window, Ruby needs ruby installed, WASM needs a browser) + // we do `roc build` on them but don't run them. run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], None); return; } @@ -443,6 +442,14 @@ mod cli_run { expected_ending:"Roc <3 Zig!\n", use_valgrind: true, }, + ruby:"ruby-interop" => Example { + filename: "main.roc", + executable_filename: "libhello", + stdin: &[], + input_file: None, + expected_ending:"", + use_valgrind: true, + }, fib:"algorithms" => Example { filename: "fibonacci.roc", executable_filename: "fibonacci", diff --git a/crates/cli_utils/Cargo.toml b/crates/cli_utils/Cargo.toml index 4d77a9dbf9..2b83ec6b50 100644 --- a/crates/cli_utils/Cargo.toml +++ b/crates/cli_utils/Cargo.toml @@ -3,7 +3,7 @@ name = "cli_utils" version = "0.1.0" authors = ["The Roc Contributors"] license = "UPL-1.0" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" edition = "2021" description = "Shared code for cli tests and benchmarks" diff --git a/crates/cli_utils/src/helpers.rs b/crates/cli_utils/src/helpers.rs index 4719de2585..7cc93201b9 100644 --- a/crates/cli_utils/src/helpers.rs +++ b/crates/cli_utils/src/helpers.rs @@ -214,7 +214,7 @@ pub fn run_with_valgrind<'a, I: IntoIterator>( cmd.arg("--xml=yes"); // If you are having valgrind issues on MacOS, you may need to suppress some - // of the errors. Read more here: https://github.com/rtfeldman/roc/issues/746 + // of the errors. Read more here: https://github.com/roc-lang/roc/issues/746 if let Some(suppressions_file_os_str) = env::var_os("VALGRIND_SUPPRESSIONS") { match suppressions_file_os_str.to_str() { None => { diff --git a/crates/code_markup/Cargo.toml b/crates/code_markup/Cargo.toml index c2a1445cce..f3f2f170a2 100644 --- a/crates/code_markup/Cargo.toml +++ b/crates/code_markup/Cargo.toml @@ -11,7 +11,7 @@ roc_ast = { path = "../ast" } roc_module = { path = "../compiler/module" } roc_utils = { path = "../utils" } serde = { version = "1.0.130", features = ["derive"] } -palette = "0.6.0" +palette = "0.6.1" snafu = { version = "0.7.1", features = ["backtraces"] } bumpalo = { version = "3.8.0", features = ["collections"] } itertools = "0.10.1" diff --git a/crates/compiler/README.md b/crates/compiler/README.md index d4c93aab03..e22887038b 100644 --- a/crates/compiler/README.md +++ b/crates/compiler/README.md @@ -172,7 +172,7 @@ ask the compiler to emit debug information during various stages of compilation. There are some goals for more sophisticated debugging tools: -- A nicer unification debugger, see https://github.com/rtfeldman/roc/issues/2486. +- A nicer unification debugger, see https://github.com/roc-lang/roc/issues/2486. Any interest in helping out here is greatly appreciated. ### General Tips diff --git a/crates/compiler/arena_pool/Cargo.toml b/crates/compiler/arena_pool/Cargo.toml index 708bc1160f..d6bf83722a 100644 --- a/crates/compiler/arena_pool/Cargo.toml +++ b/crates/compiler/arena_pool/Cargo.toml @@ -3,6 +3,6 @@ name = "arena-pool" version = "0.0.1" authors = ["The Roc Contributors"] license = "UPL-1.0" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" edition = "2021" description = "A CLI for Roc" diff --git a/crates/compiler/build/src/lib.rs b/crates/compiler/build/src/lib.rs index 7ee0e97721..a8a2dbc81e 100644 --- a/crates/compiler/build/src/lib.rs +++ b/crates/compiler/build/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod link; pub mod program; diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 14654c503a..0579a88782 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -113,7 +113,6 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, - _target_valgrind: bool, ) -> Output { let mut command = Command::new(&zig_executable()); command @@ -149,13 +148,10 @@ pub fn build_zig_host_native( target, ]); - // use single threaded testing for cli_run and enable this code if valgrind fails with unhandled instruction bytes, see #1963. - /*if target_valgrind { - command.args(&[ - "-mcpu", - "x86_64" - ]); - }*/ + // valgrind does not yet support avx512 instructions, see #1963. + if env::var("NO_AVX512").is_ok() { + command.args(&["-mcpu", "x86_64"]); + } if matches!(opt_level, OptLevel::Optimize) { command.args(&["-O", "ReleaseSafe"]); @@ -177,7 +173,6 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, - _target_valgrind: bool, ) -> Output { let mut command = Command::new(&zig_executable()); command @@ -234,7 +229,6 @@ pub fn build_zig_host_native( opt_level: OptLevel, shared_lib_path: Option<&Path>, // For compatibility with the non-macOS def above. Keep these in sync. - _target_valgrind: bool, ) -> Output { use serde_json::Value; @@ -463,7 +457,6 @@ pub fn rebuild_host( target: &Triple, host_input_path: &Path, shared_lib_path: Option<&Path>, - target_valgrind: bool, ) -> PathBuf { let c_host_src = host_input_path.with_file_name("host.c"); let c_host_dest = host_input_path.with_file_name("c_host.o"); @@ -535,7 +528,6 @@ pub fn rebuild_host( "native", opt_level, shared_lib_path, - target_valgrind, ) } Architecture::X86_32(_) => { @@ -549,7 +541,6 @@ pub fn rebuild_host( "i386-linux-musl", opt_level, shared_lib_path, - target_valgrind, ) } @@ -564,7 +555,6 @@ pub fn rebuild_host( target_zig_str(target), opt_level, shared_lib_path, - target_valgrind, ) } _ => panic!("Unsupported architecture {:?}", target.architecture), @@ -971,7 +961,7 @@ fn link_linux( // ld.lld requires this argument, and does not accept --arch // .args(&["-L/usr/lib/x86_64-linux-gnu"]) .args(&[ - // Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925 + // Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496365925 // for discussion and further references "-lc", "-lm", @@ -1054,7 +1044,7 @@ fn link_macos( } ld_command.args(&[ - // Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496392274 + // Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496392274 // for discussion and further references "-lSystem", "-lresolv", @@ -1079,7 +1069,7 @@ fn link_macos( "QuartzCore", // "-lrt", // TODO shouldn't we need this? // "-lc_nonshared", // TODO shouldn't we need this? - // "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/rtfeldman/roc/pull/554#discussion_r496370840 + // "-lgcc", // TODO will eventually need compiler_rt from gcc or something - see https://github.com/roc-lang/roc/pull/554#discussion_r496370840 "-framework", "Security", // Output diff --git a/crates/compiler/builtins/bitcode/benchmark.sh b/crates/compiler/builtins/bitcode/benchmark.sh index ef7b3d6b35..ae6ff3297b 100755 --- a/crates/compiler/builtins/bitcode/benchmark.sh +++ b/crates/compiler/builtins/bitcode/benchmark.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euxo pipefail diff --git a/crates/compiler/builtins/bitcode/run-tests.sh b/crates/compiler/builtins/bitcode/run-tests.sh index c34b9cba31..0d0aead164 100755 --- a/crates/compiler/builtins/bitcode/run-tests.sh +++ b/crates/compiler/builtins/bitcode/run-tests.sh @@ -1,9 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail -# Test every zig +# Execute zig tests (see build.zig) zig build test -# fmt every zig +# check formatting of zig files find src/*.zig -type f -print0 | xargs -n 1 -0 zig fmt --check || (echo "zig fmt --check FAILED! Check the previous lines to see which files were improperly formatted." && exit 1) diff --git a/crates/compiler/builtins/bitcode/run-wasm-tests.sh b/crates/compiler/builtins/bitcode/run-wasm-tests.sh index 74b1712536..4554637871 100755 --- a/crates/compiler/builtins/bitcode/run-wasm-tests.sh +++ b/crates/compiler/builtins/bitcode/run-wasm-tests.sh @@ -1,5 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail # Test failures will always point at the _start function diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 484b477e9e..bbd850698e 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -181,7 +181,7 @@ comptime { // Utils continued - SJLJ // For tests (in particular test_gen), roc_panic is implemented in terms of -// setjmp/longjmp. LLVM is unable to generate code for longjmp on AArch64 (https://github.com/rtfeldman/roc/issues/2965), +// setjmp/longjmp. LLVM is unable to generate code for longjmp on AArch64 (https://github.com/roc-lang/roc/issues/2965), // so instead we ask Zig to please provide implementations for us, which is does // (seemingly via musl). pub extern fn setjmp([*c]c_int) c_int; diff --git a/crates/compiler/builtins/roc/Decode.roc b/crates/compiler/builtins/roc/Decode.roc index 5f47c77021..46574421ad 100644 --- a/crates/compiler/builtins/roc/Decode.roc +++ b/crates/compiler/builtins/roc/Decode.roc @@ -22,6 +22,7 @@ interface Decode bool, string, list, + record, custom, decodeWith, fromBytesPartial, @@ -57,6 +58,7 @@ DecoderFormatting has bool : Decoder Bool fmt | fmt has DecoderFormatting string : Decoder Str fmt | fmt has DecoderFormatting list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting + record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting custom = \decode -> @Decoder decode diff --git a/crates/compiler/builtins/roc/Json.roc b/crates/compiler/builtins/roc/Json.roc index c7aca09cc6..914c126c86 100644 --- a/crates/compiler/builtins/roc/Json.roc +++ b/crates/compiler/builtins/roc/Json.roc @@ -16,6 +16,7 @@ interface Json Decode, Decode.{ DecoderFormatting, + DecodeResult, }, ] @@ -57,6 +58,7 @@ Json := {} has [ bool: decodeBool, string: decodeString, list: decodeList, + record: decodeRecord, }, ] @@ -316,7 +318,8 @@ decodeBool = Decode.custom \bytes, @Json {} -> else { result: Err TooShort, rest: bytes } -decodeString = Decode.custom \bytes, @Json {} -> +jsonString : List U8 -> DecodeResult Str +jsonString = \bytes -> { before, others: afterStartingQuote } = List.split bytes 1 if @@ -335,6 +338,9 @@ decodeString = Decode.custom \bytes, @Json {} -> else { result: Err TooShort, rest: bytes } +decodeString = Decode.custom \bytes, @Json {} -> + jsonString bytes + decodeList = \decodeElem -> Decode.custom \bytes, @Json {} -> decodeElems = \chunk, accum -> when Decode.decodeWith chunk decodeElem (@Json {}) is @@ -372,3 +378,72 @@ decodeList = \decodeElem -> Decode.custom \bytes, @Json {} -> { result: Err TooShort, rest } else { result: Err TooShort, rest: bytes } + +parseExactChar : List U8, U8 -> DecodeResult {} +parseExactChar = \bytes, char -> + when List.get bytes 0 is + Ok c -> + if + c == char + then + { result: Ok {}, rest: (List.split bytes 1).others } + else + { result: Err TooShort, rest: bytes } + + Err _ -> { result: Err TooShort, rest: bytes } + +openBrace : List U8 -> DecodeResult {} +openBrace = \bytes -> parseExactChar bytes (asciiByte '{') + +closingBrace : List U8 -> DecodeResult {} +closingBrace = \bytes -> parseExactChar bytes (asciiByte '}') + +recordKey : List U8 -> DecodeResult Str +recordKey = \bytes -> jsonString bytes + +anything : List U8 -> DecodeResult {} +anything = \bytes -> { result: Err TooShort, rest: bytes } + +colon : List U8 -> DecodeResult {} +colon = \bytes -> parseExactChar bytes (asciiByte ':') + +comma : List U8 -> DecodeResult {} +comma = \bytes -> parseExactChar bytes (asciiByte ',') + +tryDecode : DecodeResult a, ({ val : a, rest : List U8 } -> DecodeResult b) -> DecodeResult b +tryDecode = \{ result, rest }, mapper -> + when result is + Ok val -> mapper { val, rest } + Err e -> { result: Err e, rest } + +decodeRecord = \initialState, stepField, finalizer -> Decode.custom \bytes, @Json {} -> + # NB: the stepper function must be passed explicitly until #2894 is resolved. + decodeFields = \stepper, state, kvBytes -> + { val: key, rest } <- recordKey kvBytes |> tryDecode + { rest: afterColonBytes } <- colon rest |> tryDecode + { val: newState, rest: beforeCommaOrBreak } <- tryDecode + ( + when stepper state key is + Skip -> + { rest: beforeCommaOrBreak } <- afterColonBytes |> anything |> tryDecode + { result: Ok state, rest: beforeCommaOrBreak } + + Keep decoder -> + Decode.decodeWith afterColonBytes decoder (@Json {}) + ) + + { result: commaResult, rest: nextBytes } = comma beforeCommaOrBreak + + when commaResult is + Ok {} -> decodeFields stepField newState nextBytes + Err _ -> { result: Ok newState, rest: nextBytes } + + { rest: afterBraceBytes } <- bytes |> openBrace |> tryDecode + + { val: endStateResult, rest: beforeClosingBraceBytes } <- decodeFields stepField initialState afterBraceBytes |> tryDecode + + { rest: afterRecordBytes } <- beforeClosingBraceBytes |> closingBrace |> tryDecode + + when finalizer endStateResult is + Ok val -> { result: Ok val, rest: afterRecordBytes } + Err e -> { result: Err e, rest: afterRecordBytes } diff --git a/crates/compiler/builtins/roc/Num.roc b/crates/compiler/builtins/roc/Num.roc index d562d83100..32d560c9b9 100644 --- a/crates/compiler/builtins/roc/Num.roc +++ b/crates/compiler/builtins/roc/Num.roc @@ -857,8 +857,44 @@ isMultipleOf : Int a, Int a -> Bool bitwiseAnd : Int a, Int a -> Int a bitwiseXor : Int a, Int a -> Int a bitwiseOr : Int a, Int a -> Int a + +## Bitwise left shift of a number by another +## +## The least significant bits always become 0. This means that shifting left is +## like multiplying by factors of two for unsigned integers. +## +## >>> shiftLeftBy 0b0000_0011 2 == 0b0000_1100 +## +## >>> 0b0000_0101 |> shiftLeftBy 2 == 0b0000_1100 +## +## In some languages `shiftLeftBy` is implemented as a binary operator `<<`. shiftLeftBy : Int a, Int a -> Int a + +## Bitwise arithmetic shift of a number by another +## +## The most significant bits are copied from the current. +## +## >>> shiftRightBy 0b0000_0011 2 == 0b0000_1100 +## +## >>> 0b0001_0100 |> shiftRightBy 2 == 0b0000_0101 +## +## >>> 0b1001_0000 |> shiftRightBy 2 == 0b1110_0100 +## +## In some languages `shiftRightBy` is implemented as a binary operator `>>>`. shiftRightBy : Int a, Int a -> Int a + +## Bitwise logical right shift of a number by another +## +## The most significant bits always become 0. This means that shifting left is +## like dividing by factors of two for unsigned integers. +## +## >>> shiftRightBy 0b0010_1000 2 == 0b0000_1010 +## +## >>> 0b0010_1000 |> shiftRightBy 2 == 0b0000_1010 +## +## >>> 0b1001_0000 |> shiftRightBy 2 == 0b0010_0100 +## +## In some languages `shiftRightBy` is implemented as a binary operator `>>`. shiftRightZfBy : Int a, Int a -> Int a ## Round off the given fraction to the nearest integer. diff --git a/crates/compiler/builtins/roc/Set.roc b/crates/compiler/builtins/roc/Set.roc index 643f609594..381c94d383 100644 --- a/crates/compiler/builtins/roc/Set.roc +++ b/crates/compiler/builtins/roc/Set.roc @@ -41,10 +41,39 @@ insert = \@Set dict, key -> |> Dict.insert key {} |> @Set +# Inserting a duplicate key has no effect. +expect + actual = + Set.empty + |> Set.insert "foo" + |> Set.insert "bar" + |> Set.insert "foo" + |> Set.insert "baz" + + expected = + Set.empty + |> Set.insert "foo" + |> Set.insert "bar" + |> Set.insert "baz" + + expected == actual + len : Set k -> Nat len = \@Set dict -> Dict.len dict +# Inserting a duplicate key has no effect on length. +expect + actual = + Set.empty + |> Set.insert "foo" + |> Set.insert "bar" + |> Set.insert "foo" + |> Set.insert "baz" + |> Set.len + + actual == 3 + ## Drops the given element from the set. remove : Set k, k -> Set k remove = \@Set dict, key -> diff --git a/crates/compiler/builtins/src/lib.rs b/crates/compiler/builtins/src/lib.rs index e06182b87d..f6215cfa74 100644 --- a/crates/compiler/builtins/src/lib.rs +++ b/crates/compiler/builtins/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod bitcode; pub mod roc; diff --git a/crates/compiler/can/Cargo.toml b/crates/compiler/can/Cargo.toml index 4bdc118bb6..3c3db938b5 100644 --- a/crates/compiler/can/Cargo.toml +++ b/crates/compiler/can/Cargo.toml @@ -20,4 +20,4 @@ bitvec = "1" [dev-dependencies] pretty_assertions = "1.0.0" -indoc = "1.0.3" +indoc = "1.0.7" diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 4d9dfcbc59..6fcb06005c 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -444,7 +444,7 @@ fn no_region(value: T) -> Loc { #[inline(always)] fn tag(name: &'static str, args: Vec, var_store: &mut VarStore) -> Expr { Expr::Tag { - variant_var: var_store.fresh(), + tag_union_var: var_store.fresh(), ext_var: var_store.fresh(), name: TagName(name.into()), arguments: args diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 7bf34a8cd7..5722dcaf27 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -535,12 +535,12 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr }, Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments, } => Tag { - variant_var: sub!(*variant_var), + tag_union_var: sub!(*variant_var), ext_var: sub!(*ext_var), name: name.clone(), arguments: arguments @@ -1164,13 +1164,13 @@ mod test { let var2 = new_var(&mut subs, FlexVar(Some(b))); let expr = Expr::Tag { - variant_var: var1, + tag_union_var: var1, ext_var: Variable::EMPTY_TAG_UNION, name: TagName("F".into()), arguments: vec![( var2, Loc::at_zero(Expr::Tag { - variant_var: var2, + tag_union_var: var2, ext_var: Variable::EMPTY_TAG_UNION, name: TagName("G".into()), arguments: vec![], @@ -1185,7 +1185,7 @@ mod test { match expr { Expr::Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, mut arguments, @@ -1219,7 +1219,7 @@ mod test { match arg.value { Expr::Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments, @@ -1250,13 +1250,13 @@ mod test { let var2 = new_var(&mut source, FlexVar(Some(b))); let expr = Expr::Tag { - variant_var: var1, + tag_union_var: var1, ext_var: Variable::EMPTY_TAG_UNION, name: TagName("F".into()), arguments: vec![( var2, Loc::at_zero(Expr::Tag { - variant_var: var2, + tag_union_var: var2, ext_var: Variable::EMPTY_TAG_UNION, name: TagName("G".into()), arguments: vec![], @@ -1271,7 +1271,7 @@ mod test { match expr { Expr::Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, mut arguments, @@ -1300,7 +1300,7 @@ mod test { match arg.value { Expr::Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments, diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 00de88a8c5..21cc022736 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -232,7 +232,7 @@ impl PendingTypeDef<'_> { } } -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #[derive(Clone, Debug)] #[allow(clippy::large_enum_variant)] pub enum Declaration { diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 22443c370a..65bfe3f083 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -184,7 +184,7 @@ pub enum Expr { // Sum Types Tag { - variant_var: Variable, + tag_union_var: Variable, ext_var: Variable, name: TagName, arguments: Vec<(Variable, Loc)>, @@ -780,12 +780,12 @@ pub fn canonicalize_expr<'a>( return (fn_expr, output); } Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, .. } => Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments: args, @@ -796,7 +796,7 @@ pub fn canonicalize_expr<'a>( name, .. } => Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments: args, @@ -1422,7 +1422,7 @@ fn canonicalize_when_branch<'a>( if output.references.has_value_lookup(symbol) { pattern_bound_symbols_body_needs.insert(symbol); } else { - env.problem(Problem::UnusedDef(symbol, region)); + env.problem(Problem::UnusedBranchDef(symbol, region)); } } @@ -1893,7 +1893,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments, diff --git a/crates/compiler/can/src/lib.rs b/crates/compiler/can/src/lib.rs index f00abbd5a1..f74b5bd357 100644 --- a/crates/compiler/can/src/lib.rs +++ b/crates/compiler/can/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod abilities; pub mod annotation; diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index c52e74acdf..f896786a3e 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -696,15 +696,26 @@ pub fn canonicalize_module_defs<'a>( referenced_values.extend(env.qualified_value_lookups.iter().copied()); referenced_types.extend(env.qualified_type_lookups.iter().copied()); + let mut fix_closures_no_capture_symbols = VecSet::default(); + let mut fix_closures_closure_captures = VecMap::default(); for index in 0..declarations.len() { use crate::expr::DeclarationTag::*; + // For each declaration, we need to fixup the closures inside its def. + // Reuse the fixup buffer allocations from the previous iteration. + fix_closures_no_capture_symbols.clear(); + fix_closures_closure_captures.clear(); + match declarations.declarations[index] { Value => { // def pattern has no default expressions, so skip let loc_expr = &mut declarations.expressions[index]; - fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default()); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); } Function(f_index) | Recursive(f_index) | TailRecursive(f_index) => { let name = declarations.symbols[index].value; @@ -722,30 +733,51 @@ pub fn canonicalize_module_defs<'a>( for (_, _, loc_pat) in function_def.arguments.iter_mut() { fix_values_captured_in_closure_pattern( &mut loc_pat.value, - &mut no_capture_symbols, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, ); } - fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); } Destructure(d_index) => { let destruct_def = &mut declarations.destructs[d_index.index()]; let loc_pat = &mut destruct_def.loc_pattern; let loc_expr = &mut declarations.expressions[index]; - fix_values_captured_in_closure_pattern(&mut loc_pat.value, &mut VecSet::default()); - fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default()); + fix_values_captured_in_closure_pattern( + &mut loc_pat.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); } MutualRecursion { .. } => { // the declarations of this group will be treaded individually by later iterations } Expectation => { let loc_expr = &mut declarations.expressions[index]; - fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default()); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); } ExpectationFx => { let loc_expr = &mut declarations.expressions[index]; - fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default()); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + &mut fix_closures_no_capture_symbols, + &mut fix_closures_closure_captures, + ); } } } @@ -771,16 +803,26 @@ pub fn canonicalize_module_defs<'a>( fn fix_values_captured_in_closure_def( def: &mut crate::def::Def, no_capture_symbols: &mut VecSet, + closure_captures: &mut VecMap>, ) { // patterns can contain default expressions, so much go over them too! - fix_values_captured_in_closure_pattern(&mut def.loc_pattern.value, no_capture_symbols); + fix_values_captured_in_closure_pattern( + &mut def.loc_pattern.value, + no_capture_symbols, + closure_captures, + ); - fix_values_captured_in_closure_expr(&mut def.loc_expr.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut def.loc_expr.value, + no_capture_symbols, + closure_captures, + ); } fn fix_values_captured_in_closure_defs( defs: &mut [crate::def::Def], no_capture_symbols: &mut VecSet, + closure_captures: &mut VecMap>, ) { // recursive defs cannot capture each other for def in defs.iter() { @@ -789,16 +831,38 @@ fn fix_values_captured_in_closure_defs( ); } - // TODO mutually recursive functions should both capture the union of both their capture sets - for def in defs.iter_mut() { - fix_values_captured_in_closure_def(def, no_capture_symbols); + fix_values_captured_in_closure_def(def, no_capture_symbols, closure_captures); + } + + // Mutually recursive functions should both capture the union of all their capture sets + // + // Really unfortunate we make a lot of clones here, can this be done more efficiently? + let mut total_capture_set = Vec::default(); + for def in defs.iter_mut() { + if let Expr::Closure(ClosureData { + captured_symbols, .. + }) = &def.loc_expr.value + { + total_capture_set.extend(captured_symbols.iter().copied()); + } + } + total_capture_set.sort_by_key(|(sym, _)| *sym); + total_capture_set.dedup_by_key(|(sym, _)| *sym); + for def in defs.iter_mut() { + if let Expr::Closure(ClosureData { + captured_symbols, .. + }) = &mut def.loc_expr.value + { + *captured_symbols = total_capture_set.clone(); + } } } fn fix_values_captured_in_closure_pattern( pattern: &mut crate::pattern::Pattern, no_capture_symbols: &mut VecSet, + closure_captures: &mut VecMap>, ) { use crate::pattern::Pattern::*; @@ -808,24 +872,35 @@ fn fix_values_captured_in_closure_pattern( .. } => { for (_, loc_arg) in loc_args.iter_mut() { - fix_values_captured_in_closure_pattern(&mut loc_arg.value, no_capture_symbols); + fix_values_captured_in_closure_pattern( + &mut loc_arg.value, + no_capture_symbols, + closure_captures, + ); } } UnwrappedOpaque { argument, .. } => { let (_, loc_arg) = &mut **argument; - fix_values_captured_in_closure_pattern(&mut loc_arg.value, no_capture_symbols); + fix_values_captured_in_closure_pattern( + &mut loc_arg.value, + no_capture_symbols, + closure_captures, + ); } RecordDestructure { destructs, .. } => { for loc_destruct in destructs.iter_mut() { use crate::pattern::DestructType::*; match &mut loc_destruct.value.typ { Required => {} - Optional(_, loc_expr) => { - fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols) - } + Optional(_, loc_expr) => fix_values_captured_in_closure_expr( + &mut loc_expr.value, + no_capture_symbols, + closure_captures, + ), Guard(_, loc_pattern) => fix_values_captured_in_closure_pattern( &mut loc_pattern.value, no_capture_symbols, + closure_captures, ), } } @@ -848,19 +923,28 @@ fn fix_values_captured_in_closure_pattern( fn fix_values_captured_in_closure_expr( expr: &mut crate::expr::Expr, no_capture_symbols: &mut VecSet, + closure_captures: &mut VecMap>, ) { use crate::expr::Expr::*; match expr { LetNonRec(def, loc_expr) => { // LetNonRec(Box, Box>, Variable, Aliases), - fix_values_captured_in_closure_def(def, no_capture_symbols); - fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols); + fix_values_captured_in_closure_def(def, no_capture_symbols, closure_captures); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + no_capture_symbols, + closure_captures, + ); } LetRec(defs, loc_expr, _) => { // LetRec(Vec, Box>, Variable, Aliases), - fix_values_captured_in_closure_defs(defs, no_capture_symbols); - fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols); + fix_values_captured_in_closure_defs(defs, no_capture_symbols, closure_captures); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + no_capture_symbols, + closure_captures, + ); } Expect { @@ -868,8 +952,16 @@ fn fix_values_captured_in_closure_expr( loc_continuation, lookups_in_cond: _, } => { - fix_values_captured_in_closure_expr(&mut loc_condition.value, no_capture_symbols); - fix_values_captured_in_closure_expr(&mut loc_continuation.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_condition.value, + no_capture_symbols, + closure_captures, + ); + fix_values_captured_in_closure_expr( + &mut loc_continuation.value, + no_capture_symbols, + closure_captures, + ); } ExpectFx { @@ -891,16 +983,58 @@ fn fix_values_captured_in_closure_expr( captured_symbols.retain(|(s, _)| !no_capture_symbols.contains(s)); captured_symbols.retain(|(s, _)| s != name); + let original_captures_len = captured_symbols.len(); + let mut num_visited = 0; + let mut i = 0; + while num_visited < original_captures_len { + // If we've captured a capturing closure, replace the captured closure symbol with + // the symbols of its captures. That way, we can construct the closure with the + // captures it needs inside our body. + // + // E.g. + // x = "" + // inner = \{} -> x + // outer = \{} -> inner {} + // + // initially `outer` captures [inner], but this is then replaced with just [x]. + let (captured_symbol, _) = captured_symbols[i]; + if let Some(captures) = closure_captures.get(&captured_symbol) { + debug_assert!(!captures.is_empty()); + captured_symbols.swap_remove(i); + captured_symbols.extend(captures); + // Jump two, because the next element is now one of the newly-added captures, + // which we don't need to check. + i += 2; + } else { + i += 1; + } + num_visited += 1; + } + if captured_symbols.len() > original_captures_len { + // Re-sort, since we've added new captures. + captured_symbols.sort_by_key(|(sym, _)| *sym); + } + if captured_symbols.is_empty() { no_capture_symbols.insert(*name); + } else { + closure_captures.insert(*name, captured_symbols.to_vec()); } // patterns can contain default expressions, so much go over them too! for (_, _, loc_pat) in arguments.iter_mut() { - fix_values_captured_in_closure_pattern(&mut loc_pat.value, no_capture_symbols); + fix_values_captured_in_closure_pattern( + &mut loc_pat.value, + no_capture_symbols, + closure_captures, + ); } - fix_values_captured_in_closure_expr(&mut loc_body.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_body.value, + no_capture_symbols, + closure_captures, + ); } Num(..) @@ -918,28 +1052,45 @@ fn fix_values_captured_in_closure_expr( List { loc_elems, .. } => { for elem in loc_elems.iter_mut() { - fix_values_captured_in_closure_expr(&mut elem.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut elem.value, + no_capture_symbols, + closure_captures, + ); } } When { loc_cond, branches, .. } => { - fix_values_captured_in_closure_expr(&mut loc_cond.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_cond.value, + no_capture_symbols, + closure_captures, + ); for branch in branches.iter_mut() { - fix_values_captured_in_closure_expr(&mut branch.value.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut branch.value.value, + no_capture_symbols, + closure_captures, + ); // patterns can contain default expressions, so much go over them too! for loc_pat in branch.patterns.iter_mut() { fix_values_captured_in_closure_pattern( &mut loc_pat.pattern.value, no_capture_symbols, + closure_captures, ); } if let Some(guard) = &mut branch.guard { - fix_values_captured_in_closure_expr(&mut guard.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut guard.value, + no_capture_symbols, + closure_captures, + ); } } } @@ -950,23 +1101,43 @@ fn fix_values_captured_in_closure_expr( .. } => { for (loc_cond, loc_then) in branches.iter_mut() { - fix_values_captured_in_closure_expr(&mut loc_cond.value, no_capture_symbols); - fix_values_captured_in_closure_expr(&mut loc_then.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_cond.value, + no_capture_symbols, + closure_captures, + ); + fix_values_captured_in_closure_expr( + &mut loc_then.value, + no_capture_symbols, + closure_captures, + ); } - fix_values_captured_in_closure_expr(&mut final_else.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut final_else.value, + no_capture_symbols, + closure_captures, + ); } Call(function, arguments, _) => { - fix_values_captured_in_closure_expr(&mut function.1.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut function.1.value, + no_capture_symbols, + closure_captures, + ); for (_, loc_arg) in arguments.iter_mut() { - fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_arg.value, + no_capture_symbols, + closure_captures, + ); } } RunLowLevel { args, .. } | ForeignCall { args, .. } => { for (_, arg) in args.iter_mut() { - fix_values_captured_in_closure_expr(arg, no_capture_symbols); + fix_values_captured_in_closure_expr(arg, no_capture_symbols, closure_captures); } } @@ -975,22 +1146,38 @@ fn fix_values_captured_in_closure_expr( updates: fields, .. } => { for (_, field) in fields.iter_mut() { - fix_values_captured_in_closure_expr(&mut field.loc_expr.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut field.loc_expr.value, + no_capture_symbols, + closure_captures, + ); } } Access { loc_expr, .. } => { - fix_values_captured_in_closure_expr(&mut loc_expr.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + no_capture_symbols, + closure_captures, + ); } Tag { arguments, .. } => { for (_, loc_arg) in arguments.iter_mut() { - fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_arg.value, + no_capture_symbols, + closure_captures, + ); } } OpaqueRef { argument, .. } => { let (_, loc_arg) = &mut **argument; - fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols); + fix_values_captured_in_closure_expr( + &mut loc_arg.value, + no_capture_symbols, + closure_captures, + ); } OpaqueWrapFunction(_) => {} } diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index fe0d9f4c6e..6795923cec 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -244,7 +244,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { walk_record_fields(visitor, updates.iter()); } Expr::Tag { - variant_var: _, + tag_union_var: _, ext_var: _, name: _, arguments, diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 5002146f1f..a115faf55d 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -1023,7 +1023,7 @@ mod test_can { # There was a bug where annotating a def meant that its # references no longer got reported. # - # https://github.com/rtfeldman/roc/issues/298 + # https://github.com/roc-lang/roc/issues/298 x : List Booly x = [y] diff --git a/crates/compiler/collections/Cargo.toml b/crates/compiler/collections/Cargo.toml index 357dd18744..3fcf2bae89 100644 --- a/crates/compiler/collections/Cargo.toml +++ b/crates/compiler/collections/Cargo.toml @@ -10,5 +10,5 @@ im = "15.0.0" im-rc = "15.0.0" wyhash = "0.5.0" bumpalo = { version = "3.8.0", features = ["collections"] } -hashbrown = { version = "0.12.1", features = [ "bumpalo" ] } +hashbrown = { version = "0.12.3", features = [ "bumpalo" ] } bitvec = "1" diff --git a/crates/compiler/collections/src/lib.rs b/crates/compiler/collections/src/lib.rs index 9bce46c2f2..299e743caf 100644 --- a/crates/compiler/collections/src/lib.rs +++ b/crates/compiler/collections/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod all; diff --git a/crates/compiler/collections/src/vec_map.rs b/crates/compiler/collections/src/vec_map.rs index c1b19d5817..53552f9938 100644 --- a/crates/compiler/collections/src/vec_map.rs +++ b/crates/compiler/collections/src/vec_map.rs @@ -140,6 +140,12 @@ impl VecMap { cur_idx: 0, } } + + /// Removes all key/value pairs from the map, without affecting its allocated capacity. + pub fn clear(&mut self) { + self.keys.clear(); + self.values.clear(); + } } impl Extend<(K, V)> for VecMap { diff --git a/crates/compiler/collections/src/vec_set.rs b/crates/compiler/collections/src/vec_set.rs index 0752376dc3..d5bf8d22a8 100644 --- a/crates/compiler/collections/src/vec_set.rs +++ b/crates/compiler/collections/src/vec_set.rs @@ -83,6 +83,11 @@ impl VecSet { pub fn iter_mut(&mut self) -> impl Iterator { self.elements.iter_mut() } + + /// Removes all elements from the set, without affecting its allocated capacity. + pub fn clear(&mut self) { + self.elements.clear() + } } impl Extend for VecSet { diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index bcaaddd01a..1fec4018c0 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -1040,7 +1040,7 @@ pub fn constrain_expr( body_con } Tag { - variant_var, + tag_union_var: variant_var, ext_var, name, arguments, diff --git a/crates/compiler/constrain/src/lib.rs b/crates/compiler/constrain/src/lib.rs index 94067ab076..13a9709e45 100644 --- a/crates/compiler/constrain/src/lib.rs +++ b/crates/compiler/constrain/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod builtins; pub mod expr; diff --git a/crates/compiler/derive/Cargo.toml b/crates/compiler/derive/Cargo.toml index d0ca82a7a2..b19432ecb3 100644 --- a/crates/compiler/derive/Cargo.toml +++ b/crates/compiler/derive/Cargo.toml @@ -19,3 +19,7 @@ bumpalo = { version = "3.8.0", features = ["collections"] } [features] default = [] debug-derived-symbols = ["roc_module/debug-symbols"] +# Enables open extension variables for constructed records and tag unions. +# This is not necessary for code generation, but may be necessary if you are +# constraining and solving generated derived bodies. +open-extension-vars = [] diff --git a/crates/compiler/derive/src/decoding.rs b/crates/compiler/derive/src/decoding.rs index 04b291cbd5..0671d7fdf3 100644 --- a/crates/compiler/derive/src/decoding.rs +++ b/crates/compiler/derive/src/decoding.rs @@ -1,18 +1,23 @@ //! Derivers for the `Decoding` ability. -use roc_can::expr::{AnnotatedMark, ClosureData, Expr, Recursive}; +use roc_can::expr::{ + AnnotatedMark, ClosureData, Expr, Field, Recursive, WhenBranch, WhenBranchPattern, +}; use roc_can::pattern::Pattern; +use roc_collections::SendMap; use roc_derive_key::decoding::FlatDecodableKey; use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; +use roc_module::ident::Lowercase; use roc_module::symbol::Symbol; -use roc_region::all::Loc; +use roc_region::all::{Loc, Region}; use roc_types::subs::{ - Content, FlatType, GetSubsSlice, LambdaSet, OptVariable, SubsSlice, UnionLambdas, Variable, + Content, ExhaustiveMark, FlatType, GetSubsSlice, LambdaSet, OptVariable, RecordFields, + RedundantMark, SubsSlice, UnionLambdas, UnionTags, Variable, }; -use roc_types::types::AliasKind; +use roc_types::types::{AliasKind, RecordField}; -use crate::util::Env; +use crate::util::{Env, ExtensionKind}; use crate::{synth_var, DerivedBody}; pub(crate) fn derive_decoder( @@ -22,6 +27,7 @@ pub(crate) fn derive_decoder( ) -> DerivedBody { let (body, body_type) = match key { FlatDecodableKey::List() => decoder_list(env, def_symbol), + FlatDecodableKey::Record(fields) => decoder_record(env, def_symbol, fields), }; let specialization_lambda_sets = @@ -34,6 +40,957 @@ pub(crate) fn derive_decoder( } } +// Implements decoding of a record. For example, for +// +// {first: a, second: b} +// +// we'd like to generate an impl like +// +// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting +// decoder = +// initialState : {f0: Result a [NoField], f1: Result b [NoField]} +// initialState = {f0: Err NoField, f1: Err NoField} +// +// stepField = \state, field -> +// when field is +// "first" -> +// Keep (Decode.custom \bytes, fmt -> +// when Decode.decodeWith bytes Decode.decoder fmt is +// {result, rest} -> +// {result: Result.map result \val -> {state & f0: Ok val}, rest}) +// "second" -> +// Keep (Decode.custom \bytes, fmt -> +// when Decode.decodeWith bytes Decode.decoder fmt is +// {result, rest} -> +// {result: Result.map result \val -> {state & f1: Ok val}, rest}) +// _ -> Skip +// +// finalizer = \{f0, f1} -> +// when f0 is +// Ok first -> +// when f1 is +// Ok second -> Ok {first, second} +// Err NoField -> Err TooShort +// Err NoField -> Err TooShort +// +// Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.record initialState stepField finalizer) fmt +fn decoder_record(env: &mut Env, _def_symbol: Symbol, fields: Vec) -> (Expr, Variable) { + // The decoded type of each field in the record, e.g. {first: a, second: b}. + let mut field_vars = Vec::with_capacity(fields.len()); + // The type of each field in the decoding state, e.g. {first: Result a [NoField], second: Result b [NoField]} + let mut result_field_vars = Vec::with_capacity(fields.len()); + + // initialState = ... + let (initial_state_var, initial_state) = + decoder_record_initial_state(env, &fields, &mut field_vars, &mut result_field_vars); + + // finalizer = ... + let (finalizer, finalizer_var, decode_err_var) = decoder_record_finalizer( + env, + initial_state_var, + &fields, + &field_vars, + &result_field_vars, + ); + + // stepField = ... + let (step_field, step_var) = decoder_record_step_field( + env, + fields, + &field_vars, + &result_field_vars, + initial_state_var, + decode_err_var, + ); + + // Build up the type of `Decode.record` we expect + let record_decoder_var = env.subs.fresh_unnamed_flex_var(); + let decode_record_lambda_set = env.subs.fresh_unnamed_flex_var(); + let decode_record_var = env.import_builtin_symbol_var(Symbol::DECODE_RECORD); + let this_decode_record_var = { + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [initial_state_var, step_var, finalizer_var]), + decode_record_lambda_set, + record_decoder_var, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_record_var, this_decode_record_var); + + // Decode.record initialState stepField finalizer + let call_decode_record = Expr::Call( + Box::new(( + this_decode_record_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_RECORD, + None, + this_decode_record_var, + )), + decode_record_lambda_set, + record_decoder_var, + )), + vec![ + (initial_state_var, Loc::at_zero(initial_state)), + (step_var, Loc::at_zero(step_field)), + (finalizer_var, Loc::at_zero(finalizer)), + ], + CalledVia::Space, + ); + + let (call_decode_custom, decode_custom_ret_var) = { + let bytes_sym = env.new_symbol("bytes"); + let fmt_sym = env.new_symbol("fmt"); + let fmt_var = env.subs.fresh_unnamed_flex_var(); + + let (decode_custom, decode_custom_var) = wrap_in_decode_custom_decode_with( + env, + bytes_sym, + (fmt_sym, fmt_var), + vec![], + (call_decode_record, record_decoder_var), + ); + + (decode_custom, decode_custom_var) + }; + + (call_decode_custom, decode_custom_ret_var) +} + +// Example: +// stepField = \state, field -> +// when field is +// "first" -> +// Keep (Decode.custom \bytes, fmt -> +// # Uses a single-branch `when` because `let` is more expensive to monomorphize +// # due to checks for polymorphic expressions, and `rec` would be polymorphic. +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & first: Ok val}, +// Err err -> Err err +// }) +// +// "second" -> +// Keep (Decode.custom \bytes, fmt -> +// when Decode.decodeWith bytes Decode.decoder fmt is +// rec -> +// { +// rest: rec.rest, +// result: when rec.result is +// Ok val -> Ok {state & second: Ok val}, +// Err err -> Err err +// }) +// +// _ -> Skip +fn decoder_record_step_field( + env: &mut Env, + fields: Vec, + field_vars: &[Variable], + result_field_vars: &[Variable], + state_record_var: Variable, + decode_err_var: Variable, +) -> (Expr, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let field_arg_symbol = env.new_symbol("field"); + + // +1 because of the default branch. + let mut branches = Vec::with_capacity(fields.len() + 1); + let keep_payload_var = env.subs.fresh_unnamed_flex_var(); + let keep_or_skip_var = { + let keep_payload_subs_slice = SubsSlice::insert_into_subs(env.subs, [keep_payload_var]); + let flat_type = FlatType::TagUnion( + UnionTags::insert_slices_into_subs( + env.subs, + [ + ("Keep".into(), keep_payload_subs_slice), + ("Skip".into(), Default::default()), + ], + ), + Variable::EMPTY_TAG_UNION, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for ((field_name, &field_var), &result_field_var) in fields + .into_iter() + .zip(field_vars.iter()) + .zip(result_field_vars.iter()) + { + // Example: + // "first" -> + // Keep (Decode.custom \bytes, fmt -> + // # Uses a single-branch `when` because `let` is more expensive to monomorphize + // # due to checks for polymorphic expressions, and `rec` would be polymorphic. + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + + let this_custom_callback_var; + let custom_callback_ret_var; + let custom_callback = { + // \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let bytes_arg_symbol = env.new_symbol("bytes"); + let fmt_arg_symbol = env.new_symbol("fmt"); + let bytes_arg_var = env.subs.fresh_unnamed_flex_var(); + let fmt_arg_var = env.subs.fresh_unnamed_flex_var(); + + // rec.result : [Ok field_var, Err DecodeError] + let rec_dot_result = { + let tag_union = FlatType::TagUnion( + UnionTags::for_result(env.subs, field_var, decode_err_var), + Variable::EMPTY_TAG_UNION, + ); + + synth_var(env.subs, Content::Structure(tag_union)) + }; + + // rec : { rest: List U8, result: (typeof rec.result) } + let rec_var = { + let fields = RecordFields::insert_into_subs( + env.subs, + [ + ("rest".into(), RecordField::Required(Variable::LIST_U8)), + ("result".into(), RecordField::Required(rec_dot_result)), + ], + ); + let record = FlatType::Record(fields, Variable::EMPTY_RECORD); + + synth_var(env.subs, Content::Structure(record)) + }; + + // `Decode.decoder` for the field's value + let decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); + let decode_with_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); + let lambda_set_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_with_var = { + let subs_slice = SubsSlice::insert_into_subs( + env.subs, + [bytes_arg_var, decoder_var, fmt_arg_var], + ); + let this_decode_with_var = synth_var( + env.subs, + Content::Structure(FlatType::Func(subs_slice, lambda_set_var, rec_var)), + ); + + env.unify(decode_with_var, this_decode_with_var); + + this_decode_with_var + }; + + // The result of decoding this field's value - either the updated state, or a decoding error. + let when_expr_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(env.subs, state_record_var, decode_err_var), + Variable::EMPTY_TAG_UNION, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + // What our decoder passed to `Decode.custom` returns - the result of decoding the + // field's value, and the remaining bytes. + custom_callback_ret_var = { + let rest_field = RecordField::Required(Variable::LIST_U8); + let result_field = RecordField::Required(when_expr_var); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs( + env.subs, + [("rest".into(), rest_field), ("result".into(), result_field)], + ), + Variable::EMPTY_RECORD, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + let custom_callback_body = { + let rec_symbol = env.new_symbol("rec"); + + // # Uses a single-branch `when` because `let` is more expensive to monomorphize + // # due to checks for polymorphic expressions, and `rec` would be polymorphic. + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let branch_body = { + let result_val = { + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + let ok_val_symbol = env.new_symbol("val"); + let err_val_symbol = env.new_symbol("err"); + let ok_branch_expr = { + // Ok {state & first: Ok val}, + let mut updates = SendMap::default(); + + updates.insert( + field_name.clone(), + Field { + var: result_field_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Tag { + tag_union_var: result_field_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![( + field_var, + Loc::at_zero(Expr::Var(ok_val_symbol)), + )], + })), + }, + ); + + let updated_record = Expr::Update { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + symbol: state_arg_symbol, + updates, + }; + + Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(state_record_var, Loc::at_zero(updated_record))], + } + }; + + let branches = vec![ + // Ok val -> Ok {state & first: Ok val}, + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![( + field_var, + Loc::at_zero(Pattern::Identifier(ok_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(ok_branch_expr), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + // Err err -> Err err + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: rec_dot_result, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Pattern::Identifier(err_val_symbol)), + )], + }), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: when_expr_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Var(err_val_symbol)), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }, + ]; + + // when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::Access { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: rec_dot_result, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol))), + field: "result".into(), + })), + cond_var: rec_dot_result, + expr_var: when_expr_var, + region: Region::zero(), + branches, + branches_cond_var: rec_dot_result, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + let mut fields_map = SendMap::default(); + + fields_map.insert( + "rest".into(), + Field { + var: Variable::LIST_U8, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Access { + record_var: rec_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: Variable::LIST_U8, + loc_expr: Box::new(Loc::at_zero(Expr::Var(rec_symbol))), + field: "rest".into(), + })), + }, + ); + + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + fields_map.insert( + "result".into(), + Field { + var: when_expr_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(result_val)), + }, + ); + + Expr::Record { + record_var: custom_callback_ret_var, + fields: fields_map, + } + }; + + let branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Identifier(rec_symbol)), + degenerate: false, + }], + value: Loc::at_zero(branch_body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + let condition_expr = Expr::Call( + Box::new(( + this_decode_with_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_DECODE_WITH)), + lambda_set_var, + rec_var, + )), + vec![ + (Variable::LIST_U8, Loc::at_zero(Expr::Var(bytes_arg_symbol))), + ( + decoder_var, + Loc::at_zero(Expr::AbilityMember( + Symbol::DECODE_DECODER, + None, + decoder_var, + )), + ), + (fmt_arg_var, Loc::at_zero(Expr::Var(fmt_arg_symbol))), + ], + CalledVia::Space, + ); + + // when Decode.decodeWith bytes Decode.decoder fmt is + Expr::When { + loc_cond: Box::new(Loc::at_zero(condition_expr)), + cond_var: rec_var, + expr_var: custom_callback_ret_var, + region: Region::zero(), + branches: vec![branch], + branches_cond_var: rec_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + } + }; + + let custom_closure_symbol = env.new_symbol("customCallback"); + this_custom_callback_var = env.subs.fresh_unnamed_flex_var(); + let custom_callback_lambda_set_var = { + let content = Content::LambdaSet(LambdaSet { + solved: UnionLambdas::insert_into_subs( + env.subs, + [(custom_closure_symbol, [state_record_var])], + ), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: this_custom_callback_var, + }); + let custom_callback_lambda_set_var = synth_var(env.subs, content); + let subs_slice = + SubsSlice::insert_into_subs(env.subs, [bytes_arg_var, fmt_arg_var]); + + env.subs.set_content( + this_custom_callback_var, + Content::Structure(FlatType::Func( + subs_slice, + custom_callback_lambda_set_var, + custom_callback_ret_var, + )), + ); + + custom_callback_lambda_set_var + }; + + // \bytes, fmt -> … + Expr::Closure(ClosureData { + function_type: this_custom_callback_var, + closure_type: custom_callback_lambda_set_var, + return_type: custom_callback_ret_var, + name: custom_closure_symbol, + captured_symbols: vec![(state_arg_symbol, state_record_var)], + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + bytes_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(bytes_arg_symbol)), + ), + ( + fmt_arg_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(fmt_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(custom_callback_body)), + }) + }; + + let decode_custom_ret_var = env.subs.fresh_unnamed_flex_var(); + let decode_custom = { + let decode_custom_var = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); + let decode_custom_closure_var = env.subs.fresh_unnamed_flex_var(); + let this_decode_custom_var = { + let subs_slice = SubsSlice::insert_into_subs(env.subs, [this_custom_callback_var]); + let flat_type = + FlatType::Func(subs_slice, decode_custom_closure_var, decode_custom_ret_var); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + env.unify(decode_custom_var, this_decode_custom_var); + + // Decode.custom \bytes, fmt -> … + Expr::Call( + Box::new(( + this_decode_custom_var, + Loc::at_zero(Expr::Var(Symbol::DECODE_CUSTOM)), + decode_custom_closure_var, + decode_custom_ret_var, + )), + vec![(this_custom_callback_var, Loc::at_zero(custom_callback))], + CalledVia::Space, + ) + }; + + env.unify(keep_payload_var, decode_custom_ret_var); + + let keep = { + // Keep (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Keep".into(), + arguments: vec![(decode_custom_ret_var, Loc::at_zero(decode_custom))], + } + }; + + let branch = { + // "first" -> + // Keep (Decode.custom \bytes, fmt -> + // when Decode.decodeWith bytes Decode.decoder fmt is + // rec -> + // { + // rest: rec.rest, + // result: when rec.result is + // Ok val -> Ok {state & first: Ok val}, + // Err err -> Err err + // } + // ) + WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::StrLiteral(field_name.into())), + degenerate: false, + }], + value: Loc::at_zero(keep), + guard: None, + redundant: RedundantMark::known_non_redundant(), + } + }; + + branches.push(branch); + } + + // Example: `_ -> Skip` + let default_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: keep_or_skip_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Skip".into(), + arguments: Vec::new(), + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + branches.push(default_branch); + + // when field is + let body = Expr::When { + loc_cond: Box::new(Loc::at_zero(Expr::Var(field_arg_symbol))), + cond_var: Variable::STR, + expr_var: keep_or_skip_var, + region: Region::zero(), + branches, + branches_cond_var: Variable::STR, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + + let step_field_closure = env.new_symbol("stepField"); + let function_type = env.subs.fresh_unnamed_flex_var(); + let closure_type = { + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, step_field_closure), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_type, + }; + + synth_var(env.subs, Content::LambdaSet(lambda_set)) + }; + + { + let args_slice = SubsSlice::insert_into_subs(env.subs, [state_record_var, Variable::STR]); + + env.subs.set_content( + function_type, + Content::Structure(FlatType::Func(args_slice, closure_type, keep_or_skip_var)), + ) + }; + + let expr = Expr::Closure(ClosureData { + function_type, + closure_type, + return_type: keep_or_skip_var, + name: step_field_closure, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![ + ( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + ), + ( + Variable::STR, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(field_arg_symbol)), + ), + ], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (expr, function_type) +} + +// Example: +// finalizer = \rec -> +// when rec.first is +// Ok first -> +// when rec.second is +// Ok second -> Ok {first, second} +// Err NoField -> Err TooShort +// Err NoField -> Err TooShort +fn decoder_record_finalizer( + env: &mut Env, + state_record_var: Variable, + fields: &[Lowercase], + field_vars: &[Variable], + result_field_vars: &[Variable], +) -> (Expr, Variable, Variable) { + let state_arg_symbol = env.new_symbol("stateRecord"); + let mut fields_map = SendMap::default(); + let mut pattern_symbols = Vec::with_capacity(fields.len()); + let decode_err_var = { + let flat_type = FlatType::TagUnion( + UnionTags::tag_without_arguments(env.subs, "TooShort".into()), + Variable::EMPTY_TAG_UNION, + ); + + synth_var(env.subs, Content::Structure(flat_type)) + }; + + for (field_name, &field_var) in fields.iter().zip(field_vars.iter()) { + let symbol = env.new_symbol(field_name.as_str()); + + pattern_symbols.push(symbol); + + let field_expr = Expr::Var(symbol); + let field = Field { + var: field_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(field_expr)), + }; + + fields_map.insert(field_name.clone(), field); + } + + // The bottom of the happy path - return the decoded record {first: a, second: b} wrapped with + // "Ok". + let return_type_var; + let mut body = { + let subs = &mut env.subs; + let record_field_iter = fields + .iter() + .zip(field_vars.iter()) + .map(|(field_name, &field_var)| (field_name.clone(), RecordField::Required(field_var))); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs(subs, record_field_iter), + Variable::EMPTY_RECORD, + ); + let done_record_var = synth_var(subs, Content::Structure(flat_type)); + let done_record = Expr::Record { + record_var: done_record_var, + fields: fields_map, + }; + + return_type_var = { + let flat_type = FlatType::TagUnion( + UnionTags::for_result(subs, done_record_var, decode_err_var), + Variable::EMPTY_TAG_UNION, + ); + + synth_var(subs, Content::Structure(flat_type)) + }; + + Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Ok".into(), + arguments: vec![(done_record_var, Loc::at_zero(done_record))], + } + }; + + // Unwrap each result in the decoded state + // + // when rec.first is + // Ok first -> ...happy path... + // Err NoField -> Err TooShort + for (((symbol, field_name), &field_var), &result_field_var) in pattern_symbols + .iter() + .rev() + .zip(fields.iter().rev()) + .zip(field_vars.iter().rev()) + .zip(result_field_vars.iter().rev()) + { + // when rec.first is + let cond_expr = Expr::Access { + record_var: state_record_var, + ext_var: env.new_ext_var(ExtensionKind::Record), + field_var: result_field_var, + loc_expr: Box::new(Loc::at_zero(Expr::Var(state_arg_symbol))), + field: field_name.clone(), + }; + + // Example: `Ok x -> expr` + let ok_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::AppliedTag { + whole_var: result_field_var, + ext_var: Variable::EMPTY_TAG_UNION, + tag_name: "Ok".into(), + arguments: vec![(field_var, Loc::at_zero(Pattern::Identifier(*symbol)))], + }), + degenerate: false, + }], + value: Loc::at_zero(body), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + // Example: `_ -> Err TooShort` + let err_branch = WhenBranch { + patterns: vec![WhenBranchPattern { + pattern: Loc::at_zero(Pattern::Underscore), + degenerate: false, + }], + value: Loc::at_zero(Expr::Tag { + tag_union_var: return_type_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: "Err".into(), + arguments: vec![( + decode_err_var, + Loc::at_zero(Expr::Tag { + tag_union_var: decode_err_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: "TooShort".into(), + arguments: Vec::new(), + }), + )], + }), + guard: None, + redundant: RedundantMark::known_non_redundant(), + }; + + body = Expr::When { + loc_cond: Box::new(Loc::at_zero(cond_expr)), + cond_var: result_field_var, + expr_var: return_type_var, + region: Region::zero(), + branches: vec![ok_branch, err_branch], + branches_cond_var: result_field_var, + exhaustive: ExhaustiveMark::known_exhaustive(), + }; + } + + let function_var = synth_var(env.subs, Content::Error); // We'll fix this up in subs later. + let function_symbol = env.new_symbol("finalizer"); + let lambda_set = LambdaSet { + solved: UnionLambdas::tag_without_arguments(env.subs, function_symbol), + recursion_var: OptVariable::NONE, + unspecialized: Default::default(), + ambient_function: function_var, + }; + let closure_type = synth_var(env.subs, Content::LambdaSet(lambda_set)); + let flat_type = FlatType::Func( + SubsSlice::insert_into_subs(env.subs, [state_record_var]), + closure_type, + return_type_var, + ); + + // Fix up function_var so it's not Content::Error anymore + env.subs + .set_content(function_var, Content::Structure(flat_type)); + + let finalizer = Expr::Closure(ClosureData { + function_type: function_var, + closure_type, + return_type: return_type_var, + name: function_symbol, + captured_symbols: Vec::new(), + recursive: Recursive::NotRecursive, + arguments: vec![( + state_record_var, + AnnotatedMark::known_exhaustive(), + Loc::at_zero(Pattern::Identifier(state_arg_symbol)), + )], + loc_body: Box::new(Loc::at_zero(body)), + }); + + (finalizer, function_var, decode_err_var) +} + +// Example: +// initialState : {first: Result a [NoField], second: Result b [NoField]} +// initialState = {first: Err NoField, second: Err NoField} +fn decoder_record_initial_state( + env: &mut Env<'_>, + field_names: &[Lowercase], + field_vars: &mut Vec, + result_field_vars: &mut Vec, +) -> (Variable, Expr) { + let mut initial_state_fields = SendMap::default(); + + for field_name in field_names { + let subs = &mut env.subs; + let field_var = subs.fresh_unnamed_flex_var(); + + field_vars.push(field_var); + + let no_field_label = "NoField"; + let union_tags = UnionTags::tag_without_arguments(subs, no_field_label.into()); + let no_field_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion(union_tags, Variable::EMPTY_TAG_UNION)), + ); + let no_field = Expr::Tag { + tag_union_var: no_field_var, + ext_var: Variable::EMPTY_TAG_UNION, + name: no_field_label.into(), + arguments: Vec::new(), + }; + let err_label = "Err"; + let union_tags = UnionTags::for_result(subs, field_var, no_field_var); + let result_var = synth_var( + subs, + Content::Structure(FlatType::TagUnion(union_tags, Variable::EMPTY_TAG_UNION)), + ); + let field_expr = Expr::Tag { + tag_union_var: result_var, + ext_var: env.new_ext_var(ExtensionKind::TagUnion), + name: err_label.into(), + arguments: vec![(no_field_var, Loc::at_zero(no_field))], + }; + result_field_vars.push(result_var); + let field = Field { + var: result_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(field_expr)), + }; + + initial_state_fields.insert(field_name.clone(), field); + } + + let subs = &mut env.subs; + let record_field_iter = field_names + .iter() + .zip(result_field_vars.iter()) + .map(|(field_name, &var)| (field_name.clone(), RecordField::Required(var))); + let flat_type = FlatType::Record( + RecordFields::insert_into_subs(subs, record_field_iter), + Variable::EMPTY_RECORD, + ); + + let state_record_var = synth_var(subs, Content::Structure(flat_type)); + + ( + state_record_var, + Expr::Record { + record_var: state_record_var, + fields: initial_state_fields, + }, + ) +} + fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { // Build // @@ -112,18 +1069,48 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { }; let bytes_sym = env.new_symbol("bytes"); - let bytes_var = env.subs.fresh_unnamed_flex_var(); let fmt_sym = env.new_symbol("fmt"); let fmt_var = env.subs.fresh_unnamed_flex_var(); + let captures = vec![]; - // Decode.decodeWith bytes (Decode.list Decode.decoder) fmt : DecodeResult (List elem) - let (decode_with_call, decode_result_list_elem_var) = { + wrap_in_decode_custom_decode_with( + env, + bytes_sym, + (fmt_sym, fmt_var), + captures, + (decode_list_call, this_decode_list_ret_var), + ) +} + +// Wraps `myDecoder` in `Decode.custom \bytes, fmt -> Decode.decodeWith bytes myDecoder fmt`. +// I think most can be removed when https://github.com/roc-lang/roc/issues/3724 is resolved. +fn wrap_in_decode_custom_decode_with( + env: &mut Env, + bytes: Symbol, + fmt: (Symbol, Variable), + sorted_inner_decoder_captures: Vec<(Symbol, Variable)>, + inner_decoder: (Expr, Variable), +) -> (Expr, Variable) { + use Expr::*; + + debug_assert!({ + let mut sorted = sorted_inner_decoder_captures.clone(); + sorted.sort_by_key(|(sym, _)| *sym); + sorted == sorted_inner_decoder_captures + }); + + let (bytes_sym, bytes_var) = (bytes, Variable::LIST_U8); + let (fmt_sym, fmt_var) = fmt; + let (inner_decoder, inner_decoder_var) = inner_decoder; + + // Decode.decodeWith bytes inner_decoder fmt : DecodeResult val + let (decode_with_call, decode_with_result_var) = { // Decode.decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting let decode_with_type = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); - // Decode.decodeWith : bytes, Decoder (List elem) fmt, fmt -> DecoderResult (List val) + // Decode.decodeWith : bytes, inner_decoder, fmt -> DecoderResult (List val) let this_decode_with_var_slice = - SubsSlice::insert_into_subs(env.subs, [bytes_var, this_decode_list_ret_var, fmt_var]); + SubsSlice::insert_into_subs(env.subs, [bytes_var, inner_decoder_var, fmt_var]); let this_decode_with_clos_var = env.subs.fresh_unnamed_flex_var(); let this_decode_with_ret_var = env.subs.fresh_unnamed_flex_var(); let this_decode_with_fn_var = synth_var( @@ -149,9 +1136,9 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { let decode_with_call = Call( decode_with_fn, vec![ - // bytes (Decode.list Decode.decoder) fmt + // bytes inner_decoder fmt (bytes_var, Loc::at_zero(Var(bytes_sym))), - (this_decode_list_ret_var, Loc::at_zero(decode_list_call)), + (inner_decoder_var, Loc::at_zero(inner_decoder)), (fmt_var, Loc::at_zero(Var(fmt_sym))), ], CalledVia::Space, @@ -160,7 +1147,7 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { (decode_with_call, this_decode_with_ret_var) }; - // \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt + // \bytes, fmt -> Decode.decodeWith bytes myDecoder fmt let (custom_lambda, custom_var) = { let fn_name = env.new_symbol("custom"); @@ -168,7 +1155,13 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { let fn_var = synth_var(env.subs, Content::Error); // -[[fn_name]]-> - let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, [(fn_name, vec![])]); + let fn_name_labels = UnionLambdas::insert_into_subs( + env.subs, + [( + fn_name, + sorted_inner_decoder_captures.iter().map(|(_, var)| *var), + )], + ); let fn_clos_var = synth_var( env.subs, Content::LambdaSet(LambdaSet { @@ -180,23 +1173,23 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { ); // bytes, fmt -[[fn_name]]-> DecoderResult (List elem) - let args_slice = SubsSlice::insert_into_subs(env.subs, vec![bytes_var, fmt_var]); + let args_slice = SubsSlice::insert_into_subs(env.subs, [bytes_var, fmt_var]); env.subs.set_content( fn_var, Content::Structure(FlatType::Func( args_slice, fn_clos_var, - decode_result_list_elem_var, + decode_with_result_var, )), ); - // \bytes, fmt -[[fn_name]]-> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt + // \bytes, fmt -[[fn_name]]-> Decode.decodeWith bytes inner_decoder fmt let clos = Closure(ClosureData { function_type: fn_var, closure_type: fn_clos_var, - return_type: decode_result_list_elem_var, + return_type: decode_with_result_var, name: fn_name, - captured_symbols: vec![], + captured_symbols: sorted_inner_decoder_captures, recursive: Recursive::NotRecursive, arguments: vec![ ( @@ -216,7 +1209,7 @@ fn decoder_list(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { (clos, fn_var) }; - // Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt + // Decode.custom \bytes, fmt -> Decode.decodeWith bytes inner_decoder fmt let (decode_custom_call, decoder_var) = { // (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting let decode_custom_type = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index 45b8263649..7d7299c5dd 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -133,6 +133,12 @@ impl DerivedModule { self.map.entry(key).or_insert(triple) } + pub fn is_derived_def(&self, def_symbol: Symbol) -> bool { + self.map + .iter() + .any(|(_, (symbol, _, _))| *symbol == def_symbol) + } + pub fn iter_all( &self, ) -> impl Iterator { diff --git a/crates/compiler/derive/src/util.rs b/crates/compiler/derive/src/util.rs index e9661c8847..35c8c5c7aa 100644 --- a/crates/compiler/derive/src/util.rs +++ b/crates/compiler/derive/src/util.rs @@ -130,7 +130,7 @@ impl Env<'_> { // we only expect `bar` to polymorphic at this stage! // // TODO: it would be better if `unify` could prune these for us. See also - // https://github.com/rtfeldman/roc/issues/3207; that is a blocker for this TODO. + // https://github.com/roc-lang/roc/issues/3207; that is a blocker for this TODO. #[cfg(debug_assertions)] { for (spec_var, lambda_sets) in _lambda_sets_to_specialize.drain() { @@ -152,4 +152,27 @@ impl Env<'_> { } } } + + /// Creates an extension variable for a tag union or record. + /// + /// Derivers should always construct tag union and record types such that they are closed. + /// If the `open-extension-vars` feature is turned on, flex extension vars will be + /// returned; otherwise, the appropriate closed extension variable for the type will be + /// returned. + #[inline(always)] + pub fn new_ext_var(&mut self, kind: ExtensionKind) -> Variable { + if cfg!(feature = "open-extension-vars") { + self.subs.fresh_unnamed_flex_var() + } else { + match kind { + ExtensionKind::Record => Variable::EMPTY_RECORD, + ExtensionKind::TagUnion => Variable::EMPTY_TAG_UNION, + } + } + } +} + +pub(crate) enum ExtensionKind { + Record, + TagUnion, } diff --git a/crates/compiler/derive_key/src/decoding.rs b/crates/compiler/derive_key/src/decoding.rs index 73dfdee74d..e83bac6815 100644 --- a/crates/compiler/derive_key/src/decoding.rs +++ b/crates/compiler/derive_key/src/decoding.rs @@ -1,7 +1,7 @@ -use roc_module::symbol::Symbol; +use roc_module::{ident::Lowercase, symbol::Symbol}; use roc_types::subs::{Content, FlatType, Subs, Variable}; -use crate::DeriveError; +use crate::{util::debug_name_record, DeriveError}; #[derive(Hash)] pub enum FlatDecodable { @@ -12,12 +12,16 @@ pub enum FlatDecodable { #[derive(Hash, PartialEq, Eq, Debug, Clone)] pub enum FlatDecodableKey { List(/* takes one variable */), + + // Unfortunate that we must allocate here, c'est la vie + Record(Vec), } impl FlatDecodableKey { pub(crate) fn debug_name(&self) -> String { match self { FlatDecodableKey::List() => "list".to_string(), + FlatDecodableKey::Record(fields) => debug_name_record(fields), } } } @@ -33,8 +37,25 @@ impl FlatDecodable { Symbol::STR_STR => Ok(Immediate(Symbol::DECODE_STRING)), _ => Err(Underivable), }, - FlatType::Record(_fields, _ext) => { - Err(Underivable) // yet + FlatType::Record(fields, ext) => { + let fields_iter = match fields.unsorted_iterator(subs, ext) { + Ok(it) => it, + Err(_) => return Err(Underivable), + }; + + let mut field_names = Vec::with_capacity(fields.len()); + for (field_name, record_field) in fields_iter { + if record_field.is_optional() { + // Can't derive a concrete decoder for optional fields, since those are + // compile-time-polymorphic + return Err(Underivable); + } + field_names.push(field_name.clone()); + } + + field_names.sort(); + + Ok(Key(FlatDecodableKey::Record(field_names))) } FlatType::TagUnion(_tags, _ext) | FlatType::RecursiveTagUnion(_, _tags, _ext) => { Err(Underivable) // yet @@ -42,9 +63,7 @@ impl FlatDecodable { FlatType::FunctionOrTagUnion(_name_index, _, _) => { Err(Underivable) // yet } - FlatType::EmptyRecord => { - Err(Underivable) // yet - } + FlatType::EmptyRecord => Ok(Key(FlatDecodableKey::Record(vec![]))), FlatType::EmptyTagUnion => { Err(Underivable) // yet } diff --git a/crates/compiler/derive_key/src/encoding.rs b/crates/compiler/derive_key/src/encoding.rs index 33d7d670db..9466100939 100644 --- a/crates/compiler/derive_key/src/encoding.rs +++ b/crates/compiler/derive_key/src/encoding.rs @@ -4,7 +4,10 @@ use roc_module::{ }; use roc_types::subs::{Content, FlatType, GetSubsSlice, Subs, Variable}; -use crate::DeriveError; +use crate::{ + util::{check_empty_ext_var, debug_name_record}, + DeriveError, +}; #[derive(Hash)] pub enum FlatEncodable { @@ -28,17 +31,7 @@ impl FlatEncodableKey { FlatEncodableKey::List() => "list".to_string(), FlatEncodableKey::Set() => "set".to_string(), FlatEncodableKey::Dict() => "dict".to_string(), - FlatEncodableKey::Record(fields) => { - let mut str = String::from('{'); - fields.iter().enumerate().for_each(|(i, f)| { - if i > 0 { - str.push(','); - } - str.push_str(f.as_str()); - }); - str.push('}'); - str - } + FlatEncodableKey::Record(fields) => debug_name_record(fields), FlatEncodableKey::TagUnion(tags) => { let mut str = String::from('['); tags.iter().enumerate().for_each(|(i, (tag, arity))| { @@ -56,22 +49,6 @@ impl FlatEncodableKey { } } -fn check_ext_var( - subs: &Subs, - ext_var: Variable, - is_empty_ext: impl Fn(&Content) -> bool, -) -> Result<(), DeriveError> { - let ext_content = subs.get_content_without_compacting(ext_var); - if is_empty_ext(ext_content) { - Ok(()) - } else { - match ext_content { - Content::FlexVar(_) => Err(DeriveError::UnboundVar), - _ => Err(DeriveError::Underivable), - } - } -} - impl FlatEncodable { pub(crate) fn from_var(subs: &Subs, var: Variable) -> Result { use DeriveError::*; @@ -86,7 +63,7 @@ impl FlatEncodable { _ => Err(Underivable), }, FlatType::Record(fields, ext) => { - check_ext_var(subs, ext, |ext| { + check_empty_ext_var(subs, ext, |ext| { matches!(ext, Content::Structure(FlatType::EmptyRecord)) })?; @@ -106,7 +83,7 @@ impl FlatEncodable { // [ A t1, B t1 t2 ] as R // look the same on the surface, because `R` is only somewhere inside of the // `t`-prefixed payload types. - check_ext_var(subs, ext, |ext| { + check_empty_ext_var(subs, ext, |ext| { matches!(ext, Content::Structure(FlatType::EmptyTagUnion)) })?; diff --git a/crates/compiler/derive_key/src/lib.rs b/crates/compiler/derive_key/src/lib.rs index 8c6f97caaf..f160f27e9a 100644 --- a/crates/compiler/derive_key/src/lib.rs +++ b/crates/compiler/derive_key/src/lib.rs @@ -15,6 +15,7 @@ pub mod decoding; pub mod encoding; +mod util; use decoding::{FlatDecodable, FlatDecodableKey}; use encoding::{FlatEncodable, FlatEncodableKey}; diff --git a/crates/compiler/derive_key/src/util.rs b/crates/compiler/derive_key/src/util.rs new file mode 100644 index 0000000000..609f214956 --- /dev/null +++ b/crates/compiler/derive_key/src/util.rs @@ -0,0 +1,32 @@ +use roc_module::ident::Lowercase; +use roc_types::subs::{Content, Subs, Variable}; + +use crate::DeriveError; + +pub(crate) fn check_empty_ext_var( + subs: &Subs, + ext_var: Variable, + is_empty_ext: impl Fn(&Content) -> bool, +) -> Result<(), DeriveError> { + let ext_content = subs.get_content_without_compacting(ext_var); + if is_empty_ext(ext_content) { + Ok(()) + } else { + match ext_content { + Content::FlexVar(_) => Err(DeriveError::UnboundVar), + _ => Err(DeriveError::Underivable), + } + } +} + +pub(crate) fn debug_name_record(fields: &[Lowercase]) -> String { + let mut str = String::from('{'); + fields.iter().enumerate().for_each(|(i, f)| { + if i > 0 { + str.push(','); + } + str.push_str(f.as_str()); + }); + str.push('}'); + str +} diff --git a/crates/compiler/fmt/Cargo.toml b/crates/compiler/fmt/Cargo.toml index 2659ae29a5..670a0eab1d 100644 --- a/crates/compiler/fmt/Cargo.toml +++ b/crates/compiler/fmt/Cargo.toml @@ -14,6 +14,6 @@ bumpalo = { version = "3.8.0", features = ["collections"] } [dev-dependencies] pretty_assertions = "1.0.0" -indoc = "1.0.3" +indoc = "1.0.7" roc_test_utils = { path = "../../test_utils" } walkdir = "2.3.2" diff --git a/crates/compiler/fmt/src/lib.rs b/crates/compiler/fmt/src/lib.rs index 00ed45379d..90224fdb3b 100644 --- a/crates/compiler/fmt/src/lib.rs +++ b/crates/compiler/fmt/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod annotation; pub mod collection; diff --git a/crates/compiler/gen_dev/README.md b/crates/compiler/gen_dev/README.md index 6a86279cce..2972eeb7ef 100644 --- a/crates/compiler/gen_dev/README.md +++ b/crates/compiler/gen_dev/README.md @@ -1,7 +1,7 @@ # Dev Backend The dev backend is focused on generating decent binaries extremely fast. -It goes from Roc's [mono ir](https://github.com/rtfeldman/roc/blob/trunk/compiler/mono/src/ir.rs) to an object file ready to be linked. +It goes from Roc's [mono ir](https://github.com/roc-lang/roc/blob/main/crates/compiler/mono/src/ir.rs) to an object file ready to be linked. ## General Process @@ -22,14 +22,14 @@ rust should be abled compile each specific target (`linux-arm`, `darwin-x86_64`, ### Backend -[Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) is the core abstraction. +[Backend](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/lib.rs) is the core abstraction. It understands Roc's mono ir and some high level ideas about the generation process. The main job of Backend is to do high level optimizatons (like lazy literal loading) and parse the mono ir. Every target specific backend must implement this trait. ### Backend64Bit -[Backend64Bit](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is more or less what it sounds like. +[Backend64Bit](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is more or less what it sounds like. It is the backend that understands 64 bit architectures. Currently it is the only backend implementation, but a 32 bit implementation will probably come in the future. This backend understands that the unit of data movement is 64 bit. @@ -44,39 +44,39 @@ Backend64Bit is generic over these types instead of containing these types withi ### Assembler -[Assembler](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is the trait for generating assembly bytes. +[Assembler](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is the trait for generating assembly bytes. It defines a set of RISC-like assembly calls that must be implemented for each architecture. A lot of these calls may not map one to one with actual assembly instructions for each architecture. Instead, they are a general abstraction over functionality shared between all architectures. This will grow regularly as more Roc builtins are added. -Here are example implementations for [arm](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/x86_64.rs). +Here are example implementations for [arm](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/x86_64.rs). ### CallConv -[CallConv](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs) is the abstraction over calling conventions. +[CallConv](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs) is the abstraction over calling conventions. It deals with register and stack specific information related to passing and returning arguments. -Here are example implementations for [arm](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/x86_64.rs). +Here are example implementations for [arm](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/aarch64.rs) and [x86_64](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/x86_64.rs). ## Adding New Features Adding a new builtin to the dev backend can be pretty simple. -Here is [an example](https://github.com/rtfeldman/roc/pull/893/files) of adding `Num.Sub`. +Here is [an example](https://github.com/roc-lang/roc/pull/893/files) of adding `Num.Sub`. This is the general procedure I follow with some helpful links: 1. Find a feature that is just n+1. For example, since we already have integers, adding a builtin that functions on them should be n+1. On the other hand, since we don't yet have booleans/conditionals, adding if statements may not yet be n+1. - A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/rtfeldman/roc/tree/trunk/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend. + A good place to look for missing features is in the test files for generation in [test_gen](https://github.com/roc-lang/roc/tree/main/crates/compiler/test_gen). Any test that is not enabled for the `gen-dev` feature still needs to be added to the dev backend. Eventually all features should be enabled for the dev backend. 1. Pick/write the simplest test case you can find for the new feature. Just add `feature = "gen-dev"` to the `cfg` line for the test case. -1. Uncomment the code to print out procedures [from here](https://github.com/rtfeldman/roc/blob/b03ed18553569314a420d5bf1fb0ead4b6b5ecda/compiler/test_gen/src/helpers/dev.rs#L76) and run the test. +1. Uncomment the code to print out procedures [from here](https://github.com/roc-lang/roc/blob/b03ed18553569314a420d5bf1fb0ead4b6b5ecda/compiler/test_gen/src/helpers/dev.rs#L76) and run the test. It should fail and print out the mono ir for this test case. Seeing the actual mono ir tends to be very helpful for complex additions. -1. Generally it will fail in one of the match statements in the [Backend](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/lib.rs) trait. +1. Generally it will fail in one of the match statements in the [Backend](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/lib.rs) trait. Add the correct pattern matching and likely new function for your new builtin. This will break the compile until you add the same function to places that implement the trait, - like [Backend64Bit](https://github.com/rtfeldman/roc/blob/trunk/compiler/gen_dev/src/generic64/mod.rs). + like [Backend64Bit](https://github.com/roc-lang/roc/blob/main/crates/compiler/gen_dev/src/generic64/mod.rs). 1. Keep following the chain down. To implement the function in Backend64Bit, you may need to add new assembly calls. Feel free to ignore backends that aren't x86_64 for now and just add `unimplemented!`. diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index d5fba65acf..94ec3af567 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -440,7 +440,39 @@ impl Assembler for AArch64Assembler { _src1: AArch64GeneralReg, _src2: AArch64GeneralReg, ) { - todo!("register multiplication for AArch64"); + todo!("register signed multiplication for AArch64"); + } + + fn umul_reg64_reg64_reg64<'a, ASM, CC>( + _buf: &mut Vec<'a, u8>, + _storage_manager: &mut StorageManager<'a, AArch64GeneralReg, AArch64FloatReg, ASM, CC>, + _dst: AArch64GeneralReg, + _src1: AArch64GeneralReg, + _src2: AArch64GeneralReg, + ) where + ASM: Assembler, + CC: CallConv, + { + todo!("register unsigned multiplication for AArch64"); + } + + #[inline(always)] + fn mul_freg32_freg32_freg32( + _buf: &mut Vec<'_, u8>, + _dst: AArch64FloatReg, + _src1: AArch64FloatReg, + _src2: AArch64FloatReg, + ) { + todo!("multiplication for floats for AArch64"); + } + #[inline(always)] + fn mul_freg64_freg64_freg64( + _buf: &mut Vec<'_, u8>, + _dst: AArch64FloatReg, + _src1: AArch64FloatReg, + _src2: AArch64FloatReg, + ) { + todo!("multiplication for floats for AArch64"); } #[inline(always)] diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 57bf70e517..afac67e7f5 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -21,7 +21,7 @@ mod disassembler_test_macro; pub(crate) mod storage; pub(crate) mod x86_64; -use storage::StorageManager; +use storage::{RegStorage, StorageManager}; const REFCOUNT_ONE: u64 = i64::MIN as u64; // TODO: on all number functions double check and deal with over/underflow. @@ -210,12 +210,33 @@ pub trait Assembler: Sized + Copy { fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg); fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg); + fn mul_freg32_freg32_freg32( + buf: &mut Vec<'_, u8>, + dst: FloatReg, + src1: FloatReg, + src2: FloatReg, + ); + fn mul_freg64_freg64_freg64( + buf: &mut Vec<'_, u8>, + dst: FloatReg, + src1: FloatReg, + src2: FloatReg, + ); fn imul_reg64_reg64_reg64( buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, src2: GeneralReg, ); + fn umul_reg64_reg64_reg64<'a, ASM, CC>( + buf: &mut Vec<'a, u8>, + storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, CC>, + dst: GeneralReg, + src1: GeneralReg, + src2: GeneralReg, + ) where + ASM: Assembler, + CC: CallConv; fn sub_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32); fn sub_reg64_reg64_reg64( @@ -339,6 +360,19 @@ pub fn new_backend_64bit< } } +macro_rules! quadword_and_smaller { + () => { + IntWidth::I64 + | IntWidth::U64 + | IntWidth::I32 + | IntWidth::U32 + | IntWidth::I16 + | IntWidth::U16 + | IntWidth::I8 + | IntWidth::U8 + }; +} + impl< 'a, GeneralReg: RegTrait, @@ -699,16 +733,7 @@ impl< fn build_num_add(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>) { match layout { - Layout::Builtin(Builtin::Int( - IntWidth::I64 - | IntWidth::U64 - | IntWidth::I32 - | IntWidth::U32 - | IntWidth::I16 - | IntWidth::U16 - | IntWidth::I8 - | IntWidth::U8, - )) => { + Layout::Builtin(Builtin::Int(quadword_and_smaller!())) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); let src1_reg = self .storage_manager @@ -736,7 +761,9 @@ impl< fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>) { match layout { - Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => { + Layout::Builtin(Builtin::Int( + IntWidth::I64 | IntWidth::I32 | IntWidth::I16 | IntWidth::I8, + )) => { let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst); let src1_reg = self .storage_manager @@ -746,6 +773,37 @@ impl< .load_to_general_reg(&mut self.buf, src2); ASM::imul_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg); } + Layout::Builtin(Builtin::Int( + IntWidth::U64 | IntWidth::U32 | IntWidth::U16 | IntWidth::U8, + )) => { + 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::umul_reg64_reg64_reg64( + &mut self.buf, + &mut self.storage_manager, + dst_reg, + src1_reg, + src2_reg, + ); + } + Layout::Builtin(Builtin::Float(FloatWidth::F64)) => { + let dst_reg = self.storage_manager.claim_float_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::mul_freg64_freg64_freg64(&mut self.buf, dst_reg, src1_reg, src2_reg); + } + Layout::Builtin(Builtin::Float(FloatWidth::F32)) => { + let dst_reg = self.storage_manager.claim_float_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::mul_freg32_freg32_freg32(&mut self.buf, dst_reg, src1_reg, src2_reg); + } x => todo!("NumMul: layout, {:?}", x), } } diff --git a/crates/compiler/gen_dev/src/generic64/storage.rs b/crates/compiler/gen_dev/src/generic64/storage.rs index 30a352d190..fb2c524e60 100644 --- a/crates/compiler/gen_dev/src/generic64/storage.rs +++ b/crates/compiler/gen_dev/src/generic64/storage.rs @@ -22,7 +22,7 @@ use StackStorage::*; use Storage::*; #[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum RegStorage { +pub enum RegStorage { General(GeneralReg), Float(FloatReg), } @@ -756,7 +756,7 @@ impl< #[allow(dead_code)] /// Ensures that a register is free. If it is not free, data will be moved to make it free. - fn ensure_reg_free( + pub fn ensure_reg_free( &mut self, buf: &mut Vec<'a, u8>, wanted_reg: RegStorage, diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index cb3e2e085c..334a0b82fe 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1009,6 +1009,58 @@ impl Assembler for X86_64Assembler { imul_reg64_reg64(buf, dst, src2); } + fn umul_reg64_reg64_reg64<'a, ASM, CC>( + buf: &mut Vec<'a, u8>, + storage_manager: &mut StorageManager<'a, X86_64GeneralReg, X86_64FloatReg, ASM, CC>, + dst: X86_64GeneralReg, + src1: X86_64GeneralReg, + src2: X86_64GeneralReg, + ) where + ASM: Assembler, + CC: CallConv, + { + use crate::generic64::RegStorage; + + storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RAX)); + storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RDX)); + + mov_reg64_reg64(buf, X86_64GeneralReg::RAX, src1); + mul_reg64_reg64(buf, src2); + mov_reg64_reg64(buf, dst, X86_64GeneralReg::RAX); + } + + fn mul_freg32_freg32_freg32( + buf: &mut Vec<'_, u8>, + dst: X86_64FloatReg, + src1: X86_64FloatReg, + src2: X86_64FloatReg, + ) { + if dst == src1 { + mulss_freg32_freg32(buf, dst, src2); + } else if dst == src2 { + mulss_freg32_freg32(buf, dst, src1); + } else { + movss_freg32_freg32(buf, dst, src1); + mulss_freg32_freg32(buf, dst, src2); + } + } + #[inline(always)] + fn mul_freg64_freg64_freg64( + buf: &mut Vec<'_, u8>, + dst: X86_64FloatReg, + src1: X86_64FloatReg, + src2: X86_64FloatReg, + ) { + if dst == src1 { + mulsd_freg64_freg64(buf, dst, src2); + } else if dst == src2 { + mulsd_freg64_freg64(buf, dst, src1); + } else { + movsd_freg64_freg64(buf, dst, src1); + mulsd_freg64_freg64(buf, dst, src2); + } + } + #[inline(always)] fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize { jmp_imm32(buf, offset); @@ -1280,12 +1332,25 @@ impl X86_64Assembler { } } const REX: u8 = 0x40; -const REX_W: u8 = REX | 0x8; + +// 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 +const REX_PREFIX_R: u8 = 0b0100; +#[allow(unused)] +/// Extension to the SIB.index field +const REX_PREFIX_X: u8 = 0b0010; +/// Extension to the MODRM.rm +const REX_PREFIX_B: u8 = 0b0001; + +/// Wide REX +const REX_W: u8 = REX | REX_PREFIX_W; #[inline(always)] fn add_rm_extension(reg: T, byte: u8) -> u8 { if reg.value() > 7 { - byte | 1 + byte | REX_PREFIX_B } else { byte } @@ -1299,7 +1364,7 @@ fn add_opcode_extension(reg: X86_64GeneralReg, byte: u8) -> u8 { #[inline(always)] fn add_reg_extension(reg: T, byte: u8) -> u8 { if reg.value() > 7 { - byte | 4 + byte | REX_PREFIX_R } else { byte } @@ -1396,6 +1461,46 @@ fn addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl } } +/// `MULSD xmm1,xmm2/m64` -> Multiply the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1. +#[inline(always)] +fn mulsd_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, + 0x59, + 0xC0 | (dst_mod << 3) | (src_mod), + ]) + } else { + buf.extend(&[0xF2, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) + } +} + +/// `ADDSS xmm1,xmm2/m64` -> Add the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1. +#[inline(always)] +fn mulss_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, + 0x59, + 0xC0 | (dst_mod << 3) | (src_mod), + ]) + } else { + buf.extend(&[0xF3, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) + } +} + #[inline(always)] fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { let dst_high = dst as u8 > 7; @@ -1465,6 +1570,19 @@ fn imul_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Gen extended_binop_reg64_reg64(0x0F, 0xAF, buf, src, dst); } +/// `MUL r/m64` -> Unsigned Multiply r/m64 to r64. +#[inline(always)] +fn mul_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) { + let mut rex = REX_W; + rex = add_reg_extension(src, rex); + + if src.value() > 7 { + rex |= REX_PREFIX_B; + } + + buf.extend(&[rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]); +} + /// Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits. #[inline(always)] fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) { @@ -2086,6 +2204,35 @@ mod tests { ); } + #[test] + fn test_mul_reg64_reg64() { + disassembler_test!( + mul_reg64_reg64, + |reg| format!("mul {}", reg), + ALL_GENERAL_REGS + ); + } + + #[test] + fn test_mulsd_freg64_freg64() { + disassembler_test!( + mulsd_freg64_freg64, + |reg1, reg2| format!("mulsd {}, {}", reg1, reg2), + ALL_FLOAT_REGS, + ALL_FLOAT_REGS + ); + } + + #[test] + fn test_mulss_freg32_freg32() { + disassembler_test!( + mulss_freg32_freg32, + |reg1, reg2| format!("mulss {}, {}", reg1, reg2), + ALL_FLOAT_REGS, + ALL_FLOAT_REGS + ); + } + #[test] fn test_jmp_imm32() { const INST_SIZE: i32 = 5; diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 500ed7f467..0fbe0fa115 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] use bumpalo::{collections::Vec, Bump}; diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index d5bf4aaa87..85cb925361 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -172,7 +172,7 @@ fn build_object<'a, B: Backend<'a>>( let arena = backend.env().arena; /* - // Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/rtfeldman/roc/pull/1323 + // Commented out because we couldn't figure out how to get it to work on mac - see https://github.com/roc-lang/roc/pull/1323 let comment = output.add_section(vec![], b".comment".to_vec(), SectionKind::OtherString); output.append_section_data( comment, diff --git a/crates/compiler/gen_llvm/src/lib.rs b/crates/compiler/gen_llvm/src/lib.rs index bef97f894c..dde9b39162 100644 --- a/crates/compiler/gen_llvm/src/lib.rs +++ b/crates/compiler/gen_llvm/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits #![allow(clippy::float_cmp)] diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 2c3c4c65ac..66958fe4f2 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -3929,7 +3929,7 @@ pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValu pub fn build_setjmp_call<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValueEnum<'ctx> { let jmp_buf = get_sjlj_buffer(env); if cfg!(target_arch = "aarch64") { - // Due to https://github.com/rtfeldman/roc/issues/2965, we use a setjmp we linked in from Zig + // Due to https://github.com/roc-lang/roc/issues/2965, we use a setjmp we linked in from Zig call_bitcode_fn(env, &[jmp_buf.into()], bitcode::UTILS_SETJMP) } else { // Anywhere else, use the LLVM intrinsic. @@ -6459,11 +6459,11 @@ fn to_cc_type<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>, ) -> BasicTypeEnum<'ctx> { - match layout { - Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin), - _ => { + match layout.runtime_representation() { + Layout::Builtin(builtin) => to_cc_type_builtin(env, &builtin), + layout => { // TODO this is almost certainly incorrect for bigger structs - basic_type_from_layout(env, layout) + basic_type_from_layout(env, &layout) } } } @@ -7087,22 +7087,13 @@ fn build_int_binop<'a, 'ctx, 'env>( NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(), NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(), NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(), - NumShiftLeftBy => { - // NOTE arguments are flipped; - // we write `assert_eq!(0b0000_0001 << 0, 0b0000_0001);` - // as `Num.shiftLeftBy 0 0b0000_0001 - bd.build_left_shift(rhs, lhs, "int_shift_left").into() - } - NumShiftRightBy => { - // NOTE arguments are flipped; - bd.build_right_shift(rhs, lhs, true, "int_shift_right") - .into() - } - NumShiftRightZfBy => { - // NOTE arguments are flipped; - bd.build_right_shift(rhs, lhs, false, "int_shift_right_zf") - .into() - } + NumShiftLeftBy => bd.build_left_shift(lhs, rhs, "int_shift_left").into(), + NumShiftRightBy => bd + .build_right_shift(lhs, rhs, true, "int_shift_right") + .into(), + NumShiftRightZfBy => bd + .build_right_shift(lhs, rhs, false, "int_shift_right_zf") + .into(), _ => { unreachable!("Unrecognized int binary operation: {:?}", op); diff --git a/crates/compiler/gen_llvm/src/llvm/convert.rs b/crates/compiler/gen_llvm/src/llvm/convert.rs index 22ea1c0991..02c3c5758b 100644 --- a/crates/compiler/gen_llvm/src/llvm/convert.rs +++ b/crates/compiler/gen_llvm/src/llvm/convert.rs @@ -361,7 +361,7 @@ impl<'ctx> RocUnion<'ctx> { // set the tag id // // NOTE: setting the tag id initially happened before writing the data into it. - // That turned out to expose UB. More info at https://github.com/rtfeldman/roc/issues/3554 + // That turned out to expose UB. More info at https://github.com/roc-lang/roc/issues/3554 if let Some(tag_id) = tag_id { let tag_id_type = match self.tag_type.unwrap() { TagType::I8 => env.context.i8_type(), diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index 767f66ca4f..79ff53640a 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -517,7 +517,7 @@ impl<'a> WasmBackend<'a> { // Load all the arguments for the inner function for (i, wrapper_arg) in wrapper_arg_layouts.iter().enumerate() { - let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner) + let is_closure_data = i == 0; // Skip closure data (first for wrapper, last for inner). We'll handle it below. let is_return_pointer = i == wrapper_arg_layouts.len() - 1; // Skip return pointer (may not be an arg for inner. And if it is, swaps from end to start) if is_closure_data || is_return_pointer { continue; @@ -540,7 +540,23 @@ impl<'a> WasmBackend<'a> { // If the inner function has closure data, it's the last arg of the inner fn let closure_data_layout = wrapper_arg_layouts[0]; if closure_data_layout.stack_size(TARGET_INFO) > 0 { + // The closure data exists, and will have been passed in to the wrapper as a + // one-element struct. + let inner_closure_data_layout = match closure_data_layout { + Layout::Struct { + field_layouts: [inner], + .. + } => inner, + other => internal_error!( + "Expected a boxed layout for wrapped closure data, got {:?}", + other + ), + }; self.code_builder.get_local(LocalId(0)); + // Since the closure data is wrapped in a one-element struct, we've been passed in the + // pointer to that struct in the stack memory. To get the closure data we just need to + // dereference the pointer. + self.dereference_boxed_value(inner_closure_data_layout); } // Call the wrapped inner function diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 6dadb056b4..1d740c000b 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -183,7 +183,7 @@ impl<'a> LowLevelCall<'a> { /// Wrap an integer that should have less than 32 bits, but is represented in Wasm as i32. /// This may seem like deliberately introducing an error! /// But we want all targets to behave the same, and hash algos rely on wrapping. - /// Discussion: https://github.com/rtfeldman/roc/pull/2117#discussion_r760723063 + /// Discussion: https://github.com/roc-lang/roc/pull/2117#discussion_r760723063 fn wrap_small_int(&self, backend: &mut WasmBackend<'a>, int_width: IntWidth) { let bits = 8 * int_width.stack_size() as i32; let shift = 32 - bits; @@ -1599,11 +1599,11 @@ impl<'a> LowLevelCall<'a> { } } NumShiftLeftBy => { - // Swap order of arguments - backend.storage.load_symbols( - &mut backend.code_builder, - &[self.arguments[1], self.arguments[0]], - ); + let num = self.arguments[0]; + let bits = self.arguments[1]; + backend + .storage + .load_symbols(&mut backend.code_builder, &[num, bits]); match CodeGenNumType::from(self.ret_layout) { I32 => backend.code_builder.i32_shl(), I64 => backend.code_builder.i64_shl(), @@ -1612,8 +1612,8 @@ impl<'a> LowLevelCall<'a> { } } NumShiftRightBy => { - let bits = self.arguments[0]; - let num = self.arguments[1]; + let num = self.arguments[0]; + let bits = self.arguments[1]; match CodeGenNumType::from(self.ret_layout) { I32 => { // In most languages this operation is for signed numbers, but Roc defines it on all integers. @@ -1657,41 +1657,39 @@ impl<'a> LowLevelCall<'a> { } } NumShiftRightZfBy => { + let num = self.arguments[0]; + let bits = self.arguments[1]; match CodeGenNumType::from(self.ret_layout) { I32 => { // In most languages this operation is for unsigned numbers, but Roc defines it on all integers. // So the argument is implicitly converted to unsigned before the shift operator. // We need to make that conversion explicit for i8 and i16, which use Wasm's i32 type. let bit_width = 8 * self.ret_layout.stack_size(TARGET_INFO); - if bit_width < 32 && symbol_is_signed_int(backend, self.arguments[0]) { + if bit_width < 32 && symbol_is_signed_int(backend, bits) { let mask = (1 << bit_width) - 1; backend .storage - .load_symbols(&mut backend.code_builder, &[self.arguments[1]]); + .load_symbols(&mut backend.code_builder, &[num]); backend.code_builder.i32_const(mask); backend.code_builder.i32_and(); backend .storage - .load_symbols(&mut backend.code_builder, &[self.arguments[0]]); + .load_symbols(&mut backend.code_builder, &[bits]); } else { - // swap the arguments - backend.storage.load_symbols( - &mut backend.code_builder, - &[self.arguments[1], self.arguments[0]], - ); + backend + .storage + .load_symbols(&mut backend.code_builder, &[num, bits]); } backend.code_builder.i32_shr_u(); } I64 => { - // swap the arguments - backend.storage.load_symbols( - &mut backend.code_builder, - &[self.arguments[1], self.arguments[0]], - ); + backend + .storage + .load_symbols(&mut backend.code_builder, &[num, bits]); backend.code_builder.i64_shr_u(); } I128 => todo!("{:?} for I128", self.lowlevel), @@ -1857,11 +1855,15 @@ impl<'a> LowLevelCall<'a> { /// Equality and inequality /// These can operate on any data type (except functions) so they're more complex than other operators. fn eq_or_neq(&self, backend: &mut WasmBackend<'a>) { - let arg_layout = backend.storage.symbol_layouts[&self.arguments[0]]; - let other_arg_layout = backend.storage.symbol_layouts[&self.arguments[1]]; + let arg_layout = + backend.storage.symbol_layouts[&self.arguments[0]].runtime_representation(); + let other_arg_layout = + backend.storage.symbol_layouts[&self.arguments[1]].runtime_representation(); debug_assert!( arg_layout == other_arg_layout, - "Cannot do `==` comparison on different types" + "Cannot do `==` comparison on different types: {:?} vs {:?}", + arg_layout, + other_arg_layout ); let invert_result = matches!(self.lowlevel, LowLevel::NotEq); @@ -2113,6 +2115,18 @@ pub fn call_higher_order_lowlevel<'a>( .. } = passed_function; + // The zig lowlevel builtins expect the passed functions' closure data to always + // be sent as an opaque pointer. On the Roc side, however, we need to call the passed function + // with the Roc representation of the closure data. There are three possible cases for that + // representation: + // + // 1. The closure data is a struct + // 2. The closure data is an unwrapped value + // 3. There is no closure data + // + // To uniformly deal with the first two cases, always put the closure data, when it exists, + // into a one-element struct. That way, we get a pointer (i32) that can be passed to the zig lowlevels. + // The wrapper around the passed function will access the actual closure data in the struct. let (closure_data_layout, closure_data_exists) = match backend.storage.symbol_layouts[captured_environment] { Layout::LambdaSet(lambda_set) => { @@ -2131,6 +2145,46 @@ pub fn call_higher_order_lowlevel<'a>( x => internal_error!("Closure data has an invalid layout\n{:?}", x), }; + let (wrapped_captured_environment, wrapped_captures_layout) = if closure_data_exists { + // If there is closure data, make sure we put in a struct it before passing it to the + // external builtin impl. That way it's always an `i32` pointer. + let wrapped_closure_data_sym = backend.create_symbol("wrapped_captures"); + let wrapped_captures_layout = + Layout::struct_no_name_order(backend.env.arena.alloc([closure_data_layout])); + + // make sure that the wrapping struct is available in stack memory, so we can hand out a + // pointer to it. + let wrapped_storage = backend.storage.allocate_var( + wrapped_captures_layout, + wrapped_closure_data_sym, + crate::storage::StoredVarKind::Variable, + ); + + let (wrapped_storage_local_ptr, wrapped_storage_offset) = match wrapped_storage { + StoredValue::StackMemory { location, .. } => { + location.local_and_offset(backend.storage.stack_frame_pointer) + } + other => internal_error!( + "Struct should be allocated in stack memory, but it's in {:?}", + other + ), + }; + + // copy the actual closure data into the first and only element of the wrapping struct. + backend.storage.copy_value_to_memory( + &mut backend.code_builder, + wrapped_storage_local_ptr, + wrapped_storage_offset, + *captured_environment, + ); + + (wrapped_closure_data_sym, wrapped_captures_layout) + } else { + // If we don't capture anything, pass along the captured environment as-is - the wrapper + // function will take care not to unwrap this. + (*captured_environment, closure_data_layout) + }; + // We create a wrapper around the passed function, which just unboxes the arguments. // This allows Zig builtins to have a generic pointer-based interface. let helper_proc_source = { @@ -2164,7 +2218,7 @@ pub fn call_higher_order_lowlevel<'a>( argument_layouts.len() }; - wrapper_arg_layouts.push(closure_data_layout); + wrapper_arg_layouts.push(wrapped_captures_layout); wrapper_arg_layouts.extend( argument_layouts .iter() @@ -2204,7 +2258,7 @@ pub fn call_higher_order_lowlevel<'a>( .get_refcount_fn_index(Layout::Builtin(Builtin::Int(IntWidth::I32)), HelperOp::Inc); backend.get_fn_ptr(inc_fn) } else { - let inc_fn = backend.get_refcount_fn_index(closure_data_layout, HelperOp::Inc); + let inc_fn = backend.get_refcount_fn_index(wrapped_captures_layout, HelperOp::Inc); backend.get_fn_ptr(inc_fn) }; @@ -2218,7 +2272,7 @@ pub fn call_higher_order_lowlevel<'a>( wrapper_fn_ptr, inc_fn_ptr, closure_data_exists, - *captured_environment, + wrapped_captured_environment, *owns_captured_environment, ), @@ -2231,7 +2285,7 @@ pub fn call_higher_order_lowlevel<'a>( wrapper_fn_ptr, inc_fn_ptr, closure_data_exists, - *captured_environment, + wrapped_captured_environment, *owns_captured_environment, ), @@ -2244,7 +2298,7 @@ pub fn call_higher_order_lowlevel<'a>( wrapper_fn_ptr, inc_fn_ptr, closure_data_exists, - *captured_environment, + wrapped_captured_environment, *owns_captured_environment, ), @@ -2257,7 +2311,7 @@ pub fn call_higher_order_lowlevel<'a>( wrapper_fn_ptr, inc_fn_ptr, closure_data_exists, - *captured_environment, + wrapped_captured_environment, *owns_captured_environment, ), @@ -2280,7 +2334,9 @@ pub fn call_higher_order_lowlevel<'a>( backend.storage.load_symbol_zig(cb, *xs); cb.i32_const(wrapper_fn_ptr); if closure_data_exists { - backend.storage.load_symbols(cb, &[*captured_environment]); + backend + .storage + .load_symbols(cb, &[wrapped_captured_environment]); } else { // load_symbols assumes that a zero-size arg should be eliminated in code gen, // but that's a specialization that our Zig code doesn't have! Pass a null pointer. diff --git a/crates/compiler/gen_wasm/src/storage.rs b/crates/compiler/gen_wasm/src/storage.rs index 90000cc0ff..830361f20d 100644 --- a/crates/compiler/gen_wasm/src/storage.rs +++ b/crates/compiler/gen_wasm/src/storage.rs @@ -719,9 +719,10 @@ impl<'a> Storage<'a> { ) => { debug_assert!(to_value_type == from_value_type); debug_assert!(to_size == from_size); + // Note: load_symbols will not destroy the value, so we can use it again later. + // It will leave a Popped marker in the VM stack model in CodeBuilder self.load_symbols(code_builder, &[from_symbol]); code_builder.set_local(*to_local_id); - self.symbol_storage_map.insert(from_symbol, to.clone()); } ( diff --git a/crates/compiler/gen_wasm/src/wasm32_result.rs b/crates/compiler/gen_wasm/src/wasm32_result.rs index 881ab81c07..7f0851e2d8 100644 --- a/crates/compiler/gen_wasm/src/wasm32_result.rs +++ b/crates/compiler/gen_wasm/src/wasm32_result.rs @@ -234,6 +234,14 @@ impl Wasm32Result for () { } } +impl Wasm32Result for std::convert::Infallible { + fn build_wrapper_body(code_builder: &mut CodeBuilder, main_function_index: u32) { + code_builder.call(main_function_index, 0, false); + code_builder.get_global(0); + code_builder.build_fn_header_and_footer(&[], 0, None); + } +} + impl Wasm32Result for (T, U) where T: Wasm32Result + Wasm32Sized, diff --git a/crates/compiler/ident/src/lib.rs b/crates/compiler/ident/src/lib.rs index 23e7fc6759..12d48a9721 100644 --- a/crates/compiler/ident/src/lib.rs +++ b/crates/compiler/ident/src/lib.rs @@ -203,6 +203,30 @@ impl From<&str> for IdentStr { } } +impl From for String { + fn from(ident_str: IdentStr) -> Self { + if ident_str.is_small_str() { + // Copy it to a heap allocation + ident_str.as_str().to_string() + } else { + // Reuse the existing heap allocation + let string = unsafe { + String::from_raw_parts( + ident_str.as_ptr() as *mut u8, + ident_str.len(), + ident_str.len(), + ) + }; + + // Make sure not to drop the IdentStr, since now there's + // a String referencing its heap-allocated contents. + std::mem::forget(ident_str); + + string + } + } +} + impl From for IdentStr { fn from(string: String) -> Self { if string.len() <= Self::SMALL_STR_BYTES { diff --git a/crates/compiler/late_solve/src/lib.rs b/crates/compiler/late_solve/src/lib.rs index 76bab34e76..058d89e74d 100644 --- a/crates/compiler/late_solve/src/lib.rs +++ b/crates/compiler/late_solve/src/lib.rs @@ -358,7 +358,7 @@ pub fn unify( ); // At this point we can't do anything with must-implement constraints, since we're no // longer solving. We must assume that they were totally caught during solving. - // After we land https://github.com/rtfeldman/roc/issues/3207 this concern should totally + // After we land https://github.com/roc-lang/roc/issues/3207 this concern should totally // go away. let _ = must_implement_constraints; // Pools are only used to keep track of variable ranks for generalization purposes. diff --git a/crates/compiler/load/src/lib.rs b/crates/compiler/load/src/lib.rs index d9b7d6f1ba..524783d5b9 100644 --- a/crates/compiler/load/src/lib.rs +++ b/crates/compiler/load/src/lib.rs @@ -54,6 +54,29 @@ pub fn load_single_threaded<'a>( ) } +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum LoadMonomorphizedError<'a> { + LoadingProblem(LoadingProblem<'a>), + /// Errors in the module that should be reported, without compiling the executable. + /// Relevant in check-and-then-build mode. + ErrorModule(LoadedModule), +} + +impl<'a> From> for LoadMonomorphizedError<'a> { + fn from(problem: LoadingProblem<'a>) -> Self { + Self::LoadingProblem(problem) + } +} + +// HACK only relevant because of some uses of `map_err` that decay into this error, but call `todo` - +// rustc seems to be unhappy with that. +impl<'a> From<()> for LoadMonomorphizedError<'a> { + fn from(_: ()) -> Self { + todo!() + } +} + #[allow(clippy::too_many_arguments)] pub fn load_and_monomorphize_from_str<'a>( arena: &'a Bump, @@ -78,14 +101,14 @@ pub fn load_and_monomorphize( filename: PathBuf, exposed_types: ExposedByModule, load_config: LoadConfig, -) -> Result, LoadingProblem<'_>> { +) -> Result, LoadMonomorphizedError<'_>> { use LoadResult::*; let load_start = LoadStart::from_path(arena, filename, load_config.render)?; match load(arena, load_start, exposed_types, load_config)? { Monomorphized(module) => Ok(module), - TypeChecked(_) => unreachable!(""), + TypeChecked(module) => Err(LoadMonomorphizedError::ErrorModule(module)), } } diff --git a/crates/compiler/load_internal/Cargo.toml b/crates/compiler/load_internal/Cargo.toml index 0967a2a2c2..74cec5f3b8 100644 --- a/crates/compiler/load_internal/Cargo.toml +++ b/crates/compiler/load_internal/Cargo.toml @@ -29,10 +29,10 @@ roc_debug_flags = { path = "../debug_flags" } ven_pretty = { path = "../../vendor/pretty" } bumpalo = { version = "3.8.0", features = ["collections"] } parking_lot = "0.12" -crossbeam = "0.8.1" +crossbeam = "0.8.2" [dev-dependencies] pretty_assertions = "1.0.0" maplit = "1.0.2" -indoc = "1.0.3" +indoc = "1.0.7" roc_test_utils = { path = "../../test_utils" } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index e331e8b584..b733e0845c 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -130,13 +130,15 @@ pub enum ExecutionMode { Test, Check, Executable, + /// Like [`ExecutionMode::Executable`], but stops in the presence of type errors. + ExecutableIfCheck, } impl ExecutionMode { fn goal_phase(&self) -> Phase { match self { ExecutionMode::Test | ExecutionMode::Executable => Phase::MakeSpecializations, - ExecutionMode::Check => Phase::SolveTypes, + ExecutionMode::Check | ExecutionMode::ExecutableIfCheck => Phase::SolveTypes, } } } @@ -168,6 +170,22 @@ struct ModuleCache<'a> { sources: MutMap, } +impl<'a> ModuleCache<'a> { + pub fn total_problems(&self) -> usize { + let mut total = 0; + + for problems in self.can_problems.values() { + total += problems.len(); + } + + for problems in self.type_problems.values() { + total += problems.len(); + } + + total + } +} + impl Default for ModuleCache<'_> { fn default() -> Self { let mut module_names = MutMap::default(); @@ -2385,12 +2403,35 @@ fn update<'a>( .extend(solved_module.aliases.keys().copied()); } - if is_host_exposed && state.goal_phase() == Phase::SolveTypes { + let finish_type_checking = is_host_exposed && + (state.goal_phase() == Phase::SolveTypes) + // If we're running in check-and-then-build mode, only exit now there are errors. + && (!matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) || state.module_cache.total_problems() > 0); + + if finish_type_checking { debug_assert!(work.is_empty()); debug_assert!(state.dependencies.solved_all()); state.timings.insert(module_id, module_timing); + if matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) { + // We there may outstanding modules in the typecheked cache whose ident IDs + // aren't registered; transfer all of their idents over to the state, since + // we're now done and ready to report errors. + for ( + module_id, + TypeCheckedModule { + ident_ids, + module_timing, + .. + }, + ) in state.module_cache.typechecked.drain() + { + state.constrained_ident_ids.insert(module_id, ident_ids); + state.timings.insert(module_id, module_timing); + } + } + let documentation = { let mut empty = MutMap::default(); std::mem::swap(&mut empty, &mut state.module_cache.documentation); @@ -2427,7 +2468,9 @@ fn update<'a>( }, ); - if state.goal_phase() > Phase::SolveTypes { + if state.goal_phase() > Phase::SolveTypes + || matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) + { let layout_cache = state .layout_caches .pop() @@ -2452,6 +2495,25 @@ fn update<'a>( state.timings.insert(module_id, module_timing); } + let work = if is_host_exposed + && matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) + { + debug_assert!( + work.is_empty(), + "work left over after host exposed is checked" + ); + + // Update the goal phase to target full codegen. + state.exec_mode = ExecutionMode::Executable; + + // Load the find + make specializations portion of the dependency graph. + state + .dependencies + .load_find_and_make_specializations_after_check() + } else { + work + }; + start_tasks(arena, &mut state, work, injector, worker_listeners)?; } @@ -2810,7 +2872,7 @@ fn finish_specialization( let entry_point = { match exec_mode { ExecutionMode::Test => EntryPoint::Test, - ExecutionMode::Executable => { + ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => { let path_to_platform = { use PlatformPath::*; let package_name = match platform_path { @@ -5007,7 +5069,9 @@ fn build_pending_specializations<'a>( // skip expectations if we're not going to run them match execution_mode { ExecutionMode::Test => { /* fall through */ } - ExecutionMode::Check | ExecutionMode::Executable => continue, + ExecutionMode::Check + | ExecutionMode::Executable + | ExecutionMode::ExecutableIfCheck => continue, } // mark this symbol as a top-level thunk before any other work on the procs @@ -5081,7 +5145,9 @@ fn build_pending_specializations<'a>( // skip expectations if we're not going to run them match execution_mode { ExecutionMode::Test => { /* fall through */ } - ExecutionMode::Check | ExecutionMode::Executable => continue, + ExecutionMode::Check + | ExecutionMode::Executable + | ExecutionMode::ExecutableIfCheck => continue, } // mark this symbol as a top-level thunk before any other work on the procs diff --git a/crates/compiler/load_internal/src/lib.rs b/crates/compiler/load_internal/src/lib.rs index 96dc076acf..e7131d151e 100644 --- a/crates/compiler/load_internal/src/lib.rs +++ b/crates/compiler/load_internal/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod docs; pub mod file; diff --git a/crates/compiler/load_internal/src/work.rs b/crates/compiler/load_internal/src/work.rs index 6c2226f6cb..a264866395 100644 --- a/crates/compiler/load_internal/src/work.rs +++ b/crates/compiler/load_internal/src/work.rs @@ -166,11 +166,10 @@ impl<'a> Dependencies<'a> { } } - if goal_phase >= MakeSpecializations { - // Add make specialization dependents - self.make_specializations_dependents - .add_succ(module_id, dependencies.iter().map(|dep| *dep.as_inner())); - } + // Add "make specialization" dependents. Even if we're not targeting making + // specializations right now, we may re-enter to do so later. + self.make_specializations_dependents + .add_succ(module_id, dependencies.iter().map(|dep| *dep.as_inner())); // add dependencies for self // phase i + 1 of a file always depends on phase i being completed @@ -374,6 +373,80 @@ impl<'a> Dependencies<'a> { } } + /// Loads the dependency graph to find and make specializations, and returns the next jobs to + /// be run. + /// + /// This should be used when the compiler wants to build or run a Roc executable if and only if + /// previous stages succeed; in such cases we load the dependency graph dynamically. + pub fn load_find_and_make_specializations_after_check(&mut self) -> MutSet<(ModuleId, Phase)> { + let mut output = MutSet::default(); + + let mut make_specializations_dependents = MakeSpecializationsDependents::default(); + let default_make_specializations_dependents_len = make_specializations_dependents.0.len(); + std::mem::swap( + &mut self.make_specializations_dependents, + &mut make_specializations_dependents, + ); + + for (&module, info) in make_specializations_dependents.0.iter_mut() { + debug_assert!(self.status.get_mut(&Job::Step(module, Phase::FindSpecializations)).is_none(), "should only have targeted solving types, but there is already a goal to find specializations"); + debug_assert!(self.status.get_mut(&Job::Step(module, Phase::MakeSpecializations)).is_none(), "should only have targeted solving types, but there is already a goal to make specializations"); + debug_assert!( + module == ModuleId::DERIVED_GEN || info.succ.contains(&ModuleId::DERIVED_GEN), + "derived module not accounted for in {:?}", + (module, info) + ); + + let mut has_find_specialization_dep = false; + for &module_dep in info.succ.iter() { + // The modules in `succ` are the modules for which specializations should be made + // after the current one. But, their specializations should be found before the + // current one. + if module_dep != ModuleId::DERIVED_GEN { + // We never find specializations for DERIVED_GEN + self.add_dependency(module, module_dep, Phase::FindSpecializations); + has_find_specialization_dep = true; + } + + self.add_dependency(module_dep, module, Phase::MakeSpecializations); + self.add_dependency(ModuleId::DERIVED_GEN, module, Phase::MakeSpecializations); + + // `module_dep` can't make its specializations until the current module does. + info.has_pred = true; + } + + if module != ModuleId::DERIVED_GEN { + self.add_to_status_for_phase(module, Phase::FindSpecializations); + self.add_dependency_help( + module, + module, + Phase::MakeSpecializations, + Phase::FindSpecializations, + ); + } + self.add_to_status_for_phase(module, Phase::MakeSpecializations); + + if !has_find_specialization_dep && module != ModuleId::DERIVED_GEN { + // We don't depend on any other modules having their specializations found first, + // so start finding specializations from this module. + output.insert((module, Phase::FindSpecializations)); + } + } + + std::mem::swap( + &mut self.make_specializations_dependents, + &mut make_specializations_dependents, + ); + debug_assert_eq!( + make_specializations_dependents.0.len(), + default_make_specializations_dependents_len, + "more modules were added to the graph: {:?}", + make_specializations_dependents + ); + + output + } + /// Load the entire "make specializations" dependency graph and start from the top. pub fn reload_make_specialization_pass(&mut self) -> MutSet<(ModuleId, Phase)> { let mut output = MutSet::default(); diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 23ff1e9443..cd471c875a 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -699,7 +699,7 @@ fn platform_parse_error() { } #[test] -// See https://github.com/rtfeldman/roc/issues/2413 +// See https://github.com/roc-lang/roc/issues/2413 fn platform_exposes_main_return_by_pointer_issue() { let modules = vec![ ( diff --git a/crates/compiler/module/src/ident.rs b/crates/compiler/module/src/ident.rs index df8e90be0d..ff5f7f8fe6 100644 --- a/crates/compiler/module/src/ident.rs +++ b/crates/compiler/module/src/ident.rs @@ -65,6 +65,12 @@ impl TagName { } } +impl From<&str> for TagName { + fn from(string: &str) -> Self { + Self(string.into()) + } +} + impl ModuleName { // NOTE: After adding one of these, go to `impl ModuleId` and // add a corresponding ModuleId to there! @@ -187,6 +193,20 @@ impl Lowercase { } } +impl From for String { + fn from(lowercase: Lowercase) -> Self { + lowercase.0.into() + } +} + +impl From for Box { + fn from(lowercase: Lowercase) -> Self { + let string: String = lowercase.0.into(); + + string.into() + } +} + impl<'a> From<&'a str> for Lowercase { fn from(string: &'a str) -> Self { Self(string.into()) diff --git a/crates/compiler/module/src/lib.rs b/crates/compiler/module/src/lib.rs index 044f697a07..e553c858d6 100644 --- a/crates/compiler/module/src/lib.rs +++ b/crates/compiler/module/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] pub mod called_via; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 455d439e2c..fd48b888b2 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -152,6 +152,7 @@ impl Symbol { } pub const fn to_ne_bytes(self) -> [u8; 8] { + // repr(packed(4)) is repr(c), and with the fields as defined will not having padding. unsafe { std::mem::transmute(self) } } @@ -1416,10 +1417,11 @@ define_builtins! { 19 DECODE_BOOL: "bool" 20 DECODE_STRING: "string" 21 DECODE_LIST: "list" - 22 DECODE_CUSTOM: "custom" - 23 DECODE_DECODE_WITH: "decodeWith" - 24 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial" - 25 DECODE_FROM_BYTES: "fromBytes" + 22 DECODE_RECORD: "record" + 23 DECODE_CUSTOM: "custom" + 24 DECODE_DECODE_WITH: "decodeWith" + 25 DECODE_FROM_BYTES_PARTIAL: "fromBytesPartial" + 26 DECODE_FROM_BYTES: "fromBytes" } 13 JSON: "Json" => { 0 JSON_JSON: "Json" diff --git a/crates/compiler/mono/Cargo.toml b/crates/compiler/mono/Cargo.toml index 83650ee24f..6119488ac9 100644 --- a/crates/compiler/mono/Cargo.toml +++ b/crates/compiler/mono/Cargo.toml @@ -23,5 +23,5 @@ roc_error_macros = {path="../../error_macros"} roc_debug_flags = {path="../debug_flags"} ven_pretty = { path = "../../vendor/pretty" } bumpalo = { version = "3.8.0", features = ["collections"] } -hashbrown = { version = "0.12.1", features = [ "bumpalo" ] } +hashbrown = { version = "0.12.3", features = [ "bumpalo" ] } static_assertions = "1.1.0" diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 066c3a1857..c70c5c05f4 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -1,8 +1,9 @@ #![allow(clippy::manual_map)] use crate::layout::{ - Builtin, CapturesNiche, ClosureRepresentation, LambdaName, LambdaSet, Layout, LayoutCache, - LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout, WrappedVariant, + Builtin, CapturesNiche, ClosureCallOptions, ClosureRepresentation, EnumDispatch, LambdaName, + LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, TagIdIntType, UnionLayout, + WrappedVariant, }; use bumpalo::collections::{CollectIn, Vec}; use bumpalo::Bump; @@ -343,10 +344,16 @@ impl<'a> Proc<'a> { D::Doc: Clone, A: Clone, { - let args_doc = self - .args - .iter() - .map(|(_, symbol)| symbol_to_doc(alloc, *symbol)); + let args_doc = self.args.iter().map(|(layout, symbol)| { + let arg_doc = symbol_to_doc(alloc, *symbol); + if pretty_print_ir_symbols() { + arg_doc + .append(alloc.reflow(": ")) + .append(layout.to_doc(alloc, Parens::NotNeeded)) + } else { + arg_doc + } + }); if pretty_print_ir_symbols() { alloc @@ -621,6 +628,10 @@ impl<'a> Suspended<'a> { } } + fn is_empty(&self) -> bool { + self.symbol_or_lambdas.is_empty() + } + fn specialization( &mut self, subs: &mut Subs, @@ -654,8 +665,22 @@ enum PendingSpecializations<'a> { /// that we can give specializations we need to modules higher up in the dependency chain, so /// that they can start making specializations too Finding(Suspended<'a>), - /// We are making specializations. If any new one comes up, we can just make it immediately - Making, + /// We are making specializations. + /// If any new one comes up while specializing a body, we can do one of two things: + /// - if the new specialization is for a symbol that is not in the current stack of symbols + /// being specialized, make it immediately + /// - if it is, we must suspend the specialization, and we'll do it once the stack is clear + /// again. + Making(Suspended<'a>), +} + +impl<'a> PendingSpecializations<'a> { + fn is_empty(&self) -> bool { + match self { + PendingSpecializations::Finding(suspended) + | PendingSpecializations::Making(suspended) => suspended.is_empty(), + } + } } #[derive(Clone, Debug, Default)] @@ -940,6 +965,8 @@ pub struct Procs<'a> { pub runtime_errors: BumpMap, pub externals_we_need: BumpMap>, symbol_specializations: SymbolSpecializations<'a>, + /// The current set of functions under specialization. + pub specialization_stack: Vec<'a, Symbol>, } impl<'a> Procs<'a> { @@ -954,8 +981,43 @@ impl<'a> Procs<'a> { runtime_errors: BumpMap::new_in(arena), externals_we_need: BumpMap::new_in(arena), symbol_specializations: Default::default(), + specialization_stack: Vec::with_capacity_in(16, arena), } } + + fn push_active_specialization(&mut self, specialization: Symbol) { + self.specialization_stack.push(specialization); + } + + fn pop_active_specialization(&mut self, specialization: Symbol) { + let popped = self + .specialization_stack + .pop() + .expect("specialization stack is empty"); + debug_assert_eq!( + popped, specialization, + "incorrect popped specialization: passed {:?}, but was {:?}", + specialization, popped + ); + } + + /// If we need to specialize a function that is already in the stack, we must wait to do so + /// until that function is popped off. That's because the type environment will be configured + /// for the existing specialization on the stack. + /// + /// For example, in + /// + /// foo = \val, b -> if b then "done" else bar val + /// bar = \_ -> foo {} True + /// foo "" False + /// + /// During the specialization of `foo : Str False -> Str`, we specialize `bar : Str -> Str`, + /// which in turn needs a specialization of `foo : {} False -> Str`. However, we can't + /// specialize both `foo : Str False -> Str` and `foo : {} False -> Str` at the same time, so + /// the latter specialization must be deferred. + fn symbol_needs_suspended_specialization(&self, specialization: Symbol) -> bool { + self.specialization_stack.contains(&specialization) + } } #[derive(Clone, Debug, PartialEq)] @@ -1045,8 +1107,14 @@ impl<'a> Procs<'a> { debug_assert!(layout.arguments.is_empty()); } - match &mut self.pending_specializations { - PendingSpecializations::Finding(suspended) => { + let needs_suspended_specialization = + self.symbol_needs_suspended_specialization(name.name()); + match ( + &mut self.pending_specializations, + needs_suspended_specialization, + ) { + (PendingSpecializations::Finding(suspended), _) + | (PendingSpecializations::Making(suspended), true) => { // register the pending specialization, so this gets code genned later suspended.specialization(env.subs, name, layout, annotation); @@ -1080,7 +1148,7 @@ impl<'a> Procs<'a> { } } } - PendingSpecializations::Making => { + (PendingSpecializations::Making(_), false) => { // Mark this proc as in-progress, so if we're dealing with // mutually recursive functions, we don't loop forever. // (We had a bug around this before this system existed!) @@ -1178,7 +1246,8 @@ impl<'a> Procs<'a> { } // If this is an imported symbol, let its home module make this specialization - if env.is_imported_symbol(name.name()) { + if env.is_imported_symbol(name.name()) || env.is_unloaded_derived_symbol(name.name(), self) + { add_needed_external(self, env, fn_var, name); return; } @@ -1190,11 +1259,17 @@ impl<'a> Procs<'a> { // This should only be called when pending_specializations is Some. // Otherwise, it's being called in the wrong pass! - match &mut self.pending_specializations { - PendingSpecializations::Finding(suspended) => { + let needs_suspended_specialization = + self.symbol_needs_suspended_specialization(name.name()); + match ( + &mut self.pending_specializations, + needs_suspended_specialization, + ) { + (PendingSpecializations::Finding(suspended), _) + | (PendingSpecializations::Making(suspended), true) => { suspended.specialization(env.subs, name, layout, fn_var); } - PendingSpecializations::Making => { + (PendingSpecializations::Making(_), false) => { let symbol = name; let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.name()) { @@ -1207,7 +1282,7 @@ impl<'a> Procs<'a> { // (We had a bug around this before this system existed!) self.specialized.mark_in_progress(symbol.name(), layout); - // See https://github.com/rtfeldman/roc/issues/1600 + // See https://github.com/roc-lang/roc/issues/1600 // // The annotation variable is the generic/lifted/top-level annotation. // It is connected to the variables of the function's body @@ -1342,6 +1417,13 @@ impl<'a, 'i> Env<'a, 'i> { self.home == ModuleId::DERIVED_GEN && symbol.module_id() == ModuleId::DERIVED_SYNTH && !procs.partial_procs.contains_key(symbol) + // TODO: locking to find the answer in the `Derived_gen` module is not great, since + // Derived_gen also blocks other modules specializing. Improve this later. + && self + .derived_module + .lock() + .expect("derived module is poisoned") + .is_derived_def(symbol) } /// Unifies two variables and performs lambda set compaction. @@ -2501,7 +2583,7 @@ fn patterns_to_when<'a>( // are only stores anyway, no branches. // // NOTE this fails if the pattern contains rigid variables, - // see https://github.com/rtfeldman/roc/issues/786 + // see https://github.com/roc-lang/roc/issues/786 // this must be fixed when moving exhaustiveness checking to the new canonical AST for (pattern_var, annotated_mark, pattern) in patterns.into_iter() { if annotated_mark.exhaustive.is_non_exhaustive(env.subs) { @@ -2739,26 +2821,51 @@ pub fn specialize_all<'a>( specializations_for_host: HostSpecializations<'a>, layout_cache: &mut LayoutCache<'a>, ) -> Procs<'a> { - for externals in externals_others_need { - specialize_external_specializations(env, &mut procs, layout_cache, externals); - } - // When calling from_can, pending_specializations should be unavailable. // This must be a single pass, and we must not add any more entries to it! let pending_specializations = std::mem::replace( &mut procs.pending_specializations, - PendingSpecializations::Making, + PendingSpecializations::Making(Suspended::new_in(env.arena)), ); + // Add all of our existing pending specializations. match pending_specializations { - PendingSpecializations::Making => {} PendingSpecializations::Finding(suspended) => { specialize_suspended(env, &mut procs, layout_cache, suspended) } + PendingSpecializations::Making(suspended) => { + debug_assert!( + suspended.is_empty(), + "suspended specializations cannot ever start off non-empty when making" + ); + } } + // Specialize all the symbols everyone else needs. + for externals in externals_others_need { + specialize_external_specializations(env, &mut procs, layout_cache, externals); + } + + // Specialize any symbols the host needs. specialize_host_specializations(env, &mut procs, layout_cache, specializations_for_host); + // Now, we must go through and continuously complete any new suspended specializations that were + // discovered in specializing the other demanded symbols. + while !procs.pending_specializations.is_empty() { + let pending_specializations = std::mem::replace( + &mut procs.pending_specializations, + PendingSpecializations::Making(Suspended::new_in(env.arena)), + ); + match pending_specializations { + PendingSpecializations::Making(suspended) => { + specialize_suspended(env, &mut procs, layout_cache, suspended); + } + PendingSpecializations::Finding(_) => { + internal_error!("should not have this variant after making specializations") + } + } + } + debug_assert!( procs.symbol_specializations.is_empty(), "{:?}", @@ -3127,6 +3234,8 @@ fn specialize_external<'a>( closure: opt_closure_layout, ret_layout, } => { + let mut proc_args = Vec::from_iter_in(proc_args.iter().copied(), env.arena); + // unpack the closure symbols, if any match (opt_closure_layout, captured_symbols) { (Some(closure_layout), CapturedSymbols::Captured(captured)) => { @@ -3196,6 +3305,9 @@ fn specialize_external<'a>( ClosureRepresentation::AlphabeticOrderStruct(field_layouts) => { // captured variables are in symbol-alphabetic order, but now we want // them ordered by their alignment requirements + // + // TODO: sort only the fields and apply the found permutation to the symbols + // TODO: can we move this ordering to `layout_for_member`? let mut combined = Vec::from_iter_in( captured.iter().map(|(x, _)| x).zip(field_layouts.iter()), env.arena, @@ -3210,19 +3322,25 @@ fn specialize_external<'a>( size2.cmp(&size1) }); + let ordered_field_layouts = Vec::from_iter_in( + combined.iter().map(|(_, layout)| **layout), + env.arena, + ); + let ordered_field_layouts = ordered_field_layouts.into_bump_slice(); + debug_assert_eq!( captured.len(), - field_layouts.len(), + ordered_field_layouts.len(), "{:?} captures {:?} but has layout {:?}", lambda_name, &captured, - &field_layouts + &ordered_field_layouts ); for (index, (symbol, layout)) in combined.iter().enumerate() { let expr = Expr::StructAtIndex { index: index as _, - field_layouts, + field_layouts: ordered_field_layouts, structure: Symbol::ARG_CLOSURE, }; @@ -3235,51 +3353,37 @@ fn specialize_external<'a>( env.arena.alloc(specialized_body), ); } - // let symbol = captured[0].0; - // - // substitute_in_exprs( - // env.arena, - // &mut specialized_body, - // symbol, - // Symbol::ARG_CLOSURE, - // ); } - ClosureRepresentation::Other(layout) => match layout { - Layout::Builtin(Builtin::Bool) => { - // just ignore this value - // IDEA don't pass this value in the future - } - Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - // just ignore this value - // IDEA don't pass this value in the future - } - other => { - // NOTE other values always should be wrapped in a 1-element record - unreachable!( - "{:?} is not a valid closure data representation", - other - ) - } - }, + ClosureRepresentation::UnwrappedCapture(_layout) => { + debug_assert_eq!(captured.len(), 1); + let (captured_symbol, _captured_layout) = captured[0]; + + // The capture set is unwrapped, so simply replace the closure argument + // to the function with the unwrapped capture name. + let captured_symbol = get_specialized_name(captured_symbol); + let closure_arg = proc_args.last_mut().unwrap(); + debug_assert_eq!(closure_arg.1, Symbol::ARG_CLOSURE); + closure_arg.1 = captured_symbol; + } + + ClosureRepresentation::EnumDispatch(_) => { + // just ignore this value, since it's not a capture + // IDEA don't pass this value in the future + } } } (None, CapturedSymbols::None) | (None, CapturedSymbols::Captured([])) => {} _ => unreachable!("to closure or not to closure?"), } - let proc_args: Vec<_> = proc_args - .iter() - .map(|&(layout, symbol)| { - // Grab the specialization symbol, if it exists. - let symbol = procs - .symbol_specializations - .remove_single(symbol) - .unwrap_or(symbol); - - (layout, symbol) - }) - .collect_in(env.arena); + proc_args.iter_mut().for_each(|(_layout, symbol)| { + // Grab the specialization symbol, if it exists. + *symbol = procs + .symbol_specializations + .remove_single(*symbol) + .unwrap_or(*symbol); + }); // reset subs, so we don't get type errors when specializing for a different signature layout_cache.rollback_to(cache_snapshot); @@ -3566,6 +3670,8 @@ where let annotation_var = procs.partial_procs.get_id(partial_proc_id).annotation; instantiate_rigids(env.subs, annotation_var); + procs.push_active_specialization(proc_name.name()); + let specialized = specialize_external( env, procs, @@ -3576,6 +3682,8 @@ where partial_proc_id, ); + procs.pop_active_specialization(proc_name.name()); + match specialized { Ok(proc) => { // when successful, the layout after unification should be the layout before unification @@ -3869,7 +3977,7 @@ pub fn with_hole<'a>( ) } Tag { - variant_var, + tag_union_var: variant_var, name: tag_name, arguments: args, .. @@ -5396,29 +5504,42 @@ where Stmt::Let(assigned, expr, lambda_set_layout, hole) } - ClosureRepresentation::Other(Layout::Builtin(Builtin::Bool)) => { - debug_assert_eq!(symbols.len(), 0); + ClosureRepresentation::UnwrappedCapture(_layout) => { + debug_assert_eq!(symbols.len(), 1); - debug_assert_eq!(lambda_set.set.len(), 2); - let tag_id = name.name() != lambda_set.iter_set().next().unwrap().name(); - let expr = Expr::Literal(Literal::Bool(tag_id)); + let mut symbols = symbols; + let (captured_symbol, _) = symbols.next().unwrap(); - Stmt::Let(assigned, expr, lambda_set_layout, hole) + // The capture set is unwrapped, so just replaced the assigned capture symbol with the + // only capture. + let mut hole = hole.clone(); + substitute_in_exprs(env.arena, &mut hole, assigned, *captured_symbol); + hole } - ClosureRepresentation::Other(Layout::Builtin(Builtin::Int(IntWidth::U8))) => { - debug_assert_eq!(symbols.len(), 0); + ClosureRepresentation::EnumDispatch(repr) => match repr { + EnumDispatch::Bool => { + debug_assert_eq!(symbols.len(), 0); - debug_assert!(lambda_set.set.len() > 2); - let tag_id = lambda_set - .iter_set() - .position(|s| s.name() == name.name()) - .unwrap() as u8; + debug_assert_eq!(lambda_set.len(), 2); + let tag_id = name.name() != lambda_set.iter_set().next().unwrap().name(); + let expr = Expr::Literal(Literal::Bool(tag_id)); - let expr = Expr::Literal(Literal::Byte(tag_id)); + Stmt::Let(assigned, expr, lambda_set_layout, hole) + } + EnumDispatch::U8 => { + debug_assert_eq!(symbols.len(), 0); - Stmt::Let(assigned, expr, lambda_set_layout, hole) - } - _ => unreachable!(), + debug_assert!(lambda_set.len() > 2); + let tag_id = lambda_set + .iter_set() + .position(|s| s.name() == name.name()) + .unwrap() as u8; + + let expr = Expr::Literal(Literal::Byte(tag_id)); + + Stmt::Let(assigned, expr, lambda_set_layout, hole) + } + }, }; result @@ -5692,7 +5813,7 @@ fn tag_union_to_function<'a>( } let loc_body = Loc::at_zero(roc_can::expr::Expr::Tag { - variant_var: return_variable, + tag_union_var: return_variable, name: tag_name, arguments: loc_expr_args, ext_var, @@ -5857,10 +5978,7 @@ fn register_capturing_closure<'a>( Content::Structure(FlatType::Func(_, closure_var, _)) => { match LambdaSet::from_var(env.arena, env.subs, closure_var, env.target_info) { Ok(lambda_set) => { - if let Layout::Struct { - field_layouts: &[], .. - } = lambda_set.runtime_representation() - { + if lambda_set.is_represented().is_none() { CapturedSymbols::None } else { let mut temp = Vec::from_iter_in(captured_symbols, env.arena); @@ -5869,7 +5987,7 @@ fn register_capturing_closure<'a>( } } Err(_) => { - // just allow this. see https://github.com/rtfeldman/roc/issues/1585 + // just allow this. see https://github.com/roc-lang/roc/issues/1585 if captured_symbols.is_empty() { CapturedSymbols::None } else { @@ -7162,7 +7280,7 @@ fn can_reuse_symbol<'a>( if arguments.contains(&symbol) { Value(symbol) - } else if env.is_imported_symbol(symbol) { + } else if env.is_imported_symbol(symbol) || env.is_unloaded_derived_symbol(symbol, procs) { Imported(symbol) } else if procs.partial_procs.contains_key(symbol) { LocalFunction(symbol) @@ -7334,7 +7452,10 @@ fn specialize_symbol<'a>( match procs.get_partial_proc(original) { None => { match arg_var { - Some(arg_var) if env.is_imported_symbol(original) => { + Some(arg_var) + if env.is_imported_symbol(original) + || env.is_unloaded_derived_symbol(original, procs) => + { let raw = match layout_cache.raw_from_var(env.arena, arg_var, env.subs) { Ok(v) => v, Err(e) => return_on_layout_error_help!(env, e, "specialize_symbol"), @@ -7603,7 +7724,7 @@ fn build_call<'a>( Stmt::Let(assigned, Expr::Call(call), return_layout, hole) } -/// See https://github.com/rtfeldman/roc/issues/1549 +/// See https://github.com/roc-lang/roc/issues/1549 /// /// What happened is that a function has a type error, but the arguments are not processed. /// That means specializations were missing. Normally that is not a problem, but because @@ -7936,8 +8057,14 @@ fn call_by_name_help<'a>( debug_assert!(top_level_layout.arguments.is_empty()); } - match &mut procs.pending_specializations { - PendingSpecializations::Finding(suspended) => { + let needs_suspended_specialization = + procs.symbol_needs_suspended_specialization(proc_name.name()); + match ( + &mut procs.pending_specializations, + needs_suspended_specialization, + ) { + (PendingSpecializations::Finding(suspended), _) + | (PendingSpecializations::Making(suspended), true) => { debug_assert!(!env.is_imported_symbol(proc_name.name())); // register the pending specialization, so this gets code genned later @@ -7966,7 +8093,7 @@ fn call_by_name_help<'a>( hole, ) } - PendingSpecializations::Making => { + (PendingSpecializations::Making(_), false) => { let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name.name()); let field_symbols = field_symbols.into_bump_slice(); @@ -8100,8 +8227,13 @@ fn call_by_name_module_thunk<'a>( debug_assert!(top_level_layout.arguments.is_empty()); } - match &mut procs.pending_specializations { - PendingSpecializations::Finding(suspended) => { + let needs_suspended_specialization = procs.symbol_needs_suspended_specialization(proc_name); + match ( + &mut procs.pending_specializations, + needs_suspended_specialization, + ) { + (PendingSpecializations::Finding(suspended), _) + | (PendingSpecializations::Making(suspended), true) => { debug_assert!(!env.is_imported_symbol(proc_name)); // register the pending specialization, so this gets code genned later @@ -8114,7 +8246,7 @@ fn call_by_name_module_thunk<'a>( force_thunk(env, proc_name, inner_layout, assigned, hole) } - PendingSpecializations::Making => { + (PendingSpecializations::Making(_), false) => { let opt_partial_proc = procs.partial_procs.symbol_to_id(proc_name); match opt_partial_proc { @@ -9166,9 +9298,9 @@ fn lowlevel_match_on_lambda_set<'a, ToLowLevelCall>( where ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy, { - match lambda_set.runtime_representation() { - Layout::VOID => empty_lambda_set_error(), - Layout::Union(union_layout) => { + match lambda_set.call_by_name_options() { + ClosureCallOptions::Void => empty_lambda_set_error(), + ClosureCallOptions::Union(union_layout) => { let closure_tag_id_symbol = env.unique_symbol(); let result = lowlevel_union_lambda_set_to_switch( @@ -9197,7 +9329,7 @@ where env.arena.alloc(result), ) } - Layout::Struct { .. } => match lambda_set.iter_set().next() { + ClosureCallOptions::Struct { .. } => match lambda_set.iter_set().next() { Some(lambda_name) => { let call_spec_id = env.next_call_specialization_id(); let update_mode = env.next_update_mode_id(); @@ -9222,39 +9354,58 @@ where hole.clone() } }, - Layout::Builtin(Builtin::Bool) => { - let closure_tag_id_symbol = closure_data_symbol; + ClosureCallOptions::UnwrappedCapture(_) => { + let lambda_name = lambda_set + .iter_set() + .next() + .expect("no function in lambda set"); - lowlevel_enum_lambda_set_to_switch( - env, - lambda_set.iter_set(), - closure_tag_id_symbol, - Layout::Builtin(Builtin::Bool), + let call_spec_id = env.next_call_specialization_id(); + let update_mode = env.next_update_mode_id(); + let call = to_lowlevel_call(( + lambda_name, closure_data_symbol, lambda_set.is_represented(), - to_lowlevel_call, - return_layout, - assigned, - hole, - ) - } - Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - let closure_tag_id_symbol = closure_data_symbol; + call_spec_id, + update_mode, + )); - lowlevel_enum_lambda_set_to_switch( - env, - lambda_set.iter_set(), - closure_tag_id_symbol, - Layout::Builtin(Builtin::Int(IntWidth::U8)), - closure_data_symbol, - lambda_set.is_represented(), - to_lowlevel_call, - return_layout, - assigned, - hole, - ) + build_call(env, call, assigned, return_layout, env.arena.alloc(hole)) } - other => todo!("{:?}", other), + ClosureCallOptions::EnumDispatch(repr) => match repr { + EnumDispatch::Bool => { + let closure_tag_id_symbol = closure_data_symbol; + + lowlevel_enum_lambda_set_to_switch( + env, + lambda_set.iter_set(), + closure_tag_id_symbol, + Layout::Builtin(Builtin::Bool), + closure_data_symbol, + lambda_set.is_represented(), + to_lowlevel_call, + return_layout, + assigned, + hole, + ) + } + EnumDispatch::U8 => { + let closure_tag_id_symbol = closure_data_symbol; + + lowlevel_enum_lambda_set_to_switch( + env, + lambda_set.iter_set(), + closure_tag_id_symbol, + Layout::Builtin(Builtin::Int(IntWidth::U8)), + closure_data_symbol, + lambda_set.is_represented(), + to_lowlevel_call, + return_layout, + assigned, + hole, + ) + } + }, } } @@ -9345,15 +9496,14 @@ fn match_on_lambda_set<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - match lambda_set.runtime_representation() { - Layout::VOID => empty_lambda_set_error(), - Layout::Union(union_layout) => { + match lambda_set.call_by_name_options() { + ClosureCallOptions::Void => empty_lambda_set_error(), + ClosureCallOptions::Union(union_layout) => { let closure_tag_id_symbol = env.unique_symbol(); let result = union_lambda_set_to_switch( env, lambda_set, - Layout::Union(union_layout), closure_tag_id_symbol, union_layout.tag_id_layout(), closure_data_symbol, @@ -9377,9 +9527,9 @@ fn match_on_lambda_set<'a>( env.arena.alloc(result), ) } - Layout::Struct { + ClosureCallOptions::Struct { field_layouts, - field_order_hash, + field_order_hash: _, } => { let function_symbol = match lambda_set.iter_set().next() { Some(function_symbol) => function_symbol, @@ -9403,10 +9553,6 @@ fn match_on_lambda_set<'a>( _ => ClosureInfo::Captures { lambda_set, closure_data_symbol, - closure_data_layout: Layout::Struct { - field_layouts, - field_order_hash, - }, }, }; @@ -9421,15 +9567,21 @@ fn match_on_lambda_set<'a>( hole, ) } - Layout::Builtin(Builtin::Bool) => { - let closure_tag_id_symbol = closure_data_symbol; + ClosureCallOptions::UnwrappedCapture(_) => { + let function_symbol = lambda_set + .iter_set() + .next() + .expect("no function in lambda set"); - enum_lambda_set_to_switch( - env, - lambda_set.iter_set(), - closure_tag_id_symbol, - Layout::Builtin(Builtin::Bool), + let closure_info = ClosureInfo::Captures { + lambda_set, closure_data_symbol, + }; + + union_lambda_set_branch_help( + env, + function_symbol, + closure_info, argument_symbols, argument_layouts, return_layout, @@ -9437,23 +9589,38 @@ fn match_on_lambda_set<'a>( hole, ) } - Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - let closure_tag_id_symbol = closure_data_symbol; + ClosureCallOptions::EnumDispatch(repr) => match repr { + EnumDispatch::Bool => { + let closure_tag_id_symbol = closure_data_symbol; - enum_lambda_set_to_switch( - env, - lambda_set.iter_set(), - closure_tag_id_symbol, - Layout::Builtin(Builtin::Int(IntWidth::U8)), - closure_data_symbol, - argument_symbols, - argument_layouts, - return_layout, - assigned, - hole, - ) - } - other => todo!("{:?}", other), + enum_lambda_set_to_switch( + env, + lambda_set.iter_set(), + closure_tag_id_symbol, + Layout::Builtin(Builtin::Bool), + argument_symbols, + argument_layouts, + return_layout, + assigned, + hole, + ) + } + EnumDispatch::U8 => { + let closure_tag_id_symbol = closure_data_symbol; + + enum_lambda_set_to_switch( + env, + lambda_set.iter_set(), + closure_tag_id_symbol, + Layout::Builtin(Builtin::Int(IntWidth::U8)), + argument_symbols, + argument_layouts, + return_layout, + assigned, + hole, + ) + } + }, } } @@ -9461,7 +9628,6 @@ fn match_on_lambda_set<'a>( fn union_lambda_set_to_switch<'a>( env: &mut Env<'a, '_>, lambda_set: LambdaSet<'a>, - closure_layout: Layout<'a>, closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, closure_data_symbol: Symbol, @@ -9471,7 +9637,7 @@ fn union_lambda_set_to_switch<'a>( assigned: Symbol, hole: &'a Stmt<'a>, ) -> Stmt<'a> { - if lambda_set.set.is_empty() { + if lambda_set.is_empty() { // NOTE this can happen if there is a type error somewhere. Since the lambda set is empty, // there is really nothing we can do here. We generate a runtime error here which allows // code gen to proceed. We then assume that we hit another (more descriptive) error before @@ -9481,7 +9647,7 @@ fn union_lambda_set_to_switch<'a>( let join_point_id = JoinPointId(env.unique_symbol()); - let mut branches = Vec::with_capacity_in(lambda_set.set.len(), env.arena); + let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena); for (i, lambda_name) in lambda_set.iter_set().enumerate() { let closure_info = if lambda_name.no_captures() { @@ -9490,7 +9656,6 @@ fn union_lambda_set_to_switch<'a>( ClosureInfo::Captures { lambda_set, closure_data_symbol, - closure_data_layout: closure_layout, } }; @@ -9560,11 +9725,10 @@ fn union_lambda_set_branch<'a>( ) } +#[derive(Clone, Copy)] enum ClosureInfo<'a> { Captures { closure_data_symbol: Symbol, - /// The layout of this closure variant - closure_data_layout: Layout<'a>, /// The whole lambda set representation this closure is a variant of lambda_set: LambdaSet<'a>, }, @@ -9586,34 +9750,21 @@ fn union_lambda_set_branch_help<'a>( ClosureInfo::Captures { lambda_set, closure_data_symbol, - closure_data_layout, - } => match closure_data_layout { - Layout::Struct { - field_layouts: &[], .. - } - | Layout::Builtin(Builtin::Bool) - | Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - (argument_layouts_slice, argument_symbols_slice) - } - _ => { - // extend layouts with the layout of the closure environment - let mut argument_layouts = - Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena); - argument_layouts.extend(argument_layouts_slice); - argument_layouts.push(Layout::LambdaSet(lambda_set)); - + } => { + let argument_layouts = + lambda_set.extend_argument_list(env.arena, argument_layouts_slice); + let argument_symbols = if argument_layouts.len() > argument_layouts_slice.len() { // extend symbols with the symbol of the closure environment let mut argument_symbols = Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena); argument_symbols.extend(argument_symbols_slice); argument_symbols.push(closure_data_symbol); - - ( - argument_layouts.into_bump_slice(), - argument_symbols.into_bump_slice(), - ) - } - }, + argument_symbols.into_bump_slice() + } else { + argument_symbols_slice + }; + (argument_layouts, argument_symbols) + } ClosureInfo::DoesNotCapture => { // sometimes unification causes a function that does not itself capture anything // to still get a lambda set that does store information. We must not pass a closure @@ -9637,13 +9788,14 @@ fn union_lambda_set_branch_help<'a>( build_call(env, call, assigned, *return_layout, hole) } +/// Switches over a enum lambda set, which may dispatch to different functions, none of which +/// capture. #[allow(clippy::too_many_arguments)] fn enum_lambda_set_to_switch<'a>( env: &mut Env<'a, '_>, lambda_set: impl ExactSizeIterator>, closure_tag_id_symbol: Symbol, closure_tag_id_layout: Layout<'a>, - closure_data_symbol: Symbol, argument_symbols: &'a [Symbol], argument_layouts: &'a [Layout<'a>], return_layout: &'a Layout<'a>, @@ -9656,15 +9808,11 @@ fn enum_lambda_set_to_switch<'a>( let mut branches = Vec::with_capacity_in(lambda_set.len(), env.arena); - let closure_layout = closure_tag_id_layout; - for (i, lambda_name) in lambda_set.into_iter().enumerate() { let stmt = enum_lambda_set_branch( env, join_point_id, lambda_name, - closure_data_symbol, - closure_layout, argument_symbols, argument_layouts, return_layout, @@ -9700,15 +9848,14 @@ fn enum_lambda_set_to_switch<'a>( } } +/// A branch for an enum lambda set branch dispatch, which never capture! #[allow(clippy::too_many_arguments)] fn enum_lambda_set_branch<'a>( env: &mut Env<'a, '_>, join_point_id: JoinPointId, lambda_name: LambdaName<'a>, - closure_data_symbol: Symbol, - closure_data_layout: Layout<'a>, - argument_symbols_slice: &'a [Symbol], - argument_layouts_slice: &'a [Layout<'a>], + argument_symbols: &'a [Symbol], + argument_layouts: &'a [Layout<'a>], return_layout: &'a Layout<'a>, ) -> Stmt<'a> { let result_symbol = env.unique_symbol(); @@ -9717,34 +9864,6 @@ fn enum_lambda_set_branch<'a>( let assigned = result_symbol; - let (argument_layouts, argument_symbols) = match closure_data_layout { - Layout::Struct { - field_layouts: &[], .. - } - | Layout::Builtin(Builtin::Bool) - | Layout::Builtin(Builtin::Int(IntWidth::U8)) => { - (argument_layouts_slice, argument_symbols_slice) - } - _ => { - // extend layouts with the layout of the closure environment - let mut argument_layouts = - Vec::with_capacity_in(argument_layouts_slice.len() + 1, env.arena); - argument_layouts.extend(argument_layouts_slice); - argument_layouts.push(closure_data_layout); - - // extend symbols with the symbol of the closure environment - let mut argument_symbols = - Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena); - argument_symbols.extend(argument_symbols_slice); - argument_symbols.push(closure_data_symbol); - - ( - argument_layouts.into_bump_slice(), - argument_symbols.into_bump_slice(), - ) - } - }; - let call = self::Call { call_type: CallType::ByName { name: lambda_name, diff --git a/crates/compiler/mono/src/layout.rs b/crates/compiler/mono/src/layout.rs index f75a61b910..48efdf91f8 100644 --- a/crates/compiler/mono/src/layout.rs +++ b/crates/compiler/mono/src/layout.rs @@ -263,7 +263,7 @@ pub enum Layout<'a> { /// so keep a hash of the record order for disambiguation. This still of course may result /// in collisions, but it's unlikely. /// - /// See also https://github.com/rtfeldman/roc/issues/2535. + /// See also https://github.com/roc-lang/roc/issues/2535. field_order_hash: FieldOrderHash, field_layouts: &'a [Layout<'a>], }, @@ -731,7 +731,7 @@ impl std::fmt::Debug for LambdaSet<'_> { /// By recording the captures layouts this lambda expects in its identifier, we can distinguish /// between such differences when constructing closure capture data. /// -/// See also https://github.com/rtfeldman/roc/issues/3336. +/// See also https://github.com/roc-lang/roc/issues/3336. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct CapturesNiche<'a>(&'a [Layout<'a>]); @@ -783,28 +783,56 @@ impl<'a> LambdaName<'a> { #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct LambdaSet<'a> { /// collection of function names and their closure arguments - pub set: &'a [(Symbol, &'a [Layout<'a>])], + set: &'a [(Symbol, &'a [Layout<'a>])], /// how the closure will be represented at runtime representation: &'a Layout<'a>, } +#[derive(Debug)] +pub enum EnumDispatch { + Bool, + U8, +} + /// representation of the closure *for a particular function* #[derive(Debug)] pub enum ClosureRepresentation<'a> { - /// the closure is represented as a union. Includes the tag ID! + /// The closure is represented as a union. Includes the tag ID! + /// Each variant is a different function, and its payloads are the captures. Union { alphabetic_order_fields: &'a [Layout<'a>], closure_name: Symbol, tag_id: TagIdIntType, union_layout: UnionLayout<'a>, }, - /// The closure is represented as a struct. The layouts are sorted - /// alphabetically by the identifier that is captured. + /// The closure is one function, whose captures are represented as a struct. + /// The layouts are sorted alphabetically by the identifier that is captured. /// /// We MUST sort these according to their stack size before code gen! AlphabeticOrderStruct(&'a [Layout<'a>]), - /// the representation is anything but a union - Other(Layout<'a>), + /// The closure is one function that captures a single identifier, whose value is unwrapped. + UnwrappedCapture(Layout<'a>), + /// The closure dispatches to multiple functions, but none of them capture anything, so this is + /// a boolean or integer flag. + EnumDispatch(EnumDispatch), +} + +/// How the closure should be seen when determining a call-by-name. +#[derive(Debug)] +pub enum ClosureCallOptions<'a> { + /// This is an empty lambda set, dispatching is an error + Void, + /// One of a few capturing functions can be called to + Union(UnionLayout<'a>), + /// The closure is one function, whose captures are represented as a struct. + Struct { + field_layouts: &'a [Layout<'a>], + field_order_hash: FieldOrderHash, + }, + /// The closure is one function that captures a single identifier, whose value is unwrapped. + UnwrappedCapture(Layout<'a>), + /// The closure dispatches to multiple possible functions, none of which capture. + EnumDispatch(EnumDispatch), } impl<'a> LambdaSet<'a> { @@ -818,13 +846,17 @@ impl<'a> LambdaSet<'a> { } pub fn is_represented(&self) -> Option> { - match self.representation { - Layout::Struct { - field_layouts: &[], .. + if self.has_unwrapped_capture_repr() { + Some(*self.representation) + } else if self.has_enum_dispatch_repr() { + None + } else { + match self.representation { + Layout::Struct { + field_layouts: &[], .. + } => None, + repr => Some(*repr), } - | Layout::Builtin(Builtin::Bool) - | Layout::Builtin(Builtin::Int(..)) => None, - repr => Some(*repr), } } @@ -835,6 +867,16 @@ impl<'a> LambdaSet<'a> { }) } + #[inline(always)] + pub fn len(&self) -> usize { + self.set.len() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.set.is_empty() + } + pub fn layout_for_member_with_lambda_name( &self, lambda_name: LambdaName, @@ -923,6 +965,11 @@ impl<'a> LambdaSet<'a> { where F: Fn(Symbol, &[Layout]) -> bool, { + if self.has_unwrapped_capture_repr() { + // Only one function, that captures one identifier. + return ClosureRepresentation::UnwrappedCapture(*self.representation); + } + match self.representation { Layout::Union(union) => { // here we rely on the fact that a union in a closure would be stored in a one-element record. @@ -1004,7 +1051,58 @@ impl<'a> LambdaSet<'a> { ClosureRepresentation::AlphabeticOrderStruct(fields) } - _ => ClosureRepresentation::Other(*self.representation), + layout => { + debug_assert!(self.has_enum_dispatch_repr(),); + let enum_repr = match layout { + Layout::Builtin(Builtin::Bool) => EnumDispatch::Bool, + Layout::Builtin(Builtin::Int(IntWidth::U8)) => EnumDispatch::U8, + other => internal_error!("Invalid layout for enum dispatch: {:?}", other), + }; + ClosureRepresentation::EnumDispatch(enum_repr) + } + } + } + + fn has_unwrapped_capture_repr(&self) -> bool { + self.set.len() == 1 && self.set[0].1.len() == 1 + } + + fn has_enum_dispatch_repr(&self) -> bool { + self.set.len() > 1 && self.set.iter().all(|(_, captures)| captures.is_empty()) + } + + pub fn call_by_name_options(&self) -> ClosureCallOptions<'a> { + if self.has_unwrapped_capture_repr() { + return ClosureCallOptions::UnwrappedCapture(*self.representation); + } + + match self.representation { + Layout::Union(union_layout) => { + if self.representation == &Layout::VOID { + debug_assert!(self.set.is_empty()); + return ClosureCallOptions::Void; + } + ClosureCallOptions::Union(*union_layout) + } + Layout::Struct { + field_layouts, + field_order_hash, + } => { + debug_assert_eq!(self.set.len(), 1); + ClosureCallOptions::Struct { + field_layouts, + field_order_hash: *field_order_hash, + } + } + layout => { + debug_assert!(self.has_enum_dispatch_repr()); + let enum_repr = match layout { + Layout::Builtin(Builtin::Bool) => EnumDispatch::Bool, + Layout::Builtin(Builtin::Int(IntWidth::U8)) => EnumDispatch::U8, + other => internal_error!("Invalid layout for enum dispatch: {:?}", other), + }; + ClosureCallOptions::EnumDispatch(enum_repr) + } } } @@ -1013,30 +1111,27 @@ impl<'a> LambdaSet<'a> { arena: &'a Bump, argument_layouts: &'a [Layout<'a>], ) -> &'a [Layout<'a>] { - if let [] = self.set { - // TERRIBLE HACK for builting functions - argument_layouts - } else { - match self.representation { - Layout::Struct { - field_layouts: &[], .. - } => { - // this function does not have anything in its closure, and the lambda set is a - // singleton, so we pass no extra argument - argument_layouts - } - Layout::Builtin(Builtin::Bool) - | Layout::Builtin(Builtin::Int(IntWidth::I8 | IntWidth::U8)) => { - // we don't pass this along either - argument_layouts - } - _ => { - let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena); - arguments.extend(argument_layouts); - arguments.push(Layout::LambdaSet(*self)); + match self.call_by_name_options() { + ClosureCallOptions::Void => argument_layouts, + ClosureCallOptions::Struct { + field_layouts: &[], .. + } => { + // this function does not have anything in its closure, and the lambda set is a + // singleton, so we pass no extra argument + argument_layouts + } + ClosureCallOptions::Struct { .. } + | ClosureCallOptions::Union(_) + | ClosureCallOptions::UnwrappedCapture(_) => { + let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena); + arguments.extend(argument_layouts); + arguments.push(Layout::LambdaSet(*self)); - arguments.into_bump_slice() - } + arguments.into_bump_slice() + } + ClosureCallOptions::EnumDispatch(_) => { + // No captures, don't pass this along + argument_layouts } } } @@ -1053,7 +1148,7 @@ impl<'a> LambdaSet<'a> { lambdas.sort_by_key(|(sym, _)| *sym); let mut set: Vec<(Symbol, &[Layout])> = Vec::with_capacity_in(lambdas.len(), arena); - let mut set_with_variables: std::vec::Vec<(Symbol, std::vec::Vec)> = + let mut set_with_variables: std::vec::Vec<(&Symbol, &[Variable])> = std::vec::Vec::with_capacity(lambdas.len()); let mut last_function_symbol = None; @@ -1090,7 +1185,7 @@ impl<'a> LambdaSet<'a> { has_duplicate_lambda_names = has_duplicate_lambda_names || is_multimorphic; set.push((*function_symbol, arguments)); - set_with_variables.push((*function_symbol, variables.to_vec())); + set_with_variables.push((function_symbol, variables.as_slice())); last_function_symbol = Some(function_symbol); } @@ -1139,7 +1234,7 @@ impl<'a> LambdaSet<'a> { } ResolvedLambdaSet::Unbound => { // The lambda set is unbound which means it must be unused. Just give it the empty lambda set. - // See also https://github.com/rtfeldman/roc/issues/3163. + // See also https://github.com/roc-lang/roc/issues/3163. Ok(LambdaSet { set: &[], representation: arena.alloc(Layout::UNIT), @@ -1151,70 +1246,23 @@ impl<'a> LambdaSet<'a> { fn make_representation( arena: &'a Bump, subs: &Subs, - tags: std::vec::Vec<(Symbol, std::vec::Vec)>, + tags: std::vec::Vec<(&Symbol, &[Variable])>, opt_rec_var: Option, target_info: TargetInfo, ) -> Layout<'a> { - if let Some(rec_var) = opt_rec_var { - let tags: std::vec::Vec<_> = tags - .iter() - .map(|(sym, vars)| (sym, vars.as_slice())) - .collect(); - let tags = UnsortedUnionLabels { tags }; - let mut env = Env { - seen: Vec::new_in(arena), - target_info, - arena, - subs, - }; + let union_labels = UnsortedUnionLabels { tags }; + let mut env = Env { + seen: Vec::new_in(arena), + target_info, + arena, + subs, + }; - return layout_from_recursive_union(&mut env, rec_var, &tags) - .expect("unable to create lambda set representation"); - } + match opt_rec_var { + Some(rec_var) => layout_from_recursive_union(&mut env, rec_var, &union_labels) + .expect("unable to create lambda set representation"), - // otherwise, this is a closure with a payload - let variant = union_sorted_tags_help(arena, tags, opt_rec_var, subs, target_info); - - use UnionVariant::*; - match variant { - Never => Layout::VOID, - BoolUnion { .. } => Layout::bool(), - ByteUnion { .. } => Layout::u8(), - Unit | UnitWithArguments => { - // no useful information to store - Layout::UNIT - } - Newtype { - arguments: layouts, .. - } => Layout::struct_no_name_order(layouts.into_bump_slice()), - Wrapped(variant) => { - use WrappedVariant::*; - - match variant { - NonRecursive { - sorted_tag_layouts: tags, - } => { - debug_assert!(tags.len() > 1); - - // if the closed-over value is actually a layout, it should be wrapped in a 1-element record - debug_assert!(matches!(tags[0].0, TagOrClosure::Closure(_))); - - let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena); - - for (_, tag_args) in tags.iter() { - tag_arguments.push(&tag_args[0..]); - } - Layout::Union(UnionLayout::NonRecursive(tag_arguments.into_bump_slice())) - } - - Recursive { .. } - | NullableUnwrapped { .. } - | NullableWrapped { .. } - | NonNullableUnwrapped { .. } => { - internal_error!("Recursive layouts should be produced in an earlier branch") - } - } - } + None => layout_from_union(&mut env, &union_labels), } } @@ -1239,7 +1287,7 @@ enum ResolvedLambdaSet { OptVariable, ), /// TODO: figure out if this can happen in a correct program, or is the result of a bug in our - /// compiler. See https://github.com/rtfeldman/roc/issues/3163. + /// compiler. See https://github.com/roc-lang/roc/issues/3163. Unbound, } @@ -1777,6 +1825,13 @@ impl<'a> Layout<'a> { } } } + + pub fn runtime_representation(&self) -> Self { + match self { + Layout::LambdaSet(lambda_set) => lambda_set.runtime_representation(), + other => *other, + } + } } /// Avoid recomputing Layout from Variable multiple times. diff --git a/crates/compiler/mono/src/lib.rs b/crates/compiler/mono/src/lib.rs index 14b55c5bd6..70f48d3161 100644 --- a/crates/compiler/mono/src/lib.rs +++ b/crates/compiler/mono/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] pub mod borrow; diff --git a/crates/compiler/parse/Cargo.toml b/crates/compiler/parse/Cargo.toml index 8663767852..6102abbd3a 100644 --- a/crates/compiler/parse/Cargo.toml +++ b/crates/compiler/parse/Cargo.toml @@ -13,12 +13,12 @@ roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } bumpalo = { version = "3.8.0", features = ["collections"] } -encode_unicode = "0.3.6" +encode_unicode = "1.0.0" [dev-dependencies] criterion = { git = "https://github.com/Anton-4/criterion.rs", features = ["html_reports"]} pretty_assertions = "1.0.0" -indoc = "1.0.3" +indoc = "1.0.7" quickcheck = "1.0.3" quickcheck_macros = "1.0.0" roc_test_utils = { path = "../../test_utils" } diff --git a/crates/compiler/parse/fuzz/Cargo.lock b/crates/compiler/parse/fuzz/Cargo.lock index 78597e9fca..a017af0b89 100644 --- a/crates/compiler/parse/fuzz/Cargo.lock +++ b/crates/compiler/parse/fuzz/Cargo.lock @@ -1,11 +1,54 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +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 = "arbitrary" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitmaps" version = "2.1.0" @@ -16,16 +59,40 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.4.0" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "cc" -version = "1.0.61" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[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" @@ -34,13 +101,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "im" -version = "14.3.0" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.5.1", + "rand_core", "rand_xoshiro", "sized-chunks", "typenum", @@ -49,30 +155,30 @@ dependencies = [ [[package]] name = "im-rc" -version = "14.3.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303f7e6256d546e01979071417432425f15c1891fb309a5f2d724ee908fabd6e" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" dependencies = [ "bitmaps", - "rand_core 0.5.1", + "rand_core", "rand_xoshiro", "sized-chunks", "typenum", "version_check", ] -[[package]] -name = "inlinable_string" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6ee2a7da03bfc3b66ca47c92c2e392fcc053ea040a85561749b026f7aad09a" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.131" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40" + [[package]] name = "libfuzzer-sys" version = "0.3.4" @@ -84,54 +190,114 @@ dependencies = [ ] [[package]] -name = "rand_core" -version = "0.4.2" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +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.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" [[package]] name = "rand_xoshiro" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] name = "roc_collections" -version = "0.1.0" +version = "0.0.1" dependencies = [ + "bitvec", "bumpalo", + "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.1.0" +version = "0.0.1" dependencies = [ "bumpalo", - "inlinable_string", "lazy_static", "roc_collections", + "roc_error_macros", + "roc_ident", "roc_region", + "snafu", + "static_assertions", ] [[package]] name = "roc_parse" -version = "0.1.0" +version = "0.0.1" dependencies = [ "bumpalo", "encode_unicode", - "inlinable_string", "roc_collections", "roc_module", "roc_region", @@ -148,24 +314,85 @@ dependencies = [ [[package]] name = "roc_region" -version = "0.1.0" +version = "0.0.1" +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.5.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" dependencies = [ "bitmaps", "typenum", ] +[[package]] +name = "snafu" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" +dependencies = [ + "backtrace", + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" +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.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +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.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "unicode-ident" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" + [[package]] name = "version_check" version = "0.9.2" @@ -173,10 +400,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] -name = "wyhash" -version = "0.3.0" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782a50f48ac4336916227cd199c61c7b42f38d0ad705421b49eb12c74c53ae00" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wyhash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf6e163c25e3fac820b4b453185ea2dea3b6a3e0a721d4d23d75bd33734c295" dependencies = [ - "rand_core 0.4.2", + "rand_core", +] + +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", ] diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index a6954f8edd..ab8f11709b 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] #[macro_use] diff --git a/crates/compiler/parse/tests/test_parse.rs b/crates/compiler/parse/tests/test_parse.rs index 70ed7c2ee4..7d6722ca5c 100644 --- a/crates/compiler/parse/tests/test_parse.rs +++ b/crates/compiler/parse/tests/test_parse.rs @@ -173,8 +173,8 @@ mod test_parse { pass/lowest_float.expr, pass/lowest_int.expr, pass/malformed_ident_due_to_underscore.expr, - pass/malformed_pattern_field_access.expr, // See https://github.com/rtfeldman/roc/issues/399 - pass/malformed_pattern_module_name.expr, // See https://github.com/rtfeldman/roc/issues/399 + pass/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399 + pass/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399 pass/minimal_app_header.header, pass/minus_twelve_minus_five.expr, pass/mixed_docs.expr, @@ -191,7 +191,7 @@ mod test_parse { pass/nested_def_annotation.module, pass/nested_if.expr, pass/nested_module.header, - pass/newline_after_equals.expr, // Regression test for https://github.com/rtfeldman/roc/issues/51 + pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51 pass/newline_after_mul.expr, pass/newline_after_sub.expr, pass/newline_and_spaces_before_less_than.expr, @@ -229,7 +229,7 @@ mod test_parse { pass/parenthetical_var.expr, pass/parse_alias.expr, pass/parse_as_ann.expr, - pass/pattern_with_space_in_parens.expr, // https://github.com/rtfeldman/roc/issues/929 + pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929 pass/plus_if.expr, pass/plus_when.expr, pass/pos_inf_float.expr, @@ -263,7 +263,7 @@ mod test_parse { pass/two_branch_when.expr, pass/two_spaced_def.expr, pass/type_decl_with_underscore.expr, - pass/unary_negation_access.expr, // Regression test for https://github.com/rtfeldman/roc/issues/509 + pass/unary_negation_access.expr, // Regression test for https://github.com/roc-lang/roc/issues/509 pass/unary_negation_arg.expr, pass/unary_negation_with_parens.expr, pass/unary_negation.expr, diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index a8513c3532..5f203600c2 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -38,6 +38,7 @@ pub enum Problem { /// Bool is whether the closure is anonymous /// Second symbol is the name of the argument that is unused UnusedArgument(Symbol, bool, Symbol, Region), + UnusedBranchDef(Symbol, Region), PrecedenceProblem(PrecedenceProblem), // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! UnsupportedPattern(BadPattern, Region), diff --git a/crates/compiler/problem/src/lib.rs b/crates/compiler/problem/src/lib.rs index 9da9d7b105..23da3b500b 100644 --- a/crates/compiler/problem/src/lib.rs +++ b/crates/compiler/problem/src/lib.rs @@ -1,4 +1,4 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod can; diff --git a/crates/compiler/region/src/lib.rs b/crates/compiler/region/src/lib.rs index 885d50b458..cd251c8e78 100644 --- a/crates/compiler/region/src/lib.rs +++ b/crates/compiler/region/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod all; diff --git a/crates/compiler/roc_target/src/lib.rs b/crates/compiler/roc_target/src/lib.rs index 96352c872b..7e5e613782 100644 --- a/crates/compiler/roc_target/src/lib.rs +++ b/crates/compiler/roc_target/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] use strum_macros::{EnumCount, EnumIter}; diff --git a/crates/compiler/solve/Cargo.toml b/crates/compiler/solve/Cargo.toml index 34611f4b78..7c56c400d7 100644 --- a/crates/compiler/solve/Cargo.toml +++ b/crates/compiler/solve/Cargo.toml @@ -32,9 +32,9 @@ roc_target = { path = "../roc_target" } roc_reporting = { path = "../../reporting" } roc_derive = { path = "../derive", features = ["debug-derived-symbols"] } pretty_assertions = "1.0.0" -indoc = "1.0.3" +indoc = "1.0.7" tempfile = "3.2.0" bumpalo = { version = "3.8.0", features = ["collections"] } regex = "1.5.5" lazy_static = "1.4.0" -insta = "1.15.0" +insta = "1.18.2" diff --git a/crates/compiler/solve/docs/ambient_lambda_set_specialization.md b/crates/compiler/solve/docs/ambient_lambda_set_specialization.md index 77927b0767..d11ffa5d7b 100644 --- a/crates/compiler/solve/docs/ambient_lambda_set_specialization.md +++ b/crates/compiler/solve/docs/ambient_lambda_set_specialization.md @@ -32,7 +32,7 @@ f = if True then id1 else id2 ^ f : a -[[id1, id2]] -> a ``` -The syntax `-[[id1]]->` can be read as “a function that dispatches to `id1`". Then the arrow `-[[id1, id2]]->` then is “a function that dispatches to `id1`, or `id2`". The tag union `[id1, id2]` can contain payloads to represent the captures of `id1` and `id2`; however, the implications of that are out of scope for this discussion, see [Folkert’s great explanation](https://github.com/rtfeldman/roc/pull/2307#discussion_r777042512) for more information. During compile-time, Roc would attach a run-time examinable tag to the value in each branch of the `f` expression body, representing whether to dispatch to `id1` or `id2`. Whenever `f` is dispatched, that tag is examined to determine exactly which function should be dispatched to. This is “**defunctionalization**”. +The syntax `-[[id1]]->` can be read as “a function that dispatches to `id1`". Then the arrow `-[[id1, id2]]->` then is “a function that dispatches to `id1`, or `id2`". The tag union `[id1, id2]` can contain payloads to represent the captures of `id1` and `id2`; however, the implications of that are out of scope for this discussion, see [Folkert’s great explanation](https://github.com/roc-lang/roc/pull/2307#discussion_r777042512) for more information. During compile-time, Roc would attach a run-time examinable tag to the value in each branch of the `f` expression body, representing whether to dispatch to `id1` or `id2`. Whenever `f` is dispatched, that tag is examined to determine exactly which function should be dispatched to. This is “**defunctionalization**”. In the presence of [abilities](https://docs.google.com/document/d/1kUh53p1Du3fWP_jZp-sdqwb5C9DuS43YJwXHg1NzETY/edit), lambda sets get more complicated. Now, I can write something like @@ -100,7 +100,7 @@ The unification trace for the call `f (@Foo {})` proceeds as follows. I use `'tN Foo -[[zeroHash] + Foo:hashThunk:1]-> ({} -[[lam1] + Foo:hashThunk:2]-> U64) ``` -Now that the specialization lambdas’ type variables point to concrete types, we can resolve the concrete lambdas of `Foo:hashThunk:1` and `Foo:hashThunk:2`. Cool! Let’s do that. We know that +Now that the specialization lambdas’ type variables point to concrete types, we can resolve the concrete lambdas of `Foo:hashThunk:1` and `Foo:hashThunk:2`. Cool! Let’s do that. We know that ``` hashThunk = \@Foo {} -> \{} -> 1 @@ -205,46 +205,46 @@ Okay, so first we’ll enumerate some terminology, and the exact algorithm. Then ### Some definitions - **The region invariant.** Previously we discussed the “region” of a lambda set in a specialization function definition. The way regions are assigned in the compiler follows a very specific ordering and holds a invariant we’ll call the “region invariant”. First, let’s define a procedure for creating function types and assigning regions: - + ``` - Type = \region -> + Type = \region -> (Type_atom, region) | Type_function region - + Type_function = \region -> let left_type, new_region = Type (region + 1) let right_type, new_region = Type (new_region) let func_type = left_type -[Lambda region]-> right_type (func_type, new_region) ``` - + This procedure would create functions that look like the trees(abbreviating `L=Lambda`, `a=atom` below) - + ``` -[L 1]-> a a - + === - + -[L 1]-> -[L 2]-> -[L 3]-> a a a a - + === -[L 1]-> -[L 2]-> -[L 5]-> -[L 3]-> -[L 4]-> -[L 6]-> -[L 7]-> a a a a a a a a ``` - + The invariant is this: for a region `r`, the only functions enclosing `r` have a region number that is less than `r`. Moreover, every region `r' < r`, either the function at `r'` encloses `r`, or is disjoint from `r`. - + - **Ambient functions.** For a given lambda set at region `r`, any function that encloses `r` is called an **ambient function** of `r`. The function directly at region `r` is called the **directly ambient function**. - + For example, the functions identified by `L 4`, `L 2`, and `L 1` in the last example tree above are all ambient functions of the function identified by `L 4`. - - The region invariant means that the only functions that are ambient of a region `r` are those identified by regions `< r`. - + + The region invariant means that the only functions that are ambient of a region `r` are those identified by regions `< r`. + - `uls_of_var`. A look aside table of the unspecialized lambda sets (uls) depending on a variable. For example, in `a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {})`, there would be a mapping of `a => { [[] + a:f:1]; [[] + a:f:2] }`. When `a` gets instantiated with a concrete type, we know that these lambda sets are ready to be resolved. ### Explicit Description @@ -440,13 +440,13 @@ F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G # ^ a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G G has g : b -> ({} -> {}) | b has G # ^ b -[[] + b:g:1]-> ({} -[[] + b:g:2]-> {}) | b has G - + Fo := {} f = \@Fo {}, b -> \{} -> g b #^ Fo, b -[[Fo#f]]-> ({} -[[lamF b]]-> ({} -[[] + b:g:2]]-> {})) | b has G # instantiation with a=Fo of # a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G - + Go := {} g = \@Go {} -> \{} -> {} #^ {} -[[Go#g]]-> ({} -[[lamG]]-> {}) @@ -670,7 +670,7 @@ You may have observed that step 1 and step 2 of the algorithm are somewhat overk This optimization is correct with a change to the region numbering scheme: ```python -Type = \region -> +Type = \region -> (Type_atom, region) | Type_function region diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index dc88e6a877..a356fa74d0 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -442,16 +442,6 @@ trait DerivableVisitor { false } - #[inline(always)] - fn visit_flex(var: Variable) -> Result<(), DerivableError> { - Err(DerivableError::NotDerivable(var)) - } - - #[inline(always)] - fn visit_rigid(var: Variable) -> Result<(), DerivableError> { - Err(DerivableError::NotDerivable(var)) - } - #[inline(always)] fn visit_flex_able(var: Variable, ability: Symbol) -> Result<(), DerivableError> { if ability != Self::ABILITY { @@ -529,7 +519,7 @@ trait DerivableVisitor { fn is_derivable( obligation_cache: &mut ObligationCache, abilities_store: &AbilitiesStore, - subs: &Subs, + subs: &mut Subs, var: Variable, ) -> Result<(), DerivableError> { let mut stack = vec![var]; @@ -552,8 +542,11 @@ trait DerivableVisitor { use DerivableError::*; use FlatType::*; match *content { - FlexVar(_) => Self::visit_flex(var)?, - RigidVar(_) => Self::visit_rigid(var)?, + FlexVar(opt_name) => { + // Promote the flex var to be bound to the ability. + subs.set_content(var, Content::FlexAbleVar(opt_name, Self::ABILITY)); + } + RigidVar(_) => return Err(NotDerivable(var)), FlexAbleVar(_, ability) => Self::visit_flex_able(var, ability)?, RigidAbleVar(_, ability) => Self::visit_rigid_able(var, ability)?, RecursionVar { @@ -584,7 +577,15 @@ trait DerivableVisitor { let descend = Self::visit_record(var)?; if descend.0 { push_var_slice!(fields.variables()); - stack.push(ext); + if !matches!( + subs.get_content_without_compacting(ext), + Content::FlexVar(_) | Content::RigidVar(_) + ) { + // TODO: currently, just we suppose the presence of a flex var may + // include more or less things which we can derive. But, we should + // instead recurse here, and add a `t ~ u | u has Decode` constraint as needed. + stack.push(ext); + } } } TagUnion(tags, ext) => { diff --git a/crates/compiler/solve/src/lib.rs b/crates/compiler/solve/src/lib.rs index 8f6a0bcafb..3b4e296869 100644 --- a/crates/compiler/solve/src/lib.rs +++ b/crates/compiler/solve/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod ability; diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index ced09fe0e6..9eadb602e8 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -3113,7 +3113,7 @@ fn adjust_rank_content( // inside a lambda set but not on the left or right of an arrow, and records should not // force de-generalization in such cases. // - // See https://github.com/rtfeldman/roc/issues/3641 for a longer discussion and + // See https://github.com/roc-lang/roc/issues/3641 for a longer discussion and // example. group_rank } diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index ab4f665748..c1c11e06fa 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -182,7 +182,13 @@ mod solve_expr { // Disregard UnusedDef problems, because those are unavoidable when // returning a function from the test expression. - can_problems.retain(|prob| !matches!(prob, roc_problem::can::Problem::UnusedDef(_, _))); + can_problems.retain(|prob| { + !matches!( + prob, + roc_problem::can::Problem::UnusedDef(_, _) + | roc_problem::can::Problem::UnusedBranchDef(..) + ) + }); let (can_problems, type_problems) = format_problems(&src, home, &interns, can_problems, type_problems); @@ -4916,7 +4922,7 @@ mod solve_expr { #[test] fn rigid_type_variable_problem() { - // see https://github.com/rtfeldman/roc/issues/1162 + // see https://github.com/roc-lang/roc/issues/1162 infer_eq_without_problem( indoc!( r#" @@ -5024,7 +5030,7 @@ mod solve_expr { #[test] fn inference_var_tag_union_ext() { // TODO: we should really be inferring [Blue, Orange]a -> [Lavender, Peach]a here. - // See https://github.com/rtfeldman/roc/issues/2053 + // See https://github.com/roc-lang/roc/issues/2053 infer_eq_without_problem( indoc!( r#" @@ -5491,7 +5497,7 @@ mod solve_expr { ) } - // https://github.com/rtfeldman/roc/issues/2379 + // https://github.com/roc-lang/roc/issues/2379 #[test] fn copy_vars_referencing_copied_vars() { infer_eq_without_problem( @@ -5866,7 +5872,7 @@ mod solve_expr { } #[test] - // https://github.com/rtfeldman/roc/issues/2702 + // https://github.com/roc-lang/roc/issues/2702 fn tag_inclusion_behind_opaque() { infer_eq_without_problem( indoc!( @@ -7691,4 +7697,68 @@ mod solve_expr { "### ); } + + #[test] + fn transient_captures() { + infer_queries!( + indoc!( + r#" + x = "abc" + + getX = \{} -> x + + h = \{} -> (getX {}) + #^{-1} + + h {} + "# + ), + @"h : {}* -[[h(3) Str]]-> Str" + ); + } + + #[test] + fn transient_captures_after_def_ordering() { + infer_queries!( + indoc!( + r#" + h = \{} -> (getX {}) + #^{-1} + + getX = \{} -> x + + x = "abc" + + h {} + "# + ), + @"h : {}* -[[h(1) Str]]-> Str" + ); + } + + #[test] + fn mutually_recursive_captures() { + infer_queries!( + indoc!( + r#" + x = True + y = False + + a = "foo" + b = "bar" + + foo = \{} -> if x then a else bar {} + #^^^{-1} + bar = \{} -> if y then b else foo {} + #^^^{-1} + + bar {} + "# + ), + @r###" + foo : {} -[[foo(5) [True]* [False]* Str Str]]-> Str + bar : {} -[[bar(6) [True]* [False]* Str Str]]-> Str + "### + ); + } } diff --git a/crates/compiler/test_derive/Cargo.toml b/crates/compiler/test_derive/Cargo.toml index a3b1647548..f1b4562240 100644 --- a/crates/compiler/test_derive/Cargo.toml +++ b/crates/compiler/test_derive/Cargo.toml @@ -16,7 +16,7 @@ roc_builtins = { path = "../builtins" } roc_load_internal = { path = "../load_internal" } roc_can = { path = "../can" } roc_derive_key = { path = "../derive_key" } -roc_derive = { path = "../derive", features = ["debug-derived-symbols"] } +roc_derive = { path = "../derive", features = ["debug-derived-symbols", "open-extension-vars"] } roc_target = { path = "../roc_target" } roc_types = { path = "../types" } roc_reporting = { path = "../../reporting" } @@ -26,7 +26,7 @@ roc_solve = { path = "../solve" } roc_debug_flags = { path = "../debug_flags" } bumpalo = { version = "3.8.0", features = ["collections"] } lazy_static = "1.4.0" -indoc = "1.0.3" +indoc = "1.0.7" ven_pretty = { path = "../../vendor/pretty" } pretty_assertions = "1.0.0" -insta = "1.15.0" +insta = "1.18.2" diff --git a/crates/compiler/test_derive/src/decoding.rs b/crates/compiler/test_derive/src/decoding.rs index 9477710305..2b9a596a22 100644 --- a/crates/compiler/test_derive/src/decoding.rs +++ b/crates/compiler/test_derive/src/decoding.rs @@ -5,14 +5,48 @@ #![allow(non_snake_case)] use crate::{ - util::{check_immediate, derive_test}, + test_key_eq, test_key_neq, + util::{check_immediate, check_underivable, derive_test}, v, }; use insta::assert_snapshot; use roc_module::symbol::Symbol; use roc_types::subs::Variable; -use roc_derive_key::DeriveBuiltin::Decoder; +use roc_derive_key::{DeriveBuiltin::Decoder, DeriveError}; + +test_key_eq! { + Decoder, + + same_record: + v!({ a: v!(U8), }), v!({ a: v!(U8), }) + same_record_fields_diff_types: + v!({ a: v!(U8), }), v!({ a: v!(STR), }) + same_record_fields_any_order: + v!({ a: v!(U8), b: v!(U8), c: v!(U8), }), + v!({ c: v!(U8), a: v!(U8), b: v!(U8), }) + explicit_empty_record_and_implicit_empty_record: + v!(EMPTY_RECORD), v!({}) + + list_list_diff_types: + v!(Symbol::LIST_LIST v!(STR)), v!(Symbol::LIST_LIST v!(U8)) + str_str: + v!(Symbol::STR_STR), v!(Symbol::STR_STR) +} + +test_key_neq! { + Decoder, + + different_record_fields: + v!({ a: v!(U8), }), v!({ b: v!(U8), }) + record_empty_vs_nonempty: + v!(EMPTY_RECORD), v!({ a: v!(U8), }) +} + +#[test] +fn optional_record_field_derive_error() { + check_underivable(Decoder, v!({ ?a: v!(U8), }), DeriveError::Underivable); +} #[test] fn immediates() { @@ -49,3 +83,66 @@ fn list() { ) }) } + +#[test] +fn record_2_fields() { + derive_test(Decoder, v!({first: v!(STR), second: v!(STR),}), |golden| { + assert_snapshot!(golden, @r###" + # derived for { first : Str, second : Str } + # Decoder { first : val, second : val1 } fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding + # List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding + # Specialization lambda sets: + # @<1>: [[custom(22)]] + #Derived.decoder_{first,second} = + Decode.custom + \#Derived.bytes3, #Derived.fmt3 -> + Decode.decodeWith + #Derived.bytes3 + (Decode.record + { second: Err NoField, first: Err NoField } + \#Derived.stateRecord2, #Derived.field -> + when #Derived.field is + "first" -> + Keep (Decode.custom + \#Derived.bytes, #Derived.fmt -> + when Decode.decodeWith + #Derived.bytes + Decode.decoder + #Derived.fmt is + #Derived.rec -> + { + result: when #Derived.rec.result is + Ok #Derived.val -> + Ok { stateRecord2 & first: Ok #Derived.val } + Err #Derived.err -> Err #Derived.err, + rest: #Derived.rec.rest + }) + "second" -> + Keep (Decode.custom + \#Derived.bytes2, #Derived.fmt2 -> + when Decode.decodeWith + #Derived.bytes2 + Decode.decoder + #Derived.fmt2 is + #Derived.rec2 -> + { + result: when #Derived.rec2.result is + Ok #Derived.val2 -> + Ok { stateRecord2 & second: Ok #Derived.val2 } + Err #Derived.err2 -> Err #Derived.err2, + rest: #Derived.rec2.rest + }) + _ -> Skip + \#Derived.stateRecord -> + when #Derived.stateRecord.first is + Ok #Derived.first -> + when #Derived.stateRecord.second is + Ok #Derived.second -> + Ok { second: #Derived.second, first: #Derived.first } + _ -> Err TooShort + _ -> Err TooShort) + #Derived.fmt3 + "### + ) + }) +} diff --git a/crates/compiler/test_derive/src/encoding.rs b/crates/compiler/test_derive/src/encoding.rs index 1c72e631af..1e9c94ff5f 100644 --- a/crates/compiler/test_derive/src/encoding.rs +++ b/crates/compiler/test_derive/src/encoding.rs @@ -7,7 +7,7 @@ use insta::assert_snapshot; use crate::{ - test_hash_eq, test_hash_neq, + test_key_eq, test_key_neq, util::{check_immediate, derive_test}, v, }; @@ -17,7 +17,7 @@ use roc_types::subs::Variable; // {{{ hash tests -test_hash_eq! { +test_key_eq! { ToEncoder, same_record: @@ -70,7 +70,7 @@ test_hash_eq! { v!(@Symbol::BOOL_BOOL => v!([ True, False ])), v!(Symbol::UNDERSCORE => v!([False, True])) } -test_hash_neq! { +test_key_neq! { ToEncoder, different_record_fields: @@ -174,10 +174,7 @@ fn one_field_record() { \#Derived.bytes, #Derived.fmt -> Encode.appendWith #Derived.bytes - (Encode.record - [ - { value: Encode.toEncoder #Derived.rcd.a, key: "a", }, - ]) + (Encode.record [{ value: Encode.toEncoder #Derived.rcd.a, key: "a" }]) #Derived.fmt "### ) @@ -202,8 +199,8 @@ fn two_field_record() { #Derived.bytes (Encode.record [ - { value: Encode.toEncoder #Derived.rcd.a, key: "a", }, - { value: Encode.toEncoder #Derived.rcd.b, key: "b", }, + { value: Encode.toEncoder #Derived.rcd.a, key: "a" }, + { value: Encode.toEncoder #Derived.rcd.b, key: "b" }, ]) #Derived.fmt "### diff --git a/crates/compiler/test_derive/src/pretty_print.rs b/crates/compiler/test_derive/src/pretty_print.rs index fe73f37df3..953ea05057 100644 --- a/crates/compiler/test_derive/src/pretty_print.rs +++ b/crates/compiler/test_derive/src/pretty_print.rs @@ -19,7 +19,10 @@ pub fn pretty_print_def(c: &Ctx, d: &Def) -> String { macro_rules! maybe_paren { ($paren_if_above:expr, $my_prec:expr, $doc:expr) => { - if $my_prec > $paren_if_above { + maybe_paren!($paren_if_above, $my_prec, || true, $doc) + }; + ($paren_if_above:expr, $my_prec:expr, $extra_cond:expr, $doc:expr) => { + if $my_prec > $paren_if_above && $extra_cond() { $doc.parens().group() } else { $doc @@ -47,7 +50,7 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> { #[derive(PartialEq, PartialOrd)] enum EPrec { Free, - CallArg, + AppArg, } fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Arena<'a>> { @@ -137,11 +140,11 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, maybe_paren!( Free, p, - expr(c, CallArg, f, &fun.value) + expr(c, AppArg, f, &fun.value) .append( f.concat(args.iter().map(|le| f.line().append(expr( c, - CallArg, + AppArg, f, &le.1.value )))) @@ -175,15 +178,18 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Record { fields, .. } => f .reflow("{") .append( - f.concat(fields.iter().map(|(name, field)| { - let field = f - .text(name.as_str()) - .append(f.reflow(": ")) - .append(expr(c, Free, f, &field.loc_expr.value)) - .nest(2) - .group(); - f.line().append(field).append(",") - })) + f.intersperse( + fields.iter().map(|(name, field)| { + let field = f + .text(name.as_str()) + .append(f.reflow(": ")) + .append(expr(c, Free, f, &field.loc_expr.value)) + .nest(2) + .group(); + f.line().append(field) + }), + f.reflow(","), + ) .nest(2) .group(), ) @@ -193,15 +199,61 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, EmptyRecord => f.text("{}"), Access { loc_expr, field, .. - } => expr(c, CallArg, f, &loc_expr.value) + } => expr(c, AppArg, f, &loc_expr.value) .append(f.text(format!(".{}", field.as_str()))) .group(), OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => { f.text(format!("@{}", opaque_name.as_str(c.interns))) } Accessor(_) => todo!(), - Update { .. } => todo!(), - Tag { .. } => todo!(), + Update { + symbol, updates, .. + } => f + .reflow("{") + .append(f.line()) + .append(f.text(symbol.as_str(c.interns).to_string())) + .append(f.reflow(" &")) + .append( + f.intersperse( + updates.iter().map(|(name, field)| { + let field = f + .text(name.as_str()) + .append(f.reflow(": ")) + .append(expr(c, Free, f, &field.loc_expr.value)) + .nest(2) + .group(); + f.line().append(field) + }), + f.reflow(","), + ) + .nest(2) + .group(), + ) + .append(f.line()) + .append(f.text("}")) + .group(), + Tag { + name, arguments, .. + } => maybe_paren!( + Free, + p, + || !arguments.is_empty(), + f.text(name.0.as_str()) + .append(if arguments.is_empty() { + f.nil() + } else { + f.space() + }) + .append( + f.intersperse( + arguments + .iter() + .map(|(_, le)| expr(c, AppArg, f, &le.value)), + f.space(), + ) + ) + .group() + ), ZeroArgumentTag { .. } => todo!(), OpaqueRef { .. } => todo!(), Expect { .. } => todo!(), diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 228c6490b7..64fc4ea052 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -18,7 +18,7 @@ use roc_collections::VecSet; use roc_constrain::expr::constrain_decls; use roc_debug_flags::dbg_do; use roc_derive::DerivedModule; -use roc_derive_key::{DeriveBuiltin, DeriveKey, Derived}; +use roc_derive_key::{DeriveBuiltin, DeriveError, DeriveKey, Derived}; use roc_load_internal::file::{add_imports, default_aliases, LoadedModule, Threading}; use roc_module::symbol::{IdentIds, Interns, ModuleId, Symbol}; use roc_region::all::LineInfo; @@ -53,7 +53,7 @@ fn module_source_and_path(builtin: DeriveBuiltin) -> (ModuleId, &'static str, Pa } } -/// DSL for creating [`Content`][crate::subs::Content]. +/// DSL for creating [`Content`][roc_types::subs::Content]. #[macro_export] macro_rules! v { ({ $($field:ident: $make_v:expr,)* $(?$opt_field:ident : $make_opt_v:expr,)* }) => {{ @@ -65,7 +65,7 @@ macro_rules! v { $(let $opt_field = $make_opt_v(subs);)* let fields = vec![ $( (stringify!($field).into(), RecordField::Required($field)) ,)* - $( (stringify!($opt_field).into(), RecordField::Required($opt_field)) ,)* + $( (stringify!($opt_field).into(), RecordField::Optional($opt_field)) ,)* ]; let fields = RecordFields::insert_into_subs(subs, fields); roc_derive::synth_var(subs, Content::Structure(FlatType::Record(fields, Variable::EMPTY_RECORD))) @@ -178,7 +178,7 @@ where } #[macro_export] -macro_rules! test_hash_eq { +macro_rules! test_key_eq { ($builtin:expr, $($name:ident: $synth1:expr, $synth2:expr)*) => {$( #[test] fn $name() { @@ -188,7 +188,7 @@ macro_rules! test_hash_eq { } #[macro_export] -macro_rules! test_hash_neq { +macro_rules! test_key_neq { ($builtin:expr, $($name:ident: $synth1:expr, $synth2:expr)*) => {$( #[test] fn $name() { @@ -197,6 +197,18 @@ macro_rules! test_hash_neq { )*}; } +pub(crate) fn check_underivable(builtin: DeriveBuiltin, synth: Sy, err: DeriveError) +where + Sy: FnOnce(&mut Subs) -> Variable, +{ + let mut subs = Subs::new(); + let var = synth(&mut subs); + + let key = Derived::builtin(builtin, &subs, var); + + assert_eq!(key, Err(err)); +} + pub(crate) fn check_immediate(builtin: DeriveBuiltin, synth: S, immediate: Symbol) where S: FnOnce(&mut Subs) -> Variable, @@ -324,7 +336,7 @@ fn check_derived_typechecks_and_golden( // run the solver, print and fail if we have errors dbg_do!( roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, - std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, "1") + std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "1") ); let (mut solved_subs, _, problems, _) = roc_solve::module::run_solve( test_module, @@ -338,6 +350,10 @@ fn check_derived_typechecks_and_golden( &exposed_for_module.exposed_by_module, Default::default(), ); + dbg_do!( + roc_debug_flags::ROC_PRINT_UNIFICATIONS_DERIVED, + std::env::set_var(roc_debug_flags::ROC_PRINT_UNIFICATIONS, "0") + ); let subs = solved_subs.inner_mut(); if !problems.is_empty() { diff --git a/crates/compiler/test_gen/Cargo.toml b/crates/compiler/test_gen/Cargo.toml index c3931dbda8..d2fb6d4c61 100644 --- a/crates/compiler/test_gen/Cargo.toml +++ b/crates/compiler/test_gen/Cargo.toml @@ -43,9 +43,9 @@ inkwell = { path = "../../vendor/inkwell" } target-lexicon = "0.12.3" libloading = "0.7.1" tempfile = "3.2.0" -indoc = "1.0.3" +indoc = "1.0.7" criterion = { git = "https://github.com/Anton-4/criterion.rs" } -wasm3 = "0.3.1" +wasm3 = { git = "https://github.com/roc-lang/wasm3-rs", rev = "f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" } lazy_static = "1.4.0" [features] diff --git a/crates/compiler/test_gen/src/gen_abilities.rs b/crates/compiler/test_gen/src/gen_abilities.rs index 059fdc275e..14879049b1 100644 --- a/crates/compiler/test_gen/src/gen_abilities.rs +++ b/crates/compiler/test_gen/src/gen_abilities.rs @@ -468,9 +468,9 @@ mod encode_immediate { 17, u32 17, u64 17, u128 - // 17.23, f32 TODO https://github.com/rtfeldman/roc/issues/3522 + 17.25, f32 17.23, f64 - // 17.23, dec TODO https://github.com/rtfeldman/roc/issues/3522 + 17.23, dec } } @@ -691,7 +691,6 @@ fn encode_derived_list_of_records() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -#[ignore = "#3696: Currently hits some weird panic in borrow checking, not sure if it's directly related to abilities."] fn encode_derived_list_of_lists_of_strings() { assert_evals_to!( indoc!( @@ -715,10 +714,7 @@ fn encode_derived_list_of_lists_of_strings() { } #[test] -#[cfg(all( - any(feature = "gen-llvm", feature = "gen-wasm"), - not(feature = "gen-llvm-wasm") // hits a stack limit in wasm3 -))] +#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))] fn encode_derived_record_with_many_types() { assert_evals_to!( indoc!( @@ -877,7 +873,7 @@ mod decode_immediate { } #[test] - #[cfg(any(feature = "gen-llvm"))] + #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn dec() { use roc_std::RocDec; @@ -899,7 +895,7 @@ mod decode_immediate { } #[test] -#[cfg(any(feature = "gen-llvm"))] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn decode_list_of_strings() { assert_evals_to!( indoc!( @@ -918,10 +914,7 @@ fn decode_list_of_strings() { } #[test] -#[cfg(all( - any(feature = "gen-llvm"), // currently fails on gen-wasm - not(feature = "gen-llvm-wasm") // hits a stack limit in wasm3 -))] +#[cfg(all(any(feature = "gen-llvm", feature = "gen-wasm")))] fn encode_then_decode_list_of_strings() { assert_evals_to!( indoc!( @@ -958,3 +951,142 @@ fn encode_then_decode_list_of_lists_of_strings() { RocStr ) } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn decode_record_two_fields() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Encode, Decode, Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 is + Ok {first: "ab", second: "cd"} -> "abcd" + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn decode_record_two_fields_string_and_int() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Encode, Decode, Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "{\"first\":\"ab\",\"second\":10}" |> Decode.fromBytes Json.fromUtf8 is + Ok {first: "ab", second: 10u8} -> "ab10" + _ -> "something went wrong" + "# + ), + RocStr::from("ab10"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn decode_record_two_fields_string_and_string_infer() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Encode, Decode, Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 is + Ok {first, second} -> Str.concat first second + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn decode_record_two_fields_string_and_string_infer_local_var() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Decode, Json] provides [main] to "./platform" + + main = + decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 + when decoded is + Ok rcd -> Str.concat rcd.first rcd.second + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn decode_record_two_fields_string_and_string_infer_local_var_destructured() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Decode, Json] provides [main] to "./platform" + + main = + decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 + when decoded is + Ok {first, second} -> Str.concat first second + _ -> "something went wrong" + "# + ), + RocStr::from("abcd"), + RocStr + ) +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +#[ignore = "json parsing impl must be fixed first"] +fn decode_empty_record() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Encode, Decode, Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "{}" |> Decode.fromBytes Json.fromUtf8 is + Ok {} -> "empty" + _ -> "something went wrong" + "# + ), + RocStr::from("empty"), + RocStr + ) +} + +#[test] +#[cfg(all( + any(feature = "gen-llvm", feature = "gen-wasm"), + not(feature = "gen-llvm-wasm") // hits a wasm3 stack overflow +))] +fn decode_record_of_record() { + assert_evals_to!( + indoc!( + r#" + app "test" imports [Encode, Decode, Json] provides [main] to "./platform" + + main = + when Str.toUtf8 "{\"outer\":{\"inner\":\"a\"},\"other\":{\"one\":\"b\",\"two\":10}}" |> Decode.fromBytes Json.fromUtf8 is + Ok {outer: {inner: "a"}, other: {one: "b", two: 10u8}} -> "ab10" + _ -> "something went wrong" + "# + ), + RocStr::from("ab10"), + RocStr + ) +} diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index d2685dda74..c3540e619d 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -2831,7 +2831,7 @@ fn lists_with_incompatible_type_param_in_if() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn map_with_index_multi_record() { - // see https://github.com/rtfeldman/roc/issues/1700 + // see https://github.com/roc-lang/roc/issues/1700 assert_evals_to!( indoc!( r#" @@ -2846,7 +2846,7 @@ fn map_with_index_multi_record() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn empty_list_of_function_type() { - // see https://github.com/rtfeldman/roc/issues/1732 + // see https://github.com/roc-lang/roc/issues/1732 assert_evals_to!( indoc!( r#" @@ -3276,7 +3276,7 @@ fn monomorphized_lists() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn with_capacity() { - // see https://github.com/rtfeldman/roc/issues/1732 + // see https://github.com/roc-lang/roc/issues/1732 assert_evals_to!( indoc!( r#" @@ -3369,3 +3369,23 @@ fn issue_3530_uninitialized_capacity_in_list_literal() { |(_, _, cap)| cap ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn list_let_generalization() { + assert_evals_to!( + indoc!( + r#" + empty : List a + empty = [] + + xs : List Str + xs = List.append empty "foo" + + List.len xs + "# + ), + 1, + usize + ); +} diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index daacebded2..a43815a749 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -1114,18 +1114,35 @@ fn gen_mul_dec() { i128 ); } + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] -fn gen_mul_i64() { - assert_evals_to!( - indoc!( - r#" - 2 * 4 * 6 - "# - ), - 48, - i64 - ); +fn gen_signed_mul_quadword_and_lower() { + assert_evals_to!("2i64 * 4 * 6", 48, i64); + assert_evals_to!("2i32 * 4 * 6", 48, i32); + assert_evals_to!("2i16 * 4 * 6", 48, i16); + assert_evals_to!("2i8 * 4 * 6", 48, i8); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn gen_unsigned_mul_quadword_and_lower() { + assert_evals_to!("2u64 * 4 * 6", 48, u64); + assert_evals_to!("2u32 * 4 * 6", 48, u32); + assert_evals_to!("2u16 * 4 * 6", 48, u16); + assert_evals_to!("2u8 * 4 * 6", 48, u8); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn gen_mul_f64() { + assert_evals_to!("2f64 * 4 * 6", 48.0, f64); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn gen_mul_f32() { + assert_evals_to!("2f32 * 4 * 6", 48.0, f32); } #[test] @@ -2091,9 +2108,9 @@ fn float_mul_checked() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn shift_left_by() { - assert_evals_to!("Num.shiftLeftBy 0 0b0000_0001", 0b0000_0001, i64); - assert_evals_to!("Num.shiftLeftBy 1 0b0000_0001", 0b0000_0010, i64); - assert_evals_to!("Num.shiftLeftBy 2 0b0000_0011", 0b0000_1100, i64); + assert_evals_to!("Num.shiftLeftBy 0b0000_0001 0", 0b0000_0001, i64); + assert_evals_to!("Num.shiftLeftBy 0b0000_0001 1", 0b0000_0010, i64); + assert_evals_to!("Num.shiftLeftBy 0b0000_0011 2", 0b0000_1100, i64); } #[test] @@ -2105,43 +2122,43 @@ fn shift_right_by() { // FIXME (Brian) Something funny happening with 8-bit binary literals in tests assert_evals_to!( - "Num.shiftRightBy 2 (Num.toI8 0b1100_0000u8)", + "Num.shiftRightBy (Num.toI8 0b1100_0000u8) 2", 0b1111_0000u8 as i8, i8 ); - assert_evals_to!("Num.shiftRightBy 2 0b0100_0000i8", 0b0001_0000i8, i8); - assert_evals_to!("Num.shiftRightBy 1 0b1110_0000u8", 0b1111_0000u8, u8); - assert_evals_to!("Num.shiftRightBy 2 0b1100_0000u8", 0b1111_0000u8, u8); - assert_evals_to!("Num.shiftRightBy 12 0b0100_0000u8", 0b0000_0000u8, u8); + assert_evals_to!("Num.shiftRightBy 0b0100_0000i8 2", 0b0001_0000i8, i8); + assert_evals_to!("Num.shiftRightBy 0b1110_0000u8 1", 0b1111_0000u8, u8); + assert_evals_to!("Num.shiftRightBy 0b1100_0000u8 2", 0b1111_0000u8, u8); + assert_evals_to!("Num.shiftRightBy 0b0100_0000u8 12", 0b0000_0000u8, u8); // LLVM in release mode returns 0 instead of -1 for some reason if !is_llvm_release_mode { - assert_evals_to!("Num.shiftRightBy 12 0b1000_0000u8", 0b1111_1111u8, u8); + assert_evals_to!("Num.shiftRightBy 0b1000_0000u8 12", 0b1111_1111u8, u8); } - assert_evals_to!("Num.shiftRightBy 0 12", 12, i64); - assert_evals_to!("Num.shiftRightBy 1 12", 6, i64); - assert_evals_to!("Num.shiftRightBy 1 -12", -6, i64); - assert_evals_to!("Num.shiftRightBy 8 12", 0, i64); - assert_evals_to!("Num.shiftRightBy 8 -12", -1, i64); - assert_evals_to!("Num.shiftRightBy -1 12", 0, i64); + assert_evals_to!("Num.shiftRightBy 12 0", 12, i64); + assert_evals_to!("Num.shiftRightBy 12 1", 6, i64); + assert_evals_to!("Num.shiftRightBy -12 1", -6, i64); + assert_evals_to!("Num.shiftRightBy 12 8", 0, i64); + assert_evals_to!("Num.shiftRightBy -12 8", -1, i64); + assert_evals_to!("Num.shiftRightBy 12 -1", 0, i64); assert_evals_to!("Num.shiftRightBy 0 0", 0, i64); - assert_evals_to!("Num.shiftRightBy 1 0", 0, i64); + assert_evals_to!("Num.shiftRightBy 0 1", 0, i64); - assert_evals_to!("Num.shiftRightBy 0 12i32", 12, i32); - assert_evals_to!("Num.shiftRightBy 1 12i32", 6, i32); - assert_evals_to!("Num.shiftRightBy 1 -12i32", -6, i32); - assert_evals_to!("Num.shiftRightBy 8 12i32", 0, i32); - assert_evals_to!("Num.shiftRightBy 8 -12i32", -1, i32); + assert_evals_to!("Num.shiftRightBy 12i32 0", 12, i32); + assert_evals_to!("Num.shiftRightBy 12i32 1", 6, i32); + assert_evals_to!("Num.shiftRightBy -12i32 1", -6, i32); + assert_evals_to!("Num.shiftRightBy 12i32 8", 0, i32); + assert_evals_to!("Num.shiftRightBy -12i32 8", -1, i32); - assert_evals_to!("Num.shiftRightBy 0 12i8", 12, i8); - assert_evals_to!("Num.shiftRightBy 1 12i8", 6, i8); - assert_evals_to!("Num.shiftRightBy 1 -12i8", -6, i8); - assert_evals_to!("Num.shiftRightBy 8 12i8", 0, i8); + assert_evals_to!("Num.shiftRightBy 12i8 0", 12, i8); + assert_evals_to!("Num.shiftRightBy 12i8 1", 6, i8); + assert_evals_to!("Num.shiftRightBy -12i8 1", -6, i8); + assert_evals_to!("Num.shiftRightBy 12i8 8", 0, i8); if !is_llvm_release_mode { - assert_evals_to!("Num.shiftRightBy -1 0", 0, i64); - assert_evals_to!("Num.shiftRightBy -1 -12", -1, i64); - assert_evals_to!("Num.shiftRightBy 8 -12i8", -1, i8); + assert_evals_to!("Num.shiftRightBy 0 -1", 0, i64); + assert_evals_to!("Num.shiftRightBy -12 -1", -1, i64); + assert_evals_to!("Num.shiftRightBy -12i8 8", -1, i8); } } @@ -2150,14 +2167,14 @@ fn shift_right_by() { fn shift_right_zf_by() { // Logical Right Shift assert_evals_to!( - "Num.shiftRightZfBy 2 (Num.toI8 0b1100_0000u8)", + "Num.shiftRightZfBy (Num.toI8 0b1100_0000u8) 2", 0b0011_0000i8, i8 ); - assert_evals_to!("Num.shiftRightZfBy 2 0b1100_0000u8", 0b0011_0000u8, u8); - assert_evals_to!("Num.shiftRightZfBy 1 0b0000_0010u8", 0b0000_0001u8, u8); - assert_evals_to!("Num.shiftRightZfBy 2 0b0000_1100u8", 0b0000_0011u8, u8); - assert_evals_to!("Num.shiftRightZfBy 12 0b1000_0000u8", 0b0000_0000u8, u8); + assert_evals_to!("Num.shiftRightZfBy 0b1100_0000u8 2", 0b0011_0000u8, u8); + assert_evals_to!("Num.shiftRightZfBy 0b0000_0010u8 1", 0b0000_0001u8, u8); + assert_evals_to!("Num.shiftRightZfBy 0b0000_1100u8 2", 0b0000_0011u8, u8); + assert_evals_to!("Num.shiftRightZfBy 0b1000_0000u8 12", 0b0000_0000u8, u8); } #[test] @@ -3567,7 +3584,7 @@ fn to_float_f64() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// https://github.com/rtfeldman/roc/issues/2696 +// https://github.com/roc-lang/roc/issues/2696 fn upcast_of_int_is_zext() { assert_evals_to!( indoc!( @@ -3582,7 +3599,7 @@ fn upcast_of_int_is_zext() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// https://github.com/rtfeldman/roc/issues/2696 +// https://github.com/roc-lang/roc/issues/2696 fn upcast_of_int_checked_is_zext() { assert_evals_to!( indoc!( diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 14376cbf9a..270cc73cea 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -9,6 +9,8 @@ use crate::helpers::wasm::assert_evals_to; use indoc::indoc; #[allow(unused_imports)] +use roc_std::RocList; +#[allow(unused_imports)] use roc_std::RocStr; #[test] @@ -1232,8 +1234,8 @@ fn return_wrapped_closure() { main = foo "# ), - [5], - [i64; 1] + 5, + i64 ); } @@ -2643,12 +2645,12 @@ fn pattern_match_unit_tag() { ); } -// see for why this is disabled on wasm32 https://github.com/rtfeldman/roc/issues/1687 +// see for why this is disabled on wasm32 https://github.com/roc-lang/roc/issues/1687 #[cfg(not(feature = "gen-llvm-wasm"))] #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn mirror_llvm_alignment_padding() { - // see https://github.com/rtfeldman/roc/issues/1569 + // see https://github.com/roc-lang/roc/issues/1569 assert_evals_to!( indoc!( r#" @@ -2781,7 +2783,7 @@ fn lambda_set_enum_byte_byte() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn list_walk_until() { - // see https://github.com/rtfeldman/roc/issues/1576 + // see https://github.com/roc-lang/roc/issues/1576 assert_evals_to!( indoc!( r#" @@ -2807,7 +2809,7 @@ fn list_walk_until() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_literal_not_specialized_with_annotation() { - // see https://github.com/rtfeldman/roc/issues/1600 + // see https://github.com/roc-lang/roc/issues/1600 assert_evals_to!( indoc!( r#" @@ -2835,7 +2837,7 @@ fn int_literal_not_specialized_with_annotation() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn int_literal_not_specialized_no_annotation() { - // see https://github.com/rtfeldman/roc/issues/1600 + // see https://github.com/roc-lang/roc/issues/1600 assert_evals_to!( indoc!( r#" @@ -2862,7 +2864,7 @@ fn int_literal_not_specialized_no_annotation() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn unresolved_tvar_when_capture_is_unused() { - // see https://github.com/rtfeldman/roc/issues/1585 + // see https://github.com/roc-lang/roc/issues/1585 assert_evals_to!( indoc!( r#" @@ -2908,7 +2910,7 @@ fn value_not_exposed_hits_panic() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn mix_function_and_closure() { - // see https://github.com/rtfeldman/roc/pull/1706 + // see https://github.com/roc-lang/roc/pull/1706 assert_evals_to!( indoc!( r#" @@ -2934,7 +2936,7 @@ fn mix_function_and_closure() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn mix_function_and_closure_level_of_indirection() { - // see https://github.com/rtfeldman/roc/pull/1706 + // see https://github.com/roc-lang/roc/pull/1706 assert_evals_to!( indoc!( r#" @@ -2960,7 +2962,7 @@ fn mix_function_and_closure_level_of_indirection() { #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg_attr(debug_assertions, ignore)] // this test stack-overflows the compiler in debug mode fn do_pass_bool_byte_closure_layout() { - // see https://github.com/rtfeldman/roc/pull/1706 + // see https://github.com/roc-lang/roc/pull/1706 // the distinction is actually important, dropping that info means some functions just get // skipped assert_evals_to!( @@ -3422,7 +3424,7 @@ fn polymorphic_lambda_set_multiple_specializations() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn list_map2_conslist() { - // this had an RC problem, https://github.com/rtfeldman/roc/issues/2968 + // this had an RC problem, https://github.com/roc-lang/roc/issues/2968 assert_evals_to!( indoc!( r#" @@ -3724,7 +3726,7 @@ fn recursive_lambda_set_issue_3444() { ), RocStr::from("c"), RocStr - ); + ) } #[test] @@ -3838,3 +3840,222 @@ fn compose_recursive_lambda_set_productive_inferred() { RocStr ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn local_binding_aliases_function() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ main ] to "./platform" + + f : {} -> List a + f = \_ -> [] + + main : List U8 + main = + g = f + + g {} + "# + ), + RocList::::from_slice(&[]), + RocList + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn local_binding_aliases_function_inferred() { + assert_evals_to!( + indoc!( + r#" + app "test" provides [ main ] to "./platform" + + f = \_ -> [] + + main = + g = f + + g {} + "# + ), + RocList::from_slice(&[]), + RocList + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn transient_captures() { + assert_evals_to!( + indoc!( + r#" + x = "abc" + + getX = \{} -> x + + h = \{} -> getX {} + + h {} + "# + ), + RocStr::from("abc"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn transient_captures_after_def_ordering() { + assert_evals_to!( + indoc!( + r#" + h = \{} -> getX {} + + getX = \{} -> x + + x = "abc" + + h {} + "# + ), + RocStr::from("abc"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn deep_transient_capture_chain() { + assert_evals_to!( + indoc!( + r#" + z = "abc" + + getX = \{} -> getY {} + getY = \{} -> getZ {} + getZ = \{} -> z + + h = \{} -> getX {} + + h {} + "# + ), + RocStr::from("abc"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn deep_transient_capture_chain_with_multiple_captures() { + assert_evals_to!( + indoc!( + r#" + h = "h" + x = "x" + y = "y" + z = "z" + + getX = \{} -> Str.concat x (getY {}) + getY = \{} -> Str.concat y (getZ {}) + getZ = \{} -> z + + getH = \{} -> Str.concat h (getX {}) + + getH {} + "# + ), + RocStr::from("hxyz"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn transient_captures_from_outer_scope() { + assert_evals_to!( + indoc!( + r#" + x = "abc" + + getX = \{} -> x + + innerScope = + h = \{} -> getX {} + h {} + + innerScope + "# + ), + RocStr::from("abc"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn mutually_recursive_captures() { + assert_evals_to!( + indoc!( + r#" + x : Bool + x = True + + y : Bool + y = False + + a = "foo" + b = "bar" + + foo = \{} -> if x then a else bar {} + bar = \{} -> if y then b else foo {} + + bar {} + "# + ), + RocStr::from("foo"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn monomorphization_sees_polymorphic_recursion() { + assert_evals_to!( + indoc!( + r#" + foo : a, Bool -> Str + foo = \in, b -> if b then "done" else bar in + + bar = \_ -> foo {} True + + foo "" False + "# + ), + RocStr::from("done"), + RocStr + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn int_let_generalization() { + assert_evals_to!( + indoc!( + r#" + manyAux : {} -> I32 + manyAux = \_ -> + output = \_ -> 42 + + output {} + + when manyAux {} is + _ -> "done" + "# + ), + RocStr::from("done"), + RocStr + ); +} diff --git a/crates/compiler/test_gen/src/gen_records.rs b/crates/compiler/test_gen/src/gen_records.rs index 66c085dd08..5d1e67f00a 100644 --- a/crates/compiler/test_gen/src/gen_records.rs +++ b/crates/compiler/test_gen/src/gen_records.rs @@ -964,7 +964,7 @@ fn update_the_only_field() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// https://github.com/rtfeldman/roc/issues/1513 +// https://github.com/roc-lang/roc/issues/1513 fn both_have_unique_fields() { assert_evals_to!( indoc!( @@ -985,7 +985,7 @@ fn both_have_unique_fields() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -// https://github.com/rtfeldman/roc/issues/2535 +// https://github.com/roc-lang/roc/issues/2535 fn different_proc_types_specialized_to_same_layout() { assert_evals_to!( indoc!( diff --git a/crates/compiler/test_gen/src/gen_tags.rs b/crates/compiler/test_gen/src/gen_tags.rs index c78ee53441..6eccdd3bd1 100644 --- a/crates/compiler/test_gen/src/gen_tags.rs +++ b/crates/compiler/test_gen/src/gen_tags.rs @@ -1154,7 +1154,7 @@ fn applied_tag_function_result() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] -#[ignore = "This test has incorrect refcounts: https://github.com/rtfeldman/roc/issues/2968"] +#[ignore = "This test has incorrect refcounts: https://github.com/roc-lang/roc/issues/2968"] fn applied_tag_function_linked_list() { assert_evals_to!( indoc!( @@ -1469,7 +1469,7 @@ fn issue_2458() { } #[test] -#[ignore = "See https://github.com/rtfeldman/roc/issues/2466"] +#[ignore = "See https://github.com/roc-lang/roc/issues/2466"] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn issue_2458_deep_recursion_var() { assert_evals_to!( @@ -1921,10 +1921,10 @@ fn issue_2165_recursive_tag_destructure() { indoc!( r#" SomeTag : [ Ctor { rec : List SomeTag } ] - + x : SomeTag x = Ctor { rec: [] } - + when x is Ctor { rec } -> Num.toStr (List.len rec) "# @@ -1933,3 +1933,25 @@ fn issue_2165_recursive_tag_destructure() { RocStr ) } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn tag_union_let_generalization() { + assert_evals_to!( + indoc!( + r#" + manyAux : {} -> [ Loop, Done ] + manyAux = \_ -> + output = Done + + output + + when manyAux {} is + Loop -> "loop" + Done -> "done" + "# + ), + RocStr::from("done"), + RocStr + ); +} diff --git a/crates/compiler/test_gen/src/tests.rs b/crates/compiler/test_gen/src/tests.rs index a8ecc5fa83..81c431acd7 100644 --- a/crates/compiler/test_gen/src/tests.rs +++ b/crates/compiler/test_gen/src/tests.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits #![allow(clippy::float_cmp)] diff --git a/crates/compiler/test_mono/Cargo.toml b/crates/compiler/test_mono/Cargo.toml index 377c57ebbe..24a2e017eb 100644 --- a/crates/compiler/test_mono/Cargo.toml +++ b/crates/compiler/test_mono/Cargo.toml @@ -20,4 +20,4 @@ roc_target = { path = "../roc_target" } roc_reporting = { path = "../../reporting" } test_mono_macros = { path = "../test_mono_macros" } bumpalo = { version = "3.8.0", features = ["collections"] } -indoc = "1.0.3" +indoc = "1.0.7" diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index 520a8a1808..26e9a06eae 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -4,17 +4,15 @@ procedure List.6 (#Attr.2): procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; - let Test.10 : {I64} = Struct {Test.2}; - let Test.9 : List {I64} = Array [Test.10]; + let Test.9 : List I64 = Array [Test.2]; ret Test.9; -procedure Test.3 (Test.8, #Attr.12): - let Test.2 : I64 = StructAtIndex 0 #Attr.12; +procedure Test.3 (Test.8, Test.2): ret Test.2; procedure Test.0 (): let Test.7 : {} = Struct {}; - let Test.4 : List {I64} = CallByName Test.1 Test.7; + let Test.4 : List I64 = CallByName Test.1 Test.7; let Test.6 : U64 = CallByName List.6 Test.4; dec Test.4; ret Test.6; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index f7bc46f32d..03cb371d02 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -12,22 +12,20 @@ procedure List.71 (#Attr.2, #Attr.3): let List.388 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; ret List.388; -procedure Test.23 (Test.24, Test.35, #Attr.12): - let Test.22 : U8 = StructAtIndex 0 #Attr.12; +procedure Test.23 (Test.24, Test.35, Test.22): let Test.37 : List U8 = CallByName List.4 Test.24 Test.22; ret Test.37; procedure Test.8 (Test.22): - let Test.34 : {U8} = Struct {Test.22}; - ret Test.34; + ret Test.22; procedure Test.9 (Test.27): - let Test.33 : {U8} = CallByName Test.8 Test.27; + let Test.33 : U8 = CallByName Test.8 Test.27; ret Test.33; procedure Test.0 (): let Test.32 : U8 = 15i64; - let Test.28 : {U8} = CallByName Test.9 Test.32; + let Test.28 : U8 = CallByName Test.9 Test.32; let Test.30 : List U8 = Array []; let Test.31 : {} = Struct {}; let Test.29 : List U8 = CallByName Test.23 Test.30 Test.31 Test.28; 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 98e270cb45..2e9010b165 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 @@ -1,34 +1,26 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.1 : {Str} = Struct {#Derived.1}; - let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; ret #Derived_gen.0; -procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12): - let #Derived.1 : Str = StructAtIndex 0 #Attr.12; - inc #Derived.1; - dec #Attr.12; +procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.7 : Str = "a"; - let #Derived_gen.8 : {Str} = CallByName #Derived.5 #Derived.1; - let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8}; - let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6]; - let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5; + let #Derived_gen.8 : Str = CallByName #Derived.5 #Derived.1; + let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; + let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; + let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; procedure #Derived.5 (#Derived.6): - let #Derived_gen.15 : {Str} = Struct {#Derived.6}; - let #Derived_gen.14 : {Str} = CallByName Encode.22 #Derived_gen.15; + let #Derived_gen.14 : Str = CallByName Encode.22 #Derived.6; ret #Derived_gen.14; -procedure #Derived.7 (#Derived.8, #Derived.9, #Attr.12): - let #Derived.6 : Str = StructAtIndex 0 #Attr.12; - inc #Derived.6; - dec #Attr.12; +procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6): let #Derived_gen.21 : Str = "b"; - let #Derived_gen.22 : {Str} = CallByName Json.18 #Derived.6; - let #Derived_gen.20 : {Str, {Str}} = Struct {#Derived_gen.21, #Derived_gen.22}; - let #Derived_gen.19 : List {Str, {Str}} = Array [#Derived_gen.20]; - let #Derived_gen.18 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.19; + let #Derived_gen.22 : Str = CallByName Json.18 #Derived.6; + let #Derived_gen.20 : {Str, Str} = Struct {#Derived_gen.21, #Derived_gen.22}; + let #Derived_gen.19 : List {Str, Str} = Array [#Derived_gen.20]; + let #Derived_gen.18 : List {Str, Str} = CallByName Json.20 #Derived_gen.19; let #Derived_gen.17 : List U8 = CallByName Encode.23 #Derived.8 #Derived_gen.18 #Derived.9; ret #Derived_gen.17; @@ -69,7 +61,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {Str} = CallByName #Derived.0 Encode.100; + let Encode.105 : Str = CallByName #Derived.0 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -77,10 +69,7 @@ procedure Json.1 (): let Json.318 : {} = Struct {}; ret Json.318; -procedure Json.103 (Json.104, Json.321, #Attr.12): - let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12; - inc Json.102; - dec #Attr.12; +procedure Json.103 (Json.104, Json.321, Json.102): let Json.354 : I32 = 123i64; let Json.353 : U8 = CallByName Num.123 Json.354; let Json.106 : List U8 = CallByName List.4 Json.104 Json.353; @@ -97,10 +86,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12): let Json.325 : List U8 = CallByName List.4 Json.108 Json.326; ret Json.325; -procedure Json.103 (Json.104, Json.321, #Attr.12): - let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12; - inc Json.102; - dec #Attr.12; +procedure Json.103 (Json.104, Json.321, Json.102): let Json.397 : I32 = 123i64; let Json.396 : U8 = CallByName Num.123 Json.397; let Json.106 : List U8 = CallByName List.4 Json.104 Json.396; @@ -120,7 +106,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12): procedure Json.105 (Json.323, Json.324): let Json.111 : Str = StructAtIndex 0 Json.324; inc Json.111; - let Json.112 : {Str} = StructAtIndex 1 Json.324; + let Json.112 : Str = StructAtIndex 1 Json.324; inc Json.112; dec Json.324; let Json.109 : List U8 = StructAtIndex 0 Json.323; @@ -159,7 +145,7 @@ procedure Json.105 (Json.323, Json.324): procedure Json.105 (Json.323, Json.324): let Json.111 : Str = StructAtIndex 0 Json.324; inc Json.111; - let Json.112 : {Str} = StructAtIndex 1 Json.324; + let Json.112 : Str = StructAtIndex 1 Json.324; inc Json.112; dec Json.324; let Json.109 : List U8 = StructAtIndex 0 Json.323; @@ -196,24 +182,18 @@ procedure Json.105 (Json.323, Json.324): jump Json.378 Json.113; procedure Json.18 (Json.86): - let Json.365 : {Str} = Struct {Json.86}; - let Json.364 : {Str} = CallByName Encode.22 Json.365; + let Json.364 : Str = CallByName Encode.22 Json.86; ret Json.364; procedure Json.20 (Json.102): - let Json.320 : {List {Str, {Str}}} = Struct {Json.102}; - let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320; + let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102; ret Json.319; procedure Json.20 (Json.102): - let Json.362 : {List {Str, {Str}}} = Struct {Json.102}; - let Json.361 : {List {Str, {Str}}} = CallByName Encode.22 Json.362; + let Json.361 : List {Str, Str} = CallByName Encode.22 Json.102; ret Json.361; -procedure Json.87 (Json.88, Json.366, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.366, Json.86): let Json.406 : I32 = 34i64; let Json.405 : U8 = CallByName Num.123 Json.406; let Json.403 : List U8 = CallByName List.4 Json.88 Json.405; @@ -224,21 +204,18 @@ procedure Json.87 (Json.88, Json.366, #Attr.12): let Json.399 : List U8 = CallByName List.4 Json.400 Json.401; ret Json.399; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.434 : {List U8, U64} = CallByName Json.105 List.134 List.135; let List.433 : [C [], C {List U8, U64}] = TagId(1) List.434; ret List.433; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.515 : {List U8, U64} = CallByName Json.105 List.134 List.135; let List.514 : [C [], C {List U8, U64}] = TagId(1) List.515; ret List.514; procedure List.18 (List.130, List.131, List.132): - let List.411 : {{}} = Struct {List.132}; - let List.405 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.411; + let List.405 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.408 : U8 = 1i64; let List.409 : U8 = GetTagId List.405; let List.410 : Int1 = lowlevel Eq List.408 List.409; @@ -254,8 +231,7 @@ procedure List.18 (List.130, List.131, List.132): ret List.407; procedure List.18 (List.130, List.131, List.132): - let List.491 : {{}} = Struct {List.132}; - let List.485 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.491; + let List.485 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.488 : U8 = 1i64; let List.489 : U8 = GetTagId List.485; let List.490 : Int1 = lowlevel Eq List.488 List.489; @@ -289,11 +265,11 @@ procedure List.6 (#Attr.2): ret List.494; procedure List.66 (#Attr.2, #Attr.3): - let List.432 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.432 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.432; procedure List.66 (#Attr.2, #Attr.3): - let List.513 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.513 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.513; procedure List.69 (#Attr.2): @@ -328,7 +304,7 @@ procedure List.86 (List.448, List.449, List.450, List.451, List.452): joinpoint List.420 List.364 List.365 List.366 List.367 List.368: let List.422 : Int1 = CallByName Num.22 List.367 List.368; if List.422 then - let List.431 : {Str, {Str}} = CallByName List.66 List.364 List.367; + let List.431 : {Str, Str} = CallByName List.66 List.364 List.367; let List.423 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.431 List.366; let List.428 : U8 = 1i64; let List.429 : U8 = GetTagId List.423; @@ -355,7 +331,7 @@ procedure List.86 (List.529, List.530, List.531, List.532, List.533): joinpoint List.501 List.364 List.365 List.366 List.367 List.368: let List.503 : Int1 = CallByName Num.22 List.367 List.368; if List.503 then - let List.512 : {Str, {Str}} = CallByName List.66 List.364 List.367; + let List.512 : {Str, Str} = CallByName List.66 List.364 List.367; let List.504 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.512 List.366; let List.509 : U8 = 1i64; let List.510 : U8 = GetTagId List.504; @@ -399,31 +375,31 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Num.285; procedure Str.12 (#Attr.2): - let Str.212 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.212; + let Str.219 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.219; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.12 : Str = "bar"; 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 fda4fb2049..3398a4c5e9 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 @@ -1,17 +1,13 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.1 : {Str} = Struct {#Derived.1}; - let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; ret #Derived_gen.0; -procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12): - let #Derived.1 : Str = StructAtIndex 0 #Attr.12; - inc #Derived.1; - dec #Attr.12; +procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.7 : Str = "a"; - let #Derived_gen.8 : {Str} = CallByName Json.18 #Derived.1; - let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.7, #Derived_gen.8}; - let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6]; - let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5; + let #Derived_gen.8 : Str = CallByName Json.18 #Derived.1; + let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; + let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; + let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; @@ -38,7 +34,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {Str} = CallByName #Derived.0 Encode.100; + let Encode.105 : Str = CallByName #Derived.0 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -46,10 +42,7 @@ procedure Json.1 (): let Json.318 : {} = Struct {}; ret Json.318; -procedure Json.103 (Json.104, Json.321, #Attr.12): - let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12; - inc Json.102; - dec #Attr.12; +procedure Json.103 (Json.104, Json.321, Json.102): let Json.357 : I32 = 123i64; let Json.356 : U8 = CallByName Num.123 Json.357; let Json.106 : List U8 = CallByName List.4 Json.104 Json.356; @@ -69,7 +62,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12): procedure Json.105 (Json.326, Json.327): let Json.111 : Str = StructAtIndex 0 Json.327; inc Json.111; - let Json.112 : {Str} = StructAtIndex 1 Json.327; + let Json.112 : Str = StructAtIndex 1 Json.327; inc Json.112; dec Json.327; let Json.109 : List U8 = StructAtIndex 0 Json.326; @@ -106,19 +99,14 @@ procedure Json.105 (Json.326, Json.327): jump Json.338 Json.113; procedure Json.18 (Json.86): - let Json.323 : {Str} = Struct {Json.86}; - let Json.322 : {Str} = CallByName Encode.22 Json.323; + let Json.322 : Str = CallByName Encode.22 Json.86; ret Json.322; procedure Json.20 (Json.102): - let Json.320 : {List {Str, {Str}}} = Struct {Json.102}; - let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320; + let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102; ret Json.319; -procedure Json.87 (Json.88, Json.324, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.324, Json.86): let Json.366 : I32 = 34i64; let Json.365 : U8 = CallByName Num.123 Json.366; let Json.363 : List U8 = CallByName List.4 Json.88 Json.365; @@ -129,15 +117,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12): let Json.359 : List U8 = CallByName List.4 Json.360 Json.361; ret Json.359; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.441 : {List U8, U64} = CallByName Json.105 List.134 List.135; let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441; ret List.440; procedure List.18 (List.130, List.131, List.132): - let List.417 : {{}} = Struct {List.132}; - let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417; + let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.414 : U8 = 1i64; let List.415 : U8 = GetTagId List.411; let List.416 : Int1 = lowlevel Eq List.414 List.415; @@ -167,7 +153,7 @@ procedure List.6 (#Attr.2): ret List.420; procedure List.66 (#Attr.2, #Attr.3): - let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.439 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.439; procedure List.69 (#Attr.2): @@ -196,7 +182,7 @@ procedure List.86 (List.455, List.456, List.457, List.458, List.459): joinpoint List.427 List.364 List.365 List.366 List.367 List.368: let List.429 : Int1 = CallByName Num.22 List.367 List.368; if List.429 then - let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367; + let List.438 : {Str, Str} = CallByName List.66 List.364 List.367; let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366; let List.435 : U8 = 1i64; let List.436 : U8 = GetTagId List.430; @@ -240,31 +226,31 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Num.266; procedure Str.12 (#Attr.2): - let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.210; + let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.217; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.11 : Str = "foo"; 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 e2211c6e60..0aff7f2c04 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 @@ -1,25 +1,21 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.1 : {{Str, Str}} = Struct {#Derived.1}; - let #Derived_gen.0 : {{Str, Str}} = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : {Str, Str} = CallByName Encode.22 #Derived.1; ret #Derived_gen.0; -procedure #Derived.2 (#Derived.3, #Derived.4, #Attr.12): - let #Derived.1 : {Str, Str} = StructAtIndex 0 #Attr.12; - inc #Derived.1; - dec #Attr.12; +procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.11 : Str = "a"; let #Derived_gen.13 : Str = StructAtIndex 0 #Derived.1; inc #Derived_gen.13; - let #Derived_gen.12 : {Str} = CallByName Json.18 #Derived_gen.13; - let #Derived_gen.6 : {Str, {Str}} = Struct {#Derived_gen.11, #Derived_gen.12}; + let #Derived_gen.12 : Str = CallByName Json.18 #Derived_gen.13; + let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.11, #Derived_gen.12}; let #Derived_gen.8 : Str = "b"; let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1; inc #Derived_gen.10; dec #Derived.1; - let #Derived_gen.9 : {Str} = CallByName Json.18 #Derived_gen.10; - let #Derived_gen.7 : {Str, {Str}} = Struct {#Derived_gen.8, #Derived_gen.9}; - let #Derived_gen.5 : List {Str, {Str}} = Array [#Derived_gen.6, #Derived_gen.7]; - let #Derived_gen.4 : {List {Str, {Str}}} = CallByName Json.20 #Derived_gen.5; + let #Derived_gen.9 : Str = CallByName Json.18 #Derived_gen.10; + let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9}; + let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7]; + let #Derived_gen.4 : List {Str, Str} = CallByName Json.20 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; @@ -46,7 +42,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {{Str, Str}} = CallByName #Derived.0 Encode.100; + let Encode.105 : {Str, Str} = CallByName #Derived.0 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -54,10 +50,7 @@ procedure Json.1 (): let Json.318 : {} = Struct {}; ret Json.318; -procedure Json.103 (Json.104, Json.321, #Attr.12): - let Json.102 : List {Str, {Str}} = StructAtIndex 0 #Attr.12; - inc Json.102; - dec #Attr.12; +procedure Json.103 (Json.104, Json.321, Json.102): let Json.360 : I32 = 123i64; let Json.359 : U8 = CallByName Num.123 Json.360; let Json.106 : List U8 = CallByName List.4 Json.104 Json.359; @@ -77,7 +70,7 @@ procedure Json.103 (Json.104, Json.321, #Attr.12): procedure Json.105 (Json.329, Json.330): let Json.111 : Str = StructAtIndex 0 Json.330; inc Json.111; - let Json.112 : {Str} = StructAtIndex 1 Json.330; + let Json.112 : Str = StructAtIndex 1 Json.330; inc Json.112; dec Json.330; let Json.109 : List U8 = StructAtIndex 0 Json.329; @@ -114,19 +107,14 @@ procedure Json.105 (Json.329, Json.330): jump Json.341 Json.113; procedure Json.18 (Json.86): - let Json.326 : {Str} = Struct {Json.86}; - let Json.325 : {Str} = CallByName Encode.22 Json.326; + let Json.325 : Str = CallByName Encode.22 Json.86; ret Json.325; procedure Json.20 (Json.102): - let Json.320 : {List {Str, {Str}}} = Struct {Json.102}; - let Json.319 : {List {Str, {Str}}} = CallByName Encode.22 Json.320; + let Json.319 : List {Str, Str} = CallByName Encode.22 Json.102; ret Json.319; -procedure Json.87 (Json.88, Json.324, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.324, Json.86): let Json.369 : I32 = 34i64; let Json.368 : U8 = CallByName Num.123 Json.369; let Json.366 : List U8 = CallByName List.4 Json.88 Json.368; @@ -137,15 +125,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12): let Json.362 : List U8 = CallByName List.4 Json.363 Json.364; ret Json.362; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.441 : {List U8, U64} = CallByName Json.105 List.134 List.135; let List.440 : [C [], C {List U8, U64}] = TagId(1) List.441; ret List.440; procedure List.18 (List.130, List.131, List.132): - let List.417 : {{}} = Struct {List.132}; - let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.417; + let List.411 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.414 : U8 = 1i64; let List.415 : U8 = GetTagId List.411; let List.416 : Int1 = lowlevel Eq List.414 List.415; @@ -175,7 +161,7 @@ procedure List.6 (#Attr.2): ret List.420; procedure List.66 (#Attr.2, #Attr.3): - let List.439 : {Str, {Str}} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.439 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.439; procedure List.69 (#Attr.2): @@ -204,7 +190,7 @@ procedure List.86 (List.455, List.456, List.457, List.458, List.459): joinpoint List.427 List.364 List.365 List.366 List.367 List.368: let List.429 : Int1 = CallByName Num.22 List.367 List.368; if List.429 then - let List.438 : {Str, {Str}} = CallByName List.66 List.364 List.367; + let List.438 : {Str, Str} = CallByName List.66 List.364 List.367; let List.430 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.438 List.366; let List.435 : U8 = 1i64; let List.436 : U8 = GetTagId List.430; @@ -248,31 +234,31 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Num.266; procedure Str.12 (#Attr.2): - let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.210; + let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.217; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.11 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index ee3bd4f34a..5fd131fd3f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -7,7 +7,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {Str} = CallByName Json.18 Encode.100; + let Encode.105 : Str = CallByName Json.18 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -16,14 +16,10 @@ procedure Json.1 (): ret Json.318; procedure Json.18 (Json.86): - let Json.320 : {Str} = Struct {Json.86}; - let Json.319 : {Str} = CallByName Encode.22 Json.320; + let Json.319 : Str = CallByName Encode.22 Json.86; ret Json.319; -procedure Json.87 (Json.88, Json.321, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.321, Json.86): let Json.330 : I32 = 34i64; let Json.329 : U8 = CallByName Num.123 Json.330; let Json.327 : List U8 = CallByName List.4 Json.88 Json.329; @@ -61,31 +57,31 @@ procedure Num.123 (#Attr.2): ret Num.258; procedure Str.12 (#Attr.2): - let Str.209 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.209; + let Str.216 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.216; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.9 : Str = "abc"; 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 781e31459e..1dab5efd61 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 @@ -1,20 +1,16 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.1 : {Str} = Struct {#Derived.1}; - let #Derived_gen.0 : {Str} = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : Str = CallByName Encode.22 #Derived.1; ret #Derived_gen.0; -procedure #Derived.3 (#Derived.4, #Derived.5, #Attr.12): - let #Derived.1 : Str = StructAtIndex 0 #Attr.12; - inc #Derived.1; - dec #Attr.12; +procedure #Derived.3 (#Derived.4, #Derived.5, #Derived.1): joinpoint #Derived_gen.5 #Derived_gen.4: let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.4 #Derived_gen.4 #Derived.5; ret #Derived_gen.3; in let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : {Str} = CallByName Json.18 #Derived.1; - let #Derived_gen.8 : List {Str} = Array [#Derived_gen.9]; - let #Derived_gen.6 : {Str, List {Str}} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.9 : Str = CallByName Json.18 #Derived.1; + let #Derived_gen.8 : List Str = Array [#Derived_gen.9]; + let #Derived_gen.6 : {Str, List Str} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; procedure Encode.22 (Encode.93): @@ -40,7 +36,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {Str} = CallByName #Derived.0 Encode.100; + let Encode.105 : Str = CallByName #Derived.0 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -49,7 +45,7 @@ procedure Json.1 (): ret Json.318; procedure Json.117 (Json.118, Json.321, #Attr.12): - let Json.116 : List {Str} = StructAtIndex 1 #Attr.12; + let Json.116 : List Str = StructAtIndex 1 #Attr.12; inc Json.116; let Json.115 : Str = StructAtIndex 0 #Attr.12; inc Json.115; @@ -111,19 +107,15 @@ procedure Json.119 (Json.326, Json.125): jump Json.340 Json.126; procedure Json.18 (Json.86): - let Json.323 : {Str} = Struct {Json.86}; - let Json.322 : {Str} = CallByName Encode.22 Json.323; + let Json.322 : Str = CallByName Encode.22 Json.86; ret Json.322; procedure Json.21 (Json.115, Json.116): - let Json.320 : {Str, List {Str}} = Struct {Json.115, Json.116}; - let Json.319 : {Str, List {Str}} = CallByName Encode.22 Json.320; + let Json.320 : {Str, List Str} = Struct {Json.115, Json.116}; + let Json.319 : {Str, List Str} = CallByName Encode.22 Json.320; ret Json.319; -procedure Json.87 (Json.88, Json.324, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.324, Json.86): let Json.371 : I32 = 34i64; let Json.370 : U8 = CallByName Num.123 Json.371; let Json.368 : List U8 = CallByName List.4 Json.88 Json.370; @@ -134,15 +126,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12): let Json.364 : List U8 = CallByName List.4 Json.365 Json.366; ret Json.364; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.447 : {List U8, U64} = CallByName Json.119 List.134 List.135; let List.446 : [C [], C {List U8, U64}] = TagId(1) List.447; ret List.446; procedure List.18 (List.130, List.131, List.132): - let List.423 : {{}} = Struct {List.132}; - let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.423; + let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.420 : U8 = 1i64; let List.421 : U8 = GetTagId List.417; let List.422 : Int1 = lowlevel Eq List.420 List.421; @@ -172,7 +162,7 @@ procedure List.6 (#Attr.2): ret List.424; procedure List.66 (#Attr.2, #Attr.3): - let List.445 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.445 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.445; procedure List.69 (#Attr.2): @@ -201,7 +191,7 @@ procedure List.86 (List.461, List.462, List.463, List.464, List.465): joinpoint List.433 List.364 List.365 List.366 List.367 List.368: let List.435 : Int1 = CallByName Num.22 List.367 List.368; if List.435 then - let List.444 : {Str} = CallByName List.66 List.364 List.367; + let List.444 : Str = CallByName List.66 List.364 List.367; let List.436 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.444 List.366; let List.441 : U8 = 1i64; let List.442 : U8 = GetTagId List.436; @@ -245,31 +235,31 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Num.268; procedure Str.12 (#Attr.2): - let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.210; + let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.217; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.12 : Str = "foo"; 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 2379877e3b..81035c77b5 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 @@ -1,12 +1,8 @@ procedure #Derived.0 (#Derived.1): - let #Derived_gen.1 : {{Str, Str}} = Struct {#Derived.1}; - let #Derived_gen.0 : {{Str, Str}} = CallByName Encode.22 #Derived_gen.1; + let #Derived_gen.0 : {Str, Str} = CallByName Encode.22 #Derived.1; ret #Derived_gen.0; -procedure #Derived.4 (#Derived.5, #Derived.6, #Attr.12): - let #Derived.1 : {Str, Str} = StructAtIndex 0 #Attr.12; - inc #Derived.1; - dec #Attr.12; +procedure #Derived.4 (#Derived.5, #Derived.6, #Derived.1): joinpoint #Derived_gen.5 #Derived_gen.4: let #Derived_gen.3 : List U8 = CallByName Encode.23 #Derived.5 #Derived_gen.4 #Derived.6; ret #Derived_gen.3; @@ -17,10 +13,10 @@ procedure #Derived.4 (#Derived.5, #Derived.6, #Attr.12): inc #Derived.3; dec #Derived.1; let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : {Str} = CallByName Json.18 #Derived.2; - let #Derived_gen.10 : {Str} = CallByName Json.18 #Derived.3; - let #Derived_gen.8 : List {Str} = Array [#Derived_gen.9, #Derived_gen.10]; - let #Derived_gen.6 : {Str, List {Str}} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.9 : Str = CallByName Json.18 #Derived.2; + let #Derived_gen.10 : Str = CallByName Json.18 #Derived.3; + let #Derived_gen.8 : List Str = Array [#Derived_gen.9, #Derived_gen.10]; + let #Derived_gen.6 : {Str, List Str} = CallByName Json.21 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; procedure Encode.22 (Encode.93): @@ -46,7 +42,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): procedure Encode.25 (Encode.100, Encode.101): let Encode.104 : List U8 = Array []; - let Encode.105 : {{Str, Str}} = CallByName #Derived.0 Encode.100; + let Encode.105 : {Str, Str} = CallByName #Derived.0 Encode.100; let Encode.103 : List U8 = CallByName Encode.23 Encode.104 Encode.105 Encode.101; ret Encode.103; @@ -55,7 +51,7 @@ procedure Json.1 (): ret Json.318; procedure Json.117 (Json.118, Json.321, #Attr.12): - let Json.116 : List {Str} = StructAtIndex 1 #Attr.12; + let Json.116 : List Str = StructAtIndex 1 #Attr.12; inc Json.116; let Json.115 : Str = StructAtIndex 0 #Attr.12; inc Json.115; @@ -117,19 +113,15 @@ procedure Json.119 (Json.329, Json.125): jump Json.343 Json.126; procedure Json.18 (Json.86): - let Json.326 : {Str} = Struct {Json.86}; - let Json.325 : {Str} = CallByName Encode.22 Json.326; + let Json.325 : Str = CallByName Encode.22 Json.86; ret Json.325; procedure Json.21 (Json.115, Json.116): - let Json.320 : {Str, List {Str}} = Struct {Json.115, Json.116}; - let Json.319 : {Str, List {Str}} = CallByName Encode.22 Json.320; + let Json.320 : {Str, List Str} = Struct {Json.115, Json.116}; + let Json.319 : {Str, List Str} = CallByName Encode.22 Json.320; ret Json.319; -procedure Json.87 (Json.88, Json.324, #Attr.12): - let Json.86 : Str = StructAtIndex 0 #Attr.12; - inc Json.86; - dec #Attr.12; +procedure Json.87 (Json.88, Json.324, Json.86): let Json.374 : I32 = 34i64; let Json.373 : U8 = CallByName Num.123 Json.374; let Json.371 : List U8 = CallByName List.4 Json.88 Json.373; @@ -140,15 +132,13 @@ procedure Json.87 (Json.88, Json.324, #Attr.12): let Json.367 : List U8 = CallByName List.4 Json.368 Json.369; ret Json.367; -procedure List.133 (List.134, List.135, #Attr.12): - let List.132 : {} = StructAtIndex 0 #Attr.12; +procedure List.133 (List.134, List.135, List.132): let List.447 : {List U8, U64} = CallByName Json.119 List.134 List.135; let List.446 : [C [], C {List U8, U64}] = TagId(1) List.447; ret List.446; procedure List.18 (List.130, List.131, List.132): - let List.423 : {{}} = Struct {List.132}; - let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.423; + let List.417 : [C [], C {List U8, U64}] = CallByName List.75 List.130 List.131 List.132; let List.420 : U8 = 1i64; let List.421 : U8 = GetTagId List.417; let List.422 : Int1 = lowlevel Eq List.420 List.421; @@ -178,7 +168,7 @@ procedure List.6 (#Attr.2): ret List.424; procedure List.66 (#Attr.2, #Attr.3): - let List.445 : {Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + let List.445 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; ret List.445; procedure List.69 (#Attr.2): @@ -207,7 +197,7 @@ procedure List.86 (List.461, List.462, List.463, List.464, List.465): joinpoint List.433 List.364 List.365 List.366 List.367 List.368: let List.435 : Int1 = CallByName Num.22 List.367 List.368; if List.435 then - let List.444 : {Str} = CallByName List.66 List.364 List.367; + let List.444 : Str = CallByName List.66 List.364 List.367; let List.436 : [C [], C {List U8, U64}] = CallByName List.133 List.365 List.444 List.366; let List.441 : U8 = 1i64; let List.442 : U8 = GetTagId List.436; @@ -251,31 +241,31 @@ procedure Num.24 (#Attr.2, #Attr.3): ret Num.268; procedure Str.12 (#Attr.2): - let Str.210 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.210; + let Str.217 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.217; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.204 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.204; + let Str.211 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.211; procedure Str.9 (Str.69): - let Str.202 : U64 = 0i64; - let Str.203 : U64 = CallByName List.6 Str.69; - let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.202 Str.203; - let Str.199 : Int1 = StructAtIndex 2 Str.70; - if Str.199 then - let Str.201 : Str = StructAtIndex 1 Str.70; - inc Str.201; + let Str.209 : U64 = 0i64; + let Str.210 : U64 = CallByName List.6 Str.69; + let Str.70 : {U64, Str, Int1, U8} = CallByName Str.48 Str.69 Str.209 Str.210; + let Str.206 : Int1 = StructAtIndex 2 Str.70; + if Str.206 then + let Str.208 : Str = StructAtIndex 1 Str.70; + inc Str.208; dec Str.70; - let Str.200 : [C {U64, U8}, C Str] = TagId(1) Str.201; - ret Str.200; + let Str.207 : [C {U64, U8}, C Str] = TagId(1) Str.208; + ret Str.207; else - let Str.197 : U8 = StructAtIndex 3 Str.70; - let Str.198 : U64 = StructAtIndex 0 Str.70; + let Str.204 : U8 = StructAtIndex 3 Str.70; + let Str.205 : U64 = StructAtIndex 0 Str.70; dec Str.70; - let Str.196 : {U64, U8} = Struct {Str.198, Str.197}; - let Str.195 : [C {U64, U8}, C Str] = TagId(0) Str.196; - ret Str.195; + let Str.203 : {U64, U8} = Struct {Str.205, Str.204}; + let Str.202 : [C {U64, U8}, C Str] = TagId(0) Str.203; + ret Str.202; procedure Test.0 (): let Test.13 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index 793db11a85..ccbd053d38 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -42,26 +42,26 @@ procedure Num.22 (#Attr.2, #Attr.3): ret Num.257; procedure Str.27 (Str.89): - let Str.195 : [C Int1, C I64] = CallByName Str.62 Str.89; - ret Str.195; + let Str.202 : [C Int1, C I64] = CallByName Str.62 Str.89; + ret Str.202; procedure Str.47 (#Attr.2): - let Str.203 : {I64, U8} = lowlevel StrToNum #Attr.2; - ret Str.203; + let Str.210 : {I64, U8} = lowlevel StrToNum #Attr.2; + ret Str.210; procedure Str.62 (Str.190): let Str.191 : {I64, U8} = CallByName Str.47 Str.190; - let Str.201 : U8 = StructAtIndex 1 Str.191; - let Str.202 : U8 = 0i64; - let Str.198 : Int1 = CallByName Bool.7 Str.201 Str.202; - if Str.198 then - let Str.200 : I64 = StructAtIndex 0 Str.191; - let Str.199 : [C Int1, C I64] = TagId(1) Str.200; - ret Str.199; + let Str.208 : U8 = StructAtIndex 1 Str.191; + let Str.209 : U8 = 0i64; + let Str.205 : Int1 = CallByName Bool.7 Str.208 Str.209; + if Str.205 then + let Str.207 : I64 = StructAtIndex 0 Str.191; + let Str.206 : [C Int1, C I64] = TagId(1) Str.207; + ret Str.206; else - let Str.197 : Int1 = false; - let Str.196 : [C Int1, C I64] = TagId(0) Str.197; - ret Str.196; + let Str.204 : Int1 = false; + let Str.203 : [C Int1, C I64] = TagId(0) Str.204; + ret Str.203; procedure Test.0 (): let Test.4 : Int1 = true; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt index 5228441c5b..83658eacbb 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_have_captured_function_in_closure.txt @@ -2,10 +2,7 @@ procedure Test.11 (Test.37): let Test.38 : Str = ""; ret Test.38; -procedure Test.13 (Test.51, #Attr.12): - let Test.12 : Str = StructAtIndex 0 #Attr.12; - inc Test.12; - dec #Attr.12; +procedure Test.13 (Test.51, Test.12): ret Test.12; procedure Test.15 (Test.39): @@ -29,8 +26,8 @@ procedure Test.3 (Test.17): ret Test.36; procedure Test.4 (Test.18): - let Test.50 : {Str} = Struct {Test.18}; - ret Test.50; + inc Test.18; + ret Test.18; procedure Test.9 (Test.29, #Attr.12): let Test.8 : {} = UnionAtIndex (Id 0) (Index 1) #Attr.12; @@ -48,7 +45,8 @@ procedure Test.9 (Test.29, #Attr.12): let Test.7 : {} = UnionAtIndex (Id 1) (Index 0) #Attr.12; let Test.49 : {} = Struct {}; let Test.48 : Str = CallByName Test.16 Test.49; - let Test.45 : {Str} = CallByName Test.4 Test.48; + let Test.45 : Str = CallByName Test.4 Test.48; + dec Test.48; let Test.47 : {} = Struct {}; let Test.46 : Str = CallByName Test.13 Test.47 Test.45; ret Test.46; diff --git a/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt b/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt index ae024a188f..7e624d7a76 100644 --- a/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt +++ b/crates/compiler/test_mono/generated/lambda_set_niche_same_layout_different_constructor.txt @@ -1,11 +1,9 @@ procedure Test.1 (Test.4): - let Test.5 : {Str} = Struct {Test.4}; - ret Test.5; - -procedure Test.5 (Test.12, #Attr.12): - let Test.4 : Str = StructAtIndex 0 #Attr.12; inc Test.4; - dec #Attr.12; + ret Test.4; + +procedure Test.5 (Test.12, Test.4): + dec Test.4; let Test.14 : Str = ""; ret Test.14; @@ -18,9 +16,11 @@ procedure Test.0 (): let Test.20 : Int1 = lowlevel Eq Test.19 Test.2; if Test.20 then let Test.15 : Str = ""; - let Test.10 : {Str} = CallByName Test.1 Test.15; + let Test.10 : Str = CallByName Test.1 Test.15; + dec Test.15; jump Test.9 Test.10; else let Test.18 : Str = ""; - let Test.16 : {Str} = CallByName Test.1 Test.18; + let Test.16 : Str = CallByName Test.1 Test.18; + dec Test.18; jump Test.9 Test.16; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 881b2ed236..ec453f3360 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -27,12 +27,12 @@ procedure Num.22 (#Attr.2, #Attr.3): ret Num.257; procedure Str.16 (#Attr.2, #Attr.3): - let Str.195 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; - ret Str.195; + let Str.202 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; + ret Str.202; procedure Str.3 (#Attr.2, #Attr.3): - let Str.196 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.196; + let Str.203 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.203; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index a78d58d911..2cd40864a0 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -29,8 +29,8 @@ procedure Num.22 (#Attr.2, #Attr.3): ret Num.257; procedure Str.3 (#Attr.2, #Attr.3): - let Str.196 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.196; + let Str.203 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.203; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/nested_closure.txt b/crates/compiler/test_mono/generated/nested_closure.txt index 304722fa96..1b9d1a6f1d 100644 --- a/crates/compiler/test_mono/generated/nested_closure.txt +++ b/crates/compiler/test_mono/generated/nested_closure.txt @@ -1,15 +1,13 @@ procedure Test.1 (Test.5): let Test.2 : I64 = 42i64; - let Test.3 : {I64} = Struct {Test.2}; - ret Test.3; + ret Test.2; -procedure Test.3 (Test.9, #Attr.12): - let Test.2 : I64 = StructAtIndex 0 #Attr.12; +procedure Test.3 (Test.9, Test.2): ret Test.2; procedure Test.0 (): let Test.8 : {} = Struct {}; - let Test.4 : {I64} = CallByName Test.1 Test.8; + let Test.4 : I64 = CallByName Test.1 Test.8; let Test.7 : {} = Struct {}; let Test.6 : I64 = CallByName Test.3 Test.7 Test.4; ret Test.6; diff --git a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt index a298bf4ab4..95837c3b32 100644 --- a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt +++ b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt @@ -4,20 +4,17 @@ procedure Num.19 (#Attr.2, #Attr.3): procedure Test.1 (Test.2): let Test.9 : U32 = 0i64; - let Test.16 : {U32} = Struct {Test.2}; - let Test.8 : U32 = CallByName Test.3 Test.9 Test.16; + let Test.8 : U32 = CallByName Test.3 Test.9 Test.2; ret Test.8; procedure Test.3 (Test.18, Test.19): - joinpoint Test.10 Test.4 #Attr.12: - let Test.2 : U32 = StructAtIndex 0 #Attr.12; + joinpoint Test.10 Test.4 Test.2: let Test.14 : Int1 = true; if Test.14 then ret Test.4; else let Test.12 : U32 = CallByName Num.19 Test.4 Test.2; - let Test.13 : {U32} = Struct {Test.2}; - jump Test.10 Test.12 Test.13; + jump Test.10 Test.12 Test.2; in jump Test.10 Test.18 Test.19; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index c4a68e4000..07da014fe6 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -1,6 +1,6 @@ #![cfg(test)] #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] // we actually want to compare against the literal float bits #![allow(clippy::float_cmp)] @@ -1084,7 +1084,7 @@ fn specialize_lowlevel() { #[mono_test] fn empty_list_of_function_type() { - // see https://github.com/rtfeldman/roc/issues/1732 + // see https://github.com/roc-lang/roc/issues/1732 indoc!( r#" app "test" provides [main] to "./platform" diff --git a/crates/compiler/types/src/lib.rs b/crates/compiler/types/src/lib.rs index 09e98b7d82..8483ac38bb 100644 --- a/crates/compiler/types/src/lib.rs +++ b/crates/compiler/types/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod num; pub mod pretty_print; diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index 0b12bc99e9..896aba3cb4 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -66,6 +66,7 @@ struct ErrorTypeState { recursive_tag_unions_seen: Vec, } +#[repr(C)] #[derive(Clone, Copy, Debug)] struct SubsHeader { utable: u64, @@ -99,10 +100,12 @@ impl SubsHeader { } fn to_array(self) -> [u8; std::mem::size_of::()] { + // Safety: With repr(c) all fields are in order and properly aligned without padding. unsafe { std::mem::transmute(self) } } fn from_array(array: [u8; std::mem::size_of::()]) -> Self { + // Safety: With repr(c) all fields are in order and properly aligned without padding. unsafe { std::mem::transmute(array) } } } @@ -2258,13 +2261,13 @@ pub struct LambdaSet { /// /// ```text /// XEffect : A -> B - /// + /// /// after : ({} -> XEffect) -> XEffect /// after = /// \cont -> /// f = \A -[`f (typeof cont)]-> when cont {} is A -> B /// f - /// + /// /// nestForever : {} -> XEffect /// nestForever = \{} -[`nestForever]-> after nestForever /// ^^^^^^^^^^^ {} -[`nestForever]-> A -[`f ({} -[`nestForever]-> A -[`f ...]-> B)]-> B @@ -2482,6 +2485,26 @@ pub trait Label: Sized + Clone { pub type UnionTags = UnionLabels; pub type UnionLambdas = UnionLabels; +impl UnionTags { + pub fn for_result(subs: &mut Subs, ok_payload: Variable, err_payload: Variable) -> Self { + let ok_tuple = { + let variables_slice = + VariableSubsSlice::insert_into_subs(subs, std::iter::once(ok_payload)); + + ("Ok".into(), variables_slice) + }; + + let err_tuple = { + let variables_slice = + VariableSubsSlice::insert_into_subs(subs, std::iter::once(err_payload)); + + ("Err".into(), variables_slice) + }; + + UnionTags::insert_slices_into_subs(subs, [err_tuple, ok_tuple]) + } +} + impl Label for TagName { fn index_subs(subs: &Subs, idx: SubsIndex) -> &Self { &subs[idx] @@ -2995,7 +3018,7 @@ impl RecordFields { (it, ext) } - /// Get a sorted iterator over the fields of this record type + /// get a sorted iterator over the fields of this record type /// /// Implementation: When the record has an `ext` variable that is the empty record, then /// we read the (assumed sorted) fields directly from Subs. Otherwise we have to chase the diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 66d3ab064c..cd4641dca5 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -102,6 +102,10 @@ impl RecordField { RigidOptional(t) => RigidOptional(f(t)), } } + + pub fn is_optional(&self) -> bool { + matches!(self, RecordField::Optional(..)) + } } impl RecordField { diff --git a/crates/compiler/unify/src/lib.rs b/crates/compiler/unify/src/lib.rs index 06addd9f5b..9955d8bd85 100644 --- a/crates/compiler/unify/src/lib.rs +++ b/crates/compiler/unify/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod unify; diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index 827738cddd..9c9315a959 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -1005,7 +1005,7 @@ fn unify_lambda_set( FlexVar(_) => { if M::UNIFYING_SPECIALIZATION { // TODO: It appears that this can happen in well-typed, reasonable programs, but it's - // open question as to why! See also https://github.com/rtfeldman/roc/issues/3163. + // open question as to why! See also https://github.com/roc-lang/roc/issues/3163. let zero_lambda_set = LambdaSet { solved: UnionLabels::default(), recursion_var: OptVariable::NONE, @@ -1209,7 +1209,7 @@ fn separate_union_lambdas( // Lambda sets are effectively tags under another name, and their usage can also result // in the arguments of a lambda name being recursive. It very well may happen that // during unification, a lambda set previously marked as not recursive becomes - // recursive. See the docs of [LambdaSet] for one example, or https://github.com/rtfeldman/roc/pull/2307. + // recursive. See the docs of [LambdaSet] for one example, or https://github.com/roc-lang/roc/pull/2307. // // Like with tag unions, if it has, we'll always pass through this branch. So, take // this opportunity to promote the lambda set to recursive if need be. diff --git a/crates/docs/Cargo.toml b/crates/docs/Cargo.toml index 15a5b33a6d..a582257543 100644 --- a/crates/docs/Cargo.toml +++ b/crates/docs/Cargo.toml @@ -6,7 +6,7 @@ authors = ["The Roc Contributors"] edition = "2021" [dependencies] -pulldown-cmark = { version = "0.9.1", default-features = false } +pulldown-cmark = { version = "0.9.2", default-features = false } roc_ast = { path = "../ast" } roc_load = { path = "../compiler/load" } roc_builtins = { path = "../compiler/builtins" } diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index e58a35a3e0..abeb7f0ccc 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -50,18 +50,18 @@ colored = "2.0.0" pest = "2.1.3" pest_derive = "2.1.0" copypasta = "0.8.1" -palette = "0.6.0" +palette = "0.6.1" confy = { git = 'https://github.com/rust-cli/confy', features = [ "yaml_conf" ], default-features = false } serde = { version = "1.0.130", features = ["derive"] } -nonempty = "0.7.0" +nonempty = "0.8.0" fs_extra = "1.2.0" rodio = { version = "0.15.0", optional = true } # to play sounds threadpool = "1.8.1" [dependencies.bytemuck] -version = "1.7.2" +version = "1.11.0" features = ["derive"] [dev-dependencies] diff --git a/crates/editor/README.md b/crates/editor/README.md index e462661928..e4faf28df0 100644 --- a/crates/editor/README.md +++ b/crates/editor/README.md @@ -23,7 +23,7 @@ If you encounter an error like `gfx_backend_vulkan ... Failed to detect any vali If the error persists, take a look [here](https://www.techpowerup.com/gpu-specs/) to see if your GPU supports vulkan. Use of OpenGL instead of vulkan should be available in several months. -Make sure to [create an issue](https://github.com/rtfeldman/roc/issues/new/choose) if you encounter any problems not listed above. +Make sure to [create an issue](https://github.com/roc-lang/roc/issues/new/choose) if you encounter any problems not listed above. ## Inspiration diff --git a/crates/editor/src/editor/main.rs b/crates/editor/src/editor/main.rs index 63fa44768d..2478be826a 100644 --- a/crates/editor/src/editor/main.rs +++ b/crates/editor/src/editor/main.rs @@ -414,7 +414,7 @@ async fn create_device( }) .await .expect(r#"Request adapter - If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor + If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#editor "#); let color_format = surface.get_preferred_format(&adapter).unwrap(); diff --git a/crates/editor/src/graphics/lowlevel/vertex.rs b/crates/editor/src/graphics/lowlevel/vertex.rs index 85f40bce46..08a91c2d18 100644 --- a/crates/editor/src/graphics/lowlevel/vertex.rs +++ b/crates/editor/src/graphics/lowlevel/vertex.rs @@ -6,11 +6,14 @@ use cgmath::Vector2; #[derive(Copy, Clone)] +#[repr(C)] pub struct Vertex { pub position: Vector2, pub color: [f32; 4], } +// Safety: As defined, there is no padding +// the type is repr(C), and Vector2 is repr(C) unsafe impl bytemuck::Pod for Vertex {} unsafe impl bytemuck::Zeroable for Vertex {} diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index 5ee7ea9fd5..d4dfadaca5 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] #[cfg_attr(test, macro_use)] diff --git a/crates/error_macros/src/lib.rs b/crates/error_macros/src/lib.rs index 7cc51fa78e..5b40436d14 100644 --- a/crates/error_macros/src/lib.rs +++ b/crates/error_macros/src/lib.rs @@ -9,7 +9,7 @@ macro_rules! internal_error { eprintln!("An internal compiler expectation was broken."); eprintln!("This is definitely a compiler bug."); // TODO: update this to the new bug template. - eprintln!("Please file an issue here: https://github.com/rtfeldman/roc/issues/new/choose"); + eprintln!("Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose"); #[allow(clippy::panic)] { panic!($($arg)*); } @@ -92,13 +92,13 @@ macro_rules! assert_copyable { macro_rules! _incomplete_project { ($project_name:literal, $tracking_issue_no:literal) => { panic!( - "[{}] not yet implemented. Tracking issue: https://github.com/rtfeldman/roc/issues/{}", + "[{}] not yet implemented. Tracking issue: https://github.com/roc-lang/roc/issues/{}", $project_name, $tracking_issue_no, ) }; ($project_name:literal, $tracking_issue_no:literal, $($arg:tt)+) => { panic!( - "[{}] not yet implemented. Tracking issue: https://github.com/rtfeldman/roc/issues/{}.\nAdditional information: {}", + "[{}] not yet implemented. Tracking issue: https://github.com/roc-lang/roc/issues/{}.\nAdditional information: {}", $project_name, $tracking_issue_no, format_args!($($arg)+), ) diff --git a/crates/glue/Cargo.toml b/crates/glue/Cargo.toml index 7f544dd57d..adba9f4d1e 100644 --- a/crates/glue/Cargo.toml +++ b/crates/glue/Cargo.toml @@ -28,7 +28,7 @@ fnv = "1.0.7" [dev-dependencies] pretty_assertions = "1.0.0" tempfile = "3.2.0" -indoc = "1.0.3" +indoc = "1.0.7" cli_utils = { path = "../cli_utils" } roc_test_utils = { path = "../test_utils" } -dircpy = "0.3.9" +dircpy = "0.3.12" diff --git a/crates/glue/src/RocType.roc b/crates/glue/src/RocType.roc new file mode 100644 index 0000000000..ff18cda46d --- /dev/null +++ b/crates/glue/src/RocType.roc @@ -0,0 +1,156 @@ +platform "roc-lang/rbt" + requires {} { makeGlue : Str -> Types } + exposes [] + packages {} + imports [] + provides [makeGlueForHost] + +makeGlueForHost : Str -> Types +makeGlueForHost = makeGlue + +# TODO move into separate Target.roc interface once glue works across interfaces. +Target : { + architecture: Architecture, + operatingSystem: OperatingSystem, +} + +Architecture : [ + Aarch32, + Aarch64, + Wasm32, + X86x32, + X86x64, +] + +OperatingSystem : [ + Windows, + Unix, + Wasi, +] + +TypeId := Nat + +Types := { + # These are all indexed by TypeId + types: List RocType, + sizes: List U32, + aligns: List U32, + + # Needed to check for duplicates + typesByName: Dict Str TypeId, + + ## Dependencies - that is, which type depends on which other type. + ## This is important for declaration order in C; we need to output a + ## type declaration earlier in the file than where it gets referenced by another type. + deps: Dict TypeId (List TypeId), + target: Target, +} + +RocType : [ + RocStr, + Bool, + RocResult TypeId TypeId, + Num RocNum, + RocList TypeId, + RocDict TypeId TypeId, + RocSet TypeId, + RocBox TypeId, + TagUnion RocTagUnion, + EmptyTagUnion, + Struct { + name: Str, + fields: List { name: Str, type: TypeId } + }, + TagUnionPayload { + name: Str, + fields: List { discriminant: Nat, type: TypeId }, + }, + ## A recursive pointer, e.g. in StrConsList : [Nil, Cons Str StrConsList], + ## this would be the field of Cons containing the (recursive) StrConsList type, + ## and the TypeId is the TypeId of StrConsList itself. + RecursivePointer TypeId, + Function { + name: Str, + args: List TypeId, + ret: TypeId, + }, + # A zero-sized type, such as an empty record or a single-tag union with no payload + Unit, +] + +RocNum : [ + I8, + U8, + I16, + U16, + I32, + U32, + I64, + U64, + I128, + U128, + F32, + F64, + F128, + Dec, +] + +RocTagUnion : [ + Enumeration { + name: Str, + tags: List Str, + size: U32, + }, + ## A non-recursive tag union + ## e.g. `Result a e : [Ok a, Err e]` + NonRecursive { + name: Str, + tags: List { name : Str, payload : [Some TypeId, None] }, + discriminantSize: U32, + discriminantOffset: U32, + }, + ## A recursive tag union (general case) + ## e.g. `Expr : [Sym Str, Add Expr Expr]` + Recursive { + name: Str, + tags: List { name : Str, payload : [Some TypeId, None] }, + discriminantSize: U32, + discriminantOffset: U32, + }, + ## A recursive tag union that has an empty variant + ## Optimization: Represent the empty variant as null pointer => no memory usage & fast comparison + ## It has more than one other variant, so they need tag IDs (payloads are "wrapped") + ## e.g. `FingerTree a : [Empty, Single a, More (Some a) (FingerTree (Tuple a)) (Some a)]` + ## see also: https://youtu.be/ip92VMpf_-A?t=164 + NullableWrapped { + name: Str, + indexOfNullTag: U16, + tags: List { name : Str, payload : [Some TypeId, None] }, + discriminantSize: U32, + discriminantOffset: U32, + }, + ## Optimization: No need to store a tag ID (the payload is "unwrapped") + ## e.g. `RoseTree a : [Tree a (List (RoseTree a))]` + NonNullableUnwrapped { + name: Str, + tagName: Str, + payload: TypeId, # These always have a payload. + }, + ## Optimization: No need to store a tag ID (the payload is "unwrapped") + ## e.g. `[Foo Str Bool]` + SingleTagStruct { + name: Str, + tagName: Str, + payloadFields: List TypeId, + }, + ## A recursive tag union with only two variants, where one is empty. + ## Optimizations: Use null for the empty variant AND don't store a tag ID for the other variant. + ## e.g. `ConsList a : [Nil, Cons a (ConsList a)]` + NullableUnwrapped { + name: Str, + nullTag: Str, + nonNullTag: Str, + nonNullPayload: TypeId, + whichTagIsNull: [FirstTagIsNull, SecondTagIsNull], + }, +] \ No newline at end of file diff --git a/crates/glue/src/glue.rs b/crates/glue/src/glue.rs new file mode 100644 index 0000000000..a352ff820a --- /dev/null +++ b/crates/glue/src/glue.rs @@ -0,0 +1,4090 @@ +// ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command + +#![allow(unused_unsafe)] +#![allow(dead_code)] +#![allow(unused_mut)] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::unused_unit)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::let_and_return)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::clone_on_copy)] + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Types { + pub aligns: roc_std::RocList, + pub deps: roc_std::RocDict>, + pub sizes: roc_std::RocList, + pub types: roc_std::RocList, + pub typesByName: roc_std::RocDict, + pub target: Target, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum discriminant_RocType { + Bool = 0, + EmptyTagUnion = 1, + Function = 2, + Num = 3, + RecursivePointer = 4, + RocBox = 5, + RocDict = 6, + RocList = 7, + RocResult = 8, + RocSet = 9, + RocStr = 10, + Struct = 11, + TagUnion = 12, + TagUnionPayload = 13, + Unit = 14, +} + +impl core::fmt::Debug for discriminant_RocType { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Bool => f.write_str("discriminant_RocType::Bool"), + Self::EmptyTagUnion => f.write_str("discriminant_RocType::EmptyTagUnion"), + Self::Function => f.write_str("discriminant_RocType::Function"), + Self::Num => f.write_str("discriminant_RocType::Num"), + Self::RecursivePointer => f.write_str("discriminant_RocType::RecursivePointer"), + Self::RocBox => f.write_str("discriminant_RocType::RocBox"), + Self::RocDict => f.write_str("discriminant_RocType::RocDict"), + Self::RocList => f.write_str("discriminant_RocType::RocList"), + Self::RocResult => f.write_str("discriminant_RocType::RocResult"), + Self::RocSet => f.write_str("discriminant_RocType::RocSet"), + Self::RocStr => f.write_str("discriminant_RocType::RocStr"), + Self::Struct => f.write_str("discriminant_RocType::Struct"), + Self::TagUnion => f.write_str("discriminant_RocType::TagUnion"), + Self::TagUnionPayload => f.write_str("discriminant_RocType::TagUnionPayload"), + Self::Unit => f.write_str("discriminant_RocType::Unit"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[repr(C)] +pub union RocType { + Function: core::mem::ManuallyDrop, + Num: RocNum, + RecursivePointer: u32, + RocBox: u32, + RocDict: RocType_RocDict, + RocList: u32, + RocResult: RocType_RocResult, + RocSet: u32, + Struct: core::mem::ManuallyDrop, + TagUnion: core::mem::ManuallyDrop, + TagUnionPayload: core::mem::ManuallyDrop, + _sizer: [u8; 52], +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R15 { + pub discriminant: u32, + pub r#type: u32, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R12 { + pub name: roc_std::RocStr, + pub payload: U4, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R10 { + pub name: roc_std::RocStr, + pub payload: U3, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R7 { + pub name: roc_std::RocStr, + pub payload: U1, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R3 { + pub name: roc_std::RocStr, + pub r#type: u32, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Target { + pub architecture: Architecture, + pub operatingSystem: OperatingSystem, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum OperatingSystem { + Unix = 0, + Wasi = 1, + Windows = 2, +} + +impl core::fmt::Debug for OperatingSystem { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Unix => f.write_str("OperatingSystem::Unix"), + Self::Wasi => f.write_str("OperatingSystem::Wasi"), + Self::Windows => f.write_str("OperatingSystem::Windows"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum Architecture { + Aarch32 = 0, + Aarch64 = 1, + Wasm32 = 2, + X86x32 = 3, + X86x64 = 4, +} + +impl core::fmt::Debug for Architecture { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Aarch32 => f.write_str("Architecture::Aarch32"), + Self::Aarch64 => f.write_str("Architecture::Aarch64"), + Self::Wasm32 => f.write_str("Architecture::Wasm32"), + Self::X86x32 => f.write_str("Architecture::X86x32"), + Self::X86x64 => f.write_str("Architecture::X86x64"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R14 { + pub fields: roc_std::RocList, + pub name: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum discriminant_RocTagUnion { + Enumeration = 0, + NonNullableUnwrapped = 1, + NonRecursive = 2, + NullableUnwrapped = 3, + NullableWrapped = 4, + Recursive = 5, + SingleTagStruct = 6, +} + +impl core::fmt::Debug for discriminant_RocTagUnion { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Enumeration => f.write_str("discriminant_RocTagUnion::Enumeration"), + Self::NonNullableUnwrapped => f.write_str("discriminant_RocTagUnion::NonNullableUnwrapped"), + Self::NonRecursive => f.write_str("discriminant_RocTagUnion::NonRecursive"), + Self::NullableUnwrapped => f.write_str("discriminant_RocTagUnion::NullableUnwrapped"), + Self::NullableWrapped => f.write_str("discriminant_RocTagUnion::NullableWrapped"), + Self::Recursive => f.write_str("discriminant_RocTagUnion::Recursive"), + Self::SingleTagStruct => f.write_str("discriminant_RocTagUnion::SingleTagStruct"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[repr(C)] +pub union RocTagUnion { + Enumeration: core::mem::ManuallyDrop, + NonNullableUnwrapped: core::mem::ManuallyDrop, + NonRecursive: core::mem::ManuallyDrop, + NullableUnwrapped: core::mem::ManuallyDrop, + NullableWrapped: core::mem::ManuallyDrop, + Recursive: core::mem::ManuallyDrop, + SingleTagStruct: core::mem::ManuallyDrop, + _sizer: [u8; 48], +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R13 { + pub name: roc_std::RocStr, + pub payloadFields: roc_std::RocList, + pub tagName: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R11 { + pub discriminantOffset: u32, + pub discriminantSize: u32, + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum discriminant_U4 { + None = 0, + Some = 1, +} + +impl core::fmt::Debug for discriminant_U4 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::None => f.write_str("discriminant_U4::None"), + Self::Some => f.write_str("discriminant_U4::Some"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[repr(C)] +pub union U4 { + Some: u32, + _sizer: [u8; 8], +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R9 { + pub discriminantOffset: u32, + pub discriminantSize: u32, + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, + pub indexOfNullTag: u16, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum discriminant_U3 { + None = 0, + Some = 1, +} + +impl core::fmt::Debug for discriminant_U3 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::None => f.write_str("discriminant_U3::None"), + Self::Some => f.write_str("discriminant_U3::Some"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[repr(C)] +pub union U3 { + Some: u32, + _sizer: [u8; 8], +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R8 { + pub name: roc_std::RocStr, + pub nonNullPayload: u32, + pub nonNullTag: roc_std::RocStr, + pub nullTag: roc_std::RocStr, + pub whichTagIsNull: U2, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum U2 { + FirstTagIsNull = 0, + SecondTagIsNull = 1, +} + +impl core::fmt::Debug for U2 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::FirstTagIsNull => f.write_str("U2::FirstTagIsNull"), + Self::SecondTagIsNull => f.write_str("U2::SecondTagIsNull"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R6 { + pub discriminantOffset: u32, + pub discriminantSize: u32, + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum discriminant_U1 { + None = 0, + Some = 1, +} + +impl core::fmt::Debug for discriminant_U1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::None => f.write_str("discriminant_U1::None"), + Self::Some => f.write_str("discriminant_U1::Some"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[repr(C)] +pub union U1 { + Some: u32, + _sizer: [u8; 8], +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R5 { + pub name: roc_std::RocStr, + pub payload: u32, + pub tagName: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R4 { + pub name: roc_std::RocStr, + pub size: u32, + pub tags: roc_std::RocList, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R2 { + pub fields: roc_std::RocList, + pub name: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +struct RocType_RocResult { + pub f0: u32, + pub f1: u32, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +struct RocType_RocDict { + pub f0: u32, + pub f1: u32, +} + +#[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(u8)] +pub enum RocNum { + Dec = 0, + F128 = 1, + F32 = 2, + F64 = 3, + I128 = 4, + I16 = 5, + I32 = 6, + I64 = 7, + I8 = 8, + U128 = 9, + U16 = 10, + U32 = 11, + U64 = 12, + U8 = 13, +} + +impl core::fmt::Debug for RocNum { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Dec => f.write_str("RocNum::Dec"), + Self::F128 => f.write_str("RocNum::F128"), + Self::F32 => f.write_str("RocNum::F32"), + Self::F64 => f.write_str("RocNum::F64"), + Self::I128 => f.write_str("RocNum::I128"), + Self::I16 => f.write_str("RocNum::I16"), + Self::I32 => f.write_str("RocNum::I32"), + Self::I64 => f.write_str("RocNum::I64"), + Self::I8 => f.write_str("RocNum::I8"), + Self::U128 => f.write_str("RocNum::U128"), + Self::U16 => f.write_str("RocNum::U16"), + Self::U32 => f.write_str("RocNum::U32"), + Self::U64 => f.write_str("RocNum::U64"), + Self::U8 => f.write_str("RocNum::U8"), + } + } +} + +#[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R1 { + pub args: roc_std::RocList, + pub name: roc_std::RocStr, + pub ret: u32, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct Types { + pub aligns: roc_std::RocList, + pub deps: roc_std::RocDict>, + pub sizes: roc_std::RocList, + pub types: roc_std::RocList, + pub typesByName: roc_std::RocDict, + pub target: Target, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[repr(C)] +pub union RocType { + Function: core::mem::ManuallyDrop, + Num: RocNum, + RecursivePointer: u64, + RocBox: u64, + RocDict: RocType_RocDict, + RocList: u64, + RocResult: RocType_RocResult, + RocSet: u64, + Struct: core::mem::ManuallyDrop, + TagUnion: core::mem::ManuallyDrop, + TagUnionPayload: core::mem::ManuallyDrop, + _sizer: [u8; 104], +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R15 { + pub discriminant: u64, + pub r#type: u64, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R3 { + pub name: roc_std::RocStr, + pub r#type: u64, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[repr(C)] +pub union RocTagUnion { + Enumeration: core::mem::ManuallyDrop, + NonNullableUnwrapped: core::mem::ManuallyDrop, + NonRecursive: core::mem::ManuallyDrop, + NullableUnwrapped: core::mem::ManuallyDrop, + NullableWrapped: core::mem::ManuallyDrop, + Recursive: core::mem::ManuallyDrop, + SingleTagStruct: core::mem::ManuallyDrop, + _sizer: [u8; 96], +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R13 { + pub name: roc_std::RocStr, + pub payloadFields: roc_std::RocList, + pub tagName: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R11 { + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, + pub discriminantOffset: u32, + pub discriminantSize: u32, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[repr(C)] +pub union U4 { + Some: u64, + _sizer: [u8; 16], +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R9 { + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, + pub discriminantOffset: u32, + pub discriminantSize: u32, + pub indexOfNullTag: u16, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[repr(C)] +pub union U3 { + Some: u64, + _sizer: [u8; 16], +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R8 { + pub name: roc_std::RocStr, + pub nonNullPayload: u64, + pub nonNullTag: roc_std::RocStr, + pub nullTag: roc_std::RocStr, + pub whichTagIsNull: U2, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R6 { + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, + pub discriminantOffset: u32, + pub discriminantSize: u32, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[repr(C)] +pub union U1 { + Some: u64, + _sizer: [u8; 16], +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R5 { + pub name: roc_std::RocStr, + pub payload: u64, + pub tagName: roc_std::RocStr, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R4 { + pub name: roc_std::RocStr, + pub tags: roc_std::RocList, + pub size: u32, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +struct RocType_RocResult { + pub f0: u64, + pub f1: u64, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +struct RocType_RocDict { + pub f0: u64, + pub f1: u64, +} + +#[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" +))] +#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] +#[repr(C)] +pub struct R1 { + pub args: roc_std::RocList, + pub name: roc_std::RocStr, + pub ret: u64, +} + +impl RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_RocType { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(48)) + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_RocType) { + let discriminant_ptr: *mut discriminant_RocType = (self as *mut RocType).cast(); + + unsafe { + *(discriminant_ptr.add(48)) = discriminant; + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named Bool, which has no payload. + pub const Bool: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[48] = discriminant_RocType::Bool as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the Bool tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_Bool(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the Bool tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_Bool(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named EmptyTagUnion, which has no payload. + pub const EmptyTagUnion: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[48] = discriminant_RocType::EmptyTagUnion as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the EmptyTagUnion tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_EmptyTagUnion(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the EmptyTagUnion tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_EmptyTagUnion(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `Function`, with the appropriate payload + pub fn Function(arg0: R1) -> Self { + let mut answer = Self { + Function: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocType::Function); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Function` and convert it to `Function`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Function`. + pub unsafe fn into_Function(mut self) -> R1 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Function); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.Function, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Function` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Function`. + pub unsafe fn as_Function(&self) -> &R1 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Function); + let payload = &self.Function; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `Num`, with the appropriate payload + pub fn Num(arg: RocNum) -> Self { + let mut answer = Self { + Num: arg + }; + + answer.set_discriminant(discriminant_RocType::Num); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Num` and convert it to `Num`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Num`. + pub unsafe fn into_Num(self) -> RocNum { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Num); + let payload = self.Num; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Num` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Num`. + pub unsafe fn as_Num(&self) -> &RocNum { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Num); + let payload = &self.Num; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RecursivePointer`, with the appropriate payload + pub fn RecursivePointer(arg: u32) -> Self { + let mut answer = Self { + RecursivePointer: arg + }; + + answer.set_discriminant(discriminant_RocType::RecursivePointer); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RecursivePointer` and convert it to `RecursivePointer`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RecursivePointer`. + pub unsafe fn into_RecursivePointer(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RecursivePointer); + let payload = self.RecursivePointer; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RecursivePointer` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RecursivePointer`. + pub unsafe fn as_RecursivePointer(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RecursivePointer); + let payload = &self.RecursivePointer; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RocBox`, with the appropriate payload + pub fn RocBox(arg: u32) -> Self { + let mut answer = Self { + RocBox: arg + }; + + answer.set_discriminant(discriminant_RocType::RocBox); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocBox` and convert it to `RocBox`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocBox`. + pub unsafe fn into_RocBox(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocBox); + let payload = self.RocBox; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocBox` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocBox`. + pub unsafe fn as_RocBox(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocBox); + let payload = &self.RocBox; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RocDict`, with the appropriate payload + pub fn RocDict(arg0: u32, arg1: u32) -> Self { + let mut answer = Self { + RocDict: RocType_RocDict { + f0: arg0, + f1: arg1, + } + }; + + answer.set_discriminant(discriminant_RocType::RocDict); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocDict` and convert it to `RocDict`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocDict`. + pub unsafe fn into_RocDict(self) -> (u32, u32) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocDict); + let payload = self.RocDict; + + ( + payload.f0, + payload.f1 + ) + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocDict` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocDict`. + pub unsafe fn as_RocDict(&self) -> (&u32, &u32) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocDict); + let payload = &self.RocDict; + + ( + &payload.f0, + &payload.f1 + ) + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RocList`, with the appropriate payload + pub fn RocList(arg: u32) -> Self { + let mut answer = Self { + RocList: arg + }; + + answer.set_discriminant(discriminant_RocType::RocList); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocList` and convert it to `RocList`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocList`. + pub unsafe fn into_RocList(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocList); + let payload = self.RocList; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocList` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocList`. + pub unsafe fn as_RocList(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocList); + let payload = &self.RocList; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RocResult`, with the appropriate payload + pub fn RocResult(arg0: u32, arg1: u32) -> Self { + let mut answer = Self { + RocResult: RocType_RocResult { + f0: arg0, + f1: arg1, + } + }; + + answer.set_discriminant(discriminant_RocType::RocResult); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocResult` and convert it to `RocResult`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocResult`. + pub unsafe fn into_RocResult(self) -> (u32, u32) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocResult); + let payload = self.RocResult; + + ( + payload.f0, + payload.f1 + ) + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocResult` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocResult`. + pub unsafe fn as_RocResult(&self) -> (&u32, &u32) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocResult); + let payload = &self.RocResult; + + ( + &payload.f0, + &payload.f1 + ) + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `RocSet`, with the appropriate payload + pub fn RocSet(arg: u32) -> Self { + let mut answer = Self { + RocSet: arg + }; + + answer.set_discriminant(discriminant_RocType::RocSet); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocSet` and convert it to `RocSet`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocSet`. + pub unsafe fn into_RocSet(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocSet); + let payload = self.RocSet; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocSet` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocSet`. + pub unsafe fn as_RocSet(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocSet); + let payload = &self.RocSet; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named RocStr, which has no payload. + pub const RocStr: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[48] = discriminant_RocType::RocStr as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the RocStr tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_RocStr(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the RocStr tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_RocStr(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `Struct`, with the appropriate payload + pub fn Struct(arg0: R2) -> Self { + let mut answer = Self { + Struct: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocType::Struct); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Struct` and convert it to `Struct`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Struct`. + pub unsafe fn into_Struct(mut self) -> R2 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Struct); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.Struct, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `Struct` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Struct`. + pub unsafe fn as_Struct(&self) -> &R2 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::Struct); + let payload = &self.Struct; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `TagUnion`, with the appropriate payload + pub fn TagUnion(arg: RocTagUnion) -> Self { + let mut answer = Self { + TagUnion: core::mem::ManuallyDrop::new(arg) + }; + + answer.set_discriminant(discriminant_RocType::TagUnion); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `TagUnion` and convert it to `TagUnion`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `TagUnion`. + pub unsafe fn into_TagUnion(mut self) -> RocTagUnion { + debug_assert_eq!(self.discriminant(), discriminant_RocType::TagUnion); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.TagUnion, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `TagUnion` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `TagUnion`. + pub unsafe fn as_TagUnion(&self) -> &RocTagUnion { + debug_assert_eq!(self.discriminant(), discriminant_RocType::TagUnion); + let payload = &self.TagUnion; + + &payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `TagUnionPayload`, with the appropriate payload + pub fn TagUnionPayload(arg0: R14) -> Self { + let mut answer = Self { + TagUnionPayload: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocType::TagUnionPayload); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `TagUnionPayload` and convert it to `TagUnionPayload`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `TagUnionPayload`. + pub unsafe fn into_TagUnionPayload(mut self) -> R14 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::TagUnionPayload); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.TagUnionPayload, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `TagUnionPayload` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `TagUnionPayload`. + pub unsafe fn as_TagUnionPayload(&self) -> &R14 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::TagUnionPayload); + let payload = &self.TagUnionPayload; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named Unit, which has no payload. + pub const Unit: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[48] = discriminant_RocType::Unit as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the Unit tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_Unit(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the Unit tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_Unit(&self) { + () + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_RocType { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(96)) + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_RocType) { + let discriminant_ptr: *mut discriminant_RocType = (self as *mut RocType).cast(); + + unsafe { + *(discriminant_ptr.add(96)) = discriminant; + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named Bool, which has no payload. + pub const Bool: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[96] = discriminant_RocType::Bool as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named EmptyTagUnion, which has no payload. + pub const EmptyTagUnion: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[96] = discriminant_RocType::EmptyTagUnion as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RecursivePointer`, with the appropriate payload + pub fn RecursivePointer(arg: u64) -> Self { + let mut answer = Self { + RecursivePointer: arg + }; + + answer.set_discriminant(discriminant_RocType::RecursivePointer); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RecursivePointer` and convert it to `RecursivePointer`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RecursivePointer`. + pub unsafe fn into_RecursivePointer(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RecursivePointer); + let payload = self.RecursivePointer; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RecursivePointer` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RecursivePointer`. + pub unsafe fn as_RecursivePointer(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RecursivePointer); + let payload = &self.RecursivePointer; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RocBox`, with the appropriate payload + pub fn RocBox(arg: u64) -> Self { + let mut answer = Self { + RocBox: arg + }; + + answer.set_discriminant(discriminant_RocType::RocBox); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocBox` and convert it to `RocBox`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocBox`. + pub unsafe fn into_RocBox(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocBox); + let payload = self.RocBox; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocBox` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocBox`. + pub unsafe fn as_RocBox(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocBox); + let payload = &self.RocBox; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RocDict`, with the appropriate payload + pub fn RocDict(arg0: u64, arg1: u64) -> Self { + let mut answer = Self { + RocDict: RocType_RocDict { + f0: arg0, + f1: arg1, + } + }; + + answer.set_discriminant(discriminant_RocType::RocDict); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocDict` and convert it to `RocDict`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocDict`. + pub unsafe fn into_RocDict(self) -> (u64, u64) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocDict); + let payload = self.RocDict; + + ( + payload.f0, + payload.f1 + ) + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocDict` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocDict`. + pub unsafe fn as_RocDict(&self) -> (&u64, &u64) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocDict); + let payload = &self.RocDict; + + ( + &payload.f0, + &payload.f1 + ) + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RocList`, with the appropriate payload + pub fn RocList(arg: u64) -> Self { + let mut answer = Self { + RocList: arg + }; + + answer.set_discriminant(discriminant_RocType::RocList); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocList` and convert it to `RocList`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocList`. + pub unsafe fn into_RocList(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocList); + let payload = self.RocList; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocList` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocList`. + pub unsafe fn as_RocList(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocList); + let payload = &self.RocList; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RocResult`, with the appropriate payload + pub fn RocResult(arg0: u64, arg1: u64) -> Self { + let mut answer = Self { + RocResult: RocType_RocResult { + f0: arg0, + f1: arg1, + } + }; + + answer.set_discriminant(discriminant_RocType::RocResult); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocResult` and convert it to `RocResult`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocResult`. + pub unsafe fn into_RocResult(self) -> (u64, u64) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocResult); + let payload = self.RocResult; + + ( + payload.f0, + payload.f1 + ) + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocResult` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocResult`. + pub unsafe fn as_RocResult(&self) -> (&u64, &u64) { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocResult); + let payload = &self.RocResult; + + ( + &payload.f0, + &payload.f1 + ) + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `RocSet`, with the appropriate payload + pub fn RocSet(arg: u64) -> Self { + let mut answer = Self { + RocSet: arg + }; + + answer.set_discriminant(discriminant_RocType::RocSet); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocSet` and convert it to `RocSet`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocSet`. + pub unsafe fn into_RocSet(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocSet); + let payload = self.RocSet; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocType` has a `.discriminant()` of `RocSet` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `RocSet`. + pub unsafe fn as_RocSet(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_RocType::RocSet); + let payload = &self.RocSet; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named RocStr, which has no payload. + pub const RocStr: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[96] = discriminant_RocType::RocStr as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named Unit, which has no payload. + pub const Unit: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[96] = discriminant_RocType::Unit as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], RocType>(bytes) + }; +} + +impl Drop for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn drop(&mut self) { + // Drop the payloads + match self.discriminant() { + discriminant_RocType::Bool => {} + discriminant_RocType::EmptyTagUnion => {} + discriminant_RocType::Function => unsafe { core::mem::ManuallyDrop::drop(&mut self.Function) }, + discriminant_RocType::Num => {} + discriminant_RocType::RecursivePointer => {} + discriminant_RocType::RocBox => {} + discriminant_RocType::RocDict => {} + discriminant_RocType::RocList => {} + discriminant_RocType::RocResult => {} + discriminant_RocType::RocSet => {} + discriminant_RocType::RocStr => {} + discriminant_RocType::Struct => unsafe { core::mem::ManuallyDrop::drop(&mut self.Struct) }, + discriminant_RocType::TagUnion => unsafe { core::mem::ManuallyDrop::drop(&mut self.TagUnion) }, + discriminant_RocType::TagUnionPayload => unsafe { core::mem::ManuallyDrop::drop(&mut self.TagUnionPayload) }, + discriminant_RocType::Unit => {} + } + + } +} + +impl Eq for RocType {} + +impl PartialEq for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn eq(&self, other: &Self) -> bool { + if self.discriminant() != other.discriminant() { + return false; + } + + unsafe { + match self.discriminant() { + discriminant_RocType::Bool => true, + discriminant_RocType::EmptyTagUnion => true, + discriminant_RocType::Function => self.Function == other.Function, + discriminant_RocType::Num => self.Num == other.Num, + discriminant_RocType::RecursivePointer => self.RecursivePointer == other.RecursivePointer, + discriminant_RocType::RocBox => self.RocBox == other.RocBox, + discriminant_RocType::RocDict => self.RocDict == other.RocDict, + discriminant_RocType::RocList => self.RocList == other.RocList, + discriminant_RocType::RocResult => self.RocResult == other.RocResult, + discriminant_RocType::RocSet => self.RocSet == other.RocSet, + discriminant_RocType::RocStr => true, + discriminant_RocType::Struct => self.Struct == other.Struct, + discriminant_RocType::TagUnion => self.TagUnion == other.TagUnion, + discriminant_RocType::TagUnionPayload => self.TagUnionPayload == other.TagUnionPayload, + discriminant_RocType::Unit => true, + } + } + } +} + +impl PartialOrd for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn partial_cmp(&self, other: &Self) -> Option { + match self.discriminant().partial_cmp(&other.discriminant()) { + Some(core::cmp::Ordering::Equal) => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_RocType::Bool => Some(core::cmp::Ordering::Equal), + discriminant_RocType::EmptyTagUnion => Some(core::cmp::Ordering::Equal), + discriminant_RocType::Function => self.Function.partial_cmp(&other.Function), + discriminant_RocType::Num => self.Num.partial_cmp(&other.Num), + discriminant_RocType::RecursivePointer => self.RecursivePointer.partial_cmp(&other.RecursivePointer), + discriminant_RocType::RocBox => self.RocBox.partial_cmp(&other.RocBox), + discriminant_RocType::RocDict => self.RocDict.partial_cmp(&other.RocDict), + discriminant_RocType::RocList => self.RocList.partial_cmp(&other.RocList), + discriminant_RocType::RocResult => self.RocResult.partial_cmp(&other.RocResult), + discriminant_RocType::RocSet => self.RocSet.partial_cmp(&other.RocSet), + discriminant_RocType::RocStr => Some(core::cmp::Ordering::Equal), + discriminant_RocType::Struct => self.Struct.partial_cmp(&other.Struct), + discriminant_RocType::TagUnion => self.TagUnion.partial_cmp(&other.TagUnion), + discriminant_RocType::TagUnionPayload => self.TagUnionPayload.partial_cmp(&other.TagUnionPayload), + discriminant_RocType::Unit => Some(core::cmp::Ordering::Equal), + } + } + } +} + +impl Ord for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.discriminant().cmp(&other.discriminant()) { + core::cmp::Ordering::Equal => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_RocType::Bool => core::cmp::Ordering::Equal, + discriminant_RocType::EmptyTagUnion => core::cmp::Ordering::Equal, + discriminant_RocType::Function => self.Function.cmp(&other.Function), + discriminant_RocType::Num => self.Num.cmp(&other.Num), + discriminant_RocType::RecursivePointer => self.RecursivePointer.cmp(&other.RecursivePointer), + discriminant_RocType::RocBox => self.RocBox.cmp(&other.RocBox), + discriminant_RocType::RocDict => self.RocDict.cmp(&other.RocDict), + discriminant_RocType::RocList => self.RocList.cmp(&other.RocList), + discriminant_RocType::RocResult => self.RocResult.cmp(&other.RocResult), + discriminant_RocType::RocSet => self.RocSet.cmp(&other.RocSet), + discriminant_RocType::RocStr => core::cmp::Ordering::Equal, + discriminant_RocType::Struct => self.Struct.cmp(&other.Struct), + discriminant_RocType::TagUnion => self.TagUnion.cmp(&other.TagUnion), + discriminant_RocType::TagUnionPayload => self.TagUnionPayload.cmp(&other.TagUnionPayload), + discriminant_RocType::Unit => core::cmp::Ordering::Equal, + } + } + } +} + +impl Clone for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn clone(&self) -> Self { + let mut answer = unsafe { + match self.discriminant() { + discriminant_RocType::Bool => core::mem::transmute::< + core::mem::MaybeUninit, + RocType, + >(core::mem::MaybeUninit::uninit()), + discriminant_RocType::EmptyTagUnion => core::mem::transmute::< + core::mem::MaybeUninit, + RocType, + >(core::mem::MaybeUninit::uninit()), + discriminant_RocType::Function => Self { + Function: self.Function.clone(), + }, + discriminant_RocType::Num => Self { + Num: self.Num.clone(), + }, + discriminant_RocType::RecursivePointer => Self { + RecursivePointer: self.RecursivePointer.clone(), + }, + discriminant_RocType::RocBox => Self { + RocBox: self.RocBox.clone(), + }, + discriminant_RocType::RocDict => Self { + RocDict: self.RocDict.clone(), + }, + discriminant_RocType::RocList => Self { + RocList: self.RocList.clone(), + }, + discriminant_RocType::RocResult => Self { + RocResult: self.RocResult.clone(), + }, + discriminant_RocType::RocSet => Self { + RocSet: self.RocSet.clone(), + }, + discriminant_RocType::RocStr => core::mem::transmute::< + core::mem::MaybeUninit, + RocType, + >(core::mem::MaybeUninit::uninit()), + discriminant_RocType::Struct => Self { + Struct: self.Struct.clone(), + }, + discriminant_RocType::TagUnion => Self { + TagUnion: self.TagUnion.clone(), + }, + discriminant_RocType::TagUnionPayload => Self { + TagUnionPayload: self.TagUnionPayload.clone(), + }, + discriminant_RocType::Unit => core::mem::transmute::< + core::mem::MaybeUninit, + RocType, + >(core::mem::MaybeUninit::uninit()), + } + + }; + + answer.set_discriminant(self.discriminant()); + + answer + } +} + +impl core::hash::Hash for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn hash(&self, state: &mut H) { match self.discriminant() { + discriminant_RocType::Bool => discriminant_RocType::Bool.hash(state), + discriminant_RocType::EmptyTagUnion => discriminant_RocType::EmptyTagUnion.hash(state), + discriminant_RocType::Function => unsafe { + discriminant_RocType::Function.hash(state); + self.Function.hash(state); + }, + discriminant_RocType::Num => unsafe { + discriminant_RocType::Num.hash(state); + self.Num.hash(state); + }, + discriminant_RocType::RecursivePointer => unsafe { + discriminant_RocType::RecursivePointer.hash(state); + self.RecursivePointer.hash(state); + }, + discriminant_RocType::RocBox => unsafe { + discriminant_RocType::RocBox.hash(state); + self.RocBox.hash(state); + }, + discriminant_RocType::RocDict => unsafe { + discriminant_RocType::RocDict.hash(state); + self.RocDict.hash(state); + }, + discriminant_RocType::RocList => unsafe { + discriminant_RocType::RocList.hash(state); + self.RocList.hash(state); + }, + discriminant_RocType::RocResult => unsafe { + discriminant_RocType::RocResult.hash(state); + self.RocResult.hash(state); + }, + discriminant_RocType::RocSet => unsafe { + discriminant_RocType::RocSet.hash(state); + self.RocSet.hash(state); + }, + discriminant_RocType::RocStr => discriminant_RocType::RocStr.hash(state), + discriminant_RocType::Struct => unsafe { + discriminant_RocType::Struct.hash(state); + self.Struct.hash(state); + }, + discriminant_RocType::TagUnion => unsafe { + discriminant_RocType::TagUnion.hash(state); + self.TagUnion.hash(state); + }, + discriminant_RocType::TagUnionPayload => unsafe { + discriminant_RocType::TagUnionPayload.hash(state); + self.TagUnionPayload.hash(state); + }, + discriminant_RocType::Unit => discriminant_RocType::Unit.hash(state), + } + } +} + +impl core::fmt::Debug for RocType { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("RocType::")?; + + unsafe { + match self.discriminant() { + discriminant_RocType::Bool => f.write_str("Bool"), + discriminant_RocType::EmptyTagUnion => f.write_str("EmptyTagUnion"), + discriminant_RocType::Function => f.debug_tuple("Function") + .field(&*self.Function) + .finish(), + discriminant_RocType::Num => f.debug_tuple("Num") + .field(&self.Num) + .finish(), + discriminant_RocType::RecursivePointer => f.debug_tuple("RecursivePointer") + .field(&self.RecursivePointer) + .finish(), + discriminant_RocType::RocBox => f.debug_tuple("RocBox") + .field(&self.RocBox) + .finish(), + discriminant_RocType::RocDict => f.debug_tuple("RocDict") + .field(&(&self.RocDict).f0) +.field(&(&self.RocDict).f1) + .finish(), + discriminant_RocType::RocList => f.debug_tuple("RocList") + .field(&self.RocList) + .finish(), + discriminant_RocType::RocResult => f.debug_tuple("RocResult") + .field(&(&self.RocResult).f0) +.field(&(&self.RocResult).f1) + .finish(), + discriminant_RocType::RocSet => f.debug_tuple("RocSet") + .field(&self.RocSet) + .finish(), + discriminant_RocType::RocStr => f.write_str("RocStr"), + discriminant_RocType::Struct => f.debug_tuple("Struct") + .field(&*self.Struct) + .finish(), + discriminant_RocType::TagUnion => f.debug_tuple("TagUnion") + .field(&*self.TagUnion) + .finish(), + discriminant_RocType::TagUnionPayload => f.debug_tuple("TagUnionPayload") + .field(&*self.TagUnionPayload) + .finish(), + discriminant_RocType::Unit => f.write_str("Unit"), + } + } + } +} + +impl RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_RocTagUnion { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(44)) + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_RocTagUnion) { + let discriminant_ptr: *mut discriminant_RocTagUnion = (self as *mut RocTagUnion).cast(); + + unsafe { + *(discriminant_ptr.add(44)) = discriminant; + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `Enumeration`, with the appropriate payload + pub fn Enumeration(arg0: R4) -> Self { + let mut answer = Self { + Enumeration: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::Enumeration); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `Enumeration` and convert it to `Enumeration`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Enumeration`. + pub unsafe fn into_Enumeration(mut self) -> R4 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::Enumeration); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.Enumeration, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `Enumeration` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Enumeration`. + pub unsafe fn as_Enumeration(&self) -> &R4 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::Enumeration); + let payload = &self.Enumeration; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `NonNullableUnwrapped`, with the appropriate payload + pub fn NonNullableUnwrapped(arg0: R5) -> Self { + let mut answer = Self { + NonNullableUnwrapped: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::NonNullableUnwrapped); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NonNullableUnwrapped` and convert it to `NonNullableUnwrapped`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NonNullableUnwrapped`. + pub unsafe fn into_NonNullableUnwrapped(mut self) -> R5 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NonNullableUnwrapped); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.NonNullableUnwrapped, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NonNullableUnwrapped` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NonNullableUnwrapped`. + pub unsafe fn as_NonNullableUnwrapped(&self) -> &R5 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NonNullableUnwrapped); + let payload = &self.NonNullableUnwrapped; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `NonRecursive`, with the appropriate payload + pub fn NonRecursive(arg0: R6) -> Self { + let mut answer = Self { + NonRecursive: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::NonRecursive); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NonRecursive` and convert it to `NonRecursive`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NonRecursive`. + pub unsafe fn into_NonRecursive(mut self) -> R6 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NonRecursive); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.NonRecursive, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NonRecursive` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NonRecursive`. + pub unsafe fn as_NonRecursive(&self) -> &R6 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NonRecursive); + let payload = &self.NonRecursive; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `NullableUnwrapped`, with the appropriate payload + pub fn NullableUnwrapped(arg0: R8) -> Self { + let mut answer = Self { + NullableUnwrapped: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::NullableUnwrapped); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NullableUnwrapped` and convert it to `NullableUnwrapped`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NullableUnwrapped`. + pub unsafe fn into_NullableUnwrapped(mut self) -> R8 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NullableUnwrapped); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.NullableUnwrapped, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NullableUnwrapped` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NullableUnwrapped`. + pub unsafe fn as_NullableUnwrapped(&self) -> &R8 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NullableUnwrapped); + let payload = &self.NullableUnwrapped; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `NullableWrapped`, with the appropriate payload + pub fn NullableWrapped(arg0: R9) -> Self { + let mut answer = Self { + NullableWrapped: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::NullableWrapped); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NullableWrapped` and convert it to `NullableWrapped`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NullableWrapped`. + pub unsafe fn into_NullableWrapped(mut self) -> R9 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NullableWrapped); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.NullableWrapped, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `NullableWrapped` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `NullableWrapped`. + pub unsafe fn as_NullableWrapped(&self) -> &R9 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::NullableWrapped); + let payload = &self.NullableWrapped; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `Recursive`, with the appropriate payload + pub fn Recursive(arg0: R11) -> Self { + let mut answer = Self { + Recursive: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::Recursive); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `Recursive` and convert it to `Recursive`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Recursive`. + pub unsafe fn into_Recursive(mut self) -> R11 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::Recursive); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.Recursive, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `Recursive` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Recursive`. + pub unsafe fn as_Recursive(&self) -> &R11 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::Recursive); + let payload = &self.Recursive; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Construct a tag named `SingleTagStruct`, with the appropriate payload + pub fn SingleTagStruct(arg0: R13) -> Self { + let mut answer = Self { + SingleTagStruct: core::mem::ManuallyDrop::new(arg0) + }; + + answer.set_discriminant(discriminant_RocTagUnion::SingleTagStruct); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `SingleTagStruct` and convert it to `SingleTagStruct`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `SingleTagStruct`. + pub unsafe fn into_SingleTagStruct(mut self) -> R13 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::SingleTagStruct); + let payload = { + let mut uninitialized = core::mem::MaybeUninit::uninit(); + let swapped = unsafe { + core::mem::replace( + &mut self.SingleTagStruct, + core::mem::ManuallyDrop::new(uninitialized.assume_init()), + ) + }; + + core::mem::forget(self); + + core::mem::ManuallyDrop::into_inner(swapped) + }; + + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `RocTagUnion` has a `.discriminant()` of `SingleTagStruct` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `SingleTagStruct`. + pub unsafe fn as_SingleTagStruct(&self) -> &R13 { + debug_assert_eq!(self.discriminant(), discriminant_RocTagUnion::SingleTagStruct); + let payload = &self.SingleTagStruct; + + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_RocTagUnion { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(88)) + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_RocTagUnion) { + let discriminant_ptr: *mut discriminant_RocTagUnion = (self as *mut RocTagUnion).cast(); + + unsafe { + *(discriminant_ptr.add(88)) = discriminant; + } + } +} + +impl Drop for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn drop(&mut self) { + // Drop the payloads + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => unsafe { core::mem::ManuallyDrop::drop(&mut self.Enumeration) }, + discriminant_RocTagUnion::NonNullableUnwrapped => unsafe { core::mem::ManuallyDrop::drop(&mut self.NonNullableUnwrapped) }, + discriminant_RocTagUnion::NonRecursive => unsafe { core::mem::ManuallyDrop::drop(&mut self.NonRecursive) }, + discriminant_RocTagUnion::NullableUnwrapped => unsafe { core::mem::ManuallyDrop::drop(&mut self.NullableUnwrapped) }, + discriminant_RocTagUnion::NullableWrapped => unsafe { core::mem::ManuallyDrop::drop(&mut self.NullableWrapped) }, + discriminant_RocTagUnion::Recursive => unsafe { core::mem::ManuallyDrop::drop(&mut self.Recursive) }, + discriminant_RocTagUnion::SingleTagStruct => unsafe { core::mem::ManuallyDrop::drop(&mut self.SingleTagStruct) }, + } + + } +} + +impl Eq for RocTagUnion {} + +impl PartialEq for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn eq(&self, other: &Self) -> bool { + if self.discriminant() != other.discriminant() { + return false; + } + + unsafe { + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => self.Enumeration == other.Enumeration, + discriminant_RocTagUnion::NonNullableUnwrapped => self.NonNullableUnwrapped == other.NonNullableUnwrapped, + discriminant_RocTagUnion::NonRecursive => self.NonRecursive == other.NonRecursive, + discriminant_RocTagUnion::NullableUnwrapped => self.NullableUnwrapped == other.NullableUnwrapped, + discriminant_RocTagUnion::NullableWrapped => self.NullableWrapped == other.NullableWrapped, + discriminant_RocTagUnion::Recursive => self.Recursive == other.Recursive, + discriminant_RocTagUnion::SingleTagStruct => self.SingleTagStruct == other.SingleTagStruct, + } + } + } +} + +impl PartialOrd for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn partial_cmp(&self, other: &Self) -> Option { + match self.discriminant().partial_cmp(&other.discriminant()) { + Some(core::cmp::Ordering::Equal) => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => self.Enumeration.partial_cmp(&other.Enumeration), + discriminant_RocTagUnion::NonNullableUnwrapped => self.NonNullableUnwrapped.partial_cmp(&other.NonNullableUnwrapped), + discriminant_RocTagUnion::NonRecursive => self.NonRecursive.partial_cmp(&other.NonRecursive), + discriminant_RocTagUnion::NullableUnwrapped => self.NullableUnwrapped.partial_cmp(&other.NullableUnwrapped), + discriminant_RocTagUnion::NullableWrapped => self.NullableWrapped.partial_cmp(&other.NullableWrapped), + discriminant_RocTagUnion::Recursive => self.Recursive.partial_cmp(&other.Recursive), + discriminant_RocTagUnion::SingleTagStruct => self.SingleTagStruct.partial_cmp(&other.SingleTagStruct), + } + } + } +} + +impl Ord for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.discriminant().cmp(&other.discriminant()) { + core::cmp::Ordering::Equal => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => self.Enumeration.cmp(&other.Enumeration), + discriminant_RocTagUnion::NonNullableUnwrapped => self.NonNullableUnwrapped.cmp(&other.NonNullableUnwrapped), + discriminant_RocTagUnion::NonRecursive => self.NonRecursive.cmp(&other.NonRecursive), + discriminant_RocTagUnion::NullableUnwrapped => self.NullableUnwrapped.cmp(&other.NullableUnwrapped), + discriminant_RocTagUnion::NullableWrapped => self.NullableWrapped.cmp(&other.NullableWrapped), + discriminant_RocTagUnion::Recursive => self.Recursive.cmp(&other.Recursive), + discriminant_RocTagUnion::SingleTagStruct => self.SingleTagStruct.cmp(&other.SingleTagStruct), + } + } + } +} + +impl Clone for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn clone(&self) -> Self { + let mut answer = unsafe { + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => Self { + Enumeration: self.Enumeration.clone(), + }, + discriminant_RocTagUnion::NonNullableUnwrapped => Self { + NonNullableUnwrapped: self.NonNullableUnwrapped.clone(), + }, + discriminant_RocTagUnion::NonRecursive => Self { + NonRecursive: self.NonRecursive.clone(), + }, + discriminant_RocTagUnion::NullableUnwrapped => Self { + NullableUnwrapped: self.NullableUnwrapped.clone(), + }, + discriminant_RocTagUnion::NullableWrapped => Self { + NullableWrapped: self.NullableWrapped.clone(), + }, + discriminant_RocTagUnion::Recursive => Self { + Recursive: self.Recursive.clone(), + }, + discriminant_RocTagUnion::SingleTagStruct => Self { + SingleTagStruct: self.SingleTagStruct.clone(), + }, + } + + }; + + answer.set_discriminant(self.discriminant()); + + answer + } +} + +impl core::hash::Hash for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn hash(&self, state: &mut H) { match self.discriminant() { + discriminant_RocTagUnion::Enumeration => unsafe { + discriminant_RocTagUnion::Enumeration.hash(state); + self.Enumeration.hash(state); + }, + discriminant_RocTagUnion::NonNullableUnwrapped => unsafe { + discriminant_RocTagUnion::NonNullableUnwrapped.hash(state); + self.NonNullableUnwrapped.hash(state); + }, + discriminant_RocTagUnion::NonRecursive => unsafe { + discriminant_RocTagUnion::NonRecursive.hash(state); + self.NonRecursive.hash(state); + }, + discriminant_RocTagUnion::NullableUnwrapped => unsafe { + discriminant_RocTagUnion::NullableUnwrapped.hash(state); + self.NullableUnwrapped.hash(state); + }, + discriminant_RocTagUnion::NullableWrapped => unsafe { + discriminant_RocTagUnion::NullableWrapped.hash(state); + self.NullableWrapped.hash(state); + }, + discriminant_RocTagUnion::Recursive => unsafe { + discriminant_RocTagUnion::Recursive.hash(state); + self.Recursive.hash(state); + }, + discriminant_RocTagUnion::SingleTagStruct => unsafe { + discriminant_RocTagUnion::SingleTagStruct.hash(state); + self.SingleTagStruct.hash(state); + }, + } + } +} + +impl core::fmt::Debug for RocTagUnion { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("RocTagUnion::")?; + + unsafe { + match self.discriminant() { + discriminant_RocTagUnion::Enumeration => f.debug_tuple("Enumeration") + .field(&*self.Enumeration) + .finish(), + discriminant_RocTagUnion::NonNullableUnwrapped => f.debug_tuple("NonNullableUnwrapped") + .field(&*self.NonNullableUnwrapped) + .finish(), + discriminant_RocTagUnion::NonRecursive => f.debug_tuple("NonRecursive") + .field(&*self.NonRecursive) + .finish(), + discriminant_RocTagUnion::NullableUnwrapped => f.debug_tuple("NullableUnwrapped") + .field(&*self.NullableUnwrapped) + .finish(), + discriminant_RocTagUnion::NullableWrapped => f.debug_tuple("NullableWrapped") + .field(&*self.NullableWrapped) + .finish(), + discriminant_RocTagUnion::Recursive => f.debug_tuple("Recursive") + .field(&*self.Recursive) + .finish(), + discriminant_RocTagUnion::SingleTagStruct => f.debug_tuple("SingleTagStruct") + .field(&*self.SingleTagStruct) + .finish(), + } + } + } +} + +impl U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U4 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(4)) + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U4) { + let discriminant_ptr: *mut discriminant_U4 = (self as *mut U4).cast(); + + unsafe { + *(discriminant_ptr.add(4)) = discriminant; + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[4] = discriminant_U4::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U4>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_None(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_None(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u32) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U4::Some); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U4` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_U4::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U4` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_U4::Some); + let payload = &self.Some; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U4 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(8)) + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U4) { + let discriminant_ptr: *mut discriminant_U4 = (self as *mut U4).cast(); + + unsafe { + *(discriminant_ptr.add(8)) = discriminant; + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[8] = discriminant_U4::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U4>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u64) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U4::Some); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U4` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_U4::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U4` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_U4::Some); + let payload = &self.Some; + + &payload + } +} + +impl Eq for U4 {} + +impl PartialEq for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn eq(&self, other: &Self) -> bool { + if self.discriminant() != other.discriminant() { + return false; + } + + unsafe { + match self.discriminant() { + discriminant_U4::None => true, + discriminant_U4::Some => self.Some == other.Some, + } + } + } +} + +impl PartialOrd for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn partial_cmp(&self, other: &Self) -> Option { + match self.discriminant().partial_cmp(&other.discriminant()) { + Some(core::cmp::Ordering::Equal) => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U4::None => Some(core::cmp::Ordering::Equal), + discriminant_U4::Some => self.Some.partial_cmp(&other.Some), + } + } + } +} + +impl Ord for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.discriminant().cmp(&other.discriminant()) { + core::cmp::Ordering::Equal => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U4::None => core::cmp::Ordering::Equal, + discriminant_U4::Some => self.Some.cmp(&other.Some), + } + } + } +} + +impl Copy for U4 {} + +impl Clone for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn clone(&self) -> Self { + let mut answer = unsafe { + match self.discriminant() { + discriminant_U4::None => core::mem::transmute::< + core::mem::MaybeUninit, + U4, + >(core::mem::MaybeUninit::uninit()), + discriminant_U4::Some => Self { + Some: self.Some.clone(), + }, + } + + }; + + answer.set_discriminant(self.discriminant()); + + answer + } +} + +impl core::hash::Hash for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn hash(&self, state: &mut H) { match self.discriminant() { + discriminant_U4::None => discriminant_U4::None.hash(state), + discriminant_U4::Some => unsafe { + discriminant_U4::Some.hash(state); + self.Some.hash(state); + }, + } + } +} + +impl core::fmt::Debug for U4 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("U4::")?; + + unsafe { + match self.discriminant() { + discriminant_U4::None => f.write_str("None"), + discriminant_U4::Some => f.debug_tuple("Some") + .field(&self.Some) + .finish(), + } + } + } +} + +impl U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U3 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(4)) + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U3) { + let discriminant_ptr: *mut discriminant_U3 = (self as *mut U3).cast(); + + unsafe { + *(discriminant_ptr.add(4)) = discriminant; + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[4] = discriminant_U3::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U3>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_None(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_None(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u32) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U3::Some); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U3` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_U3::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U3` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_U3::Some); + let payload = &self.Some; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U3 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(8)) + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U3) { + let discriminant_ptr: *mut discriminant_U3 = (self as *mut U3).cast(); + + unsafe { + *(discriminant_ptr.add(8)) = discriminant; + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[8] = discriminant_U3::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U3>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u64) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U3::Some); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U3` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_U3::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U3` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_U3::Some); + let payload = &self.Some; + + &payload + } +} + +impl Eq for U3 {} + +impl PartialEq for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn eq(&self, other: &Self) -> bool { + if self.discriminant() != other.discriminant() { + return false; + } + + unsafe { + match self.discriminant() { + discriminant_U3::None => true, + discriminant_U3::Some => self.Some == other.Some, + } + } + } +} + +impl PartialOrd for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn partial_cmp(&self, other: &Self) -> Option { + match self.discriminant().partial_cmp(&other.discriminant()) { + Some(core::cmp::Ordering::Equal) => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U3::None => Some(core::cmp::Ordering::Equal), + discriminant_U3::Some => self.Some.partial_cmp(&other.Some), + } + } + } +} + +impl Ord for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.discriminant().cmp(&other.discriminant()) { + core::cmp::Ordering::Equal => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U3::None => core::cmp::Ordering::Equal, + discriminant_U3::Some => self.Some.cmp(&other.Some), + } + } + } +} + +impl Copy for U3 {} + +impl Clone for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn clone(&self) -> Self { + let mut answer = unsafe { + match self.discriminant() { + discriminant_U3::None => core::mem::transmute::< + core::mem::MaybeUninit, + U3, + >(core::mem::MaybeUninit::uninit()), + discriminant_U3::Some => Self { + Some: self.Some.clone(), + }, + } + + }; + + answer.set_discriminant(self.discriminant()); + + answer + } +} + +impl core::hash::Hash for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn hash(&self, state: &mut H) { match self.discriminant() { + discriminant_U3::None => discriminant_U3::None.hash(state), + discriminant_U3::Some => unsafe { + discriminant_U3::Some.hash(state); + self.Some.hash(state); + }, + } + } +} + +impl core::fmt::Debug for U3 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("U3::")?; + + unsafe { + match self.discriminant() { + discriminant_U3::None => f.write_str("None"), + discriminant_U3::Some => f.debug_tuple("Some") + .field(&self.Some) + .finish(), + } + } + } +} + +impl U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U1 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(4)) + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U1) { + let discriminant_ptr: *mut discriminant_U1 = (self as *mut U1).cast(); + + unsafe { + *(discriminant_ptr.add(4)) = discriminant; + } + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[4] = discriminant_U1::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U1>(bytes) + }; + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `into_` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn into_None(self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + /// Other `as` methods return a payload, but since the None tag + /// has no payload, this does nothing and is only here for completeness. + pub fn as_None(&self) { + () + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u32) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U1::Some); + + answer + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U1` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u32 { + debug_assert_eq!(self.discriminant(), discriminant_U1::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "arm", + target_arch = "wasm32", + target_arch = "x86" + ))] + /// Unsafely assume the given `U1` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u32 { + debug_assert_eq!(self.discriminant(), discriminant_U1::Some); + let payload = &self.Some; + + &payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Returns which variant this tag union holds. Note that this never includes a payload! + pub fn discriminant(&self) -> discriminant_U1 { + unsafe { + let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::()]>(self); + + core::mem::transmute::(*bytes.as_ptr().add(8)) + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Internal helper + fn set_discriminant(&mut self, discriminant: discriminant_U1) { + let discriminant_ptr: *mut discriminant_U1 = (self as *mut U1).cast(); + + unsafe { + *(discriminant_ptr.add(8)) = discriminant; + } + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// A tag named None, which has no payload. + pub const None: Self = unsafe { + let mut bytes = [0; core::mem::size_of::()]; + + bytes[8] = discriminant_U1::None as u8; + + core::mem::transmute::<[u8; core::mem::size_of::()], U1>(bytes) + }; + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Construct a tag named `Some`, with the appropriate payload + pub fn Some(arg: u64) -> Self { + let mut answer = Self { + Some: arg + }; + + answer.set_discriminant(discriminant_U1::Some); + + answer + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U1` has a `.discriminant()` of `Some` and convert it to `Some`'s payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn into_Some(self) -> u64 { + debug_assert_eq!(self.discriminant(), discriminant_U1::Some); + let payload = self.Some; + + payload + } + + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64" + ))] + /// Unsafely assume the given `U1` has a `.discriminant()` of `Some` and return its payload. + /// (Always examine `.discriminant()` first to make sure this is the correct variant!) + /// Panics in debug builds if the `.discriminant()` doesn't return `Some`. + pub unsafe fn as_Some(&self) -> &u64 { + debug_assert_eq!(self.discriminant(), discriminant_U1::Some); + let payload = &self.Some; + + &payload + } +} + +impl Eq for U1 {} + +impl PartialEq for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn eq(&self, other: &Self) -> bool { + if self.discriminant() != other.discriminant() { + return false; + } + + unsafe { + match self.discriminant() { + discriminant_U1::None => true, + discriminant_U1::Some => self.Some == other.Some, + } + } + } +} + +impl PartialOrd for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn partial_cmp(&self, other: &Self) -> Option { + match self.discriminant().partial_cmp(&other.discriminant()) { + Some(core::cmp::Ordering::Equal) => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U1::None => Some(core::cmp::Ordering::Equal), + discriminant_U1::Some => self.Some.partial_cmp(&other.Some), + } + } + } +} + +impl Ord for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + match self.discriminant().cmp(&other.discriminant()) { + core::cmp::Ordering::Equal => {} + not_eq => return not_eq, + } + + unsafe { + match self.discriminant() { + discriminant_U1::None => core::cmp::Ordering::Equal, + discriminant_U1::Some => self.Some.cmp(&other.Some), + } + } + } +} + +impl Copy for U1 {} + +impl Clone for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn clone(&self) -> Self { + let mut answer = unsafe { + match self.discriminant() { + discriminant_U1::None => core::mem::transmute::< + core::mem::MaybeUninit, + U1, + >(core::mem::MaybeUninit::uninit()), + discriminant_U1::Some => Self { + Some: self.Some.clone(), + }, + } + + }; + + answer.set_discriminant(self.discriminant()); + + answer + } +} + +impl core::hash::Hash for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn hash(&self, state: &mut H) { match self.discriminant() { + discriminant_U1::None => discriminant_U1::None.hash(state), + discriminant_U1::Some => unsafe { + discriminant_U1::Some.hash(state); + self.Some.hash(state); + }, + } + } +} + +impl core::fmt::Debug for U1 { + #[cfg(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "wasm32", + target_arch = "x86", + target_arch = "x86_64" + ))] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str("U1::")?; + + unsafe { + match self.discriminant() { + discriminant_U1::None => f.write_str("None"), + discriminant_U1::Some => f.debug_tuple("Some") + .field(&self.Some) + .finish(), + } + } + } +} diff --git a/crates/glue/src/lib.rs b/crates/glue/src/lib.rs index 4c78e97495..39fc74b553 100644 --- a/crates/glue/src/lib.rs +++ b/crates/glue/src/lib.rs @@ -4,4 +4,7 @@ pub mod rust_glue; pub mod structs; pub mod types; +#[rustfmt::skip] +pub mod glue; + pub use load::generate; diff --git a/crates/glue/src/rust_glue.rs b/crates/glue/src/rust_glue.rs index 7f0baf5a9c..374e5f4623 100644 --- a/crates/glue/src/rust_glue.rs +++ b/crates/glue/src/rust_glue.rs @@ -308,6 +308,8 @@ fn add_single_tag_struct( impls: &mut IndexMap, IndexMap>>, target_info: TargetInfo, ) { + let name = escape_kw(name.to_string()); + // Store single-tag unions as structs rather than enums, // because they have only one alternative. However, still // offer the usual tag union APIs. @@ -530,6 +532,8 @@ fn add_tag_union( types: &Types, impls: &mut Impls, ) { + let name = escape_kw(name.to_string()); + // We should never be attempting to generate glue for empty tag unions; // RocType should not have let this happen. debug_assert_ne!(tags.len(), 0); @@ -537,7 +541,7 @@ fn add_tag_union( let tag_names = tags.iter().map(|(name, _)| name).cloned().collect(); let discriminant_name = if discriminant_size > 0 { add_discriminant( - name, + name.as_str(), target_info, tag_names, discriminant_size, @@ -585,7 +589,7 @@ pub struct {name} {{ } Recursiveness::NonRecursive => { pub_str = "pub "; - decl_union_name = name; + decl_union_name = &name; } }; @@ -1100,7 +1104,7 @@ pub struct {name} {{ } // The Drop impl for the tag union - { + if cannot_derive_copy(typ, types) { let opt_impl = Some(format!("impl Drop for {name}")); let mut drop_payload = String::new(); @@ -1584,6 +1588,7 @@ fn add_enumeration, S: AsRef + Display>( types: &Types, impls: &mut Impls, ) { + let name = escape_kw(name.to_string()); let derive = derive_str(typ, types, false); let repr_bits = tag_bytes * 8; @@ -1624,6 +1629,7 @@ fn add_struct( impls: &mut Impls, is_tag_union_payload: bool, ) { + let name = escape_kw(name.to_string()); let derive = derive_str(types.get_type(struct_id), types, true); let pub_str = if is_tag_union_payload { "" } else { "pub " }; let repr = if fields.len() == 1 { @@ -1641,7 +1647,7 @@ fn add_struct( let label = if is_tag_union_payload { format!("f{label}") } else { - format!("{label}") + escape_kw(label.to_string()) }; writeln!(buf, "{INDENT}pub {label}: {type_str},",).unwrap(); @@ -1695,9 +1701,9 @@ fn type_name(id: TypeId, types: &Types) -> String { | RocType::TagUnion(RocTagUnion::NullableWrapped { name, .. }) | RocType::TagUnion(RocTagUnion::NullableUnwrapped { name, .. }) | RocType::TagUnion(RocTagUnion::NonNullableUnwrapped { name, .. }) - | RocType::TagUnion(RocTagUnion::SingleTagStruct { name, .. }) => name.clone(), + | RocType::TagUnion(RocTagUnion::SingleTagStruct { name, .. }) => escape_kw(name.clone()), RocType::RecursivePointer(content) => type_name(*content, types), - RocType::Function { name, .. } => name.clone(), + RocType::Function { name, .. } => escape_kw(name.clone()), } } @@ -2181,7 +2187,7 @@ fn tag_union_struct_help<'a, I: Iterator, L: Display + P // because they're numbers format!("f{}", label) } else { - format!("{}", label) + escape_kw(format!("{}", label)) }; ret_values.push(format!("payload.{label}")); @@ -2200,9 +2206,7 @@ fn tag_union_struct_help<'a, I: Iterator, L: Display + P .collect::>() .join(", "); let args_to_payload = if is_tag_union_payload { - format!( - "core::mem::ManuallyDrop::new({payload_type_name} {{\n{}\n{INDENT}{INDENT}{INDENT}{INDENT}}})", - sorted_fields + let prefixed_fields = sorted_fields .iter() .enumerate() .map(|(index, (label, _))| { @@ -2214,13 +2218,20 @@ fn tag_union_struct_help<'a, I: Iterator, L: Display + P // Tag union payload fields need "f" prefix // because they're numbers - let label = format!("f{}", label); - - format!("{indents}{label}: arg{index},") + format!("{indents}f{label}: arg{index},") }) .collect::>() - .join("\n") - ) + .join("\n"); + + if cannot_derive_copy(types.get_type(payload_id), types) { + format!( + "core::mem::ManuallyDrop::new({payload_type_name} {{\n{}\n{INDENT}{INDENT}{INDENT}{INDENT}}})",prefixed_fields) + } else { + format!( + "{payload_type_name} {{\n{}\n{INDENT}{INDENT}{INDENT}{INDENT}}}", + prefixed_fields + ) + } } else { "core::mem::ManuallyDrop::new(arg0)".to_string() }; @@ -2424,3 +2435,26 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId]) } } } + +// Based on https://doc.rust-lang.org/reference/keywords.html +const RESERVED_KEYWORDS: &[&str] = &[ + "try", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", + "unsized", "virtual", "yield", "async", "await", "dyn", "as", "break", "const", "continue", + "crate", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", + "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", + "super", "trait", "true", "type", "unsafe", "use", "where", "while", +]; + +/// Escape a Rust reserved keyword, if necessary. +fn escape_kw(input: String) -> String { + let is_reserved_keyword = RESERVED_KEYWORDS.contains(&input.as_str()); + + if is_reserved_keyword { + // Use a raw identifier for this, to prevent a syntax error due to using a reserved keyword. + // https://doc.rust-lang.org/rust-by-example/compatibility/raw_identifiers.html + // Another design would be to add an underscore after it; this is an experiment! + format!("r#{input}") + } else { + input + } +} diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 2eb09101ee..82108c44e1 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -18,7 +18,7 @@ use roc_mono::layout::{ use roc_target::TargetInfo; use roc_types::{ subs::{Content, FlatType, GetSubsSlice, Subs, UnionLabels, UnionTags, Variable}, - types::RecordField, + types::{AliasKind, RecordField}, }; use std::fmt::Display; @@ -599,8 +599,8 @@ pub struct Env<'a> { interns: &'a Interns, struct_names: Structs, enum_names: Enums, - pending_recursive_types: VecMap>, - known_recursive_types: VecMap, TypeId>, + pending_recursive_types: VecMap, + known_recursive_types: VecMap, target: TargetInfo, } @@ -652,13 +652,16 @@ impl<'a> Env<'a> { // TODO if VecMap gets a drain() method, use that instead of doing take() and into_iter let pending = core::mem::take(&mut self.pending_recursive_types); - for (type_id, layout) in pending.into_iter() { - let actual_type_id = self.known_recursive_types.get(&layout).unwrap_or_else(|| { - unreachable!( - "There was no known recursive TypeId for the pending recursive type {:?}", - layout - ); - }); + for (type_id, root_var) in pending.into_iter() { + let actual_type_id = *self + .known_recursive_types + .get(&root_var) + .unwrap_or_else(|| { + unreachable!( + "There was no known recursive TypeId for the pending recursive type {:?}", + root_var + ); + }); debug_assert!( matches!(types.get_type(type_id), RocType::RecursivePointer(TypeId::PENDING)), @@ -668,7 +671,7 @@ impl<'a> Env<'a> { // size and alignment shouldn't change; this is still // a RecursivePointer, it's just pointing to something else. - types.replace(type_id, RocType::RecursivePointer(*actual_type_id)); + types.replace(type_id, RocType::RecursivePointer(actual_type_id)); } } } @@ -717,12 +720,14 @@ fn add_type_help<'a>( Content::Structure(FlatType::TagUnion(tags, ext_var)) => { debug_assert!(ext_var_is_empty_tag_union(subs, *ext_var)); - add_tag_union(env, opt_name, tags, var, types, layout) + add_tag_union(env, opt_name, tags, var, types, layout, None) } - Content::Structure(FlatType::RecursiveTagUnion(_rec_var, tag_vars, ext_var)) => { + Content::Structure(FlatType::RecursiveTagUnion(rec_var, tags, ext_var)) => { debug_assert!(ext_var_is_empty_tag_union(subs, *ext_var)); - add_tag_union(env, opt_name, tag_vars, var, types, layout) + let rec_root = subs.get_root_key_without_compacting(*rec_var); + + add_tag_union(env, opt_name, tags, var, types, layout, Some(rec_root)) } Content::Structure(FlatType::Apply(symbol, _)) => match layout { Layout::Builtin(builtin) => { @@ -865,13 +870,18 @@ fn add_type_help<'a>( Content::Error => todo!(), Content::RecursionVar { structure, .. } => { let type_id = types.add_anonymous(RocType::RecursivePointer(TypeId::PENDING), layout); - let structure_layout = env - .layout_cache - .from_var(env.arena, *structure, subs) - .unwrap(); - env.pending_recursive_types - .insert(type_id, structure_layout); + // These should be different Variables, but the same layout! + debug_assert_eq!( + layout, + env.layout_cache + .from_var(env.arena, *structure, subs) + .unwrap() + ); + + let root_var = subs.get_root_key_without_compacting(var); + + env.pending_recursive_types.insert(type_id, root_var); type_id } @@ -924,7 +934,97 @@ fn add_builtin_type<'a>( list_id } - (layout, typ) => todo!("Handle builtin layout {:?} and type {:?}", layout, typ), + ( + Builtin::List(elem_layout), + Alias(Symbol::DICT_DICT, _alias_variables, alias_var, AliasKind::Opaque), + ) => { + match ( + elem_layout, + env.subs.get_content_without_compacting(*alias_var), + ) { + ( + Layout::Struct { field_layouts, .. }, + Content::Structure(FlatType::Apply(Symbol::LIST_LIST, args_subs_slice)), + ) => { + let (key_var, val_var) = { + let args_tuple = env.subs.get_subs_slice(*args_subs_slice); + + debug_assert_eq!(args_tuple.len(), 1); + + match env.subs.get_content_without_compacting(args_tuple[0]) { + Content::Structure(FlatType::TagUnion(union_tags, ext_var)) => { + let (mut iter, _) = union_tags.sorted_iterator_and_ext(env.subs, *ext_var); + let payloads = iter.next().unwrap().1; + + debug_assert_eq!(iter.next(), None); + + (payloads[0], payloads[1]) + } + _ => { + unreachable!() + } + } + }; + + debug_assert_eq!(field_layouts.len(), 2); + + let key_id = add_type_help(env, field_layouts[0], key_var, opt_name, types); + let val_id = add_type_help(env, field_layouts[1], val_var, opt_name, types); + let dict_id = types.add_anonymous(RocType::RocDict(key_id, val_id), layout); + + types.depends(dict_id, key_id); + types.depends(dict_id, val_id); + + dict_id + } + (elem_layout, alias_content) => unreachable!( + "Unrecognized List element for Dict. Layout was: {:?} and alias_content was: {:?}", + elem_layout, + alias_content + ), + } + } + ( + Builtin::List(elem_layout), + Alias(Symbol::SET_SET, _alias_vars, alias_var, AliasKind::Opaque), + ) => { + match ( + elem_layout, + env.subs.get_content_without_compacting(*alias_var), + ) { + ( + Layout::Struct { field_layouts, .. }, + Alias(Symbol::DICT_DICT, alias_args, _alias_var, AliasKind::Opaque), + ) => { + let dict_type_vars = env.subs.get_subs_slice(alias_args.type_variables()); + + debug_assert_eq!(dict_type_vars.len(), 2); + + // Sets only use the key of the Dict they wrap, not the value + let elem_var = dict_type_vars[0]; + + debug_assert_eq!(field_layouts.len(), 2); + + let elem_id = add_type_help(env, field_layouts[0], elem_var, opt_name, types); + let set_id = types.add_anonymous(RocType::RocSet(elem_id), layout); + + types.depends(set_id, elem_id); + + set_id + } + (elem_layout, alias_content) => unreachable!( + "Unrecognized List element for Set. Layout was: {:?} and alias_content was: {:?}", + elem_layout, + alias_content + ), + } + } + (Builtin::List(elem_layout), alias) => { + unreachable!( + "The type alias {:?} was not an Apply(Symbol::LIST_LIST) as expected, given that its builtin was Builtin::List({:?})", + alias, elem_layout + ); + } } } @@ -985,6 +1085,7 @@ fn add_tag_union<'a>( var: Variable, types: &mut Types, layout: Layout<'a>, + rec_root: Option, ) -> TypeId { let subs = env.subs; let name = match opt_name { @@ -992,7 +1093,6 @@ fn add_tag_union<'a>( None => env.enum_names.get_name(var), }; - let is_recursive; let tag_union_type = match layout { Layout::Union(union_layout) => { use UnionLayout::*; @@ -1001,17 +1101,8 @@ fn add_tag_union<'a>( // A non-recursive tag union // e.g. `Result ok err : [Ok ok, Err err]` NonRecursive(_) => { - is_recursive = false; - - let tags = union_tags_to_types( - &name, - union_tags, - subs, - env, - types, - layout, - is_recursive, - ); + let tags = + union_tags_to_types(&name, union_tags, subs, env, types, layout, false); // TODO deal with empty tag union let discriminant_size = Discriminant::from_number_of_tags(tags.len()) .stack_size() @@ -1028,17 +1119,8 @@ fn add_tag_union<'a>( // A recursive tag union (general case) // e.g. `Expr : [Sym Str, Add Expr Expr]` Recursive(_) => { - is_recursive = true; - - let tags = union_tags_to_types( - &name, - union_tags, - subs, - env, - types, - layout, - is_recursive, - ); + let tags = + union_tags_to_types(&name, union_tags, subs, env, types, layout, true); let discriminant_size = Discriminant::from_number_of_tags(tags.len()).stack_size(); let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap(); @@ -1051,17 +1133,8 @@ fn add_tag_union<'a>( } } NonNullableUnwrapped(_) => { - is_recursive = true; - - let mut tags = union_tags_to_types( - &name, - union_tags, - subs, - env, - types, - layout, - is_recursive, - ); + let mut tags = + union_tags_to_types(&name, union_tags, subs, env, types, layout, true); debug_assert_eq!(tags.len(), 1); @@ -1085,17 +1158,8 @@ fn add_tag_union<'a>( nullable_id, other_tags, } => { - is_recursive = true; - - let tags = union_tags_to_types( - &name, - union_tags, - subs, - env, - types, - layout, - is_recursive, - ); + let tags = + union_tags_to_types(&name, union_tags, subs, env, types, layout, true); let discriminant_size = Discriminant::from_number_of_tags(other_tags.len()).stack_size(); let discriminant_offset = union_layout.tag_id_offset(env.target).unwrap(); @@ -1120,17 +1184,8 @@ fn add_tag_union<'a>( nullable_id: null_represents_first_tag, other_fields: _, // TODO use this! } => { - is_recursive = true; - - let mut tags = union_tags_to_types( - &name, - union_tags, - subs, - env, - types, - layout, - is_recursive, - ); + let mut tags = + union_tags_to_types(&name, union_tags, subs, env, types, layout, true); // NullableUnwrapped tag unions should always have exactly 2 tags. debug_assert_eq!(tags.len(), 2); @@ -1161,24 +1216,9 @@ fn add_tag_union<'a>( } } Layout::Builtin(Builtin::Int(int_width)) => { - is_recursive = false; - - let tags: Vec = union_tags - .iter_from_subs(subs) - .map(|(tag_name, _)| tag_name.0.as_str().to_string()) - .collect(); - - RocTagUnion::Enumeration { - name: name.clone(), - tags, - size: int_width.stack_size(), - } + add_int_enumeration(union_tags, subs, &name, int_width) } Layout::Struct { field_layouts, .. } => { - // This is a single-tag union, but it's unclear whether it's recursive, - // since one of its fields could hold a recursive type. - is_recursive = is_recursive_tag_union(layout); - let (tag_name, payload_fields) = single_tag_payload_fields(union_tags, subs, field_layouts, env, types); @@ -1191,11 +1231,13 @@ fn add_tag_union<'a>( payload_fields, } } + Layout::Builtin(Builtin::Bool) => { + // This isn't actually a Bool, but rather a 2-tag union with no payloads + // (so it has the same layout as a Bool, but actually isn't one; if it were + // a real Bool, it would have been handled elsewhere already!) + add_int_enumeration(union_tags, subs, &name, IntWidth::U8) + } Layout::Builtin(builtin) => { - // This is a single-tag union, but it's unclear whether it's recursive, - // since it could be (for example) a single-tag union wrapping a List. - is_recursive = is_recursive_tag_union(layout); - let type_id = add_builtin_type(env, builtin, var, opt_name, types, layout); let (tag_name, _) = single_tag_payload(union_tags, subs); @@ -1206,11 +1248,6 @@ fn add_tag_union<'a>( } } Layout::Boxed(elem_layout) => { - // This is a single-tag union, but it's unclear whether it's recursive, - // since it could be (for example) a single-tag union wrapping a Box that contains - // the union somewhere in its eleemnt. - is_recursive = is_recursive_tag_union(layout); - let (tag_name, payload_fields) = single_tag_payload_fields(union_tags, subs, &[*elem_layout], env, types); @@ -1233,13 +1270,30 @@ fn add_tag_union<'a>( let typ = RocType::TagUnion(tag_union_type); let type_id = types.add_named(name, typ, layout); - if is_recursive { - env.known_recursive_types.insert(layout, type_id); + if let Some(rec_var) = rec_root { + env.known_recursive_types.insert(rec_var, type_id); } type_id } +fn add_int_enumeration( + union_tags: &UnionLabels, + subs: &Subs, + name: &str, + int_width: IntWidth, +) -> RocTagUnion { + let tags: Vec = union_tags + .iter_from_subs(subs) + .map(|(tag_name, _)| tag_name.0.as_str().to_string()) + .collect(); + RocTagUnion::Enumeration { + name: name.to_string(), + tags, + size: int_width.stack_size(), + } +} + fn union_tags_to_types( name: &str, union_tags: &UnionLabels, @@ -1337,21 +1391,6 @@ fn tags_to_types( .collect() } -fn is_recursive_tag_union(layout: Layout) -> bool { - use roc_mono::layout::UnionLayout::*; - - match layout { - Layout::Union(tag_union) => match tag_union { - NonRecursive(_) => false, - Recursive(_) - | NonNullableUnwrapped(_) - | NullableWrapped { .. } - | NullableUnwrapped { .. } => true, - }, - _ => false, - } -} - fn struct_fields_needed>(env: &mut Env<'_>, vars: I) -> usize { let subs = env.subs; let arena = env.arena; diff --git a/crates/glue/templates/header.rs b/crates/glue/templates/header.rs index b4935b9097..9f7f9707df 100644 --- a/crates/glue/templates/header.rs +++ b/crates/glue/templates/header.rs @@ -1,8 +1,17 @@ // ⚠️ GENERATED CODE ⚠️ - this entire file was generated by the `roc glue` CLI command +#![allow(unused_unsafe)] #![allow(dead_code)] #![allow(unused_mut)] #![allow(non_snake_case)] #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] #![allow(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::unused_unit)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::let_and_return)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::redundant_static_lifetimes)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::clone_on_copy)] diff --git a/crates/glue/tests/fixtures/dict/app.roc b/crates/glue/tests/fixtures/dict/app.roc new file mode 100644 index 0000000000..557c8f38b2 --- /dev/null +++ b/crates/glue/tests/fixtures/dict/app.roc @@ -0,0 +1,10 @@ +app "app" + packages { pf: "platform.roc" } + imports [] + provides [main] to pf + +main = + Dict.empty + |> Dict.insert "foo" "this will be overwritten" + |> Dict.insert "baz" "blah" + |> Dict.insert "foo" "bar" diff --git a/crates/glue/tests/fixtures/dict/platform.roc b/crates/glue/tests/fixtures/dict/platform.roc new file mode 100644 index 0000000000..1a9939cc87 --- /dev/null +++ b/crates/glue/tests/fixtures/dict/platform.roc @@ -0,0 +1,9 @@ +platform "test-platform" + requires {} { main : _ } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Dict Str Str +mainForHost = main diff --git a/crates/glue/tests/fixtures/dict/src/lib.rs b/crates/glue/tests/fixtures/dict/src/lib.rs new file mode 100644 index 0000000000..b05778186b --- /dev/null +++ b/crates/glue/tests/fixtures/dict/src/lib.rs @@ -0,0 +1,93 @@ +mod test_glue; + +use roc_std::{RocDict, RocStr}; + +extern "C" { + #[link_name = "roc__mainForHost_1_exposed_generic"] + fn roc_main(_: *mut RocDict); +} + +#[no_mangle] +pub extern "C" fn rust_main() -> i32 { + use std::cmp::Ordering; + use std::collections::hash_set::HashSet; + + let dict = unsafe { + let mut ret: core::mem::MaybeUninit> = + core::mem::MaybeUninit::uninit(); + + roc_main(ret.as_mut_ptr()); + + ret.assume_init() + }; + + // Verify that it has all the expected traits. + + assert!(dict == dict); // PartialEq + assert_eq!(dict.len(), 2); // len + assert!(dict.clone() == dict.clone()); // Clone + + assert!(dict.partial_cmp(&dict) == Some(Ordering::Equal)); // PartialOrd + assert!(dict.cmp(&dict) == Ordering::Equal); // Ord + + let mut set = HashSet::new(); + + set.insert(dict.clone()); // Eq, Hash + set.insert(dict.clone()); + + assert_eq!(set.len(), 1); + + println!("dict was: {:?}", dict); // Debug + + // Exit code + 0 +} + +// Externs required by roc_std and by the Roc app + +use core::ffi::c_void; +use std::ffi::CStr; +use std::os::raw::c_char; + +#[no_mangle] +pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + return libc::malloc(size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + return libc::realloc(c_ptr, new_size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + return libc::free(c_ptr); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { + match tag_id { + 0 => { + let slice = CStr::from_ptr(c_ptr as *const c_char); + let string = slice.to_str().unwrap(); + eprintln!("Roc hit a panic: {}", string); + std::process::exit(1); + } + _ => todo!(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void { + libc::memcpy(dst, src, n) +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { + libc::memset(dst, c, n) +} diff --git a/crates/glue/tests/fixtures/set/app.roc b/crates/glue/tests/fixtures/set/app.roc new file mode 100644 index 0000000000..583498afb1 --- /dev/null +++ b/crates/glue/tests/fixtures/set/app.roc @@ -0,0 +1,11 @@ +app "app" + packages { pf: "platform.roc" } + imports [] + provides [main] to pf + +main = + Set.empty + |> Set.insert "foo" + |> Set.insert "bar" + |> Set.insert "foo" + |> Set.insert "baz" diff --git a/crates/glue/tests/fixtures/set/platform.roc b/crates/glue/tests/fixtures/set/platform.roc new file mode 100644 index 0000000000..7b73d49ba5 --- /dev/null +++ b/crates/glue/tests/fixtures/set/platform.roc @@ -0,0 +1,9 @@ +platform "test-platform" + requires {} { main : _ } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Set Str +mainForHost = main diff --git a/crates/glue/tests/fixtures/set/src/lib.rs b/crates/glue/tests/fixtures/set/src/lib.rs new file mode 100644 index 0000000000..0c3114737b --- /dev/null +++ b/crates/glue/tests/fixtures/set/src/lib.rs @@ -0,0 +1,92 @@ +mod test_glue; + +use roc_std::{RocSet, RocStr}; + +extern "C" { + #[link_name = "roc__mainForHost_1_exposed_generic"] + fn roc_main(_: *mut RocSet); +} + +#[no_mangle] +pub extern "C" fn rust_main() -> i32 { + use std::cmp::Ordering; + use std::collections::hash_set::HashSet; + + let set = unsafe { + let mut ret: core::mem::MaybeUninit> = core::mem::MaybeUninit::uninit(); + + roc_main(ret.as_mut_ptr()); + + ret.assume_init() + }; + + // Verify that it has all the expected traits. + + assert!(set == set); // PartialEq + assert_eq!(set.len(), 3); // len + assert!(set.clone() == set.clone()); // Clone + + assert!(set.partial_cmp(&set) == Some(Ordering::Equal)); // PartialOrd + assert!(set.cmp(&set) == Ordering::Equal); // Ord + + let mut hash_set = HashSet::new(); + + hash_set.insert(set.clone()); // Eq, Hash + hash_set.insert(set.clone()); + + assert_eq!(hash_set.len(), 1); + + println!("set was: {:?}", set); // Debug + + // Exit code + 0 +} + +// Externs required by roc_std and by the Roc app + +use core::ffi::c_void; +use std::ffi::CStr; +use std::os::raw::c_char; + +#[no_mangle] +pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + return libc::malloc(size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + return libc::realloc(c_ptr, new_size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + return libc::free(c_ptr); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { + match tag_id { + 0 => { + let slice = CStr::from_ptr(c_ptr as *const c_char); + let string = slice.to_str().unwrap(); + eprintln!("Roc hit a panic: {}", string); + std::process::exit(1); + } + _ => todo!(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void { + libc::memcpy(dst, src, n) +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { + libc::memset(dst, c, n) +} diff --git a/crates/glue/tests/test_glue_cli.rs b/crates/glue/tests/test_glue_cli.rs index 6247b68711..4c83881e87 100644 --- a/crates/glue/tests/test_glue_cli.rs +++ b/crates/glue/tests/test_glue_cli.rs @@ -70,6 +70,12 @@ mod glue_cli_run { fixtures! { basic_record:"basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n", nested_record:"nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n", + dict:"dict" => indoc!(r#" + dict was: RocDict {"foo": "bar", "baz": "blah"} + "#), + set:"set" => indoc!(r#" + set was: RocSet {"foo", "bar", "baz"} + "#), enumeration:"enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n", union_with_padding:"union-with-padding" => indoc!(r#" tag_union was: NonRecursive::Foo("This is a test") diff --git a/crates/linker/Cargo.toml b/crates/linker/Cargo.toml index 445db1405e..37dbcba5dc 100644 --- a/crates/linker/Cargo.toml +++ b/crates/linker/Cargo.toml @@ -3,7 +3,7 @@ name = "roc_linker" version = "0.0.1" authors = ["The Roc Contributors"] license = "UPL-1.0" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" edition = "2021" description = "A surgical linker for Roc" diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index 6423bc1925..e5d12fbfe4 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -79,17 +79,10 @@ pub fn build_and_preprocess_host( preprocessed_host_path: &Path, exposed_to_host: Vec, exported_closure_types: Vec, - target_valgrind: bool, ) { let dummy_lib = host_input_path.with_file_name("libapp.so"); generate_dynamic_lib(target, exposed_to_host, exported_closure_types, &dummy_lib); - rebuild_host( - opt_level, - target, - host_input_path, - Some(&dummy_lib), - target_valgrind, - ); + rebuild_host(opt_level, target, host_input_path, Some(&dummy_lib)); let dynhost = host_input_path.with_file_name("dynhost"); let metadata = host_input_path.with_file_name("metadata"); // let prehost = host_input_path.with_file_name("preprocessedhost"); diff --git a/crates/repl_cli/Cargo.toml b/crates/repl_cli/Cargo.toml index 5e4c0310d8..66e5218e6c 100644 --- a/crates/repl_cli/Cargo.toml +++ b/crates/repl_cli/Cargo.toml @@ -19,8 +19,8 @@ bumpalo = {version = "3.8.0", features = ["collections"]} const_format = { version = "0.2.23", features = ["const_generics"] } inkwell = {path = "../vendor/inkwell"} libloading = "0.7.1" -rustyline = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"} -rustyline-derive = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"} +rustyline = {git = "https://github.com/roc-lang/rustyline", rev = "e74333c"} +rustyline-derive = {git = "https://github.com/roc-lang/rustyline", rev = "e74333c"} target-lexicon = "0.12.2" roc_build = {path = "../compiler/build"} diff --git a/crates/repl_expect/Cargo.toml b/crates/repl_expect/Cargo.toml index f0fafe371c..4b4451ba44 100644 --- a/crates/repl_expect/Cargo.toml +++ b/crates/repl_expect/Cargo.toml @@ -33,7 +33,7 @@ libc = "0.2.106" test_gen = { path = "../compiler/test_gen" } roc_build = { path = "../compiler/build", features = ["target-aarch64", "target-x86_64"] } tempfile = "3.2.0" -indoc = "1.0.3" +indoc = "1.0.7" pretty_assertions = "1.0.0" strip-ansi-escapes = "0.1.1" diff --git a/crates/repl_test/Cargo.toml b/crates/repl_test/Cargo.toml index ca1a83d1a8..ae21768bc7 100644 --- a/crates/repl_test/Cargo.toml +++ b/crates/repl_test/Cargo.toml @@ -12,7 +12,7 @@ roc_cli = {path = "../cli"} lazy_static = "1.4.0" [dev-dependencies] -indoc = "1.0.3" +indoc = "1.0.7" strip-ansi-escapes = "0.1.1" wasmer-wasi = "2.2.1" diff --git a/crates/repl_wasm/Cargo.toml b/crates/repl_wasm/Cargo.toml index 1a13904eca..baa0680446 100644 --- a/crates/repl_wasm/Cargo.toml +++ b/crates/repl_wasm/Cargo.toml @@ -18,7 +18,7 @@ console_error_panic_hook = {version = "0.1.7", optional = true} futures = {version = "0.3.17", optional = true} js-sys = "0.3.56" wasm-bindgen = "0.2.79" -wasm-bindgen-futures = "0.4.29" +wasm-bindgen-futures = "0.4.32" roc_collections = {path = "../compiler/collections"} roc_gen_wasm = {path = "../compiler/gen_wasm"} diff --git a/crates/reporting/Cargo.toml b/crates/reporting/Cargo.toml index b63395a313..fac8f7a8eb 100644 --- a/crates/reporting/Cargo.toml +++ b/crates/reporting/Cargo.toml @@ -32,5 +32,5 @@ roc_target = { path = "../compiler/roc_target" } roc_test_utils = { path = "../test_utils" } roc_solve = { path = "../compiler/solve" } pretty_assertions = "1.0.0" -indoc = "1.0.3" -insta = "1.15.0" +indoc = "1.0.7" +insta = "1.18.2" diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 05e6663037..1f805aec8d 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -171,6 +171,27 @@ pub fn can_problem<'b>( title = UNUSED_ARG.to_string(); severity = Severity::Warning; } + Problem::UnusedBranchDef(symbol, region) => { + doc = alloc.stack([ + alloc.concat([ + alloc.symbol_unqualified(symbol), + alloc.reflow(" is not used in this "), + alloc.keyword("when"), + alloc.reflow(" branch."), + ]), + alloc.region(lines.convert_region(region)), + alloc.concat([ + alloc.reflow("If you don't need to use "), + alloc.symbol_unqualified(symbol), + alloc.reflow(", prefix it with an underscore, like \"_"), + alloc.reflow(symbol.as_str(alloc.interns)), + alloc.reflow("\", or replace it with just an \"_\"."), + ]), + ]); + + title = UNUSED_DEF.to_string(); + severity = Severity::Warning; + } Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => { doc = alloc.stack([ if left_bin_op.value == right_bin_op.value { diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index b9efd202a3..de08bcf3f6 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -120,7 +120,7 @@ pub fn type_problem<'b>( report(title, doc, filename) } - SolvedTypeError => None, // Don't re-report cascading errors - see https://github.com/rtfeldman/roc/pull/1711 + SolvedTypeError => None, // Don't re-report cascading errors - see https://github.com/roc-lang/roc/pull/1711 // We'll also report these as a canonicalization problem, no need to re-report them. CyclicAlias(..) => None, diff --git a/crates/reporting/src/lib.rs b/crates/reporting/src/lib.rs index ff2791a5f8..a6bba7f888 100644 --- a/crates/reporting/src/lib.rs +++ b/crates/reporting/src/lib.rs @@ -1,5 +1,5 @@ #![warn(clippy::dbg_macro)] -// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. +// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] pub mod error; diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index 8de3708bca..496fc388a7 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -552,7 +552,7 @@ mod test_reporting { ); test_report!( - #[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"] + #[ignore = "Blocked on https://github.com/roc-lang/roc/issues/3385"] unrecognized_name, indoc!( r#" @@ -5245,7 +5245,7 @@ mod test_reporting { "### ); - // https://github.com/rtfeldman/roc/issues/1714 + // https://github.com/roc-lang/roc/issues/1714 test_report!( interpolate_concat_is_transparent_1714, indoc!( @@ -7570,7 +7570,7 @@ All branches in an `if` must have the same type! ); test_report!( - #[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"] + #[ignore = "Blocked on https://github.com/roc-lang/roc/issues/3385"] unimported_modules_reported, indoc!( r#" @@ -8843,7 +8843,7 @@ All branches in an `if` must have the same type! "# ); - // from https://github.com/rtfeldman/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a + // from https://github.com/roc-lang/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a // There was a bug where this reported UnusedArgument("val") // since it was used only in the returned function only. // @@ -9123,7 +9123,7 @@ All branches in an `if` must have the same type! "# ), // TODO: this error message is quite unfortunate. We should remove the duplication, and - // also support regions that point to things in other modules. See also https://github.com/rtfeldman/roc/issues/3056. + // also support regions that point to things in other modules. See also https://github.com/roc-lang/roc/issues/3056. @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ @@ -9895,13 +9895,13 @@ All branches in an `if` must have the same type! ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ - `y` is not used anywhere in your code. + `y` is not used in this `when` branch. 5│ A x | B y -> x ^ - If you didn't intend on using `y` then remove it so future readers of - your code don't wonder why it is there. + If you don't need to use `y`, prefix it with an underscore, like "_y", + or replace it with just an "_". "### ); @@ -10436,4 +10436,55 @@ All branches in an `if` must have the same type! Tip: Looks like the b field is missing. "### ); + + test_report!( + unused_def_in_branch_pattern, + indoc!( + r#" + when A "" is + A foo -> "" + "# + ), + @r###" + ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ + + `foo` is not used in this `when` branch. + + 5│ A foo -> "" + ^^^ + + If you don't need to use `foo`, prefix it with an underscore, like + "_foo", or replace it with just an "_". + "### + ); + + test_report!( + infer_decoded_record_error_with_function_field, + indoc!( + r#" + app "test" imports [Decode, Json] provides [main] to "./platform" + + main = + decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8 + when decoded is + Ok rcd -> rcd.first rcd.second + _ -> "something went wrong" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This expression has a type that does not implement the abilities it's expected to: + + 6│ Ok rcd -> rcd.first rcd.second + ^^^^^^^^^ + + Roc can't generate an implementation of the `Decode.Decoding` ability + for + + a -> b + + Note: `Decoding` cannot be generated for functions. + "### + ); } diff --git a/crates/roc_std/Cargo.toml b/crates/roc_std/Cargo.toml index d95400db08..63b6b8d736 100644 --- a/crates/roc_std/Cargo.toml +++ b/crates/roc_std/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "UPL-1.0" name = "roc_std" readme = "README.md" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" version = "0.0.1" [dependencies] diff --git a/crates/roc_std/src/lib.rs b/crates/roc_std/src/lib.rs index 7a0a10d988..130bd0fb77 100644 --- a/crates/roc_std/src/lib.rs +++ b/crates/roc_std/src/lib.rs @@ -11,12 +11,16 @@ use core::ops::Drop; use core::str; mod roc_box; +mod roc_dict; mod roc_list; +mod roc_set; mod roc_str; mod storage; pub use roc_box::RocBox; +pub use roc_dict::RocDict; pub use roc_list::RocList; +pub use roc_set::RocSet; pub use roc_str::{InteriorNulError, RocStr}; pub use storage::Storage; diff --git a/crates/roc_std/src/roc_dict.rs b/crates/roc_std/src/roc_dict.rs new file mode 100644 index 0000000000..edc8e6fd34 --- /dev/null +++ b/crates/roc_std/src/roc_dict.rs @@ -0,0 +1,77 @@ +use crate::roc_list::{self, RocList}; +use core::{ + fmt::{self, Debug}, + hash::Hash, +}; + +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RocDict(RocList<(K, V)>); + +impl RocDict { + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn with_capacity(capacity: usize) -> Self { + Self(RocList::with_capacity(capacity)) + } + + pub fn iter(&self) -> impl Iterator { + self.into_iter() + } + + pub fn iter_keys(&self) -> impl Iterator { + self.0.iter().map(|(key, _)| key) + } + + pub fn iter_values(&self) -> impl Iterator { + self.0.iter().map(|(_, val)| val) + } +} + +impl RocDict { + #[allow(unused)] + pub fn from_iter>(src: I) -> Self { + let mut ret = Self::with_capacity(src.size_hint().0); + + for (key, val) in src { + unsafe { + ret.insert_unchecked(key, val); + } + } + + ret + } + + unsafe fn insert_unchecked(&mut self, _key: K, _val: V) { + todo!(); + } +} + +impl Debug for RocDict { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("RocDict ")?; + + f.debug_map() + .entries(self.iter().map(|(k, v)| (k, v))) + .finish() + } +} + +impl IntoIterator for RocDict { + type Item = (K, V); + type IntoIter = roc_list::IntoIter<(K, V)>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, K, V> IntoIterator for &'a RocDict { + type Item = &'a (K, V); + type IntoIter = core::slice::Iter<'a, (K, V)>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } +} diff --git a/crates/roc_std/src/roc_list.rs b/crates/roc_std/src/roc_list.rs index c94afd2802..a6e91ce187 100644 --- a/crates/roc_std/src/roc_list.rs +++ b/crates/roc_std/src/roc_list.rs @@ -156,6 +156,92 @@ impl RocList where T: Clone, { + pub fn from_slice(slice: &[T]) -> Self { + let mut list = Self::empty(); + list.extend_from_slice(slice); + list + } + + pub fn extend_from_slice(&mut self, slice: &[T]) { + // TODO: Can we do better for ZSTs? Alignment might be a problem. + if slice.is_empty() { + return; + } + + let new_len = self.len() + slice.len(); + let non_null_elements = if let Some((elements, storage)) = self.elements_and_storage() { + // Decrement the list's refence count. + let mut copy = storage.get(); + let is_unique = copy.decrease(); + + if is_unique { + // If we have enough capacity, we can add to the existing elements in-place. + if self.capacity() >= slice.len() { + elements + } else { + // There wasn't enough capacity, so we need a new allocation. + // Since this is a unique RocList, we can use realloc here. + let new_ptr = unsafe { + roc_realloc( + storage.as_ptr().cast(), + Self::alloc_bytes(new_len), + Self::alloc_bytes(self.capacity), + Self::alloc_alignment(), + ) + }; + + self.capacity = new_len; + + Self::elems_from_allocation(NonNull::new(new_ptr).unwrap_or_else(|| { + todo!("Reallocation failed"); + })) + } + } else { + if !copy.is_readonly() { + // Write the decremented reference count back. + storage.set(copy); + } + + // Allocate new memory. + let new_elements = Self::elems_with_capacity(slice.len()); + + // Copy the old elements to the new allocation. + unsafe { + copy_nonoverlapping(elements.as_ptr(), new_elements.as_ptr(), self.length); + } + + new_elements + } + } else { + Self::elems_with_capacity(slice.len()) + }; + + self.elements = Some(non_null_elements); + + let elements = self.elements.unwrap().as_ptr(); + + let append_ptr = unsafe { elements.add(self.len()) }; + + // Use .cloned() to increment the elements' reference counts, if needed. + for (i, new_elem) in slice.iter().cloned().enumerate() { + unsafe { + // Write the element into the slot, without dropping it. + append_ptr + .add(i) + .write(ptr::read(&ManuallyDrop::new(new_elem))); + } + + // It's important that the length is increased one by one, to + // make sure that we don't drop uninitialized elements, even when + // a incrementing the reference count panics. + self.length += 1; + } + + self.capacity = self.length + } +} + +impl RocList { /// Increase a RocList's capacity by at least the requested number of elements (possibly more). /// /// May return a new RocList, if the provided one was not unique. @@ -241,90 +327,6 @@ where }); } - pub fn from_slice(slice: &[T]) -> Self { - let mut list = Self::empty(); - list.extend_from_slice(slice); - list - } - - pub fn extend_from_slice(&mut self, slice: &[T]) { - // TODO: Can we do better for ZSTs? Alignment might be a problem. - if slice.is_empty() { - return; - } - - let new_len = self.len() + slice.len(); - let non_null_elements = if let Some((elements, storage)) = self.elements_and_storage() { - // Decrement the list's refence count. - let mut copy = storage.get(); - let is_unique = copy.decrease(); - - if is_unique { - // If we have enough capacity, we can add to the existing elements in-place. - if self.capacity() >= slice.len() { - elements - } else { - // There wasn't enough capacity, so we need a new allocation. - // Since this is a unique RocList, we can use realloc here. - let new_ptr = unsafe { - roc_realloc( - storage.as_ptr().cast(), - Self::alloc_bytes(new_len), - Self::alloc_bytes(self.capacity), - Self::alloc_alignment(), - ) - }; - - self.capacity = new_len; - - Self::elems_from_allocation(NonNull::new(new_ptr).unwrap_or_else(|| { - todo!("Reallocation failed"); - })) - } - } else { - if !copy.is_readonly() { - // Write the decremented reference count back. - storage.set(copy); - } - - // Allocate new memory. - let new_elements = Self::elems_with_capacity(slice.len()); - - // Copy the old elements to the new allocation. - unsafe { - copy_nonoverlapping(elements.as_ptr(), new_elements.as_ptr(), self.length); - } - - new_elements - } - } else { - Self::elems_with_capacity(slice.len()) - }; - - self.elements = Some(non_null_elements); - - let elements = self.elements.unwrap().as_ptr(); - - let append_ptr = unsafe { elements.add(self.len()) }; - - // Use .cloned() to increment the elements' reference counts, if needed. - for (i, new_elem) in slice.iter().cloned().enumerate() { - unsafe { - // Write the element into the slot, without dropping it. - append_ptr - .add(i) - .write(ptr::read(&ManuallyDrop::new(new_elem))); - } - - // It's important that the length is increased one by one, to - // make sure that we don't drop uninitialized elements, even when - // a incrementing the reference count panics. - self.length += 1; - } - - self.capacity = self.length - } - /// Replace self with a new version, without letting `drop` run in between. fn update_to(&mut self, mut updated: Self) { // We want to replace `self` with `updated` in a way that makes sure @@ -481,6 +483,12 @@ where } } +impl From<[T; SIZE]> for RocList { + fn from(array: [T; SIZE]) -> Self { + Self::from_iter(array) + } +} + impl<'a, T> IntoIterator for &'a RocList { type Item = &'a T; type IntoIter = core::slice::Iter<'a, T>; @@ -553,7 +561,7 @@ impl Hash for RocList { } } -impl FromIterator for RocList { +impl FromIterator for RocList { fn from_iter(into: I) -> Self where I: IntoIterator, diff --git a/crates/roc_std/src/roc_set.rs b/crates/roc_std/src/roc_set.rs new file mode 100644 index 0000000000..032907dca3 --- /dev/null +++ b/crates/roc_std/src/roc_set.rs @@ -0,0 +1,39 @@ +use crate::roc_dict::RocDict; +use core::{ + fmt::{self, Debug}, + hash::Hash, +}; + +#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RocSet(RocDict); + +impl RocSet { + pub fn len(&self) -> usize { + self.0.len() + } + + #[allow(unused)] + pub fn with_capacity(capacity: usize) -> Self { + Self(RocDict::with_capacity(capacity)) + } + + #[allow(unused)] + pub fn iter(&self) -> impl Iterator { + self.0.iter_keys() + } +} + +impl RocSet { + #[allow(unused)] + pub fn from_iter>(src: I) -> Self { + Self(RocDict::from_iter(src.map(|elem| (elem, ())))) + } +} + +impl Debug for RocSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("RocSet ")?; + + f.debug_set().entries(self.iter()).finish() + } +} diff --git a/crates/roc_std/tests/test_roc_std.rs b/crates/roc_std/tests/test_roc_std.rs index 8423d4a537..9c5d9c1824 100644 --- a/crates/roc_std/tests/test_roc_std.rs +++ b/crates/roc_std/tests/test_roc_std.rs @@ -221,6 +221,24 @@ mod test_roc_std { assert_eq!(from_iter, from_slice); } + #[test] + fn list_from_array() { + let elems: [i64; 5] = [1, 2, 3, 4, 5]; + let from_slice = RocList::from_slice(&elems); + let from_array = RocList::from(elems); + assert_eq!(from_array, from_slice); + assert_eq!(from_array.capacity(), from_slice.capacity()); + } + + #[test] + fn list_from_array_zero_size() { + let elems: [(); 5] = [(), (), (), (), ()]; + let from_slice = RocList::from_slice(&elems); + let from_array = RocList::from(elems); + assert_eq!(from_array, from_slice); + assert_eq!(from_array.capacity(), from_slice.capacity()); + } + #[test] fn roc_result_to_rust_result() { let greeting = "Hello, World!"; diff --git a/crates/vendor/inkwell/Cargo.toml b/crates/vendor/inkwell/Cargo.toml index 64833638f4..9fed6d49f7 100644 --- a/crates/vendor/inkwell/Cargo.toml +++ b/crates/vendor/inkwell/Cargo.toml @@ -6,7 +6,7 @@ license = "UPL-1.0" edition = "2018" [dependencies] -# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything. +# NOTE: roc-lang/inkwell is a fork of TheDan64/inkwell which does not change anything. # # The reason for this fork is that the way Inkwell is designed, you have to use # a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that @@ -16,14 +16,14 @@ edition = "2018" # We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch), # but although that worked locally, it did not work on GitHub Actions. (After a few # hours of investigation, gave up trying to figure out why.) So this is the workaround: -# having an immutable tag on the rtfeldman/inkwell fork which points to +# having an immutable tag on the roc-lang/inkwell fork which points to # a particular "release" of Inkwell. # -# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest +# When we want to update Inkwell, we can sync up roc-lang/inkwell to the latest # commit of TheDan64/inkwell, push a new tag which points to the latest commit, # change the tag value in this Cargo.toml to point to that tag, and `cargo update`. # This way, GitHub Actions works and nobody's builds get broken. -inkwell = { git = "https://github.com/rtfeldman/inkwell", branch = "master", features = [ "llvm13-0" ] } +inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "master", features = [ "llvm13-0" ] } [features] target-arm = [] diff --git a/crates/wasi-libc-sys/Cargo.toml b/crates/wasi-libc-sys/Cargo.toml index 959bcdf00a..1406303e29 100644 --- a/crates/wasi-libc-sys/Cargo.toml +++ b/crates/wasi-libc-sys/Cargo.toml @@ -4,5 +4,5 @@ description = "Rust wrapper for a WebAssembly test platform built on libc" edition = "2021" license = "UPL-1.0" name = "wasi_libc_sys" -repository = "https://github.com/rtfeldman/roc" +repository = "https://github.com/roc-lang/roc" version = "0.0.1" diff --git a/default.nix b/default.nix index aae9b93b7a..c2eb6c7b53 100644 --- a/default.nix +++ b/default.nix @@ -1,29 +1,28 @@ -{ }: +{ rev ? "541a3ca27c9a8220b46f4feb7dd8e94336a77f42", # nixpkgs master +nixpkgsSource ? builtins.fetchTarball { + url = "https://github.com/nixos/nixpkgs/tarball/${rev}"; + sha256 = "sha256:1mxv0zigm98pawf05kd4s8ipvk1pvvdsn1yh978c5an97kz0ck5w"; +}, pkgs ? import nixpkgsSource { } +, cargoSha256 ? "sha256-treL2sWPcZ1NBwdab3FOb2FI2wT/Vt9tD4XRfJ8rYWA=", }: # we only this file to release a nix package, use flake.nix for development let - rev = "f6342b8b9e7a4177c7e775cdbf38e1c1b43e7ab3"; # nixpkgs master - nixpkgs = builtins.fetchTarball { - url = "https://github.com/nixos/nixpkgs/tarball/${rev}"; - sha256 = "JTiKsBT1BwMbtSUsvtSl8ffkiirby8FaujJVGV766Q8="; - }; - pkgs = import nixpkgs { }; rustPlatform = pkgs.rustPlatform; llvmPkgs = pkgs.llvmPackages_13; # nix does not store libs in /usr/lib or /lib nixGlibcPath = if pkgs.stdenv.isLinux then "${pkgs.glibc.out}/lib" else ""; -in -rustPlatform.buildRustPackage { +in rustPlatform.buildRustPackage { pname = "roc"; version = "0.0.1"; - src = pkgs.nix-gitignore.gitignoreSource [] ./.; + src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; - cargoSha256 = "sha256-Pd84GGtW1ecrP03uiCVcybIUtWCSDGfLl+fbbdmFyiE="; + inherit cargoSha256; LLVM_SYS_130_PREFIX = "${llvmPkgs.llvm.dev}"; # required for zig - XDG_CACHE_HOME = "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 + XDG_CACHE_HOME = + "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 # want to see backtrace in case of failure RUST_BACKTRACE = 1; @@ -44,17 +43,17 @@ rustPlatform.buildRustPackage { rust-bindgen ]); - buildInputs = (with pkgs; [ - libffi - libiconv - libxkbcommon - libxml2 - ncurses - zlib - cargo - makeWrapper # necessary for postBuild wrapProgram - ] - ++ lib.optionals pkgs.stdenv.isLinux [ + buildInputs = (with pkgs; + [ + libffi + libiconv + libxkbcommon + libxml2 + ncurses + zlib + cargo + makeWrapper # necessary for postBuild wrapProgram + ] ++ lib.optionals pkgs.stdenv.isLinux [ alsa-lib valgrind vulkan-headers @@ -66,24 +65,27 @@ rustPlatform.buildRustPackage { xorg.libXi xorg.libXrandr xorg.libxcb - ] - ++ lib.optionals pkgs.stdenv.isDarwin [ - AppKit - CoreFoundation - CoreServices - CoreVideo - Foundation - Metal - Security - ]); + ] ++ lib.optionals pkgs.stdenv.isDarwin [ + pkgs.darwin.apple_sdk.frameworks.AppKit + pkgs.darwin.apple_sdk.frameworks.CoreFoundation + pkgs.darwin.apple_sdk.frameworks.CoreServices + pkgs.darwin.apple_sdk.frameworks.CoreVideo + pkgs.darwin.apple_sdk.frameworks.Foundation + pkgs.darwin.apple_sdk.frameworks.Metal + pkgs.darwin.apple_sdk.frameworks.Security + ]); # cp: to copy str.zig,list.zig... # wrapProgram pkgs.stdenv.cc: to make ld available for compiler/build/src/link.rs postInstall = if pkgs.stdenv.isLinux then '' cp -r target/x86_64-unknown-linux-gnu/release/lib/. $out/lib - wrapProgram $out/bin/roc --set NIX_GLIBC_PATH ${nixGlibcPath} --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.stdenv.cc ]} + wrapProgram $out/bin/roc --set NIX_GLIBC_PATH ${nixGlibcPath} --prefix PATH : ${ + pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] + } '' else '' cp -r target/aarch64-apple-darwin/release/lib/. $out/lib - wrapProgram $out/bin/roc --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.stdenv.cc ]} + wrapProgram $out/bin/roc --prefix PATH : ${ + pkgs.lib.makeBinPath [ pkgs.stdenv.cc ] + } ''; } diff --git a/devtools/vscodeflake.nix b/devtools/vscodeflake.nix index d0c4422109..888371bd5c 100644 --- a/devtools/vscodeflake.nix +++ b/devtools/vscodeflake.nix @@ -8,10 +8,8 @@ flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, roc, flake-utils }: - let - supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; - in - flake-utils.lib.eachSystem supportedSystems (system: + let supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; + in flake-utils.lib.eachSystem supportedSystems (system: let pkgs = import roc.inputs.nixpkgs { inherit system; @@ -19,28 +17,19 @@ }; rocShell = roc.devShell.${system}; - in - { + in { devShell = pkgs.mkShell { - packages = - let - devInputs = (with pkgs; [ - less - gdb - ]); - vscodeWithExtensions = pkgs.vscode-with-extensions.override { - vscodeExtensions = with pkgs.vscode-extensions; [ - matklad.rust-analyzer - eamodio.gitlens - bbenoist.nix - vadimcn.vscode-lldb - ]; - }; - in - [ - vscodeWithExtensions - devInputs - ]; + packages = let + devInputs = (with pkgs; [ less gdb ]); + vscodeWithExtensions = pkgs.vscode-with-extensions.override { + vscodeExtensions = with pkgs.vscode-extensions; [ + matklad.rust-analyzer + eamodio.gitlens + bbenoist.nix + vadimcn.vscode-lldb + ]; + }; + in [ vscodeWithExtensions devInputs ]; inputsFrom = [ rocShell ]; diff --git a/examples/benchmarks/Base64/Decode.roc b/examples/benchmarks/Base64/Decode.roc index 8dfc19841f..623e7451d6 100644 --- a/examples/benchmarks/Base64/Decode.roc +++ b/examples/benchmarks/Base64/Decode.roc @@ -18,7 +18,7 @@ loopHelp = \{ remaining, string } -> b = Num.intCast y c : U32 c = Num.intCast z - combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c + combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c Loop { remaining: remaining - 3, @@ -33,7 +33,7 @@ loopHelp = \{ remaining, string } -> a = Num.intCast x b : U32 b = Num.intCast y - combined = Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b) + combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8) Done (Str.concat string (bitsToChars combined 1)) else @@ -43,7 +43,7 @@ loopHelp = \{ remaining, string } -> a : U32 a = Num.intCast x - Done (Str.concat string (bitsToChars (Num.shiftLeftBy 16 a) 2)) + Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2)) bitsToChars : U32, Int * -> Str bitsToChars = \bits, missing -> @@ -62,17 +62,17 @@ bitsToCharsHelp = \bits, missing -> # with `0b111111` (which is 2^6 - 1 or 63) (so, 6 1s) to remove unwanted bits on the left. # any 6-bit number is a valid base64 digit, so this is actually safe p = - Num.shiftRightZfBy 18 bits + Num.shiftRightZfBy bits 18 |> Num.intCast |> unsafeToChar q = - Num.bitwiseAnd (Num.shiftRightZfBy 12 bits) lowest6BitsMask + Num.bitwiseAnd (Num.shiftRightZfBy bits 12) lowest6BitsMask |> Num.intCast |> unsafeToChar r = - Num.bitwiseAnd (Num.shiftRightZfBy 6 bits) lowest6BitsMask + Num.bitwiseAnd (Num.shiftRightZfBy bits 6) lowest6BitsMask |> Num.intCast |> unsafeToChar diff --git a/examples/benchmarks/Base64/Encode.roc b/examples/benchmarks/Base64/Encode.roc index 3c42e2f04d..afeff2d379 100644 --- a/examples/benchmarks/Base64/Encode.roc +++ b/examples/benchmarks/Base64/Encode.roc @@ -80,11 +80,11 @@ encodeCharacters = \a, b, c, d -> if d == equals then if c == equals then - n = Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y) + n = Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12) # masking higher bits is not needed, Encode.unsignedInt8 ignores higher bits b1 : U8 - b1 = Num.intCast (Num.shiftRightBy 16 n) + b1 = Num.intCast (Num.shiftRightBy n 16) Ok (Bytes.Encode.u8 b1) else if !(isValidChar c) then @@ -95,10 +95,10 @@ encodeCharacters = \a, b, c, d -> z : U32 z = Num.intCast n3 - n = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y)) (Num.shiftLeftBy 6 z) + n = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12)) (Num.shiftLeftBy z 6) combined : U16 - combined = Num.intCast (Num.shiftRightBy 8 n) + combined = Num.intCast (Num.shiftRightBy n 8) Ok (Bytes.Encode.u16 BE combined) else if !(isValidChar d) then @@ -115,14 +115,14 @@ encodeCharacters = \a, b, c, d -> n = Num.bitwiseOr - (Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y)) - (Num.bitwiseOr (Num.shiftLeftBy 6 z) w) + (Num.bitwiseOr (Num.shiftLeftBy x 18) (Num.shiftLeftBy y 12)) + (Num.bitwiseOr (Num.shiftLeftBy z 6) w) b3 : U8 b3 = Num.intCast n combined : U16 - combined = Num.intCast (Num.shiftRightBy 8 n) + combined = Num.intCast (Num.shiftRightBy n 8) Ok (Bytes.Encode.sequence [Bytes.Encode.u16 BE combined, Bytes.Encode.u8 b3]) diff --git a/examples/benchmarks/Bytes/Encode.roc b/examples/benchmarks/Bytes/Encode.roc index de452f994a..3858f6858c 100644 --- a/examples/benchmarks/Bytes/Encode.roc +++ b/examples/benchmarks/Bytes/Encode.roc @@ -71,7 +71,7 @@ encodeHelp = \encoder, offset, output -> Unsigned16 endianness value -> a : U8 - a = Num.intCast (Num.shiftRightBy 8 value) + a = Num.intCast (Num.shiftRightBy value 8) b : U8 b = Num.intCast value @@ -95,7 +95,7 @@ encodeHelp = \encoder, offset, output -> Signed16 endianness value -> a : U8 - a = Num.intCast (Num.shiftRightBy 8 value) + a = Num.intCast (Num.shiftRightBy value 8) b : U8 b = Num.intCast value diff --git a/examples/benchmarks/Closure.roc b/examples/benchmarks/Closure.roc index 56970ec25f..920a62aa1d 100644 --- a/examples/benchmarks/Closure.roc +++ b/examples/benchmarks/Closure.roc @@ -3,7 +3,7 @@ app "closure" imports [pf.Task] provides [main] to pf -# see https://github.com/rtfeldman/roc/issues/985 +# see https://github.com/roc-lang/roc/issues/985 main : Task.Task {} [] main = closure1 {} # |> Task.after (\_ -> closure2 {}) diff --git a/examples/breakout/platform/Cargo.lock b/examples/breakout/platform/Cargo.lock index bc3e713789..f48422aeb4 100644 --- a/examples/breakout/platform/Cargo.lock +++ b/examples/breakout/platform/Cargo.lock @@ -62,7 +62,7 @@ dependencies = [ "alsa-sys", "bitflags", "libc", - "nix 0.20.0", + "nix 0.20.2", ] [[package]] @@ -176,9 +176,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "1.3.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block" @@ -549,7 +549,7 @@ dependencies = [ "mach", "ndk 0.3.0", "ndk-glue 0.3.0", - "nix 0.20.0", + "nix 0.20.2", "oboe", "parking_lot", "stdweb", @@ -1505,14 +1505,15 @@ dependencies = [ [[package]] name = "nix" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945" dependencies = [ "bitflags", "cc", "cfg-if 1.0.0", "libc", + "memoffset", ] [[package]] @@ -2405,7 +2406,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "rand", "static_assertions", ] diff --git a/examples/breakout/platform/Elem.roc b/examples/breakout/platform/Elem.roc index 274bb6b1ae..64aef819d9 100644 --- a/examples/breakout/platform/Elem.roc +++ b/examples/breakout/platform/Elem.roc @@ -64,7 +64,7 @@ none : Elem * none = None # I've often wanted this in elm/html. Usually end up resorting to (Html.text "") - this seems nicer. ## Change an element's state type. ## -## TODO: indent the following once https://github.com/rtfeldman/roc/issues/2585 is fixed. +## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. ## State : { photo : Photo } ## ## render : State -> Elem State @@ -113,7 +113,7 @@ translate = \child, toChild, toParent -> ## Convenient when you have a [List] in your state and want to make ## a [List] of child elements out of it. ## -## TODO: indent the following once https://github.com/rtfeldman/roc/issues/2585 is fixed. +## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. ## State : { photos : List Photo } ## ## render : State -> Elem State @@ -123,7 +123,7 @@ translate = \child, toChild, toParent -> ## Elem.list Photo.render state .photos &photos ## ## col {} children -## TODO: format as multiline type annotation once https://github.com/rtfeldman/roc/issues/2586 is fixed +## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed list : (child -> Elem child), parent, (parent -> List child), (parent, List child -> parent) -> List (Elem parent) list = \renderChild, parent, toChildren, toParent -> List.mapWithIndex @@ -147,7 +147,7 @@ list = \renderChild, parent, toChildren, toParent -> ## if the child has been removed from the parent, ## drops it. ## -## TODO: format as multiline type annotation once https://github.com/rtfeldman/roc/issues/2586 is fixed +## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed translateOrDrop : Elem child, (parent -> Result child *), (parent, child -> parent) -> Elem parent translateOrDrop = \child, toChild, toParent -> when child is diff --git a/examples/breakout/platform/src/graphics/lowlevel/quad.rs b/examples/breakout/platform/src/graphics/lowlevel/quad.rs index 9c1fd85ae6..7d8cf9dd8d 100644 --- a/examples/breakout/platform/src/graphics/lowlevel/quad.rs +++ b/examples/breakout/platform/src/graphics/lowlevel/quad.rs @@ -2,6 +2,7 @@ /// A polygon with 4 corners #[derive(Copy, Clone)] +#[repr(C)] pub struct Quad { pub pos: [f32; 2], pub width: f32, @@ -11,6 +12,10 @@ pub struct Quad { pub border_width: f32, } +// Safety: Pod's contract says the type must +// not have any padding, and must be repr(C). +// As currrently defined, Quad does not have +// any padding. unsafe impl bytemuck::Pod for Quad {} unsafe impl bytemuck::Zeroable for Quad {} @@ -28,4 +33,4 @@ impl Quad { 6 => Float32, ), }; -} \ No newline at end of file +} diff --git a/examples/breakout/platform/src/gui.rs b/examples/breakout/platform/src/gui.rs index ef0412ea25..c43604c638 100644 --- a/examples/breakout/platform/src/gui.rs +++ b/examples/breakout/platform/src/gui.rs @@ -73,7 +73,7 @@ pub fn run_event_loop(title: &str, window_bounds: Bounds) -> Result<(), Box Elem State @@ -113,7 +113,7 @@ translate = \child, toChild, toParent -> ## Convenient when you have a [List] in your state and want to make ## a [List] of child elements out of it. ## -## TODO: indent the following once https://github.com/rtfeldman/roc/issues/2585 is fixed. +## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. ## State : { photos : List Photo } ## ## render : State -> Elem State @@ -123,7 +123,7 @@ translate = \child, toChild, toParent -> ## Elem.list Photo.render state .photos &photos ## ## col {} children -## TODO: format as multiline type annotation once https://github.com/rtfeldman/roc/issues/2586 is fixed +## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed list : (child -> Elem child), parent, (parent -> List child), (parent, List child -> parent) -> List (Elem parent) list = \renderChild, parent, toChildren, toParent -> List.mapWithIndex @@ -147,7 +147,7 @@ list = \renderChild, parent, toChildren, toParent -> ## if the child has been removed from the parent, ## drops it. ## -## TODO: format as multiline type annotation once https://github.com/rtfeldman/roc/issues/2586 is fixed +## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed translateOrDrop : Elem child, (parent -> Result child *), (parent, child -> parent) -> Elem parent translateOrDrop = \child, toChild, toParent -> when child is diff --git a/examples/gui/platform/src/graphics/lowlevel/quad.rs b/examples/gui/platform/src/graphics/lowlevel/quad.rs index 9c1fd85ae6..e8c1f1b568 100644 --- a/examples/gui/platform/src/graphics/lowlevel/quad.rs +++ b/examples/gui/platform/src/graphics/lowlevel/quad.rs @@ -1,6 +1,7 @@ /// A polygon with 4 corners +#[repr(C)] #[derive(Copy, Clone)] pub struct Quad { pub pos: [f32; 2], @@ -11,6 +12,7 @@ pub struct Quad { pub border_width: f32, } +// Safety: repr(C), and as defined will not having padding. unsafe impl bytemuck::Pod for Quad {} unsafe impl bytemuck::Zeroable for Quad {} @@ -28,4 +30,4 @@ impl Quad { 6 => Float32, ), }; -} \ No newline at end of file +} diff --git a/examples/gui/platform/src/gui.rs b/examples/gui/platform/src/gui.rs index 755fa40ae8..c1ab9c8ce7 100644 --- a/examples/gui/platform/src/gui.rs +++ b/examples/gui/platform/src/gui.rs @@ -56,7 +56,7 @@ fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box> { }) .await .expect(r#"Request adapter - If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor + If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#editor "#); adapter diff --git a/examples/interactive/cli-platform/Cargo.lock b/examples/interactive/cli-platform/Cargo.lock index 90ac0682ae..266b9b5e72 100644 --- a/examples/interactive/cli-platform/Cargo.lock +++ b/examples/interactive/cli-platform/Cargo.lock @@ -153,7 +153,7 @@ dependencies = [ [[package]] name = "host" -version = "0.1.0" +version = "0.0.1" dependencies = [ "libc", "reqwest", @@ -432,7 +432,7 @@ dependencies = [ [[package]] name = "roc_std" -version = "0.1.0" +version = "0.0.1" dependencies = [ "arrayvec", "static_assertions", diff --git a/examples/ruby-interop/.gitignore b/examples/ruby-interop/.gitignore new file mode 100644 index 0000000000..0a5ae173be --- /dev/null +++ b/examples/ruby-interop/.gitignore @@ -0,0 +1,6 @@ +libhello +libhello.* +*.log +Makefile +*.bundle +extconf.h diff --git a/examples/ruby-interop/README.md b/examples/ruby-interop/README.md new file mode 100644 index 0000000000..fb71d6805d --- /dev/null +++ b/examples/ruby-interop/README.md @@ -0,0 +1,72 @@ +# Ruby Interop + +This is an example of calling Roc code from [Ruby](https://www.ruby-lang.org). + +## Installation + +To run this example, you will need these to be installed already (in addition to Roc): + +- [`ruby`](https://www.ruby-lang.org/en/downloads) version 2.7.6 or later +- [`clang`](https://clang.llvm.org/) version 11.0.0 or later +- [`make`](https://www.gnu.org/software/make/) version 4.0 or later + +Make sure you have the right versions of Ruby and Clang especially! This example won't work with earlier versions. + +## Building the Roc library + +First, `cd` into this directory and run this in your terminal: + +``` +roc build --lib +``` + +This compiles your Roc code into a binary library in the current directory. The library's filename will be `libhello` plus an OS-specific extension (e.g. `libhello.dylib` on macOS). + +## Generating the Makefile + +Next, run this: (remember that you need Ruby 2.7.6 or higher - otherwise later steps will fail!) + +``` +ruby extconf.rb +``` + +This generates a `Makefile`. (There are only two Roc-specific lines in `extconf.rb`; they are both commented.) You only need to do this step once; now that you have the `Makefile`, you can use it along with `roc build` to rebuild from now. + +## Building the Ruby Library from the Roc Library + +Finally, run this: + +``` +make +``` + +This uses the `Makefile` generated earlier to take the compiled Roc library and combine it with `demo.c` to generate a Ruby library. + +## Try it out! + +You can now try this out in Ruby's REPL (`irb`), like so: + +``` +$ irb +irb(main):001:0> require_relative 'demo' +Ruby just required Roc. Let's get READY TO ROC. +=> true +irb(main):002:0> RocStuff::hello 'Hello, World' +=> "Hello, World, OH YEAH!!! 🤘🤘" +``` + +## Rebuilding after Changes + +To rebuild after changing either the `demo.c` file or any `.roc` files, run: + +``` +roc build --lib && make -B +``` + +The `-B` flag is necessary when you only change .roc files, because otherwise `make` thinks there's no work to do and doesn't bother rebuilding. + +# About this example + +This was created by following a [tutorial on Ruby C extensions](https://silverhammermba.github.io/emberb/c/) and [some documentation](https://github.com/ruby/ruby/blob/master/doc/extension.rdoc#label-Prepare+extconf.rb) (along with [more nicely formatted, but potentially out-of-date docs](https://docs.ruby-lang.org/en/2.4.0/extension_rdoc.html)). + +The [mkmf](https://ruby-doc.org/stdlib-2.5.1/libdoc/mkmf/rdoc/MakeMakefile.html) ("make makefile") method in `extconf.rb` generates a Makefile, which can then be run (using `make` with no arguments) to generate a compiled library that Ruby knows how to import via `require` or `require_relative`. diff --git a/examples/ruby-interop/demo.c b/examples/ruby-interop/demo.c new file mode 100644 index 0000000000..f0572368f0 --- /dev/null +++ b/examples/ruby-interop/demo.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include +#include +#include "extconf.h" + +void *roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } + +void *roc_realloc(void *ptr, size_t new_size, size_t old_size, + unsigned int alignment) +{ + return realloc(ptr, new_size); +} + +void roc_dealloc(void *ptr, unsigned int alignment) { free(ptr); } + +__attribute__((noreturn)) void roc_panic(void *ptr, unsigned int alignment) +{ + char *msg = (char *)ptr; + fprintf(stderr, + "Application crashed with message\n\n %s\n\nShutting down\n", msg); + exit(0); +} + +void *roc_memcpy(void *dest, const void *src, size_t n) +{ + return memcpy(dest, src, n); +} + +void *roc_memset(void *str, int c, size_t n) { return memset(str, c, n); } + +struct RocStr +{ + char *bytes; + size_t len; + size_t capacity; +}; + +struct RocStr init_rocstr(char *bytes, size_t len) +{ + struct RocStr ret; + + if (len < sizeof(struct RocStr)) + { + // This is a small string. TODO do small string things. + size_t refcount_size = sizeof(size_t); + char *new_content = (char *)roc_alloc(len + refcount_size, alignof(size_t)) - refcount_size; + + roc_memcpy(new_content, bytes, len); + + ret.bytes = new_content; + ret.len = len; + ret.capacity = len; + } + else + { + size_t refcount_size = sizeof(size_t); + char *new_content = (char *)roc_alloc(len + refcount_size, alignof(size_t)) - refcount_size; + + roc_memcpy(new_content, bytes, len); + + ret.bytes = new_content; + ret.len = len; + ret.capacity = len; + } + + return ret; +} + +bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } + +// Determine the length of the string, taking into +// account the small string optimization +size_t roc_str_len(struct RocStr str) +{ + char *bytes = (char *)&str; + char last_byte = bytes[sizeof(str) - 1]; + char last_byte_xored = last_byte ^ 0b10000000; + size_t small_len = (size_t)(last_byte_xored); + size_t big_len = str.len; + + // Avoid branch misprediction costs by always + // determining both small_len and big_len, + // so this compiles to a cmov instruction. + if (is_small_str(str)) + { + return small_len; + } + else + { + return big_len; + } +} + +extern void roc__mainForHost_1_exposed_generic(struct RocStr *ret, struct RocStr *arg); + +VALUE hello(VALUE self, VALUE rb_arg) +{ + // Verify the argument is a Ruby string; raise a type error if not. + if (TYPE(rb_arg) != T_STRING) + { + rb_raise(rb_eTypeError, "`hello` only accepts strings."); + } + + struct RocStr arg = init_rocstr(RSTRING_PTR(rb_arg), RSTRING_LEN(rb_arg)); + struct RocStr ret; + + roc__mainForHost_1_exposed_generic(&ret, &arg); + + // Determine str_len and the str_bytes pointer, + // taking into account the small string optimization. + size_t str_len = roc_str_len(ret); + VALUE ruby_str; + + if (is_small_str(ret)) + { + ruby_str = rb_utf8_str_new((char *)&ret, str_len); + } + else + { + ruby_str = rb_utf8_str_new(ret.bytes, str_len); + + // TODO decrement refcount and then only dealloc if that was the last reference + roc_dealloc(ret.bytes - sizeof(size_t), alignof(char *)); + } + + return ruby_str; +} + +void Init_demo() +{ + printf("Ruby just required Roc. Let's get READY TO ROC.\n"); + + VALUE roc_stuff = rb_define_module("RocStuff"); + rb_define_module_function(roc_stuff, "hello", &hello, 1); +} diff --git a/examples/ruby-interop/extconf.rb b/examples/ruby-interop/extconf.rb new file mode 100644 index 0000000000..1af49ae6b6 --- /dev/null +++ b/examples/ruby-interop/extconf.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +require 'mkmf' + +# preparation for compilation goes here +dir_config('') # include the current directory in the library search path +have_library('hello') # depend on `libhello.dylib` being in the current path + # (.dylib is macOS-specific; other OSes would have different extensions) + +create_header +create_makefile 'demo' + diff --git a/examples/ruby-interop/main.roc b/examples/ruby-interop/main.roc new file mode 100644 index 0000000000..f0ccd64238 --- /dev/null +++ b/examples/ruby-interop/main.roc @@ -0,0 +1,11 @@ +app "libhello" + packages { pf: "platform/main.roc" } + imports [] + provides [makeItRoc] to pf + +makeItRoc : Str -> Str +makeItRoc = \str -> + if Str.isEmpty str then + "I need a string here!" + else + "\(str), OH YEAH!!! 🤘🤘" diff --git a/examples/ruby-interop/platform/host.c b/examples/ruby-interop/platform/host.c new file mode 100644 index 0000000000..3f9a63c2a2 --- /dev/null +++ b/examples/ruby-interop/platform/host.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +void* roc_alloc(size_t size, unsigned int alignment) { return malloc(size); } + +void* roc_realloc(void* ptr, size_t new_size, size_t old_size, + unsigned int alignment) { + return realloc(ptr, new_size); +} + +void roc_dealloc(void* ptr, unsigned int alignment) { free(ptr); } + +void roc_panic(void* ptr, unsigned int alignment) { + char* msg = (char*)ptr; + fprintf(stderr, + "Application crashed with message\n\n %s\n\nShutting down\n", msg); + exit(0); +} + +void* roc_memcpy(void* dest, const void* src, size_t n) { + return memcpy(dest, src, n); +} + +void* roc_memset(void* str, int c, size_t n) { return memset(str, c, n); } + +struct RocStr { + char* bytes; + size_t len; + size_t capacity; +}; + +bool is_small_str(struct RocStr str) { return ((ssize_t)str.capacity) < 0; } + +// Determine the length of the string, taking into +// account the small string optimization +size_t roc_str_len(struct RocStr str) { + char* bytes = (char*)&str; + char last_byte = bytes[sizeof(str) - 1]; + char last_byte_xored = last_byte ^ 0b10000000; + size_t small_len = (size_t)(last_byte_xored); + size_t big_len = str.len; + + // Avoid branch misprediction costs by always + // determining both small_len and big_len, + // so this compiles to a cmov instruction. + if (is_small_str(str)) { + return small_len; + } else { + return big_len; + } +} + +extern void roc__mainForHost_1_exposed_generic(struct RocStr *string); + +int main() { + + struct RocStr str; + roc__mainForHost_1_exposed_generic(&str); + + // Determine str_len and the str_bytes pointer, + // taking into account the small string optimization. + size_t str_len = roc_str_len(str); + char* str_bytes; + + if (is_small_str(str)) { + str_bytes = (char*)&str; + } else { + str_bytes = str.bytes; + } + + // Write to stdout + if (write(1, str_bytes, str_len) >= 0) { + // Writing succeeded! + + // NOTE: the string is a static string, read from in the binary + // if you make it a heap-allocated string, it'll be leaked here + return 0; + } else { + printf("Error writing to stdout: %s\n", strerror(errno)); + + // NOTE: the string is a static string, read from in the binary + // if you make it a heap-allocated string, it'll be leaked here + return 1; + } +} diff --git a/examples/ruby-interop/platform/main.roc b/examples/ruby-interop/platform/main.roc new file mode 100644 index 0000000000..339ef9eb61 --- /dev/null +++ b/examples/ruby-interop/platform/main.roc @@ -0,0 +1,9 @@ +platform "hello-world" + requires {} { makeItRoc : Str -> Str } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Str -> Str +mainForHost = \str -> makeItRoc str diff --git a/flake.lock b/flake.lock index 53e33a09ed..ec8518e231 100644 --- a/flake.lock +++ b/flake.lock @@ -30,21 +30,6 @@ "type": "github" } }, - "flake-utils_3": { - "locked": { - "lastModified": 1629481132, - "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "997f7efcb746a9c140ce1f13c72263189225f482", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixgl": { "inputs": { "nixpkgs": [ @@ -67,16 +52,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1657972522, - "narHash": "sha256-JTiKsBT1BwMbtSUsvtSl8ffkiirby8FaujJVGV766Q8=", + "lastModified": 1661088761, + "narHash": "sha256-5DGKX81wIPAAiLwUmUYECpA3vop94AHHR7WmGXSsQok=", "owner": "nixos", "repo": "nixpkgs", - "rev": "07a2e6a4e31ea48408861607198972d60adaf4ad", + "rev": "a7855f2235a1876f97473a76151fec2afa02b287", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-22.05", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } @@ -86,8 +71,7 @@ "flake-utils": "flake-utils", "nixgl": "nixgl", "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay", - "zig": "zig" + "rust-overlay": "rust-overlay" } }, "rust-overlay": { @@ -110,27 +94,6 @@ "repo": "rust-overlay", "type": "github" } - }, - "zig": { - "inputs": { - "flake-utils": "flake-utils_3", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1651452199, - "narHash": "sha256-lq6mTsoPeOCsji/oMFf6953/uOtOhZZ7HSdtjNVDh6I=", - "owner": "roarkanize", - "repo": "zig-overlay", - "rev": "c3bd59086dbc731c240c924fd2bc3581720d0035", - "type": "github" - }, - "original": { - "owner": "roarkanize", - "repo": "zig-overlay", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 95c009be70..83ebc1647f 100644 --- a/flake.nix +++ b/flake.nix @@ -2,18 +2,13 @@ description = "Roc flake"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-22.05"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; # rust from nixpkgs has some libc problems, this is patched in the rust-overlay rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; - # using an overlay allows for quick updates after zig releases - zig = { - url = "github:roarkanize/zig-overlay"; - inputs.nixpkgs.follows = "nixpkgs"; - }; # to easily make configs for multiple architectures flake-utils.url = "github:numtide/flake-utils"; # to be able to use vulkan system libs for editor graphics @@ -23,21 +18,19 @@ }; }; - outputs = { self, nixpkgs, rust-overlay, zig, flake-utils, nixgl }: - let - supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; - in - flake-utils.lib.eachSystem supportedSystems (system: + outputs = { self, nixpkgs, rust-overlay, flake-utils, nixgl }: + let supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; + in flake-utils.lib.eachSystem supportedSystems (system: let - overlays = [ (import rust-overlay) ] ++ (if system == "x86_64-linux" then [ nixgl.overlay ] else []); - pkgs = import nixpkgs { - inherit system overlays; - }; + overlays = [ (import rust-overlay) ] + ++ (if system == "x86_64-linux" then [ nixgl.overlay ] else [ ]); + pkgs = import nixpkgs { inherit system overlays; }; llvmPkgs = pkgs.llvmPackages_13; # get current working directory cwd = builtins.toString ./.; - rust = pkgs.rust-bin.fromRustupToolchainFile "${cwd}/rust-toolchain.toml"; + rust = + pkgs.rust-bin.fromRustupToolchainFile "${cwd}/rust-toolchain.toml"; linuxInputs = with pkgs; lib.optionals stdenv.isLinux [ @@ -55,7 +48,8 @@ ]; darwinInputs = with pkgs; - lib.optionals stdenv.isDarwin (with pkgs.darwin.apple_sdk.frameworks; [ + lib.optionals stdenv.isDarwin + (with pkgs.darwin.apple_sdk.frameworks; [ AppKit CoreFoundation CoreServices @@ -65,8 +59,6 @@ Security ]); - zig-toolchain = zig.packages.${system}."0.9.1"; - # For debugging LLVM IR debugir = pkgs.stdenv.mkDerivation { name = "debugir"; @@ -99,7 +91,7 @@ llvmPkgs.clang libxkbcommon pkg-config - zig-toolchain # roc builtins are implemented in zig, see compiler/builtins/bitcode/ + zig # roc builtins are implemented in zig, see compiler/builtins/bitcode/ # lib deps libffi @@ -108,28 +100,33 @@ zlib libiconv - # faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker + # faster builds - see https://github.com/roc-lang/roc/blob/main/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker llvmPkgs.lld debugir rust rust-bindgen ]); - in - { + in { devShell = pkgs.mkShell { - buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs ++ (if system == "x86_64-linux" then [ pkgs.nixgl.nixVulkanIntel ] else []); + buildInputs = sharedInputs ++ darwinInputs ++ linuxInputs + ++ (if system == "x86_64-linux" then + [ pkgs.nixgl.nixVulkanIntel ] + else + [ ]); LLVM_SYS_130_PREFIX = "${llvmPkgs.llvm.dev}"; # nix does not store libs in /usr/lib or /lib - NIX_GLIBC_PATH = if pkgs.stdenv.isLinux then "${pkgs.glibc.out}/lib" else ""; + NIX_GLIBC_PATH = + if pkgs.stdenv.isLinux then "${pkgs.glibc.out}/lib" else ""; LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath - ([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib] ++ linuxInputs); - NIXPKGS_ALLOW_UNFREE = 1; # to run the editor with NVIDIA's closed source drivers + ([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] + ++ linuxInputs); + NIXPKGS_ALLOW_UNFREE = + 1; # to run the editor with NVIDIA's closed source drivers }; formatter = pkgs.nixpkgs-fmt; - } - ); + }); } diff --git a/getting_started/README.md b/getting_started/README.md index 0cc286314f..c5e264312e 100644 --- a/getting_started/README.md +++ b/getting_started/README.md @@ -4,19 +4,19 @@ Roc is a language for making delightful software. It does not have an 0.1 releas certainly don't recommend using it in production in its current state! However, it can be fun to play around with as long as you have a high tolerance for missing features and compiler bugs. :) -The [tutorial](TUTORIAL.md) is the best place to learn about how to use the language - it assumes no prior knowledge of Roc or similar languages. (If you already know [Elm](https://elm-lang.org/), then [Roc for Elm Programmers](https://github.com/rtfeldman/roc/blob/trunk/roc-for-elm-programmers.md) may be of interest.) +The [tutorial](../TUTORIAL.md) is the best place to learn about how to use the language - it assumes no prior knowledge of Roc or similar languages. (If you already know [Elm](https://elm-lang.org/), then [Roc for Elm Programmers](https://github.com/roc-lang/roc/blob/main/roc-for-elm-programmers.md) may be of interest.) -There's also a folder of [examples](https://github.com/rtfeldman/roc/tree/trunk/examples) - the [CLI form example](https://github.com/rtfeldman/roc/tree/trunk/examples/interactive/form.roc) in particular is a reasonable starting point to build on. +There's also a folder of [examples](https://github.com/roc-lang/roc/tree/main/examples) - the [CLI form example](https://github.com/roc-lang/roc/tree/main/examples/interactive/form.roc) in particular is a reasonable starting point to build on. -If you have a specific question, the [FAQ](FAQ.md) might have an answer, although [Roc Zulip chat](https://roc.zulipchat.com) is overall the best place to ask questions and get help! It's also where we discuss [ideas](https://roc.zulipchat.com/#narrow/stream/304641-ideas) for the language. If you want to get involved in contributing to the language, Zulip is also a great place to ask about good first projects. +If you have a specific question, the [FAQ](../FAQ.md) might have an answer, although [Roc Zulip chat](https://roc.zulipchat.com) is overall the best place to ask questions and get help! It's also where we discuss [ideas](https://roc.zulipchat.com/#narrow/stream/304641-ideas) for the language. If you want to get involved in contributing to the language, Zulip is also a great place to ask about good first projects. ## Installation -- [Linux x86](getting_started/linux_x86.md) -- [MacOS Apple Silicon](getting_started/macos_apple_silicon.md) -- [MacOS x86](getting_started/macos_x86.md) -- [Windows](getting_started/windows.md) -- [Other](getting_started/other.md) +- [Linux x86](linux_x86.md) +- [MacOS Apple Silicon](macos_apple_silicon.md) +- [MacOS x86](macos_x86.md) +- [Windows](windows.md) +- [Other](other.md) ## Running Examples @@ -43,4 +43,4 @@ never done anything with Rust and also never worked on a compiler, but we've been able to find beginner-friendly projects to get people up to speed gradually.) If you're interested in getting involved, check out -[CONTRIBUTING.md](https://github.com/rtfeldman/roc/blob/trunk/CONTRIBUTING.md)! +[CONTRIBUTING.md](https://github.com/roc-lang/roc/blob/main/CONTRIBUTING.md)! diff --git a/getting_started/linux_x86.md b/getting_started/linux_x86.md index 042e54f1d3..8c8a6024ec 100644 --- a/getting_started/linux_x86.md +++ b/getting_started/linux_x86.md @@ -1,4 +1,4 @@ -0. Download the latest nightly from the assets [here](https://github.com/rtfeldman/roc/releases). +0. Download the latest nightly from the assets [here](https://github.com/roc-lang/roc/releases). 0. Untar the archive: ``` tar -xf roc_nightly-linux_x86_64-.tar.gz diff --git a/getting_started/macos_apple_silicon.md b/getting_started/macos_apple_silicon.md index 27ac12ee18..4a5191316e 100644 --- a/getting_started/macos_apple_silicon.md +++ b/getting_started/macos_apple_silicon.md @@ -1,11 +1,11 @@ -0. Download the latest nightly from the assets [here](https://github.com/rtfeldman/roc/releases). +0. Download the latest nightly from the assets [here](https://github.com/roc-lang/roc/releases). 0. To prevent "roc can't be opened because Apple can't check it...": ``` - xattr -d com.apple.quarantine roc_nightly-darwin_apple_silicon-.tar.gz + xattr -d com.apple.quarantine roc_nightly-darwin_apple_silicon-.tar.gz ``` 0. Untar the archive: ``` - roc_nightly-darwin_apple_silicon-.tar.gz + roc_nightly-darwin_apple_silicon-.tar.gz ``` 0. To be able to run examples: - for the Rust example: diff --git a/getting_started/macos_x86.md b/getting_started/macos_x86.md index 2287d5098a..8361594e64 100644 --- a/getting_started/macos_x86.md +++ b/getting_started/macos_x86.md @@ -1,11 +1,11 @@ -0. Download the latest nightly from the assets [here](https://github.com/rtfeldman/roc/releases). +0. Download the latest nightly from the assets [here](https://github.com/roc-lang/roc/releases). 0. To prevent "roc can't be opened because Apple can't check it...": ``` - xattr -d com.apple.quarantine roc_nightly-macos_x86_64-.tar.gz + xattr -d com.apple.quarantine roc_nightly-macos_x86_64-.tar.gz ``` 0. Untar the archive: ``` - roc_nightly-macos_x86_64-.tar.gz + roc_nightly-macos_x86_64-.tar.gz ``` 0. To be able to run examples: - for the Rust example: diff --git a/getting_started/windows.md b/getting_started/windows.md index da5eab6bc1..62899d1585 100644 --- a/getting_started/windows.md +++ b/getting_started/windows.md @@ -1,2 +1,2 @@ -Windows is not yet supported, we have a big project in the works (see [issue #2608](https://github.com/rtfeldman/roc/issues/2608)) that will allow this. +Windows is not yet supported, we have a big project in the works (see [issue #2608](https://github.com/roc-lang/roc/issues/2608)) that will allow this. Until then we recommend using Ubuntu through the "Windows Subsystem for Linux". diff --git a/roc-for-elm-programmers.md b/roc-for-elm-programmers.md index 788c9d4c28..943fb9be32 100644 --- a/roc-for-elm-programmers.md +++ b/roc-for-elm-programmers.md @@ -67,7 +67,7 @@ APIs, you don't need to bother writing `getUsername = Debug.todo "implement"`. Imagine if Elm's `let`...`in` worked exactly the same way, except you removed the `let` and `in` keywords. That's how it works in Roc. -For example, this Elm code computes `someNumber` to be `1234`: +For example, consider this Elm code: ```elm numbers = @@ -299,16 +299,16 @@ has the fields `x` and `y`. In Roc, you can do this like so: -```elm +```javascript table { height: 800, width: 600 } ``` ...and the `table` function will fill in its default values for `x` and `y`. There is no need to use a `defaultConfig` record. -Here's how `table` would be defined in Roc: +Here's how that `table` function would be implemented in Roc: -``` +```elixir table = \{ height, width, title ? "", description ? "" } -> ``` @@ -316,7 +316,7 @@ This is using *optional field destructuring* to destructure a record while also providing default values for any fields that might be missing. Here's the type of `table`: -``` +```elixir table : { height : Pixels, @@ -691,16 +691,6 @@ In Elm, you can choose to expose (or not) custom types' constructors in order to Since Roc's _tags_ can be constructed in any module without importing anything, Roc has a separate _opaque type_ language feature to enable information hiding. -Opaque types in Roc have some similarities to type aliases, but also some important differences. - -* Opaque type are defined with `:=` (e.g. `Username := Str` instead of `Username : Str`) -* You can get an _opaque wrapper_ by writing an `@` symbol before the name of an opaque type. For example, `@Username` would be an opaque wrapper for the opaque type `Username`. -* Applying an _opaque wrapper_ to another value creates an _opaque value_, whose type is the one referred to by the opaque wrapper. So the expression `@Username "Sasha"` has the type `Username`. -* Applying and destructuring opaque wrappers works like tags; you can write `@Username str = user` to unwrap an opaque wrapper's payload, just like you would with a tag payload. -* Opaque types can only be wrapped and unwrapped in the same module where the opaque type itself is defined. -* You can export opaque type names (e.g. `Username`) to other modules, allowing them to be used in type annotations, but there is no way to export the opaque wrappers themselves. This means that an opaque type can only be wrapped and unwrapped (using `@` syntax) in the same module where it was defined. -* Opaque types are only equal if their names are the same _and_ they were defined in the same module. - As an example, suppose I define these inside the `Username` module: ```elm @@ -715,10 +705,14 @@ toStr = \@Username str -> str ``` -I can now expose the `Username` opaque type, which other modules can use in type annotations. -However, it's not even syntactically possible for me to expose the `@Username` opaque wrapper, -because `@` is not allowed in the `exposing` list. Only code written in this `Username` module -can use the `@Username` wrapper. +Here, `Username` is an opaque type. The `fromStr` function turns a string into a `Username` +by "calling" `@Username` on that string. The `toStr` function turns a `Username` back into +a string by pattern matching `@Username str ->` to unwrap the string from the `Username`. + +Now that I've defined the `Username` opaque type, I can expose it so that other modules can use +it in type annotations. However, other modules can't use the `@Username` syntax to wrap or unwrap +`Username` values. That operation is only available in the same scope where `Username` itself was +defined; trying to use it outside that scope will give an error. > If I were to define `Username := Str` inside another module (e.g. `Main`) and use `@Username`, > it would compile, but that `Username` opaque type would not be considered equal to the one defined in @@ -1138,16 +1132,11 @@ GPUs tend to heavily prefer 32-bit floats because a serious bottleneck is how long it takes data to transfer from CPU to GPU, so having to send half as many bytes per render (compared to 64-bit floats) can be huge for performance. -Roc also supports `D64` and `D32`, which are [IEEE 754 decimal floating point numbers](https://en.wikipedia.org/wiki/IEEE_754#Decimal). The upside of these is that they are decimal-based, so -`0.1 + 0.2 == 0.3` (whereas in binary floats [this is not true](https://0.30000000000000004.com/)), -which makes them much better for calculations involving currency, among others. -The downside of decimal floats is that they do not have hardware support -(except on certain [highly uncommon processors](https://en.wikipedia.org/wiki/IEEE_754#See_also)), -so calculations involving them take longer. - -Roc does not let floating point calculations result in `Infinity`, `-Infinity`, -or `NaN`. Any operation which would result in one of these -(such as `sqrt` or `/`) will panic. +Roc also supports `Dec`, which is a 128-bit [fixed-point decimal](https://en.wikipedia.org/wiki/Fixed-point_arithmetic) number. `Dec` is decimal-based, so `0.1 + 0.2 == 0.3` +(whereas in binary floats [this is not true](https://0.30000000000000004.com/)), +which makes it much better for calculations involving currency, among other use cases. +The downside of `Dec` is that it does not have hardware support, so calculations involving +them take longer than they do with floats. Similarly to how there are different sizes of floating point numbers, there are also different sizes of integer to choose from: @@ -1190,43 +1179,34 @@ for `Num` types. For example: * `I64` is a type alias for `Num (Integer Signed64)` * `U8` is a type alias for `Num (Integer Unsigned8)` -* `F32` is a type alias for `Num (FloatingPoint Binary32)` -* `D64` is a type alias for `Num (FloatingPoint Decimal64)` +* `F32` is a type alias for `Num (Fraction Binary32)` +* `Dec` is a type alias for `Num (Fraction Decimal)` -(Those types like `Integer`, `FloatingPoint`, and `Signed64` are all defined like `Never`; you can never instantiate one. -They are used only as phantom types.) +(Those types like `Integer`, `Fraction`, and `Signed64` are all defined like `Never`; +you can never instantiate one. They are used only as phantom types.) So Roc does not use `number`, but rather uses `Num` - which works more like `List`. Either way, you get `+` being able to work on both integers and floats! Separately, there's also `Int a`, which is a type alias for `Num (Integer a)`, -and `Float a`, which is a type alias for `Num (FloatingPoint a)`. These allow functions -that can work on any integer or any float. For example, +and `Frac a`, which is a type alias for `Num (Fraction a)`. These allow functions +that can work on any integer or any fractional number. For example, `Num.bitwiseAnd : Int a, Int a -> Int a`. -In Roc, number literals with decimal points are `Float *` values. +In Roc, number literals with decimal points are `Frac *` values. Number literals *without* a decimal point are `Num *` values. Almost always these will end up becoming something more specific, but in the unlikely event (most often in a REPL) that you actually do end up with an operation that runs on either an `Int *` or a `Num *` value, it will default to being treated as -an `I64`. Similarly, a `Float *` value will default to being treated as a `D64`, +an `I64`. Similarly, a `Frac *` value will default to being treated as a `D64`, which means if someone is learning Roc as their first programming language and they type `0.1 + 0.2` into a REPL, they won't be confused by the answer. -If you encounter overflow with either integers or floats in Roc, you get a runtime -exception rather than wrapping overflow behavior (or a float becoming `Infinity` -or `-Infinity`). You can opt into wrapping overflow instead with functions like +If you encounter integer or `Dec` overflow in Roc, by default you get a runtime +exception. You can opt into wrapping integer overflow instead with functions like `Num.addWrap : Int a, Int a -> Int a`, or use a function that gives `Err` if it overflows, like `Num.addChecked : Num a, Num a -> Result (Num a) [Overflow]*`. -## `comparable`, `appendable`, and `number` - -These don't exist in Roc. - -* `appendable` is only used in Elm for the `(++)` operator, and Roc doesn't have that operator. -* `comparable` is used for comparison operators (like `<` and such), plus `List.sort`, `Dict`, and `Set`. Roc's `List.sort` accepts a "sorting function" argument which specifies how to sort the elements. Roc's comparison operators (like `<`) only accept numbers; `"foo" < "bar"` is valid Elm, but will not compile in Roc. Roc's dictionaries and sets are hashmaps behind the scenes (rather than ordered trees), and their keys only need the functionless restriction. -* `number` is replaced by `Num`, as described previously. - Also [like Python](https://www.python.org/dev/peps/pep-0515/) Roc permits underscores in number literals for readability purposes. Roc also supports hexadecimal (`0x01`), octal (`0o01`), and binary (`0b01`) integer literals; these @@ -1239,18 +1219,29 @@ If you put these into a hypothetical Roc REPL, here's what you'd see: 2048 : Num * > 1 + 2.14 -3.14 : Float * +3.14 : Frac * > 1.0 + 1 -2.0 : Float * +2.0 : Frac * > 1.1 + 0x11 - + > 11 + 0x11 28 : Int * ``` +## Abilities + +`comparable`, `appendable`, and `number` don't exist in Roc. + +* `number` is replaced by `Num`, as described previously. +* `appendable` is only used in Elm for the `(++)` operator, and Roc doesn't have that operator. +* `comparable` is used in Elm for comparison operators (like `<` and such), plus `List.sort`, `Dict`, and `Set`. Roc's comparison operators (like `<`) only accept numbers; `"foo" < "bar"` is valid Elm, but will not compile in Roc. Roc's dictionaries and sets are hashmaps behind the scenes (rather than ordered trees), so their keys need to be hashable but not necessarily comparable. + +That said, Roc's `Dict` and `Set` do have a restriction on their keys, just not `comparable`. +See the section on Abilities in [the tutorial](TUTORIAL.md) for details. + ## Standard library `elm/core` has these modules: diff --git a/www/README.md b/www/README.md index 180931fbcc..44246ca907 100644 --- a/www/README.md +++ b/www/README.md @@ -3,7 +3,7 @@ How to update www.roc-lang.org : - create a new branch, for example `update-www` based on `www` -- pull `trunk` into `update-www` +- pull `main` into `update-www` - update for example the file `www/public/index.html` - do a PR against `www` - check deploy preview diff --git a/www/build.sh b/www/build.sh index 822fc7e76b..7c0190d3d1 100755 --- a/www/build.sh +++ b/www/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euxo pipefail @@ -13,7 +13,7 @@ cp -r public/ build/ # grab the source code and copy it to Netlify's server; if it's not there, fail the build. pushd build -wget https://github.com/rtfeldman/elm-css/files/8849069/roc-source-code.zip +wget https://github.com/roc-lang/roc/archive/www.tar.gz # Download the latest pre-built Web REPL as a zip file. (Build takes longer than Netlify's timeout.) # TODO: When roc repo is public, download it from nightly builds. diff --git a/www/netlify.sh b/www/netlify.sh index f384db89c1..43aa6c01b6 100644 --- a/www/netlify.sh +++ b/www/netlify.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Runs on every Netlify build, to set up the Netlify server. diff --git a/www/netlify.toml b/www/netlify.toml index 640b5268f9..2e62e66598 100644 --- a/www/netlify.toml +++ b/www/netlify.toml @@ -26,10 +26,6 @@ # for a list of authors! [[redirects]] from = "/authors" - to = "https://github.com/rtfeldman/roc/blob/trunk/AUTHORS" + to = "https://raw.githubusercontent.com/roc-lang/roc/main/AUTHORS" force = true - status = 302 # TODO once the repo is public, use status = 200 and this URL: - # https://raw.githubusercontent.com/rtfeldman/roc/trunk/AUTHORS - # - # This way, roc-lang.org/authors will show the authors directly, - # proxied from the current AUTHORS file on GitHub, no redirects. + status = 200 diff --git a/www/public/index.html b/www/public/index.html index 73b77e1010..5e1c1c168f 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -41,13 +41,8 @@ -

To set clear expectations around Roc's readiness for serious use - it's not ready yet! - the repository where it's - developed is private for now.

-

Anyone can get access to the repo in this way, or - you can download a zip of the source code, although the zip isn't updated very - often and is - definitely behind what's in the repo.

Roc also compiles to WebAssembly and you can try it out on the web here (remembering it's not ready yet!)

+

You can check out the source code repository if you'd like to play around with it in its current state.

  • Roc at the Berlin FP Meetup - September 1, 2020 (overall vision for the language)