mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 04:08:19 +00:00
Merge remote-tracking branch 'origin/main' into abilities-syntax
This commit is contained in:
commit
2da41be29f
524 changed files with 47536 additions and 15089 deletions
3
.github/workflows/benchmarks.yml
vendored
3
.github/workflows/benchmarks.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: Benchmarks
|
||||
|
||||
|
|
3
.github/workflows/macos_x86_64.yml
vendored
3
.github/workflows/macos_x86_64.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: Macos x86-64 rust tests
|
||||
|
||||
|
|
46
.github/workflows/nightly_linux_arm64.yml
vendored
Normal file
46
.github/workflows/nightly_linux_arm64.yml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
|
||||
name: Nightly Release Linux arm64/aarch64
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build and package nightly release
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: create version.txt
|
||||
run: ./ci/write_version.sh
|
||||
|
||||
- name: build release with lto
|
||||
run: cargo build --profile=release-with-lto --locked --bin roc
|
||||
|
||||
- name: get commit SHA
|
||||
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
||||
|
||||
- name: get date
|
||||
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
|
||||
|
||||
- name: build file name
|
||||
env:
|
||||
DATE: ${{ env.DATE }}
|
||||
SHA: ${{ env.SHA }}
|
||||
run: echo "RELEASE_FOLDER_NAME=roc_nightly-linux_arm64-$DATE-$SHA" >> $GITHUB_ENV
|
||||
|
||||
# this makes the roc binary a lot smaller
|
||||
- name: strip debug info
|
||||
run: strip ./target/release-with-lto/roc
|
||||
|
||||
- name: Make nightly release tar archive
|
||||
run: ./ci/package_release.sh ${{ env.RELEASE_FOLDER_NAME }}
|
||||
|
||||
- name: Upload roc nightly tar. Actually uploading to github releases has to be done manually.
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
retention-days: 4
|
2
.github/workflows/nightly_linux_x86_64.yml
vendored
2
.github/workflows/nightly_linux_x86_64.yml
vendored
|
@ -7,7 +7,7 @@ name: Nightly Release Linux x86_64
|
|||
|
||||
jobs:
|
||||
build:
|
||||
name: Rust tests, build and package nightly release
|
||||
name: build and package nightly release
|
||||
runs-on: [self-hosted, i7-6700K]
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
|
|
38
.github/workflows/nightly_old_linux_arm64.yml
vendored
Normal file
38
.github/workflows/nightly_old_linux_arm64.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
|
||||
name: Nightly Release Old Linux arm64 using Earthly
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build and package nightly release
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
timeout-minutes: 180
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: get commit SHA
|
||||
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
||||
|
||||
- name: get date
|
||||
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
|
||||
|
||||
- name: build file name
|
||||
env:
|
||||
DATE: ${{ env.DATE }}
|
||||
SHA: ${{ env.SHA }}
|
||||
run: echo "RELEASE_FOLDER_NAME=roc_nightly-old_linux_arm64-$DATE-$SHA" >> $GITHUB_ENV
|
||||
|
||||
- run: earthly --version
|
||||
|
||||
- name: build release with earthly
|
||||
run: earthly +build-nightly-release --RELEASE_FOLDER_NAME=${{ env.RELEASE_FOLDER_NAME }} --ZIG_ARCH=aarch64
|
||||
|
||||
- name: Upload roc nightly tar. Actually uploading to github releases has to be done manually.
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
retention-days: 4
|
41
.github/workflows/nightly_old_linux_x86_64.yml
vendored
Normal file
41
.github/workflows/nightly_old_linux_x86_64.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
|
||||
name: Nightly Release Old Linux x86_64 using Earthly
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build and package nightly release
|
||||
runs-on: [ubuntu-20.04]
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: get commit SHA
|
||||
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
||||
|
||||
- name: get date
|
||||
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
|
||||
|
||||
- name: build file name
|
||||
env:
|
||||
DATE: ${{ env.DATE }}
|
||||
SHA: ${{ env.SHA }}
|
||||
run: echo "RELEASE_FOLDER_NAME=roc_nightly-old_linux_x86_64-$DATE-$SHA" >> $GITHUB_ENV
|
||||
|
||||
- name: install earthly
|
||||
run: sudo /bin/sh -c 'wget https://github.com/earthly/earthly/releases/latest/download/earthly-linux-amd64 -O /usr/local/bin/earthly && chmod +x /usr/local/bin/earthly && /usr/local/bin/earthly bootstrap --with-autocomplete'
|
||||
|
||||
- run: earthly --version
|
||||
|
||||
- name: build release with earthly
|
||||
run: earthly +build-nightly-release --RELEASE_FOLDER_NAME=${{ env.RELEASE_FOLDER_NAME }} --RUSTFLAGS="-C target-cpu=x86-64"
|
||||
|
||||
- name: Upload roc nightly tar. Actually uploading to github releases has to be done manually.
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
path: ${{ env.RELEASE_FOLDER_NAME }}.tar.gz
|
||||
retention-days: 4
|
3
.github/workflows/nix_linux_x86_64.yml
vendored
3
.github/workflows/nix_linux_x86_64.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: Nix linux x86_64 cargo test
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: Nix apple silicon cargo test
|
||||
|
||||
|
|
5
.github/workflows/nix_macos_x86_64.yml
vendored
5
.github/workflows/nix_macos_x86_64.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: Nix macOS x86_64 cargo test
|
||||
|
||||
|
@ -19,7 +20,7 @@ jobs:
|
|||
with:
|
||||
clean: "true"
|
||||
|
||||
- uses: cachix/install-nix-action@v20
|
||||
- uses: cachix/install-nix-action@v22
|
||||
|
||||
- name: execute cli_run tests only, the full tests take too long but are run nightly
|
||||
run: nix develop -c cargo test --locked --release -p roc_cli
|
||||
|
|
3
.github/workflows/spellcheck.yml
vendored
3
.github/workflows/spellcheck.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: SpellCheck
|
||||
|
||||
|
|
52
.github/workflows/test_nightly_many_os.yml
vendored
52
.github/workflows/test_nightly_many_os.yml
vendored
|
@ -1,11 +1,9 @@
|
|||
on:
|
||||
#pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
||||
|
||||
env:
|
||||
ZIG_VERSION: 0.9.1
|
||||
|
||||
jobs:
|
||||
test-nightly:
|
||||
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
||||
|
@ -17,6 +15,9 @@ jobs:
|
|||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: goto-bus-stop/setup-zig@v2
|
||||
with:
|
||||
version: 0.9.1
|
||||
|
||||
- name: get the latest release archive for linux (x86_64)
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
|
@ -26,44 +27,19 @@ jobs:
|
|||
if: startsWith(matrix.os, 'macos')
|
||||
run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-latest.tar.gz
|
||||
|
||||
- name: remove everything in this dir except the tar # we want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout
|
||||
run: ls | grep -v "roc_nightly.*tar\.gz" | xargs rm -rf
|
||||
- run: zig version
|
||||
|
||||
- name: decompress the tar
|
||||
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
|
||||
|
||||
- name: delete tar
|
||||
run: ls | grep "roc_nightly.*tar\.gz" | xargs rm -rf
|
||||
|
||||
- name: rename nightly folder
|
||||
run: mv roc_nightly* roc_nightly
|
||||
|
||||
- name: test roc hello world
|
||||
run: cd roc_nightly && ./roc examples/helloWorld.roc
|
||||
|
||||
- name: test platform switching rust
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesRust.roc
|
||||
|
||||
- name: get OS to use for zig download
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: echo "OS_TYPE=linux" >> $GITHUB_ENV
|
||||
|
||||
- name: get OS to use for zig download
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: echo "OS_TYPE=macos" >> $GITHUB_ENV
|
||||
|
||||
- name: Install zig
|
||||
- name: prep and run basic tests
|
||||
run: |
|
||||
curl -fL -o zig.tar.xz https://ziglang.org/download/${ZIG_VERSION}/zig-${{env.OS_TYPE}}-x86_64-${ZIG_VERSION}.tar.xz && tar -xf zig.tar.xz
|
||||
echo "${GITHUB_WORKSPACE}/zig-${{env.OS_TYPE}}-x86_64-${ZIG_VERSION}" >> $GITHUB_PATH
|
||||
- name: zig version
|
||||
run: zig version
|
||||
./ci/basic_nightly_test.sh
|
||||
|
||||
- name: clean up, get old linux release (x86_64), run tests
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
rm -rf roc_nightly
|
||||
curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-old_linux_x86_64-latest.tar.gz
|
||||
./ci/basic_nightly_test.sh
|
||||
|
||||
- name: test platform switching zig
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc
|
||||
|
||||
- name: test platform switching c
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc
|
||||
|
||||
|
||||
|
||||
|
|
4
.github/workflows/ubuntu_x86_64.yml
vendored
4
.github/workflows/ubuntu_x86_64.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: CI
|
||||
|
||||
|
@ -61,7 +62,6 @@ jobs:
|
|||
run: ./ci/www-repl.sh && sccache --show-stats
|
||||
|
||||
#TODO i386 (32-bit linux) cli tests
|
||||
|
||||
#TODO verify-no-git-changes
|
||||
|
||||
- name: test website build script
|
||||
|
|
7
.github/workflows/windows_release_build.yml
vendored
7
.github/workflows/windows_release_build.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: windows - release build
|
||||
|
||||
|
@ -32,8 +33,8 @@ jobs:
|
|||
- name: zig version
|
||||
run: zig version
|
||||
|
||||
- name: install rust nightly 1.66
|
||||
run: rustup install nightly-2022-10-30
|
||||
- name: install rust nightly 1.70.0
|
||||
run: rustup install nightly-2023-04-15
|
||||
|
||||
- name: set up llvm 13
|
||||
run: |
|
||||
|
|
15
.github/workflows/windows_tests.yml
vendored
15
.github/workflows/windows_tests.yml
vendored
|
@ -1,4 +1,5 @@
|
|||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: windows - subset of tests
|
||||
|
||||
|
@ -36,8 +37,8 @@ jobs:
|
|||
- name: zig version
|
||||
run: zig version
|
||||
|
||||
- name: install rust nightly 1.66
|
||||
run: rustup install nightly-2022-10-30
|
||||
- name: install rust nightly 1.70.0
|
||||
run: rustup install nightly-2023-04-15
|
||||
|
||||
- name: set up llvm 13
|
||||
run: |
|
||||
|
@ -49,7 +50,11 @@ jobs:
|
|||
|
||||
# Why are these tests not build with previous command? => fingerprint error. Use `CARGO_LOG=cargo::core::compiler::fingerprint=info` to investigate
|
||||
- name: Build specific tests without running. Twice for zig lld-link error.
|
||||
run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli
|
||||
run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli -p test_gen
|
||||
|
||||
- name: Test setjmp/longjmp logic
|
||||
run: cargo test-gen-dev --locked --release nat_alias && cargo test-gen-dev --locked --release a_crash
|
||||
|
||||
- name: Actually run the tests.
|
||||
run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli
|
||||
run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker -p roc_cli
|
||||
|
||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -82,3 +82,9 @@ www/src/roc-tutorial
|
|||
# It also ensures the compiler can always pull in 1 version of things and doesn't get restricted by sub lockfiles.
|
||||
/**/Cargo.lock
|
||||
!/Cargo.lock
|
||||
|
||||
# snapshot tests temp file
|
||||
*.pending-snap
|
||||
|
||||
# checkmate
|
||||
checkmate_*.json
|
||||
|
|
182
Cargo.lock
generated
182
Cargo.lock
generated
|
@ -88,6 +88,21 @@ dependencies = [
|
|||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
|
@ -473,6 +488,21 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"time 0.1.45",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
|
@ -1094,6 +1124,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
|
@ -1394,7 +1430,7 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
|
@ -1653,6 +1689,29 @@ dependencies = [
|
|||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys 0.8.4",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced-x86"
|
||||
version = "1.18.0"
|
||||
|
@ -1975,7 +2034,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"time",
|
||||
"time 0.3.21",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -2106,7 +2165,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
|
@ -3095,6 +3154,7 @@ dependencies = [
|
|||
"page_size",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_load",
|
||||
|
@ -3105,6 +3165,7 @@ dependencies = [
|
|||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_schema",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
|
@ -3203,6 +3264,27 @@ dependencies = [
|
|||
"ven_pretty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_checkmate"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"roc_checkmate_schema",
|
||||
"roc_module",
|
||||
"roc_solve_schema",
|
||||
"roc_types",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_checkmate_schema"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_cli"
|
||||
version = "0.0.1"
|
||||
|
@ -3311,11 +3393,13 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"bumpalo",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_derive_key",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_region",
|
||||
"roc_solve_schema",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
]
|
||||
|
@ -3351,6 +3435,7 @@ dependencies = [
|
|||
"roc_parse",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"snafu",
|
||||
|
@ -3563,11 +3648,13 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"bumpalo",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_derive",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_solve",
|
||||
"roc_solve_schema",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
]
|
||||
|
@ -3591,6 +3678,7 @@ dependencies = [
|
|||
"roc_mono",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"serde",
|
||||
"serial_test",
|
||||
|
@ -3611,6 +3699,7 @@ dependencies = [
|
|||
"roc_module",
|
||||
"roc_packaging",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
]
|
||||
|
@ -3627,6 +3716,7 @@ dependencies = [
|
|||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_debug_flags",
|
||||
|
@ -3669,6 +3759,7 @@ dependencies = [
|
|||
name = "roc_mono"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"bitvec",
|
||||
"bumpalo",
|
||||
"hashbrown 0.13.2",
|
||||
|
@ -3685,10 +3776,12 @@ dependencies = [
|
|||
"roc_module",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_solve_schema",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_tracing",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"static_assertions",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
@ -3758,6 +3851,7 @@ dependencies = [
|
|||
"roc_build",
|
||||
"roc_builtins",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_gen_llvm",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
|
@ -3792,6 +3886,7 @@ dependencies = [
|
|||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
|
@ -3847,6 +3942,7 @@ dependencies = [
|
|||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"tempfile",
|
||||
|
@ -3910,6 +4006,7 @@ dependencies = [
|
|||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_debug_flags",
|
||||
"roc_derive",
|
||||
|
@ -3925,6 +4022,7 @@ dependencies = [
|
|||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_problem",
|
||||
"roc_solve_schema",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
|
@ -3945,6 +4043,13 @@ dependencies = [
|
|||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_solve_schema"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
|
@ -4005,11 +4110,12 @@ dependencies = [
|
|||
name = "roc_unify"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_debug_flags",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_solve_schema",
|
||||
"roc_tracing",
|
||||
"roc_types",
|
||||
]
|
||||
|
@ -4155,6 +4261,30 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
|
@ -4243,6 +4373,17 @@ dependencies = [
|
|||
"syn 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.96"
|
||||
|
@ -4644,7 +4785,6 @@ dependencies = [
|
|||
"criterion",
|
||||
"indoc",
|
||||
"inkwell",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libloading",
|
||||
"roc_bitcode",
|
||||
|
@ -4797,6 +4937,17 @@ dependencies = [
|
|||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.21"
|
||||
|
@ -4941,7 +5092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"time",
|
||||
"time 0.3.21",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
|
@ -5251,6 +5402,12 @@ dependencies = [
|
|||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -5575,6 +5732,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
|
@ -5859,9 +6025,9 @@ checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
|
|||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.11"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1690519550bfa95525229b9ca2350c63043a4857b3b0013811b2ccf4a2420b01"
|
||||
checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
|
|
|
@ -82,6 +82,7 @@ bumpalo = { version = "3.12.0", features = ["collections"] }
|
|||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||
capstone = { version = "0.11.0", default-features = false }
|
||||
cgmath = "0.18.0"
|
||||
chrono = "0.4.26"
|
||||
clap = { version = "4.2.7", default-features = false, features = ["std", "color", "suggestions", "help", "usage", "error-context"] }
|
||||
colored = "2.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
@ -141,6 +142,7 @@ reqwest = { version = "0.11.14", default-features = false, features = ["blocking
|
|||
rlimit = "0.9.1"
|
||||
rustyline = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
||||
rustyline-derive = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
||||
schemars = "0.8.12"
|
||||
serde = { version = "1.0.153", features = ["derive"] } # update roc_std/Cargo.toml on change
|
||||
serde-xml-rs = "0.6.0"
|
||||
serde_json = "1.0.94" # update roc_std/Cargo.toml on change
|
||||
|
|
60
Earthfile
Normal file
60
Earthfile
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
VERSION 0.6
|
||||
FROM rust:1.70.0-slim-buster # make sure to update rust-toolchain.toml too so that everything uses the same rust version
|
||||
WORKDIR /earthbuild
|
||||
|
||||
prep-debian:
|
||||
RUN apt -y update
|
||||
|
||||
install-other-libs:
|
||||
FROM +prep-debian
|
||||
RUN apt -y install wget git
|
||||
RUN apt -y install libunwind-dev pkg-config zlib1g-dev
|
||||
RUN apt -y install unzip # for www/build.sh
|
||||
|
||||
install-zig-llvm:
|
||||
ARG ZIG_ARCH
|
||||
FROM +install-other-libs
|
||||
# zig
|
||||
RUN wget -c https://ziglang.org/download/0.9.1/zig-linux-$ZIG_ARCH-0.9.1.tar.xz --no-check-certificate
|
||||
RUN tar -xf zig-linux-$ZIG_ARCH-0.9.1.tar.xz
|
||||
RUN ln -s /earthbuild/zig-linux-$ZIG_ARCH-0.9.1/zig /bin/zig
|
||||
# zig builtins wasm tests
|
||||
RUN apt -y install build-essential
|
||||
# llvm
|
||||
RUN apt -y install lsb-release software-properties-common gnupg
|
||||
RUN wget https://apt.llvm.org/llvm.sh
|
||||
RUN chmod +x llvm.sh
|
||||
RUN ./llvm.sh 13
|
||||
RUN ln -s /usr/bin/clang-13 /usr/bin/clang
|
||||
# use lld as linker
|
||||
RUN ln -s /usr/bin/lld-13 /usr/bin/ld.lld
|
||||
ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native"
|
||||
RUN apt -y install libssl-dev
|
||||
RUN OPENSSL_NO_VENDOR=1 cargo install wasm-pack
|
||||
# sccache
|
||||
RUN cargo install sccache
|
||||
RUN sccache -V
|
||||
ENV RUSTC_WRAPPER=/usr/local/cargo/bin/sccache
|
||||
ENV SCCACHE_DIR=/earthbuild/sccache_dir
|
||||
ENV CARGO_INCREMENTAL=0 # no need to recompile package when using new function
|
||||
|
||||
copy-dirs:
|
||||
ARG ZIG_ARCH
|
||||
FROM +install-zig-llvm --ZIG_ARCH=$ZIG_ARCH
|
||||
COPY --dir crates examples Cargo.toml Cargo.lock version.txt .cargo www rust-toolchain.toml ./
|
||||
|
||||
build-nightly-release:
|
||||
ARG RELEASE_FOLDER_NAME
|
||||
ARG RUSTFLAGS
|
||||
ARG ZIG_ARCH=x86_64
|
||||
FROM +copy-dirs --ZIG_ARCH=$ZIG_ARCH
|
||||
COPY --dir .git LICENSE LEGAL_DETAILS ci ./
|
||||
# version.txt is used by the CLI: roc --version
|
||||
RUN ./ci/write_version.sh
|
||||
RUN RUSTFLAGS=$RUSTFLAGS cargo build --profile=release-with-lto --locked --bin roc
|
||||
# strip debug info
|
||||
RUN strip ./target/release-with-lto/roc
|
||||
RUN ./ci/package_release.sh $RELEASE_FOLDER_NAME
|
||||
RUN ls
|
||||
SAVE ARTIFACT ./$RELEASE_FOLDER_NAME.tar.gz AS LOCAL $RELEASE_FOLDER_NAME.tar.gz
|
16
README.md
16
README.md
|
@ -4,15 +4,20 @@ Roc is not ready for a 0.1 release yet, but we do have:
|
|||
|
||||
- [**installation** guide](https://github.com/roc-lang/roc/tree/main/getting_started)
|
||||
- [**tutorial**](https://roc-lang.org/tutorial)
|
||||
- [**docs** for the standard library](https://www.roc-lang.org/builtins/Str)
|
||||
- [frequently asked questions](https://github.com/roc-lang/roc/blob/main/FAQ.md)
|
||||
- [Zulip chat](https://roc.zulipchat.com) for help, questions and discussions
|
||||
- [**docs** for the standard library](https://www.roc-lang.org/builtins)
|
||||
- [**examples**](https://github.com/roc-lang/examples/tree/main/examples)
|
||||
- [**faq**: frequently asked questions](https://github.com/roc-lang/roc/blob/main/FAQ.md)
|
||||
- [**group chat**](https://roc.zulipchat.com) for help, questions and discussions
|
||||
|
||||
If you'd like to contribute, check out [good first issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). Don't hesitate to ask for help on our [Zulip chat](https://roc.zulipchat.com), we're friendly!
|
||||
If you'd like to contribute, check out [good first issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). Don't hesitate to ask for help on our [group chat](https://roc.zulipchat.com), we're friendly!
|
||||
|
||||
## Sponsors
|
||||
|
||||
We are very grateful for our corporate sponsors [Vendr](https://www.vendr.com/), [RWX](https://www.rwx.com), and [Tweede golf](https://tweedegolf.nl/en).
|
||||
You can 💜 **sponsor** 💜 Roc on:
|
||||
- [GitHub](https://github.com/sponsors/roc-lang)
|
||||
- [Liberapay](https://liberapay.com/roc_lang)
|
||||
|
||||
We are very grateful for our corporate sponsors [Vendr](https://www.vendr.com/), [RWX](https://www.rwx.com), and [Tweede golf](https://tweedegolf.nl/en):
|
||||
|
||||
[<img src="https://user-images.githubusercontent.com/1094080/223597445-81755626-a080-4299-a38c-3c92e7548489.png" height="60" alt="Vendr logo"/>](https://www.vendr.com)
|
||||
|
||||
|
@ -24,6 +29,7 @@ If you would like your company to become a corporate sponsor of Roc's developmen
|
|||
|
||||
We'd also like to express our gratitude to each and every one of our fantastic [GitHub sponsors](https://github.com/sponsors/roc-lang/)! A special thanks to those sponsoring $25/month or more:
|
||||
|
||||
* [Ivo Balbaert](https://github.com/Ivo-Balbaert)
|
||||
* [Lucas Rosa](https://github.com/rvcas)
|
||||
* [Jonas Schell](https://github.com/Ocupe)
|
||||
* [Christopher Dolan](https://github.com/cdolan)
|
||||
|
|
40
ci/basic_nightly_test.sh
Executable file
40
ci/basic_nightly_test.sh
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||
set -euxo pipefail
|
||||
|
||||
# if to prevent unset vars errror
|
||||
if [ -n "$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$")" ]; then
|
||||
|
||||
# Remove everything in this dir except the tar and ci folder.
|
||||
# We want to test like a user who would have downloaded the release, so we clean up all files from the repo checkout.
|
||||
to_delete=$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$")
|
||||
|
||||
for file_or_dir in $to_delete
|
||||
do
|
||||
echo "Removing: $file_or_dir"
|
||||
rm -rf "$file_or_dir"
|
||||
done
|
||||
fi
|
||||
|
||||
# decompress the tar
|
||||
ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
|
||||
|
||||
# delete tar
|
||||
ls | grep "roc_nightly.*tar\.gz" | xargs rm -rf
|
||||
|
||||
# rename nightly folder
|
||||
mv roc_nightly* roc_nightly
|
||||
|
||||
cd roc_nightly
|
||||
|
||||
# test roc hello world
|
||||
./roc examples/helloWorld.roc
|
||||
|
||||
./roc examples/platform-switching/rocLovesRust.roc
|
||||
|
||||
./roc examples/platform-switching/rocLovesZig.roc
|
||||
|
||||
./roc examples/platform-switching/rocLovesC.roc
|
||||
|
||||
cd ..
|
|
@ -10,6 +10,7 @@ version.workspace = true
|
|||
[dependencies]
|
||||
roc_builtins = { path = "../compiler/builtins" }
|
||||
roc_can = { path = "../compiler/can" }
|
||||
roc_checkmate = { path = "../compiler/checkmate" }
|
||||
roc_collections = { path = "../compiler/collections" }
|
||||
roc_error_macros = { path = "../error_macros" }
|
||||
roc_load = { path = "../compiler/load" }
|
||||
|
@ -20,6 +21,7 @@ roc_problem = { path = "../compiler/problem" }
|
|||
roc_region = { path = "../compiler/region" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_solve = { path = "../compiler/solve" }
|
||||
roc_solve_schema = { path = "../compiler/solve_schema" }
|
||||
roc_target = { path = "../compiler/roc_target" }
|
||||
roc_types = { path = "../compiler/types" }
|
||||
roc_unify = { path = "../compiler/unify" }
|
||||
|
|
|
@ -59,7 +59,7 @@ impl From<ModuleError> for ASTError {
|
|||
impl From<(Region, Loc<Ident>)> for ASTError {
|
||||
fn from(ident_exists_err: (Region, Loc<Ident>)) -> Self {
|
||||
Self::IdentExistsError {
|
||||
msg: format!("{:?}", ident_exists_err),
|
||||
msg: format!("{ident_exists_err:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ impl From<(Region, Loc<Ident>)> for ASTError {
|
|||
impl<'a> From<SyntaxError<'a>> for ASTError {
|
||||
fn from(syntax_err: SyntaxError) -> Self {
|
||||
Self::SyntaxErrorNoBacktrace {
|
||||
msg: format!("{:?}", syntax_err),
|
||||
msg: format!("{syntax_err:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,8 +94,7 @@ pub fn aliases() -> MutMap<Symbol, BuiltinAlias> {
|
|||
let mut add_alias = |symbol, alias| {
|
||||
debug_assert!(
|
||||
!aliases.contains_key(&symbol),
|
||||
"Duplicate alias definition for {:?}",
|
||||
symbol
|
||||
"Duplicate alias definition for {symbol:?}"
|
||||
);
|
||||
|
||||
// TODO instead of using Region::zero for all of these,
|
||||
|
|
|
@ -2065,7 +2065,7 @@ pub mod test_constrain {
|
|||
|
||||
assert_eq!(actual_str, expected_str);
|
||||
}
|
||||
Err(e) => panic!("syntax error {:?}", e),
|
||||
Err(e) => panic!("syntax error {e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ fn expr2_to_string_helper(
|
|||
);
|
||||
}
|
||||
Expr2::Call { .. } => {
|
||||
let _ = write!(out_string, "Call({:?})", expr2);
|
||||
let _ = write!(out_string, "Call({expr2:?})");
|
||||
}
|
||||
Expr2::Closure { args, .. } => {
|
||||
out_string.push_str("Closure:\n");
|
||||
|
@ -148,7 +148,7 @@ fn expr2_to_string_helper(
|
|||
}
|
||||
}
|
||||
&Expr2::Var { .. } => {
|
||||
let _ = write!(out_string, "{:?}", expr2);
|
||||
let _ = write!(out_string, "{expr2:?}");
|
||||
}
|
||||
Expr2::RuntimeError { .. } => {
|
||||
out_string.push_str("RuntimeError\n");
|
||||
|
|
|
@ -665,37 +665,25 @@ pub fn expr_to_expr2<'a>(
|
|||
ident,
|
||||
} => canonicalize_lookup(env, scope, module_name, ident, region),
|
||||
|
||||
ParensAround(sub_expr) => expr_to_expr2(env, scope, sub_expr, region),
|
||||
|
||||
// Below this point, we shouln't see any of these nodes anymore because
|
||||
// operator desugaring should have removed them!
|
||||
bad_expr @ ParensAround(_) => {
|
||||
panic!(
|
||||
"A ParensAround did not get removed during operator desugaring somehow: {:#?}",
|
||||
bad_expr
|
||||
);
|
||||
}
|
||||
bad_expr @ SpaceBefore(_, _) => {
|
||||
panic!(
|
||||
"A SpaceBefore did not get removed during operator desugaring somehow: {:#?}",
|
||||
bad_expr
|
||||
"A SpaceBefore did not get removed during operator desugaring somehow: {bad_expr:#?}"
|
||||
);
|
||||
}
|
||||
bad_expr @ SpaceAfter(_, _) => {
|
||||
panic!(
|
||||
"A SpaceAfter did not get removed during operator desugaring somehow: {:#?}",
|
||||
bad_expr
|
||||
"A SpaceAfter did not get removed during operator desugaring somehow: {bad_expr:#?}"
|
||||
);
|
||||
}
|
||||
bad_expr @ BinOps { .. } => {
|
||||
panic!(
|
||||
"A binary operator chain did not get desugared somehow: {:#?}",
|
||||
bad_expr
|
||||
);
|
||||
panic!("A binary operator chain did not get desugared somehow: {bad_expr:#?}");
|
||||
}
|
||||
bad_expr @ UnaryOp(_, _) => {
|
||||
panic!(
|
||||
"A unary operator did not get desugared somehow: {:#?}",
|
||||
bad_expr
|
||||
);
|
||||
panic!("A unary operator did not get desugared somehow: {bad_expr:#?}");
|
||||
}
|
||||
|
||||
rest => todo!("not yet implemented {:?}", rest),
|
||||
|
|
|
@ -227,7 +227,7 @@ pub fn update_str_expr(
|
|||
Expr2::Str(old_pool_str) => Either::OldPoolStr(*old_pool_str),
|
||||
other => UnexpectedASTNodeSnafu {
|
||||
required_node_type: "SmallStr or Str",
|
||||
encountered_node_type: format!("{:?}", other),
|
||||
encountered_node_type: format!("{other:?}"),
|
||||
}
|
||||
.fail()?,
|
||||
};
|
||||
|
|
|
@ -102,8 +102,7 @@ impl<'a> Env<'a> {
|
|||
) -> Result<Symbol, RuntimeError> {
|
||||
debug_assert!(
|
||||
!module_name.is_empty(),
|
||||
"Called env.qualified_lookup with an unqualified ident: {:?}",
|
||||
ident
|
||||
"Called env.qualified_lookup with an unqualified ident: {ident:?}"
|
||||
);
|
||||
|
||||
let module_name: ModuleName = module_name.into();
|
||||
|
|
|
@ -68,7 +68,7 @@ impl<T> Clone for NodeId<T> {
|
|||
fn clone(&self) -> Self {
|
||||
NodeId {
|
||||
index: self.index,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ impl Pool {
|
|||
|
||||
NodeId {
|
||||
index,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
} else {
|
||||
todo!("pool ran out of capacity. TODO reallocate the nodes pointer to map to a bigger space. Can use mremap on Linux, but must memcpy lots of bytes on macOS and Windows.");
|
||||
|
|
|
@ -45,7 +45,7 @@ impl PoolStr {
|
|||
PoolStr {
|
||||
first_node_id: NodeId {
|
||||
index: 0,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
},
|
||||
len: 0,
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
|
|||
PoolVec {
|
||||
first_node_id: NodeId {
|
||||
index: 0,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
},
|
||||
len: 0,
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ where
|
|||
// Advance the node pointer to the next node in the current page
|
||||
self.current_node_id = NodeId {
|
||||
index: index + 1,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
self.len_remaining = len_remaining - 1;
|
||||
|
||||
|
@ -237,7 +237,7 @@ where
|
|||
// Advance the node pointer to the next node in the current page
|
||||
self.current_node_id = NodeId {
|
||||
index: index + 1,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
self.len_remaining = len_remaining - 1;
|
||||
|
||||
|
@ -288,7 +288,7 @@ impl<T> Iterator for PoolVecIterNodeIds<T> {
|
|||
// Advance the node pointer to the next node in the current page
|
||||
self.current_node_id = NodeId {
|
||||
index: index + 1,
|
||||
_phantom: PhantomData::default(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
self.len_remaining = len_remaining - 1;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ pub fn load_module(
|
|||
) -> LoadedModule {
|
||||
let load_config = LoadConfig {
|
||||
target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused
|
||||
function_kind: roc_solve::FunctionKind::LambdaSet, // TODO the editor may need to dynamically change this
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
|
@ -25,14 +26,8 @@ pub fn load_module(
|
|||
match loaded {
|
||||
Ok(x) => x,
|
||||
Err(roc_load::LoadingProblem::FormattedReport(report)) => {
|
||||
panic!(
|
||||
"Failed to load module from src_file: {:?}. Report: {}",
|
||||
src_file, report
|
||||
);
|
||||
panic!("Failed to load module from src_file: {src_file:?}. Report: {report}");
|
||||
}
|
||||
Err(e) => panic!(
|
||||
"Failed to load module from src_file {:?}: {:?}",
|
||||
src_file, e
|
||||
),
|
||||
Err(e) => panic!("Failed to load module from src_file {src_file:?}: {e:?}"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
#![allow(dead_code)]
|
||||
use bumpalo::Bump;
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_checkmate::with_checkmate;
|
||||
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_solve::module::Solved;
|
||||
use roc_solve_schema::UnificationMode;
|
||||
use roc_types::subs::{
|
||||
self, AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields,
|
||||
Subs, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||
|
@ -17,9 +19,8 @@ use roc_types::types::{
|
|||
RecordField,
|
||||
};
|
||||
use roc_unify::unify::unify;
|
||||
use roc_unify::unify::Env as UEnv;
|
||||
use roc_unify::unify::Mode;
|
||||
use roc_unify::unify::Unified::*;
|
||||
use roc_unify::Env as UEnv;
|
||||
|
||||
use crate::constrain::{Constraint, PresenceConstraint};
|
||||
use crate::lang::core::types::Type2;
|
||||
|
@ -228,10 +229,13 @@ fn solve<'a>(
|
|||
);
|
||||
|
||||
match unify(
|
||||
&mut UEnv::new(subs),
|
||||
&mut with_checkmate!({
|
||||
on => UEnv::new(subs, None),
|
||||
off => UEnv::new(subs),
|
||||
}),
|
||||
actual,
|
||||
expected,
|
||||
Mode::EQ,
|
||||
UnificationMode::EQ,
|
||||
Polarity::OF_VALUE,
|
||||
) {
|
||||
Success {
|
||||
|
@ -326,10 +330,13 @@ fn solve<'a>(
|
|||
);
|
||||
|
||||
match unify(
|
||||
&mut UEnv::new(subs),
|
||||
&mut with_checkmate!({
|
||||
on => UEnv::new(subs, None),
|
||||
off => UEnv::new(subs),
|
||||
}),
|
||||
actual,
|
||||
expected,
|
||||
Mode::EQ,
|
||||
UnificationMode::EQ,
|
||||
Polarity::OF_VALUE,
|
||||
) {
|
||||
Success {
|
||||
|
@ -402,10 +409,13 @@ fn solve<'a>(
|
|||
|
||||
// TODO(ayazhafiz): presence constraints for Expr2/Type2
|
||||
match unify(
|
||||
&mut UEnv::new(subs),
|
||||
&mut with_checkmate!({
|
||||
on => UEnv::new(subs, None),
|
||||
off => UEnv::new(subs),
|
||||
}),
|
||||
actual,
|
||||
expected,
|
||||
Mode::EQ,
|
||||
UnificationMode::EQ,
|
||||
Polarity::OF_PATTERN,
|
||||
) {
|
||||
Success {
|
||||
|
@ -606,7 +616,10 @@ fn solve<'a>(
|
|||
let result = offenders.len();
|
||||
|
||||
if result > 0 {
|
||||
dbg!(&subs, &offenders, &let_con.def_types);
|
||||
eprintln!(
|
||||
"subs: {:?}\n\noffenders: {:?}\n\nlet_con.def_types: {:?}\n",
|
||||
&subs, &offenders, &let_con.def_types
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -715,10 +728,13 @@ fn solve<'a>(
|
|||
let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty);
|
||||
|
||||
match unify(
|
||||
&mut UEnv::new(subs),
|
||||
&mut with_checkmate!({
|
||||
on => UEnv::new(subs, None),
|
||||
off => UEnv::new(subs),
|
||||
}),
|
||||
actual,
|
||||
includes,
|
||||
Mode::PRESENT,
|
||||
UnificationMode::PRESENT,
|
||||
Polarity::OF_PATTERN,
|
||||
) {
|
||||
Success {
|
||||
|
@ -1486,6 +1502,8 @@ fn adjust_rank_content(
|
|||
rank
|
||||
}
|
||||
|
||||
ErasedLambda => group_rank,
|
||||
|
||||
RangedNumber(_vars) => group_rank,
|
||||
}
|
||||
}
|
||||
|
@ -1666,6 +1684,7 @@ fn instantiate_rigids_help(
|
|||
}
|
||||
}
|
||||
|
||||
ErasedLambda => {}
|
||||
RangedNumber(_vars) => {}
|
||||
}
|
||||
|
||||
|
@ -1978,6 +1997,12 @@ fn deep_copy_var_help(
|
|||
copy
|
||||
}
|
||||
|
||||
ErasedLambda => {
|
||||
subs.set(copy, make_descriptor(ErasedLambda));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(vars) => {
|
||||
let new_content = RangedNumber(vars);
|
||||
|
||||
|
|
|
@ -93,18 +93,18 @@ pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), Str
|
|||
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
|
||||
// I don't have the patience to debug this right now, so let's leave it for another day...
|
||||
// TODO: fix PartialEq impl on ast types
|
||||
if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) {
|
||||
if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") {
|
||||
let mut fail_file = file.clone();
|
||||
fail_file.set_extension("roc-format-failed");
|
||||
std::fs::write(&fail_file, buf.as_str()).unwrap();
|
||||
|
||||
let mut before_file = file.clone();
|
||||
before_file.set_extension("roc-format-failed-ast-before");
|
||||
std::fs::write(&before_file, format!("{:#?}\n", ast_normalized)).unwrap();
|
||||
std::fs::write(&before_file, format!("{ast_normalized:#?}\n")).unwrap();
|
||||
|
||||
let mut after_file = file.clone();
|
||||
after_file.set_extension("roc-format-failed-ast-after");
|
||||
std::fs::write(&after_file, format!("{:#?}\n", reparsed_ast_normalized)).unwrap();
|
||||
std::fs::write(&after_file, format!("{reparsed_ast_normalized:#?}\n")).unwrap();
|
||||
|
||||
internal_error!(
|
||||
"Formatting bug; formatting didn't reparse as the same tree\n\n\
|
||||
|
|
|
@ -20,6 +20,7 @@ use roc_load::{ExpectMetadata, Threading};
|
|||
use roc_mono::ir::OptLevel;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use roc_packaging::tarball::Compression;
|
||||
use roc_target::Target;
|
||||
use std::env;
|
||||
use std::ffi::{CString, OsStr, OsString};
|
||||
use std::io;
|
||||
|
@ -28,11 +29,8 @@ use std::os::raw::{c_char, c_int};
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::time::Instant;
|
||||
use strum::{EnumIter, IntoEnumIterator, IntoStaticStr};
|
||||
use target_lexicon::BinaryFormat;
|
||||
use target_lexicon::{
|
||||
Architecture, Environment, OperatingSystem, Triple, Vendor, X86_32Architecture,
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use tempfile::TempDir;
|
||||
|
||||
|
@ -387,7 +385,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
|
|||
#[cfg(not(windows))]
|
||||
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||
use roc_build::program::report_problems_monomorphized;
|
||||
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
|
||||
use roc_load::{ExecutionMode, FunctionKind, LoadConfig, LoadMonomorphizedError};
|
||||
use roc_packaging::cache;
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
|
@ -416,12 +414,10 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
|||
match matches.value_source(ROC_FILE) {
|
||||
Some(ValueSource::DefaultValue) => {
|
||||
eprintln!(
|
||||
"\nThe current directory ({}) does not contain a {} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n",
|
||||
current_dir_string,
|
||||
DEFAULT_ROC_FILENAME
|
||||
"\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"
|
||||
)
|
||||
}
|
||||
_ => eprintln!("\nThis file was not found: {}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n", expected_file_path_string),
|
||||
_ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
|
@ -431,10 +427,13 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
|||
let target = &triple;
|
||||
let opt_level = opt_level;
|
||||
let target_info = TargetInfo::from(target);
|
||||
// TODO may need to determine this dynamically based on dev builds.
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
|
@ -565,16 +564,13 @@ pub fn build(
|
|||
match matches.value_source(ROC_FILE) {
|
||||
Some(ValueSource::DefaultValue) => {
|
||||
eprintln!(
|
||||
"\nThe current directory ({}) does not contain a {} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n",
|
||||
current_dir_string,
|
||||
DEFAULT_ROC_FILENAME
|
||||
"\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let mut error_lines = Vec::new();
|
||||
error_lines.push(format!(
|
||||
"This file was not found: {}",
|
||||
expected_file_path_string
|
||||
"This file was not found: {expected_file_path_string}"
|
||||
));
|
||||
// Add some additional hints if run as `roc [FILENAME]`.
|
||||
if matches.subcommand().is_none() {
|
||||
|
@ -584,8 +580,7 @@ pub fn build(
|
|||
nearest_match(possible_typo, subcommands)
|
||||
{
|
||||
error_lines.push(format!(
|
||||
"Did you mean to use the {} subcommand?",
|
||||
nearest_command
|
||||
"Did you mean to use the {nearest_command} subcommand?"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1144,7 +1139,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
|
|||
);
|
||||
}
|
||||
|
||||
let path = PathBuf::from(format!("/proc/self/fd/{}", fd));
|
||||
let path = PathBuf::from(format!("/proc/self/fd/{fd}"));
|
||||
|
||||
std::fs::write(&path, binary_bytes)?;
|
||||
|
||||
|
@ -1177,7 +1172,7 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
|
|||
Ok(ExecutableFile::OnDisk(temp_dir, app_path_buf))
|
||||
}
|
||||
|
||||
#[cfg(all(target_family = "windows"))]
|
||||
#[cfg(target_family = "windows")]
|
||||
fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<ExecutableFile> {
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
|
@ -1275,83 +1270,3 @@ fn run_wasm<I: Iterator<Item = S>, S: AsRef<[u8]>>(wasm_path: &std::path::Path,
|
|||
fn run_wasm<I: Iterator<Item = S>, S: AsRef<[u8]>>(_wasm_path: &std::path::Path, _args: I) {
|
||||
println!("Running wasm files is not supported on this target.");
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, EnumIter, IntoStaticStr, PartialEq, Eq, Default)]
|
||||
pub enum Target {
|
||||
#[strum(serialize = "system")]
|
||||
#[default]
|
||||
System,
|
||||
#[strum(serialize = "linux32")]
|
||||
Linux32,
|
||||
#[strum(serialize = "linux64")]
|
||||
Linux64,
|
||||
#[strum(serialize = "windows64")]
|
||||
Windows64,
|
||||
#[strum(serialize = "wasm32")]
|
||||
Wasm32,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn to_triple(self) -> Triple {
|
||||
use Target::*;
|
||||
|
||||
match self {
|
||||
System => Triple::host(),
|
||||
Linux32 => Triple {
|
||||
architecture: Architecture::X86_32(X86_32Architecture::I386),
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
environment: Environment::Musl,
|
||||
binary_format: BinaryFormat::Elf,
|
||||
},
|
||||
Linux64 => Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Linux,
|
||||
environment: Environment::Musl,
|
||||
binary_format: BinaryFormat::Elf,
|
||||
},
|
||||
Windows64 => Triple {
|
||||
architecture: Architecture::X86_64,
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Windows,
|
||||
environment: Environment::Gnu,
|
||||
binary_format: BinaryFormat::Coff,
|
||||
},
|
||||
Wasm32 => Triple {
|
||||
architecture: Architecture::Wasm32,
|
||||
vendor: Vendor::Unknown,
|
||||
operating_system: OperatingSystem::Wasi,
|
||||
environment: Environment::Unknown,
|
||||
binary_format: BinaryFormat::Wasm,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Target> for Triple {
|
||||
fn from(target: &Target) -> Self {
|
||||
target.to_triple()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Target {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", Into::<&'static str>::into(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Target {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(string: &str) -> Result<Self, Self::Err> {
|
||||
match string {
|
||||
"system" => Ok(Target::System),
|
||||
"linux32" => Ok(Target::Linux32),
|
||||
"linux64" => Ok(Target::Linux64),
|
||||
"windows64" => Ok(Target::Windows64),
|
||||
"wasm32" => Ok(Target::Wasm32),
|
||||
_ => Err(format!("Roc does not know how to compile to {}", string)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,18 @@
|
|||
use roc_build::link::LinkType;
|
||||
use roc_build::program::{check_file, CodeGenBackend};
|
||||
use roc_cli::{
|
||||
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DEV,
|
||||
CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST,
|
||||
CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET,
|
||||
FLAG_TIME, GLUE_DIR, GLUE_SPEC, ROC_FILE,
|
||||
build_app, format, test, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DEV, CMD_DOCS,
|
||||
CMD_EDIT, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION,
|
||||
DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_DEV, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME,
|
||||
GLUE_DIR, GLUE_SPEC, ROC_FILE,
|
||||
};
|
||||
use roc_docs::generate_docs_html;
|
||||
use roc_error_macros::user_error;
|
||||
use roc_gen_dev::AssemblyBackendMode;
|
||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
||||
use roc_load::{LoadingProblem, Threading};
|
||||
use roc_load::{FunctionKind, LoadingProblem, Threading};
|
||||
use roc_packaging::cache::{self, RocCacheDir};
|
||||
use roc_target::Target;
|
||||
use std::fs::{self, FileType};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -122,10 +123,12 @@ fn main() -> io::Result<()> {
|
|||
.get_one::<String>(FLAG_TARGET)
|
||||
.and_then(|s| Target::from_str(s).ok())
|
||||
.unwrap_or_default();
|
||||
let function_kind = FunctionKind::LambdaSet;
|
||||
roc_linker::generate_stub_lib(
|
||||
input_path,
|
||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||
&target.to_triple(),
|
||||
function_kind,
|
||||
)
|
||||
}
|
||||
Some((CMD_BUILD, matches)) => {
|
||||
|
@ -200,12 +203,12 @@ fn main() -> io::Result<()> {
|
|||
}
|
||||
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
print!("{}", report);
|
||||
print!("{report}");
|
||||
|
||||
Ok(1)
|
||||
}
|
||||
Err(other) => {
|
||||
panic!("build_file failed with error:\n{:?}", other);
|
||||
panic!("build_file failed with error:\n{other:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +275,7 @@ fn main() -> io::Result<()> {
|
|||
let format_exit_code = match format(roc_files, format_mode) {
|
||||
Ok(_) => 0,
|
||||
Err(message) => {
|
||||
eprintln!("{}", message);
|
||||
eprintln!("{message}");
|
||||
1
|
||||
}
|
||||
};
|
||||
|
|
|
@ -90,7 +90,7 @@ mod cli_run {
|
|||
|
||||
// e.g. "1 error and 0 warnings found in 123 ms."
|
||||
let (before_first_digit, _) = err.split_at(err.rfind("found in ").unwrap());
|
||||
let err = format!("{}found in <ignored for test> ms.", before_first_digit);
|
||||
let err = format!("{before_first_digit}found in <ignored for test> ms.");
|
||||
|
||||
// make paths consistent
|
||||
let err = err.replace('\\', "/");
|
||||
|
@ -230,7 +230,7 @@ mod cli_run {
|
|||
what: _,
|
||||
xwhat,
|
||||
} = error;
|
||||
println!("Valgrind Error: {}\n", kind);
|
||||
println!("Valgrind Error: {kind}\n");
|
||||
|
||||
if let Some(ValgrindErrorXWhat {
|
||||
text,
|
||||
|
@ -238,14 +238,14 @@ mod cli_run {
|
|||
leakedblocks: _,
|
||||
}) = xwhat
|
||||
{
|
||||
println!(" {}", text);
|
||||
println!(" {text}");
|
||||
}
|
||||
}
|
||||
panic!("Valgrind reported memory errors");
|
||||
}
|
||||
} else {
|
||||
let exit_code = match valgrind_out.status.code() {
|
||||
Some(code) => format!("exit code {}", code),
|
||||
Some(code) => format!("exit code {code}"),
|
||||
None => "no exit code".to_string(),
|
||||
};
|
||||
|
||||
|
@ -301,7 +301,7 @@ mod cli_run {
|
|||
// e.g. "1 failed and 0 passed in 123 ms."
|
||||
if let Some(split) = actual.rfind("passed in ") {
|
||||
let (before_first_digit, _) = actual.split_at(split);
|
||||
actual = format!("{}passed in <ignored for test> ms.", before_first_digit);
|
||||
actual = format!("{before_first_digit}passed in <ignored for test> ms.");
|
||||
}
|
||||
|
||||
let self_path = file.display().to_string();
|
||||
|
@ -397,8 +397,7 @@ mod cli_run {
|
|||
"swiftui" | "rocLovesSwift" => {
|
||||
if cfg!(not(target_os = "macos")) {
|
||||
eprintln!(
|
||||
"WARNING: skipping testing example {} because it only works on MacOS.",
|
||||
roc_filename
|
||||
"WARNING: skipping testing example {roc_filename} because it only works on MacOS."
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
|
@ -409,8 +408,7 @@ mod cli_run {
|
|||
"rocLovesWebAssembly" => {
|
||||
// this is a web assembly example, but we don't test with JS at the moment
|
||||
eprintln!(
|
||||
"WARNING: skipping testing example {} because it only works in a browser!",
|
||||
roc_filename
|
||||
"WARNING: skipping testing example {roc_filename} because it only works in a browser!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -664,7 +662,7 @@ mod cli_run {
|
|||
fn hello_gui() {
|
||||
test_roc_app_slim(
|
||||
"examples/gui",
|
||||
"hello.roc",
|
||||
"helloBROKEN.roc",
|
||||
"hello-gui",
|
||||
"",
|
||||
UseValgrind::No,
|
||||
|
@ -677,7 +675,7 @@ mod cli_run {
|
|||
fn breakout() {
|
||||
test_roc_app_slim(
|
||||
"examples/gui/breakout",
|
||||
"breakout.roc",
|
||||
"breakoutBROKEN.roc",
|
||||
"breakout",
|
||||
"",
|
||||
UseValgrind::No,
|
||||
|
@ -689,7 +687,7 @@ mod cli_run {
|
|||
fn breakout_hello_gui() {
|
||||
test_roc_app_slim(
|
||||
"examples/gui/breakout",
|
||||
"hello-gui.roc",
|
||||
"hello-guiBROKEN.roc",
|
||||
"hello-gui",
|
||||
"",
|
||||
UseValgrind::No,
|
||||
|
@ -965,16 +963,14 @@ mod cli_run {
|
|||
match roc_filename {
|
||||
"QuicksortApp.roc" => {
|
||||
eprintln!(
|
||||
"WARNING: skipping testing benchmark {} because the test is broken right now!",
|
||||
roc_filename
|
||||
"WARNING: skipping testing benchmark {roc_filename} because the test is broken right now!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
"TestAStar.roc" => {
|
||||
if cfg!(feature = "wasm32-cli-run") {
|
||||
eprintln!(
|
||||
"WARNING: skipping testing benchmark {} because it currently does not work on wasm32 due to dictionaries.",
|
||||
roc_filename
|
||||
"WARNING: skipping testing benchmark {roc_filename} because it currently does not work on wasm32 due to dictionaries."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ mod editor_launch_test {
|
|||
|
||||
// The editor expects to be run from the root of the repo, so it can find the cli-platform to init a new project folder.
|
||||
env::set_current_dir(&root_dir)
|
||||
.unwrap_or_else(|_| panic!("Failed to set current dir to {:?}", root_dir));
|
||||
.unwrap_or_else(|_| panic!("Failed to set current dir to {root_dir:?}"));
|
||||
|
||||
let roc_binary_path = build_roc_bin(&["--features", "editor"]);
|
||||
|
||||
|
|
|
@ -6,20 +6,6 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -6,20 +6,6 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -6,20 +6,6 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -5,20 +5,6 @@ const expectEqual = testing.expectEqual;
|
|||
const expect = testing.expect;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -6,20 +6,6 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ main =
|
|||
|> Task.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
Expr : [
|
||||
Add Expr Expr,
|
||||
|
@ -100,35 +100,27 @@ constFolding = \e ->
|
|||
x1 = constFolding e1
|
||||
x2 = constFolding e2
|
||||
|
||||
when Pair x1 x2 is
|
||||
Pair (Val a) (Val b) ->
|
||||
Val (a + b)
|
||||
when x1 is
|
||||
Val a ->
|
||||
when x2 is
|
||||
Val b -> Val (a + b)
|
||||
Add (Val b) x | Add x (Val b) -> Add (Val (a + b)) x
|
||||
_ -> Add x1 x2
|
||||
|
||||
Pair (Val a) (Add (Val b) x) ->
|
||||
Add (Val (a + b)) x
|
||||
|
||||
Pair (Val a) (Add x (Val b)) ->
|
||||
Add (Val (a + b)) x
|
||||
|
||||
Pair y1 y2 ->
|
||||
Add y1 y2
|
||||
_ -> Add x1 x2
|
||||
|
||||
Mul e1 e2 ->
|
||||
x1 = constFolding e1
|
||||
x2 = constFolding e2
|
||||
|
||||
when Pair x1 x2 is
|
||||
Pair (Val a) (Val b) ->
|
||||
Val (a * b)
|
||||
when x1 is
|
||||
Val a ->
|
||||
when x2 is
|
||||
Val b -> Val (a * b)
|
||||
Mul (Val b) x | Mul x (Val b) -> Mul (Val (a * b)) x
|
||||
_ -> Mul x1 x2
|
||||
|
||||
Pair (Val a) (Mul (Val b) x) ->
|
||||
Mul (Val (a * b)) x
|
||||
|
||||
Pair (Val a) (Mul x (Val b)) ->
|
||||
Mul (Val (a * b)) x
|
||||
|
||||
Pair y1 y2 ->
|
||||
Add y1 y2
|
||||
_ -> Mul x1 x2
|
||||
|
||||
_ ->
|
||||
e
|
||||
|
|
|
@ -23,21 +23,16 @@ main =
|
|||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
|
||||
nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr
|
||||
nestHelp = \s, f, m, x -> when m is
|
||||
0 -> Task.succeed x
|
||||
_ ->
|
||||
w <- Task.after (f (s - m) x)
|
||||
nestHelp s f (m - 1) w
|
||||
|
||||
nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr
|
||||
nest = \f, n, e -> Task.loop { s: n, f, m: n, x: e } nestHelp
|
||||
|
||||
State : { s : I64, f : I64, Expr -> IO Expr, m : I64, x : Expr }
|
||||
|
||||
nestHelp : State -> IO [Step State, Done Expr]
|
||||
nestHelp = \{ s, f, m, x } ->
|
||||
when m is
|
||||
0 -> Task.succeed (Done x)
|
||||
_ ->
|
||||
w <- Task.after (f (s - m) x)
|
||||
|
||||
Task.succeed (Step { s, f, m: (m - 1), x: w })
|
||||
nest = \f, n, e -> nestHelp n f n e
|
||||
|
||||
Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr]
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ main =
|
|||
when inputResult is
|
||||
Ok n ->
|
||||
queens n # original koka 13
|
||||
|> Num.toStr
|
||||
|> Task.putLine
|
||||
|> Num.toStr
|
||||
|> Task.putLine
|
||||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
|
@ -21,7 +21,8 @@ ConsList a : [Nil, Cons a (ConsList a)]
|
|||
queens = \n -> length (findSolutions n n)
|
||||
|
||||
findSolutions = \n, k ->
|
||||
if k <= 0 then # should we use U64 as input type here instead?
|
||||
if k <= 0 then
|
||||
# should we use U64 as input type here instead?
|
||||
Cons Nil Nil
|
||||
else
|
||||
extend n Nil (findSolutions n (k - 1))
|
||||
|
@ -40,18 +41,18 @@ appendSafe = \k, soln, solns ->
|
|||
else
|
||||
appendSafe (k - 1) soln solns
|
||||
|
||||
|
||||
safe : I64, I64, ConsList I64 -> Bool
|
||||
safe = \queen, diagonal, xs ->
|
||||
when xs is
|
||||
Nil -> Bool.true
|
||||
Cons q t ->
|
||||
queen != q && queen != q + diagonal && queen != q - diagonal && safe queen (diagonal + 1) t
|
||||
|
||||
if queen != q && queen != q + diagonal && queen != q - diagonal then
|
||||
safe queen (diagonal + 1) t
|
||||
else
|
||||
Bool.false
|
||||
|
||||
length : ConsList a -> I64
|
||||
length = \xs ->
|
||||
dbg "length"
|
||||
lengthHelp xs 0
|
||||
|
||||
lengthHelp : ConsList a, I64 -> I64
|
||||
|
|
|
@ -23,7 +23,6 @@ main =
|
|||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
|
||||
sort : List I64 -> List I64
|
||||
sort = \list ->
|
||||
|
|
|
@ -93,9 +93,15 @@ ins = \tree, kx, vx ->
|
|||
|
||||
Node Black a ky vy b ->
|
||||
if lt kx ky then
|
||||
(if isRed a then balance1 (Node Black Leaf ky vy b) (ins a kx vx) else Node Black (ins a kx vx) ky vy b)
|
||||
if isRed a then
|
||||
balance1 (Node Black Leaf ky vy b) (ins a kx vx)
|
||||
else
|
||||
Node Black (ins a kx vx) ky vy b
|
||||
else if lt ky kx then
|
||||
(if isRed b then balance2 (Node Black a ky vy Leaf) (ins b kx vx) else Node Black a ky vy (ins b kx vx))
|
||||
if isRed b then
|
||||
balance2 (Node Black a ky vy Leaf) (ins b kx vx)
|
||||
else
|
||||
Node Black a ky vy (ins b kx vx)
|
||||
else
|
||||
Node Black a kx vx b
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ main =
|
|||
|
||||
Err GetIntError ->
|
||||
Task.putLine "Error: Failed to get Integer from stdin."
|
||||
|
||||
|
||||
boom : Str -> a
|
||||
boom = \_ -> boom ""
|
||||
|
|
|
@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual;
|
|||
const expect = testing.expect;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ platform "benchmarks"
|
|||
imports [Task.{ Task }]
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost : Task {} []
|
||||
mainForHost = main
|
||||
|
|
|
@ -6,20 +6,6 @@ const testing = std.testing;
|
|||
const expectEqual = testing.expectEqual;
|
||||
const expect = testing.expect;
|
||||
|
||||
comptime {
|
||||
// This is a workaround for https://github.com/ziglang/zig/issues/8218
|
||||
// which is only necessary on macOS.
|
||||
//
|
||||
// Once that issue is fixed, we can undo the changes in
|
||||
// 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing
|
||||
// -fcompiler-rt in link.rs instead of doing this. Note that this
|
||||
// workaround is present in many host.zig files, so make sure to undo
|
||||
// it everywhere!
|
||||
if (builtin.os.tag == .macos) {
|
||||
_ = @import("compiler_rt");
|
||||
}
|
||||
}
|
||||
|
||||
const Align = 2 * @alignOf(usize);
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque;
|
||||
extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque;
|
||||
|
|
|
@ -25,8 +25,7 @@ fn exec_bench_w_input<T: Measurement>(
|
|||
|
||||
assert!(
|
||||
compile_out.status.success(),
|
||||
"build ended with bad status {:?}",
|
||||
compile_out
|
||||
"build ended with bad status {compile_out:?}"
|
||||
);
|
||||
|
||||
check_cmd_output(file, stdin_str, executable_filename, expected_ending);
|
||||
|
@ -58,10 +57,7 @@ fn check_cmd_output(
|
|||
};
|
||||
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?}",
|
||||
expected_ending, out
|
||||
);
|
||||
panic!("expected output to end with {expected_ending:?} but instead got {out:#?}");
|
||||
}
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
@ -96,7 +92,7 @@ fn bench_cmd<T: Measurement>(
|
|||
}
|
||||
|
||||
if let Some(bench_group) = bench_group_opt {
|
||||
bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| {
|
||||
bench_group.bench_function(&format!("Benchmarking {executable_filename:?}"), |b| {
|
||||
b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[], []))
|
||||
});
|
||||
} else {
|
||||
|
|
|
@ -11,7 +11,6 @@ use std::env;
|
|||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, ExitStatus, Stdio};
|
||||
use std::sync::Mutex;
|
||||
|
@ -30,68 +29,13 @@ where
|
|||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let roc_binary_path = build_roc_bin_cached();
|
||||
|
||||
run_roc_with_stdin_and_env(&roc_binary_path, args, stdin_vals, extra_env)
|
||||
}
|
||||
|
||||
// If we don't already have a /target/release/roc, build it!
|
||||
pub fn build_roc_bin_cached() -> PathBuf {
|
||||
let roc_binary_path = path_to_roc_binary();
|
||||
|
||||
if !roc_binary_path.exists() {
|
||||
build_roc_bin(&[]);
|
||||
}
|
||||
|
||||
roc_binary_path
|
||||
}
|
||||
|
||||
pub fn build_roc_bin(extra_args: &[&str]) -> PathBuf {
|
||||
let roc_binary_path = path_to_roc_binary();
|
||||
|
||||
// Remove the /target/release/roc part
|
||||
let root_project_dir = roc_binary_path
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap();
|
||||
|
||||
// cargo build --bin roc
|
||||
// (with --release iff the test is being built with --release)
|
||||
let mut args = if cfg!(debug_assertions) {
|
||||
vec!["build", "--bin", "roc"]
|
||||
} else {
|
||||
vec!["build", "--release", "--bin", "roc"]
|
||||
};
|
||||
|
||||
args.extend(extra_args);
|
||||
|
||||
let mut cargo_cmd = cargo();
|
||||
|
||||
cargo_cmd.current_dir(root_project_dir).args(&args);
|
||||
|
||||
let cargo_cmd_str = format!("{:?}", cargo_cmd);
|
||||
|
||||
let cargo_output = cargo_cmd.output().unwrap();
|
||||
|
||||
if !cargo_output.status.success() {
|
||||
panic!(
|
||||
"The following cargo command failed:\n\n {}\n\n stdout was:\n\n {}\n\n stderr was:\n\n {}\n",
|
||||
cargo_cmd_str,
|
||||
String::from_utf8(cargo_output.stdout).unwrap(),
|
||||
String::from_utf8(cargo_output.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
roc_binary_path
|
||||
run_roc_with_stdin_and_env(args, stdin_vals, extra_env)
|
||||
}
|
||||
|
||||
// Since glue is always compiling the same plugin, it can not be run in parallel.
|
||||
// That would lead to a race condition in writing the output shared library.
|
||||
// Thus, all calls to glue in a test are made sequential.
|
||||
// TODO: In the future, look into compiling the shared libary once and then caching it.
|
||||
// TODO: In the future, look into compiling the shared library once and then caching it.
|
||||
static GLUE_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
pub fn run_glue<I, S>(args: I) -> Out
|
||||
|
@ -101,7 +45,7 @@ where
|
|||
{
|
||||
let _guard = GLUE_LOCK.lock().unwrap();
|
||||
|
||||
run_roc_with_stdin(&path_to_roc_binary(), args, &[])
|
||||
run_roc_with_stdin(args, &[])
|
||||
}
|
||||
|
||||
pub fn has_error(stderr: &str) -> bool {
|
||||
|
@ -170,16 +114,15 @@ pub fn strip_colors(str: &str) -> String {
|
|||
.replace(ANSI_STYLE_CODES.color_reset, "")
|
||||
}
|
||||
|
||||
pub fn run_roc_with_stdin<I, S>(path: &Path, args: I, stdin_vals: &[&str]) -> Out
|
||||
pub fn run_roc_with_stdin<I, S>(args: I, stdin_vals: &[&str]) -> Out
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
run_roc_with_stdin_and_env(path, args, stdin_vals, &[])
|
||||
run_roc_with_stdin_and_env(args, stdin_vals, &[])
|
||||
}
|
||||
|
||||
pub fn run_roc_with_stdin_and_env<I, S>(
|
||||
roc_path: &Path,
|
||||
args: I,
|
||||
stdin_vals: &[&str],
|
||||
extra_env: &[(&str, &str)],
|
||||
|
@ -188,6 +131,7 @@ where
|
|||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let roc_path = build_roc_bin_cached();
|
||||
let mut roc_cmd = Command::new(roc_path);
|
||||
|
||||
for arg in args {
|
||||
|
@ -235,6 +179,59 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// If we don't already have a /target/release/roc, build it!
|
||||
pub fn build_roc_bin_cached() -> PathBuf {
|
||||
let roc_binary_path = path_to_roc_binary();
|
||||
|
||||
if !roc_binary_path.exists() {
|
||||
build_roc_bin(&[]);
|
||||
}
|
||||
|
||||
roc_binary_path
|
||||
}
|
||||
|
||||
pub fn build_roc_bin(extra_args: &[&str]) -> PathBuf {
|
||||
let roc_binary_path = path_to_roc_binary();
|
||||
|
||||
// Remove the /target/release/roc part
|
||||
let root_project_dir = roc_binary_path
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap();
|
||||
|
||||
// cargo build --bin roc
|
||||
// (with --release iff the test is being built with --release)
|
||||
let mut args = if cfg!(debug_assertions) {
|
||||
vec!["build", "--bin", "roc"]
|
||||
} else {
|
||||
vec!["build", "--release", "--bin", "roc"]
|
||||
};
|
||||
|
||||
args.extend(extra_args);
|
||||
|
||||
let mut cargo_cmd = cargo();
|
||||
|
||||
cargo_cmd.current_dir(root_project_dir).args(&args);
|
||||
|
||||
let cargo_cmd_str = format!("{cargo_cmd:?}");
|
||||
|
||||
let cargo_output = cargo_cmd.output().unwrap();
|
||||
|
||||
if !cargo_output.status.success() {
|
||||
panic!(
|
||||
"The following cargo command failed:\n\n {}\n\n stdout was:\n\n {}\n\n stderr was:\n\n {}\n",
|
||||
cargo_cmd_str,
|
||||
String::from_utf8(cargo_output.stdout).unwrap(),
|
||||
String::from_utf8(cargo_output.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
roc_binary_path
|
||||
}
|
||||
|
||||
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>, E: IntoIterator<Item = (&'a str, &'a str)>>(
|
||||
cmd_name: &str,
|
||||
stdin_vals: I,
|
||||
|
@ -258,7 +255,7 @@ pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>, E: IntoIterator<Item = (&'a
|
|||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name));
|
||||
.unwrap_or_else(|_| panic!("failed to execute cmd `{cmd_name}` in CLI test"));
|
||||
|
||||
{
|
||||
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||
|
@ -272,7 +269,7 @@ pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>, E: IntoIterator<Item = (&'a
|
|||
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name));
|
||||
.unwrap_or_else(|_| panic!("failed to execute cmd `{cmd_name}` in CLI test"));
|
||||
|
||||
Out {
|
||||
cmd_str,
|
||||
|
|
|
@ -376,7 +376,7 @@ pub fn tree_as_string(root_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> St
|
|||
|
||||
let node = mark_node_pool.get(root_node_id);
|
||||
|
||||
writeln!(full_string, "{} mn_id {}\n", node, root_node_id).unwrap();
|
||||
writeln!(full_string, "{node} mn_id {root_node_id}\n").unwrap();
|
||||
|
||||
tree_as_string_helper(node, 1, &mut full_string, mark_node_pool);
|
||||
|
||||
|
@ -399,7 +399,7 @@ fn tree_as_string_helper(
|
|||
.join("")
|
||||
.to_owned();
|
||||
|
||||
writeln!(full_str, "{} mn_id {}", child_str, child_id).unwrap();
|
||||
writeln!(full_str, "{child_str} mn_id {child_id}").unwrap();
|
||||
|
||||
tree_string.push_str(&full_str);
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ pub type MarkResult<T, E = MarkError> = std::result::Result<T, E>;
|
|||
|
||||
impl From<UtilError> for MarkError {
|
||||
fn from(util_err: UtilError) -> Self {
|
||||
let msg = format!("{}", util_err);
|
||||
let msg = format!("{util_err}");
|
||||
|
||||
// hack to handle MarkError derive
|
||||
let dummy_res: Result<(), NoneError> = Err(NoneError {});
|
||||
|
|
|
@ -39,8 +39,8 @@ impl SlowPool {
|
|||
|
||||
for (mark_node_id, node) in self.nodes.iter().enumerate() {
|
||||
let ast_node_id_str = match mark_id_ast_id_map.get(mark_node_id) {
|
||||
Ok(ast_id) => format!("{:?}", ast_id),
|
||||
Err(err) => format!("{:?}", err),
|
||||
Ok(ast_id) => format!("{ast_id:?}"),
|
||||
Err(err) => format!("{err:?}"),
|
||||
};
|
||||
let ast_node_id: String = ast_node_id_str
|
||||
.chars()
|
||||
|
@ -52,7 +52,7 @@ impl SlowPool {
|
|||
let node_children = node.get_children_ids();
|
||||
|
||||
if !node_children.is_empty() {
|
||||
child_str = format!("children: {:?}", node_children);
|
||||
child_str = format!("children: {node_children:?}");
|
||||
}
|
||||
|
||||
write!(
|
||||
|
|
|
@ -14,8 +14,8 @@ use roc_module::low_level::LowLevel;
|
|||
use roc_module::symbol::Symbol;
|
||||
|
||||
use roc_mono::ir::{
|
||||
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
|
||||
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLambdaSet,
|
||||
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
|
||||
|
@ -117,7 +117,7 @@ where
|
|||
}
|
||||
|
||||
if debug() {
|
||||
for (i, c) in (format!("{:?}", symbol)).chars().take(25).enumerate() {
|
||||
for (i, c) in (format!("{symbol:?}")).chars().take(25).enumerate() {
|
||||
name_bytes[25 + i] = c as u8;
|
||||
}
|
||||
}
|
||||
|
@ -131,21 +131,23 @@ fn bytes_as_ascii(bytes: &[u8]) -> String {
|
|||
let mut buf = String::new();
|
||||
|
||||
for byte in bytes {
|
||||
write!(buf, "{:02X}", byte).unwrap();
|
||||
write!(buf, "{byte:02X}").unwrap();
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn spec_program<'a, 'r, I>(
|
||||
pub fn spec_program<'a, 'r, I1, I2>(
|
||||
arena: &'a Bump,
|
||||
interner: &'r mut STLayoutInterner<'a>,
|
||||
interner: &'r STLayoutInterner<'a>,
|
||||
opt_level: OptLevel,
|
||||
entry_point: roc_mono::ir::EntryPoint<'a>,
|
||||
procs: I,
|
||||
procs: I1,
|
||||
hels: I2,
|
||||
) -> Result<morphic_lib::Solutions>
|
||||
where
|
||||
I: Iterator<Item = &'r Proc<'a>>,
|
||||
I1: Iterator<Item = &'r Proc<'a>>,
|
||||
I2: Iterator<Item = &'r HostExposedLambdaSet<'a>>,
|
||||
{
|
||||
let main_module = {
|
||||
let mut m = ModDefBuilder::new();
|
||||
|
@ -181,40 +183,38 @@ where
|
|||
|
||||
let mut type_definitions = MutSet::default();
|
||||
let mut host_exposed_functions = Vec::new();
|
||||
let mut erased_functions = Vec::new();
|
||||
|
||||
for hels in hels {
|
||||
match hels.raw_function_layout {
|
||||
RawFunctionLayout::Function(_, _, _) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, it, Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, it, Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, [], Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
let bytes = func_name_bytes(proc);
|
||||
let func_name = FuncName(&bytes);
|
||||
|
||||
if let HostExposedLayouts::HostExposed { aliases, .. } = &proc.host_exposed_layouts {
|
||||
for (_, hels) in aliases {
|
||||
match hels.raw_function_layout {
|
||||
RawFunctionLayout::Function(_, _, _) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
it,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
[],
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debug() {
|
||||
eprintln!(
|
||||
"{:?}: {:?} with {:?} args",
|
||||
|
@ -226,6 +226,11 @@ where
|
|||
|
||||
let (spec, type_names) = proc_spec(arena, interner, proc)?;
|
||||
|
||||
if proc.is_erased {
|
||||
let args = &*arena.alloc_slice_fill_iter(proc.args.iter().map(|(lay, _)| *lay));
|
||||
erased_functions.push((bytes, args));
|
||||
}
|
||||
|
||||
type_definitions.extend(type_names);
|
||||
|
||||
m.add_func(func_name, spec)?;
|
||||
|
@ -253,6 +258,7 @@ where
|
|||
entry_point_layout,
|
||||
Some(roc_main),
|
||||
&host_exposed_functions,
|
||||
&erased_functions,
|
||||
)?;
|
||||
|
||||
type_definitions.extend(env.type_names);
|
||||
|
@ -279,8 +285,14 @@ where
|
|||
.collect();
|
||||
|
||||
let mut env = Env::new();
|
||||
let entry_point_function =
|
||||
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
|
||||
let entry_point_function = build_entry_point(
|
||||
&mut env,
|
||||
interner,
|
||||
layout,
|
||||
None,
|
||||
&host_exposed,
|
||||
&erased_functions,
|
||||
)?;
|
||||
|
||||
type_definitions.extend(env.type_names);
|
||||
|
||||
|
@ -360,10 +372,11 @@ fn terrible_hack(builder: &mut FuncDefBuilder, block: BlockId, type_id: TypeId)
|
|||
|
||||
fn build_entry_point<'a>(
|
||||
env: &mut Env<'a>,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
layout: roc_mono::ir::ProcLayout<'a>,
|
||||
entry_point_function: Option<FuncName>,
|
||||
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||
erased_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||
) -> Result<FuncDef> {
|
||||
let mut builder = FuncDefBuilder::new();
|
||||
let outer_block = builder.add_block();
|
||||
|
@ -394,7 +407,7 @@ fn build_entry_point<'a>(
|
|||
}
|
||||
|
||||
// add fake calls to host-exposed functions so they are specialized
|
||||
for (name_bytes, layouts) in host_exposed_functions {
|
||||
for (name_bytes, layouts) in host_exposed_functions.iter().chain(erased_functions) {
|
||||
let host_exposed_func_name = FuncName(name_bytes);
|
||||
|
||||
if Some(host_exposed_func_name) == entry_point_function {
|
||||
|
@ -403,7 +416,7 @@ fn build_entry_point<'a>(
|
|||
|
||||
let block = builder.add_block();
|
||||
|
||||
let struct_layout = interner.insert_direct_no_semantic(LayoutRepr::struct_(layouts));
|
||||
let struct_layout = LayoutRepr::struct_(layouts);
|
||||
let type_id = layout_spec(env, &mut builder, interner, struct_layout)?;
|
||||
|
||||
let argument = builder.add_unknown_with(block, &[], type_id)?;
|
||||
|
@ -433,7 +446,7 @@ fn build_entry_point<'a>(
|
|||
|
||||
fn proc_spec<'a>(
|
||||
arena: &'a Bump,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
proc: &Proc<'a>,
|
||||
) -> Result<(FuncDef, MutSet<UnionLayout<'a>>)> {
|
||||
let mut builder = FuncDefBuilder::new();
|
||||
|
@ -460,10 +473,14 @@ fn proc_spec<'a>(
|
|||
)?;
|
||||
|
||||
let root = BlockExpr(block, value_id);
|
||||
let args_struct_layout =
|
||||
interner.insert_direct_no_semantic(LayoutRepr::struct_(argument_layouts.into_bump_slice()));
|
||||
let args_struct_layout = LayoutRepr::struct_(argument_layouts.into_bump_slice());
|
||||
let arg_type_id = layout_spec(&mut env, &mut builder, interner, args_struct_layout)?;
|
||||
let ret_type_id = layout_spec(&mut env, &mut builder, interner, proc.ret_layout)?;
|
||||
let ret_type_id = layout_spec(
|
||||
&mut env,
|
||||
&mut builder,
|
||||
interner,
|
||||
interner.get_repr(proc.ret_layout),
|
||||
)?;
|
||||
|
||||
let spec = builder.build(arg_type_id, ret_type_id, root)?;
|
||||
|
||||
|
@ -506,6 +523,12 @@ fn apply_refcount_operation(
|
|||
builder.add_recursive_touch(block, argument)?;
|
||||
}
|
||||
ModifyRc::DecRef(symbol) => {
|
||||
// this is almost certainly suboptimal, but not incorrect
|
||||
let argument = env.symbols[symbol];
|
||||
builder.add_recursive_touch(block, argument)?;
|
||||
}
|
||||
ModifyRc::Free(symbol) => {
|
||||
// this is almost certainly suboptimal, but not incorrect
|
||||
let argument = env.symbols[symbol];
|
||||
builder.add_recursive_touch(block, argument)?;
|
||||
}
|
||||
|
@ -516,7 +539,7 @@ fn apply_refcount_operation(
|
|||
|
||||
fn stmt_spec<'a>(
|
||||
builder: &mut FuncDefBuilder,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
env: &mut Env<'a>,
|
||||
block: BlockId,
|
||||
layout: InLayout<'a>,
|
||||
|
@ -601,10 +624,15 @@ fn stmt_spec<'a>(
|
|||
let mut type_ids = Vec::new();
|
||||
|
||||
for p in parameters.iter() {
|
||||
type_ids.push(layout_spec(env, builder, interner, p.layout)?);
|
||||
type_ids.push(layout_spec(
|
||||
env,
|
||||
builder,
|
||||
interner,
|
||||
interner.get_repr(p.layout),
|
||||
)?);
|
||||
}
|
||||
|
||||
let ret_type_id = layout_spec(env, builder, interner, layout)?;
|
||||
let ret_type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
let jp_arg_type_id = builder.add_tuple_type(&type_ids)?;
|
||||
|
||||
|
@ -645,7 +673,7 @@ fn stmt_spec<'a>(
|
|||
builder.add_sub_block(block, BlockExpr(cont_block, cont_value_id))
|
||||
}
|
||||
Jump(id, symbols) => {
|
||||
let ret_type_id = layout_spec(env, builder, interner, layout)?;
|
||||
let ret_type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
let argument = build_tuple_value(builder, env, block, symbols)?;
|
||||
|
||||
let jpid = env.join_points[id];
|
||||
|
@ -654,7 +682,7 @@ fn stmt_spec<'a>(
|
|||
Crash(msg, _) => {
|
||||
// Model this as a foreign call rather than TERMINATE because
|
||||
// we want ownership of the message.
|
||||
let result_type = layout_spec(env, builder, interner, layout)?;
|
||||
let result_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &[env.symbols[msg]], result_type)
|
||||
}
|
||||
|
@ -692,7 +720,7 @@ fn build_recursive_tuple_type<'a>(
|
|||
let mut field_types = Vec::new();
|
||||
|
||||
for field in layouts.iter() {
|
||||
let type_id = layout_spec_help(env, builder, interner, *field)?;
|
||||
let type_id = layout_spec_help(env, builder, interner, interner.get_repr(*field))?;
|
||||
field_types.push(type_id);
|
||||
}
|
||||
|
||||
|
@ -708,7 +736,12 @@ fn build_tuple_type<'a>(
|
|||
let mut field_types = Vec::new();
|
||||
|
||||
for field in layouts.iter() {
|
||||
field_types.push(layout_spec(env, builder, interner, *field)?);
|
||||
field_types.push(layout_spec(
|
||||
env,
|
||||
builder,
|
||||
interner,
|
||||
interner.get_repr(*field),
|
||||
)?);
|
||||
}
|
||||
|
||||
builder.add_tuple_type(&field_types)
|
||||
|
@ -742,7 +775,7 @@ fn add_loop(
|
|||
|
||||
fn call_spec<'a>(
|
||||
builder: &mut FuncDefBuilder,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
env: &mut Env<'a>,
|
||||
block: BlockId,
|
||||
layout: InLayout<'a>,
|
||||
|
@ -768,6 +801,16 @@ fn call_spec<'a>(
|
|||
let module = MOD_APP;
|
||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
||||
}
|
||||
ByPointer {
|
||||
pointer,
|
||||
ret_layout,
|
||||
arg_layouts: _,
|
||||
} => {
|
||||
let result_type = layout_spec(env, builder, interner, interner.get_repr(*ret_layout))?;
|
||||
let fnptr = env.symbols[pointer];
|
||||
let arg_value_id = build_tuple_value(builder, env, block, call.arguments)?;
|
||||
builder.add_unknown_with(block, &[fnptr, arg_value_id], result_type)
|
||||
}
|
||||
Foreign {
|
||||
foreign_symbol: _,
|
||||
ret_layout,
|
||||
|
@ -778,7 +821,7 @@ fn call_spec<'a>(
|
|||
.map(|symbol| env.symbols[symbol])
|
||||
.collect();
|
||||
|
||||
let result_type = layout_spec(env, builder, interner, *ret_layout)?;
|
||||
let result_type = layout_spec(env, builder, interner, interner.get_repr(*ret_layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &arguments, result_type)
|
||||
}
|
||||
|
@ -849,11 +892,10 @@ fn call_spec<'a>(
|
|||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type = layout_spec(env, builder, interner, *return_layout)?;
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = interner.insert_direct_no_semantic(LayoutRepr::Builtin(
|
||||
Builtin::List(*return_layout),
|
||||
));
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
@ -880,8 +922,7 @@ fn call_spec<'a>(
|
|||
|
||||
let arg0_layout = argument_layouts[0];
|
||||
|
||||
let state_layout = interner
|
||||
.insert_direct_no_semantic(LayoutRepr::Builtin(Builtin::List(arg0_layout)));
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(arg0_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
let init_state = list;
|
||||
|
||||
|
@ -906,11 +947,10 @@ fn call_spec<'a>(
|
|||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type = layout_spec(env, builder, interner, *return_layout)?;
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = interner.insert_direct_no_semantic(LayoutRepr::Builtin(
|
||||
Builtin::List(*return_layout),
|
||||
));
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
@ -941,11 +981,10 @@ fn call_spec<'a>(
|
|||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type = layout_spec(env, builder, interner, *return_layout)?;
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = interner.insert_direct_no_semantic(LayoutRepr::Builtin(
|
||||
Builtin::List(*return_layout),
|
||||
));
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
@ -982,11 +1021,10 @@ fn call_spec<'a>(
|
|||
list_append(builder, block, update_mode_var, state, new_element)
|
||||
};
|
||||
|
||||
let output_element_type = layout_spec(env, builder, interner, *return_layout)?;
|
||||
let output_element_type =
|
||||
layout_spec(env, builder, interner, interner.get_repr(*return_layout))?;
|
||||
|
||||
let state_layout = interner.insert_direct_no_semantic(LayoutRepr::Builtin(
|
||||
Builtin::List(*return_layout),
|
||||
));
|
||||
let state_layout = LayoutRepr::Builtin(Builtin::List(*return_layout));
|
||||
let state_type = layout_spec(env, builder, interner, state_layout)?;
|
||||
|
||||
let init_state = new_list(builder, block, output_element_type)?;
|
||||
|
@ -1042,7 +1080,7 @@ fn lowlevel_spec<'a>(
|
|||
) -> Result<ValueId> {
|
||||
use LowLevel::*;
|
||||
|
||||
let type_id = layout_spec(env, builder, interner, layout)?;
|
||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
let mode = update_mode.to_bytes();
|
||||
let update_mode_var = UpdateModeVar(&mode);
|
||||
|
||||
|
@ -1150,7 +1188,8 @@ fn lowlevel_spec<'a>(
|
|||
|
||||
match interner.get_repr(layout) {
|
||||
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
||||
let type_id = layout_spec(env, builder, interner, element_layout)?;
|
||||
let type_id =
|
||||
layout_spec(env, builder, interner, interner.get_repr(element_layout))?;
|
||||
new_list(builder, block, type_id)
|
||||
}
|
||||
_ => unreachable!("empty array does not have a list layout"),
|
||||
|
@ -1198,7 +1237,7 @@ fn lowlevel_spec<'a>(
|
|||
// TODO overly pessimstic
|
||||
let arguments: Vec<_> = arguments.iter().map(|symbol| env.symbols[symbol]).collect();
|
||||
|
||||
let result_type = layout_spec(env, builder, interner, layout)?;
|
||||
let result_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &arguments, result_type)
|
||||
}
|
||||
|
@ -1283,7 +1322,7 @@ fn worst_case_type(context: &mut impl TypeContext) -> Result<TypeId> {
|
|||
|
||||
fn expr_spec<'a>(
|
||||
builder: &mut FuncDefBuilder,
|
||||
interner: &mut STLayoutInterner<'a>,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
env: &mut Env<'a>,
|
||||
block: BlockId,
|
||||
layout: InLayout<'a>,
|
||||
|
@ -1294,21 +1333,16 @@ fn expr_spec<'a>(
|
|||
match expr {
|
||||
Literal(literal) => literal_spec(builder, block, literal),
|
||||
NullPointer => {
|
||||
let pointer_type = layout_spec(env, builder, interner, layout)?;
|
||||
let pointer_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &[], pointer_type)
|
||||
}
|
||||
Call(call) => call_spec(builder, interner, env, block, layout, call),
|
||||
Reuse {
|
||||
tag_layout,
|
||||
tag_id,
|
||||
arguments,
|
||||
..
|
||||
}
|
||||
| Tag {
|
||||
Tag {
|
||||
tag_layout,
|
||||
tag_id,
|
||||
arguments,
|
||||
reuse: _,
|
||||
} => {
|
||||
let data_id = build_tuple_value(builder, env, block, arguments)?;
|
||||
|
||||
|
@ -1347,16 +1381,6 @@ fn expr_spec<'a>(
|
|||
|
||||
builder.add_make_named(block, MOD_APP, type_name, tag_value_id)
|
||||
}
|
||||
ExprBox { symbol } => {
|
||||
let value_id = env.symbols[symbol];
|
||||
|
||||
with_new_heap_cell(builder, block, value_id)
|
||||
}
|
||||
ExprUnbox { symbol } => {
|
||||
let tuple_id = env.symbols[symbol];
|
||||
|
||||
builder.add_get_tuple_field(block, tuple_id, BOX_VALUE_INDEX)
|
||||
}
|
||||
Struct(fields) => build_tuple_value(builder, env, block, fields),
|
||||
UnionAtIndex {
|
||||
index,
|
||||
|
@ -1412,6 +1436,38 @@ fn expr_spec<'a>(
|
|||
builder.add_get_tuple_field(block, variant_id, index)
|
||||
}
|
||||
},
|
||||
UnionFieldPtrAtIndex {
|
||||
index,
|
||||
tag_id,
|
||||
structure,
|
||||
union_layout,
|
||||
} => {
|
||||
let index = (*index) as u32;
|
||||
let tag_value_id = env.symbols[structure];
|
||||
|
||||
let type_name_bytes = recursive_tag_union_name_bytes(union_layout).as_bytes();
|
||||
let type_name = TypeName(&type_name_bytes);
|
||||
|
||||
// unwrap the named wrapper
|
||||
let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?;
|
||||
|
||||
// now we have a tuple (cell, union { ... }); decompose
|
||||
let heap_cell = builder.add_get_tuple_field(block, union_id, TAG_CELL_INDEX)?;
|
||||
let union_data = builder.add_get_tuple_field(block, union_id, TAG_DATA_INDEX)?;
|
||||
|
||||
// we're reading from this value, so touch the heap cell
|
||||
builder.add_touch(block, heap_cell)?;
|
||||
|
||||
// next, unwrap the union at the tag id that we've got
|
||||
let variant_id = builder.add_unwrap_union(block, union_data, *tag_id as u32)?;
|
||||
|
||||
let value = builder.add_get_tuple_field(block, variant_id, index)?;
|
||||
|
||||
// construct the box. Here the heap_cell of the tag is re-used, I'm hoping that that
|
||||
// conveys to morphic that we're borrowing into the existing tag?!
|
||||
builder.add_make_tuple(block, &[heap_cell, value])
|
||||
}
|
||||
|
||||
StructAtIndex {
|
||||
index, structure, ..
|
||||
} => {
|
||||
|
@ -1419,7 +1475,7 @@ fn expr_spec<'a>(
|
|||
builder.add_get_tuple_field(block, value_id, *index as u32)
|
||||
}
|
||||
Array { elem_layout, elems } => {
|
||||
let type_id = layout_spec(env, builder, interner, *elem_layout)?;
|
||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(*elem_layout))?;
|
||||
|
||||
let list = new_list(builder, block, type_id)?;
|
||||
|
||||
|
@ -1446,7 +1502,8 @@ fn expr_spec<'a>(
|
|||
|
||||
EmptyArray => match interner.get_repr(layout) {
|
||||
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
||||
let type_id = layout_spec(env, builder, interner, element_layout)?;
|
||||
let type_id =
|
||||
layout_spec(env, builder, interner, interner.get_repr(element_layout))?;
|
||||
new_list(builder, block, type_id)
|
||||
}
|
||||
_ => unreachable!("empty array does not have a list layout"),
|
||||
|
@ -1483,8 +1540,30 @@ fn expr_spec<'a>(
|
|||
let value = with_new_heap_cell(builder, block, union_data)?;
|
||||
builder.add_make_named(block, MOD_APP, type_name, value)
|
||||
}
|
||||
FunctionPointer { .. } => {
|
||||
let pointer_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_unknown_with(block, &[], pointer_type)
|
||||
}
|
||||
ErasedMake { callee, value } => {
|
||||
let value = match value {
|
||||
Some(v) => box_erasure_value_unknown(builder, block, env.symbols[v]),
|
||||
// model nullptr
|
||||
None => box_erasure_value_unknown_nullptr(builder, block),
|
||||
}?;
|
||||
|
||||
let callee = env.symbols[callee];
|
||||
|
||||
erasure_make(builder, block, value, callee)
|
||||
}
|
||||
ErasedLoad { symbol, field } => {
|
||||
let value = env.symbols[symbol];
|
||||
let loaded_type = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
erasure_load(builder, block, value, *field, loaded_type)
|
||||
}
|
||||
RuntimeErrorFunction(_) => {
|
||||
let type_id = layout_spec(env, builder, interner, layout)?;
|
||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
|
||||
builder.add_terminate(block, type_id)
|
||||
}
|
||||
|
@ -1493,6 +1572,16 @@ fn expr_spec<'a>(
|
|||
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
Alloca { initializer, .. } => {
|
||||
let initializer = &initializer.as_ref().map(|s| env.symbols[s]);
|
||||
let values = match initializer {
|
||||
Some(initializer) => std::slice::from_ref(initializer),
|
||||
None => &[],
|
||||
};
|
||||
|
||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
||||
builder.add_unknown_with(block, values, type_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1515,7 +1604,7 @@ fn layout_spec<'a>(
|
|||
env: &mut Env<'a>,
|
||||
builder: &mut impl TypeContext,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
layout: LayoutRepr<'a>,
|
||||
) -> Result<TypeId> {
|
||||
layout_spec_help(env, builder, interner, layout)
|
||||
}
|
||||
|
@ -1539,16 +1628,19 @@ fn layout_spec_help<'a>(
|
|||
env: &mut Env<'a>,
|
||||
builder: &mut impl TypeContext,
|
||||
interner: &STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
layout: LayoutRepr<'a>,
|
||||
) -> Result<TypeId> {
|
||||
use LayoutRepr::*;
|
||||
|
||||
match interner.get_repr(layout) {
|
||||
match layout {
|
||||
Builtin(builtin) => builtin_spec(env, builder, interner, &builtin),
|
||||
Struct(field_layouts) => build_recursive_tuple_type(env, builder, interner, field_layouts),
|
||||
LambdaSet(lambda_set) => {
|
||||
layout_spec_help(env, builder, interner, lambda_set.runtime_representation())
|
||||
}
|
||||
LambdaSet(lambda_set) => layout_spec_help(
|
||||
env,
|
||||
builder,
|
||||
interner,
|
||||
interner.get_repr(lambda_set.runtime_representation()),
|
||||
),
|
||||
Union(union_layout) => {
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(&[]) => {
|
||||
|
@ -1575,12 +1667,14 @@ fn layout_spec_help<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Boxed(inner_layout) => {
|
||||
let inner_type = layout_spec_help(env, builder, interner, inner_layout)?;
|
||||
Ptr(inner_layout) => {
|
||||
let inner_type =
|
||||
layout_spec_help(env, builder, interner, interner.get_repr(inner_layout))?;
|
||||
let cell_type = builder.add_heap_cell_type();
|
||||
|
||||
builder.add_tuple_type(&[cell_type, inner_type])
|
||||
}
|
||||
|
||||
// TODO(recursive-layouts): update once we have recursive pointer loops
|
||||
RecursivePointer(union_layout) => match interner.get_repr(union_layout) {
|
||||
LayoutRepr::Union(union_layout) => {
|
||||
|
@ -1592,6 +1686,9 @@ fn layout_spec_help<'a>(
|
|||
}
|
||||
_ => internal_error!("somehow, a non-recursive layout is under a recursive pointer"),
|
||||
},
|
||||
|
||||
FunctionPointer(_) => function_pointer_type(builder),
|
||||
Erased(_) => erasure_type(builder),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1608,7 +1705,8 @@ fn builtin_spec<'a>(
|
|||
Decimal | Float(_) => builder.add_tuple_type(&[]),
|
||||
Str => str_type(builder),
|
||||
List(element_layout) => {
|
||||
let element_type = layout_spec_help(env, builder, interner, *element_layout)?;
|
||||
let element_type =
|
||||
layout_spec_help(env, builder, interner, interner.get_repr(*element_layout))?;
|
||||
|
||||
let cell = builder.add_heap_cell_type();
|
||||
let bag = builder.add_bag_type(element_type)?;
|
||||
|
@ -1634,10 +1732,6 @@ fn static_list_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
|||
const LIST_CELL_INDEX: u32 = 0;
|
||||
const LIST_BAG_INDEX: u32 = 1;
|
||||
|
||||
#[allow(dead_code)]
|
||||
const BOX_CELL_INDEX: u32 = LIST_CELL_INDEX;
|
||||
const BOX_VALUE_INDEX: u32 = LIST_BAG_INDEX;
|
||||
|
||||
const TAG_CELL_INDEX: u32 = 0;
|
||||
const TAG_DATA_INDEX: u32 = 1;
|
||||
|
||||
|
@ -1671,3 +1765,78 @@ fn new_num(builder: &mut FuncDefBuilder, block: BlockId) -> Result<ValueId> {
|
|||
// we model all our numbers as unit values
|
||||
builder.add_make_tuple(block, &[])
|
||||
}
|
||||
|
||||
fn function_pointer_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||
builder.add_tuple_type(&[])
|
||||
}
|
||||
|
||||
const ERASURE_CALEE_INDEX: u32 = 0;
|
||||
const ERASURE_VALUE_INDEX: u32 = 1;
|
||||
|
||||
/// Erasure type modeled as
|
||||
///
|
||||
/// ```text
|
||||
/// Tuple(callee: FnPtr, value: HeapCell)
|
||||
/// ```
|
||||
fn erasure_type<TC: TypeContext>(builder: &mut TC) -> Result<TypeId> {
|
||||
let value_cell_id = builder.add_heap_cell_type();
|
||||
let callee_id = function_pointer_type(builder)?;
|
||||
builder.add_tuple_type(&[value_cell_id, callee_id])
|
||||
}
|
||||
|
||||
fn erasure_box_value_type<TC: TypeContext>(builder: &mut TC) -> TypeId {
|
||||
builder.add_heap_cell_type()
|
||||
}
|
||||
|
||||
fn box_erasure_value_unknown(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
) -> Result<ValueId> {
|
||||
let heap_cell = erasure_box_value_type(builder);
|
||||
builder.add_unknown_with(block, &[value], heap_cell)
|
||||
}
|
||||
|
||||
fn box_erasure_value_unknown_nullptr(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
) -> Result<ValueId> {
|
||||
let heap_cell = erasure_box_value_type(builder);
|
||||
builder.add_unknown_with(block, &[], heap_cell)
|
||||
}
|
||||
|
||||
/// Erasure value modeled as
|
||||
///
|
||||
/// ```text
|
||||
/// callee = make_tuple(&[])
|
||||
/// value = unknown(make_tuple(...captures))
|
||||
///
|
||||
/// x : Tuple(callee: FnPtr, value: HeapCell)
|
||||
/// x = make_tuple(callee, value)
|
||||
/// ```
|
||||
fn erasure_make(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
callee: ValueId,
|
||||
) -> Result<ValueId> {
|
||||
builder.add_make_tuple(block, &[value, callee])
|
||||
}
|
||||
|
||||
fn erasure_load(
|
||||
builder: &mut FuncDefBuilder,
|
||||
block: BlockId,
|
||||
value: ValueId,
|
||||
field: ErasedField,
|
||||
loaded_type: TypeId,
|
||||
) -> Result<ValueId> {
|
||||
match field {
|
||||
ErasedField::Callee => builder.add_get_tuple_field(block, value, ERASURE_CALEE_INDEX),
|
||||
ErasedField::Value | ErasedField::ValuePtr => {
|
||||
let unknown_heap_cell_value =
|
||||
builder.add_get_tuple_field(block, value, ERASURE_VALUE_INDEX)?;
|
||||
// Cast the unknown cell to the wanted type
|
||||
builder.add_unknown_with(block, &[unknown_heap_cell_value], loaded_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ fn find_wasi_libc_path() -> PathBuf {
|
|||
internal_error!("cannot find `wasi-libc.a`")
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_zig_host_native(
|
||||
env_path: &str,
|
||||
|
@ -161,7 +161,7 @@ pub fn build_zig_host_native(
|
|||
|
||||
zig_cmd.args([
|
||||
zig_host_src,
|
||||
&format!("-femit-bin={}", emit_bin),
|
||||
&format!("-femit-bin={emit_bin}"),
|
||||
"--pkg-begin",
|
||||
"glue",
|
||||
find_zig_glue_path().to_str().unwrap(),
|
||||
|
@ -257,99 +257,6 @@ pub fn build_zig_host_native(
|
|||
zig_cmd
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_zig_host_native(
|
||||
env_path: &str,
|
||||
env_home: &str,
|
||||
emit_bin: &str,
|
||||
zig_host_src: &str,
|
||||
_target: &str,
|
||||
opt_level: OptLevel,
|
||||
shared_lib_path: Option<&Path>,
|
||||
builtins_host_path: &Path,
|
||||
// For compatibility with the non-macOS def above. Keep these in sync.
|
||||
) -> Command {
|
||||
use serde_json::Value;
|
||||
|
||||
// Run `zig env` to find the location of zig's std/ directory
|
||||
let zig_env_output = zig().args(["env"]).output().unwrap();
|
||||
|
||||
let zig_env_json = if zig_env_output.status.success() {
|
||||
std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| {
|
||||
internal_error!(
|
||||
"`zig env` failed; its stderr output was invalid utf8 ({:?})",
|
||||
utf8_err
|
||||
);
|
||||
})
|
||||
} else {
|
||||
match std::str::from_utf8(&zig_env_output.stderr) {
|
||||
Ok(stderr) => internal_error!("`zig env` failed - stderr output was: {:?}", stderr),
|
||||
Err(utf8_err) => internal_error!(
|
||||
"`zig env` failed; its stderr output was invalid utf8 ({:?})",
|
||||
utf8_err
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let mut zig_compiler_rt_path = match serde_json::from_str(zig_env_json) {
|
||||
Ok(Value::Object(map)) => match map.get("std_dir") {
|
||||
Some(Value::String(std_dir)) => PathBuf::from(Path::new(std_dir)),
|
||||
_ => {
|
||||
internal_error!("Expected JSON containing a `std_dir` String field from `zig env`, but got: {:?}", zig_env_json);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
internal_error!(
|
||||
"Expected JSON containing a `std_dir` field from `zig env`, but got: {:?}",
|
||||
zig_env_json
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
zig_compiler_rt_path.push("special");
|
||||
zig_compiler_rt_path.push("compiler_rt.zig");
|
||||
|
||||
let mut zig_cmd = zig();
|
||||
zig_cmd
|
||||
.env_clear()
|
||||
.env("PATH", env_path)
|
||||
.env("HOME", env_home);
|
||||
if let Some(shared_lib_path) = shared_lib_path {
|
||||
zig_cmd.args([
|
||||
"build-exe",
|
||||
"-fPIE",
|
||||
shared_lib_path.to_str().unwrap(),
|
||||
builtins_host_path.to_str().unwrap(),
|
||||
]);
|
||||
} else {
|
||||
zig_cmd.args(["build-obj"]);
|
||||
}
|
||||
zig_cmd.args([
|
||||
zig_host_src,
|
||||
&format!("-femit-bin={}", emit_bin),
|
||||
"--pkg-begin",
|
||||
"glue",
|
||||
find_zig_glue_path().to_str().unwrap(),
|
||||
"--pkg-end",
|
||||
// include the zig runtime
|
||||
"--pkg-begin",
|
||||
"compiler_rt",
|
||||
zig_compiler_rt_path.to_str().unwrap(),
|
||||
"--pkg-end",
|
||||
// include libc
|
||||
"--library",
|
||||
"c",
|
||||
]);
|
||||
if matches!(opt_level, OptLevel::Optimize) {
|
||||
zig_cmd.args(["-O", "ReleaseSafe"]);
|
||||
} else if matches!(opt_level, OptLevel::Size) {
|
||||
zig_cmd.args(["-O", "ReleaseSmall"]);
|
||||
}
|
||||
|
||||
zig_cmd
|
||||
}
|
||||
|
||||
pub fn build_zig_host_wasm32(
|
||||
env_path: &str,
|
||||
env_home: &str,
|
||||
|
@ -492,7 +399,7 @@ pub fn build_swift_host_native(
|
|||
|
||||
match arch {
|
||||
Architecture::Aarch64(_) => command.arg("-arm64"),
|
||||
_ => command.arg(format!("-{}", arch)),
|
||||
_ => command.arg(format!("-{arch}")),
|
||||
};
|
||||
|
||||
command
|
||||
|
@ -623,7 +530,7 @@ pub fn rebuild_host(
|
|||
// on windows, we need the nightly toolchain so we can use `-Z export-executable-symbols`
|
||||
// using `+nightly` only works when running cargo through rustup
|
||||
let mut cmd = rustup();
|
||||
cmd.args(["run", "nightly-2022-10-30", "cargo"]);
|
||||
cmd.args(["run", "nightly-2023-04-15", "cargo"]);
|
||||
|
||||
cmd
|
||||
} else {
|
||||
|
@ -1021,7 +928,7 @@ fn link_linux(
|
|||
.map(|segments| segments.join("/"))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
eprintln!("We looked in the following directories:\n{}", dirs);
|
||||
eprintln!("We looked in the following directories:\n{dirs}");
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
@ -1171,6 +1078,12 @@ fn link_macos(
|
|||
// "--gc-sections",
|
||||
"-arch",
|
||||
&arch,
|
||||
// Suppress warnings, because otherwise it prints:
|
||||
//
|
||||
// ld: warning: -undefined dynamic_lookup may not work with chained fixups
|
||||
//
|
||||
// We can't disable that option without breaking either x64 mac or ARM mac
|
||||
"-w",
|
||||
"-macos_version_min",
|
||||
&get_macos_version(),
|
||||
])
|
||||
|
@ -1178,8 +1091,8 @@ fn link_macos(
|
|||
|
||||
let sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib";
|
||||
if Path::new(sdk_path).exists() {
|
||||
ld_command.arg(format!("-L{}", sdk_path));
|
||||
ld_command.arg(format!("-L{}/swift", sdk_path));
|
||||
ld_command.arg(format!("-L{sdk_path}"));
|
||||
ld_command.arg(format!("-L{sdk_path}/swift"));
|
||||
};
|
||||
|
||||
let roc_link_flags = match env::var("ROC_LINK_FLAGS") {
|
||||
|
@ -1381,9 +1294,7 @@ pub fn llvm_module_to_dylib(
|
|||
|
||||
assert!(
|
||||
exit_status.success(),
|
||||
"\n___________\nLinking command failed with status {:?}:\n\n {:?}\n___________\n",
|
||||
exit_status,
|
||||
child
|
||||
"\n___________\nLinking command failed with status {exit_status:?}:\n\n {child:?}\n___________\n"
|
||||
);
|
||||
|
||||
// Load the dylib
|
||||
|
|
|
@ -8,8 +8,8 @@ use roc_gen_dev::AssemblyBackendMode;
|
|||
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
|
||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_load::{
|
||||
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
|
||||
LoadingProblem, MonomorphizedModule, Threading,
|
||||
EntryPoint, ExecutionMode, ExpectMetadata, FunctionKind, LoadConfig, LoadMonomorphizedError,
|
||||
LoadedModule, LoadingProblem, MonomorphizedModule, Threading,
|
||||
};
|
||||
use roc_mono::ir::{OptLevel, SingleEntryPoint};
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
|
@ -133,7 +133,7 @@ pub fn gen_from_mono_module<'a>(
|
|||
// TODO make this polymorphic in the llvm functions so it can be reused for another backend.
|
||||
fn gen_from_mono_module_llvm<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
mut loaded: MonomorphizedModule<'a>,
|
||||
loaded: MonomorphizedModule<'a>,
|
||||
roc_file_path: &Path,
|
||||
target: &target_lexicon::Triple,
|
||||
opt_level: OptLevel,
|
||||
|
@ -166,7 +166,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let enum_attr = context.create_enum_attribute(kind_id, 1);
|
||||
let enum_attr = context.create_enum_attribute(kind_id, 0);
|
||||
|
||||
for function in module.get_functions() {
|
||||
let name = function.get_name().to_str().unwrap();
|
||||
|
@ -232,9 +232,10 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
|
||||
roc_gen_llvm::llvm::build::build_procedures(
|
||||
&env,
|
||||
&mut loaded.layout_interner,
|
||||
&loaded.layout_interner,
|
||||
opt_level,
|
||||
loaded.procedures,
|
||||
loaded.host_exposed_lambda_sets,
|
||||
entry_point,
|
||||
Some(&app_ll_file),
|
||||
&loaded.glue_layouts,
|
||||
|
@ -320,10 +321,10 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
if !unrecognized.is_empty() {
|
||||
let out = unrecognized
|
||||
.iter()
|
||||
.map(|x| format!("{:?}", x))
|
||||
.map(|x| format!("{x:?}"))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
eprintln!("Unrecognized sanitizer: {}\nSupported options are \"address\", \"memory\", \"thread\", \"cargo-fuzz\", and \"afl.rs\".", out);
|
||||
eprintln!("Unrecognized sanitizer: {out}\nSupported options are \"address\", \"memory\", \"thread\", \"cargo-fuzz\", and \"afl.rs\".");
|
||||
eprintln!("Note: \"cargo-fuzz\" and \"afl.rs\" both enable sanitizer coverage for fuzzing. They just use different parameters to match the respective libraries.")
|
||||
}
|
||||
|
||||
|
@ -340,7 +341,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
}
|
||||
let opt = opt.output().unwrap();
|
||||
|
||||
assert!(opt.stderr.is_empty(), "{:#?}", opt);
|
||||
assert!(opt.stderr.is_empty(), "{opt:#?}");
|
||||
|
||||
// write the .o file. Note that this builds the .o for the local machine,
|
||||
// and ignores the `target_machine` entirely.
|
||||
|
@ -358,7 +359,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(bc_to_object.status.success(), "{:#?}", bc_to_object);
|
||||
assert!(bc_to_object.status.success(), "{bc_to_object:#?}");
|
||||
|
||||
MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works")
|
||||
} else if emit_debug_info {
|
||||
|
@ -414,7 +415,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(ll_to_object.stderr.is_empty(), "{:#?}", ll_to_object);
|
||||
assert!(ll_to_object.stderr.is_empty(), "{ll_to_object:#?}");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -716,13 +717,13 @@ pub fn handle_error_module(
|
|||
pub fn handle_loading_problem(problem: LoadingProblem) -> std::io::Result<i32> {
|
||||
match problem {
|
||||
LoadingProblem::FormattedReport(report) => {
|
||||
print!("{}", report);
|
||||
print!("{report}");
|
||||
Ok(1)
|
||||
}
|
||||
_ => {
|
||||
// TODO: tighten up the types here, we should always end up with a
|
||||
// formatted report from load.
|
||||
print!("Failed with error: {:?}", problem);
|
||||
print!("Failed with error: {problem:?}");
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
@ -740,8 +741,20 @@ pub fn standard_load_config(
|
|||
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
|
||||
};
|
||||
|
||||
// UNSTABLE(lambda-erasure)
|
||||
let function_kind = if cfg!(debug_assertions) {
|
||||
if std::env::var("EXPERIMENTAL_ROC_ERASE").is_ok() {
|
||||
FunctionKind::Erased
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
}
|
||||
} else {
|
||||
FunctionKind::LambdaSet
|
||||
};
|
||||
|
||||
LoadConfig {
|
||||
target_info,
|
||||
function_kind,
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
threading,
|
||||
|
@ -889,7 +902,7 @@ fn build_loaded_file<'a>(
|
|||
buf.push('\n');
|
||||
|
||||
use std::fmt::Write;
|
||||
write!(buf, "{}", module_timing).unwrap();
|
||||
write!(buf, "{module_timing}").unwrap();
|
||||
|
||||
if it.peek().is_some() {
|
||||
buf.push('\n');
|
||||
|
@ -914,10 +927,7 @@ fn build_loaded_file<'a>(
|
|||
.expect("Failed to (re)build platform.");
|
||||
|
||||
if emit_timings && !is_platform_prebuilt {
|
||||
println!(
|
||||
"Finished rebuilding the platform in {} ms\n",
|
||||
rebuild_duration
|
||||
);
|
||||
println!("Finished rebuilding the platform in {rebuild_duration} ms\n");
|
||||
}
|
||||
|
||||
Some(HostRebuildTiming::BeforeApp(rebuild_duration))
|
||||
|
@ -957,8 +967,7 @@ fn build_loaded_file<'a>(
|
|||
|
||||
if emit_timings {
|
||||
println!(
|
||||
"\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
"\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{buf}"
|
||||
);
|
||||
|
||||
println!(
|
||||
|
@ -972,10 +981,7 @@ fn build_loaded_file<'a>(
|
|||
let rebuild_duration = thread.join().expect("Failed to (re)build platform.");
|
||||
|
||||
if emit_timings && !is_platform_prebuilt {
|
||||
println!(
|
||||
"Finished rebuilding the platform in {} ms\n",
|
||||
rebuild_duration
|
||||
);
|
||||
println!("Finished rebuilding the platform in {rebuild_duration} ms\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1007,7 +1013,7 @@ fn build_loaded_file<'a>(
|
|||
};
|
||||
let app_o_file = tempfile::Builder::new()
|
||||
.prefix("roc_app")
|
||||
.suffix(&format!(".{}", extension))
|
||||
.suffix(&format!(".{extension}"))
|
||||
.tempfile()
|
||||
.map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?;
|
||||
let app_o_file = app_o_file.path();
|
||||
|
@ -1212,6 +1218,8 @@ pub fn check_file<'a>(
|
|||
|
||||
let load_config = LoadConfig {
|
||||
target_info,
|
||||
// TODO: we may not want this for just checking.
|
||||
function_kind: FunctionKind::LambdaSet,
|
||||
// TODO: expose this from CLI?
|
||||
render: RenderTarget::ColorTerminal,
|
||||
palette: DEFAULT_PALETTE,
|
||||
|
@ -1257,8 +1265,7 @@ pub fn check_file<'a>(
|
|||
|
||||
if emit_timings {
|
||||
println!(
|
||||
"\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{}",
|
||||
buf
|
||||
"\n\nCompilation finished!\n\nHere's how long each module took to compile:\n\n{buf}"
|
||||
);
|
||||
|
||||
println!("Finished checking in {} ms\n", compilation_end.as_millis(),);
|
||||
|
|
|
@ -87,12 +87,12 @@ pub fn target_zig_str(target: &Triple) -> &'static str {
|
|||
architecture: Architecture::X86_64,
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => "x86_64-apple-darwin",
|
||||
} => "x86_64-macos-gnu",
|
||||
Triple {
|
||||
architecture: Architecture::Aarch64(_),
|
||||
operating_system: OperatingSystem::Darwin,
|
||||
..
|
||||
} => "aarch64-apple-darwin",
|
||||
} => "aarch64-macos-gnu",
|
||||
_ => internal_error!("TODO gracefully handle unsupported target: {:?}", target),
|
||||
}
|
||||
}
|
||||
|
@ -149,20 +149,24 @@ pub fn target_machine(
|
|||
|
||||
init_arch(target);
|
||||
|
||||
let code_model = match target.architecture {
|
||||
// LLVM 12 will not compile our programs without a large code model.
|
||||
// The reason is not totally clear to me, but my guess is a few special-cases in
|
||||
// llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (instructions)
|
||||
// llvm/lib/Target/AArch64/AArch64Subtarget.cpp (GoT tables)
|
||||
// Revisit when upgrading to LLVM 13.
|
||||
Architecture::Aarch64(..) => CodeModel::Large,
|
||||
_ => CodeModel::Default,
|
||||
};
|
||||
// We used to have a problem that LLVM 12 would not compile our programs without a large code model.
|
||||
// The reason was not totally clear to us, but one guess is a few special-cases in
|
||||
// llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (instructions)
|
||||
// llvm/lib/Target/AArch64/AArch64Subtarget.cpp (GoT tables)
|
||||
// Revisit when upgrading to LLVM 13.
|
||||
//
|
||||
// Most recently, we seem to only see this problem on macOS ARM64; removing this
|
||||
// failed macOS CI here: https://github.com/roc-lang/roc/pull/5644
|
||||
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
|
||||
let code_model = CodeModel::Large;
|
||||
|
||||
#[cfg(not(all(target_arch = "aarch64", target_os = "macos")))]
|
||||
let code_model = CodeModel::Default;
|
||||
|
||||
Target::from_name(arch).unwrap().create_target_machine(
|
||||
&TargetTriple::create(target_triple_str(target)),
|
||||
"generic",
|
||||
"", // TODO: this probably should be TargetMachine::get_host_cpu_features() to enable all features.
|
||||
"",
|
||||
opt,
|
||||
reloc,
|
||||
code_model,
|
||||
|
|
|
@ -35,6 +35,7 @@ fn main() {
|
|||
|
||||
generate_bc_file(&bitcode_path, "ir-i386", "builtins-i386");
|
||||
generate_bc_file(&bitcode_path, "ir-x86_64", "builtins-x86_64");
|
||||
generate_bc_file(&bitcode_path, "ir-aarch64", "builtins-aarch64");
|
||||
generate_bc_file(
|
||||
&bitcode_path,
|
||||
"ir-windows-x86_64",
|
||||
|
@ -61,12 +62,12 @@ fn generate_bc_file(bitcode_path: &Path, zig_object: &str, file_name: &str) {
|
|||
ll_path.set_extension("ll");
|
||||
let dest_ir_host = ll_path.to_str().expect("Invalid dest ir path");
|
||||
|
||||
println!("Compiling host ir to: {}", dest_ir_host);
|
||||
println!("Compiling host ir to: {dest_ir_host}");
|
||||
|
||||
let mut bc_path = bitcode_path.join(file_name);
|
||||
bc_path.set_extension("bc");
|
||||
let dest_bc_64bit = bc_path.to_str().expect("Invalid dest bc path");
|
||||
println!("Compiling 64-bit bitcode to: {}", dest_bc_64bit);
|
||||
println!("Compiling 64-bit bitcode to: {dest_bc_64bit}");
|
||||
|
||||
// workaround for github.com/ziglang/zig/issues/9711
|
||||
#[cfg(target_os = "macos")]
|
||||
|
@ -104,7 +105,7 @@ fn run_command(mut command: Command, flaky_fail_counter: usize) {
|
|||
false => {
|
||||
let error_str = match str::from_utf8(&output.stderr) {
|
||||
Ok(stderr) => stderr.to_string(),
|
||||
Err(_) => format!("Failed to run \"{}\"", command_str),
|
||||
Err(_) => format!("Failed to run \"{command_str}\""),
|
||||
};
|
||||
|
||||
// Flaky test errors that only occur sometimes on MacOS ci server.
|
||||
|
|
|
@ -66,7 +66,7 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name:
|
|||
let src_obj_path = bitcode_path.join(object_file_name);
|
||||
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
||||
|
||||
println!("Compiling zig object `{}` to: {}", zig_object, src_obj);
|
||||
println!("Compiling zig object `{zig_object}` to: {src_obj}");
|
||||
|
||||
if !DEBUG {
|
||||
let mut zig_cmd = zig();
|
||||
|
@ -77,7 +77,7 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name:
|
|||
|
||||
run_command(zig_cmd, 0);
|
||||
|
||||
println!("Moving zig object `{}` to: {}", zig_object, dest_obj);
|
||||
println!("Moving zig object `{zig_object}` to: {dest_obj}");
|
||||
|
||||
// we store this .o file in rust's `target` folder (for wasm we need to leave a copy here too)
|
||||
fs::copy(src_obj, dest_obj).unwrap_or_else(|err| {
|
||||
|
@ -167,7 +167,7 @@ fn run_command(mut command: Command, flaky_fail_counter: usize) {
|
|||
false => {
|
||||
let error_str = match str::from_utf8(&output.stderr) {
|
||||
Ok(stderr) => stderr.to_string(),
|
||||
Err(_) => format!("Failed to run \"{}\"", command_str),
|
||||
Err(_) => format!("Failed to run \"{command_str}\""),
|
||||
};
|
||||
|
||||
// Flaky test errors that only occur sometimes on MacOS ci server.
|
||||
|
|
|
@ -25,18 +25,19 @@ pub fn build(b: *Builder) void {
|
|||
const host_target = b.standardTargetOptions(.{
|
||||
.default_target = CrossTarget{
|
||||
.cpu_model = .baseline,
|
||||
// TODO allow for native target for maximum speed
|
||||
},
|
||||
});
|
||||
const linux32_target = makeLinux32Target();
|
||||
const linux64_target = makeLinux64Target();
|
||||
const linux_x64_target = makeLinuxX64Target();
|
||||
const linux_aarch64_target = makeLinuxAarch64Target();
|
||||
const windows64_target = makeWindows64Target();
|
||||
const wasm32_target = makeWasm32Target();
|
||||
|
||||
// LLVM IR
|
||||
generateLlvmIrFile(b, mode, host_target, main_path, "ir", "builtins-host");
|
||||
generateLlvmIrFile(b, mode, linux32_target, main_path, "ir-i386", "builtins-i386");
|
||||
generateLlvmIrFile(b, mode, linux64_target, main_path, "ir-x86_64", "builtins-x86_64");
|
||||
generateLlvmIrFile(b, mode, linux_x64_target, main_path, "ir-x86_64", "builtins-x86_64");
|
||||
generateLlvmIrFile(b, mode, linux_aarch64_target, main_path, "ir-aarch64", "builtins-aarch64");
|
||||
generateLlvmIrFile(b, mode, windows64_target, main_path, "ir-windows-x86_64", "builtins-windows-x86_64");
|
||||
generateLlvmIrFile(b, mode, wasm32_target, main_path, "ir-wasm32", "builtins-wasm32");
|
||||
|
||||
|
@ -89,6 +90,7 @@ fn generateObjectFile(
|
|||
obj.strip = true;
|
||||
obj.target = target;
|
||||
obj.link_function_sections = true;
|
||||
obj.force_pic = true;
|
||||
const obj_step = b.step(step_name, "Build object file for linking");
|
||||
obj_step.dependOn(&obj.step);
|
||||
}
|
||||
|
@ -103,7 +105,17 @@ fn makeLinux32Target() CrossTarget {
|
|||
return target;
|
||||
}
|
||||
|
||||
fn makeLinux64Target() CrossTarget {
|
||||
fn makeLinuxAarch64Target() CrossTarget {
|
||||
var target = CrossTarget.parse(.{}) catch unreachable;
|
||||
|
||||
target.cpu_arch = std.Target.Cpu.Arch.aarch64;
|
||||
target.os_tag = std.Target.Os.Tag.linux;
|
||||
target.abi = std.Target.Abi.musl;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
fn makeLinuxX64Target() CrossTarget {
|
||||
var target = CrossTarget.parse(.{}) catch unreachable;
|
||||
|
||||
target.cpu_arch = std.Target.Cpu.Arch.x86_64;
|
||||
|
|
|
@ -18,6 +18,7 @@ const v2u64 = @Vector(2, u64);
|
|||
// Export it as weak incase it is already linked in by something else.
|
||||
comptime {
|
||||
@export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak });
|
||||
@export(__lshrti3, .{ .name = "__lshrti3", .linkage = .Weak });
|
||||
if (want_windows_v2u64_abi) {
|
||||
@export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = .Weak });
|
||||
@export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = .Weak });
|
||||
|
@ -440,3 +441,47 @@ pub inline fn floatFractionalBits(comptime T: type) comptime_int {
|
|||
else => @compileError("unknown floating point type " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 {
|
||||
return lshrXi3(i128, a, b);
|
||||
}
|
||||
|
||||
// Logical shift right: shift in 0 from left to right
|
||||
// Precondition: 0 <= b < T.bit_count
|
||||
inline fn lshrXi3(comptime T: type, a: T, b: i32) T {
|
||||
const word_t = HalveInt(T, false);
|
||||
const S = std.math.Log2Int(word_t.HalfT);
|
||||
|
||||
const input = word_t{ .all = a };
|
||||
var output: word_t = undefined;
|
||||
|
||||
if (b >= word_t.bits) {
|
||||
output.s.high = 0;
|
||||
output.s.low = input.s.high >> @intCast(S, b - word_t.bits);
|
||||
} else if (b == 0) {
|
||||
return a;
|
||||
} else {
|
||||
output.s.high = input.s.high >> @intCast(S, b);
|
||||
output.s.low = input.s.high << @intCast(S, word_t.bits - b);
|
||||
output.s.low |= input.s.low >> @intCast(S, b);
|
||||
}
|
||||
|
||||
return output.all;
|
||||
}
|
||||
|
||||
/// Allows to access underlying bits as two equally sized lower and higher
|
||||
/// signed or unsigned integers.
|
||||
fn HalveInt(comptime T: type, comptime signed_half: bool) type {
|
||||
return extern union {
|
||||
pub const bits = @divExact(@typeInfo(T).Int.bits, 2);
|
||||
pub const HalfTU = std.meta.Int(.unsigned, bits);
|
||||
pub const HalfTS = std.meta.Int(.signed, bits);
|
||||
pub const HalfT = if (signed_half) HalfTS else HalfTU;
|
||||
|
||||
all: T,
|
||||
s: if (native_endian == .Little)
|
||||
extern struct { low: HalfT, high: HalfT }
|
||||
else
|
||||
extern struct { high: HalfT, low: HalfT },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -171,8 +171,8 @@ comptime {
|
|||
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
||||
exportStrFn(str.repeat, "repeat");
|
||||
exportStrFn(str.strTrim, "trim");
|
||||
exportStrFn(str.strTrimLeft, "trim_left");
|
||||
exportStrFn(str.strTrimRight, "trim_right");
|
||||
exportStrFn(str.strTrimStart, "trim_start");
|
||||
exportStrFn(str.strTrimEnd, "trim_end");
|
||||
exportStrFn(str.strCloneTo, "clone_to");
|
||||
exportStrFn(str.withCapacity, "with_capacity");
|
||||
exportStrFn(str.strGraphemes, "graphemes");
|
||||
|
@ -195,8 +195,10 @@ comptime {
|
|||
exportUtilsFn(utils.test_panic, "test_panic");
|
||||
exportUtilsFn(utils.increfRcPtrC, "incref_rc_ptr");
|
||||
exportUtilsFn(utils.decrefRcPtrC, "decref_rc_ptr");
|
||||
exportUtilsFn(utils.freeRcPtrC, "free_rc_ptr");
|
||||
exportUtilsFn(utils.increfDataPtrC, "incref_data_ptr");
|
||||
exportUtilsFn(utils.decrefDataPtrC, "decref_data_ptr");
|
||||
exportUtilsFn(utils.freeDataPtrC, "free_data_ptr");
|
||||
exportUtilsFn(utils.isUnique, "is_unique");
|
||||
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
|
||||
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
|
||||
|
|
|
@ -2302,7 +2302,7 @@ pub fn strTrim(input_string: RocStr) callconv(.C) RocStr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn strTrimLeft(input_string: RocStr) callconv(.C) RocStr {
|
||||
pub fn strTrimStart(input_string: RocStr) callconv(.C) RocStr {
|
||||
var string = input_string;
|
||||
|
||||
if (string.isEmpty()) {
|
||||
|
@ -2350,7 +2350,7 @@ pub fn strTrimLeft(input_string: RocStr) callconv(.C) RocStr {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn strTrimRight(input_string: RocStr) callconv(.C) RocStr {
|
||||
pub fn strTrimEnd(input_string: RocStr) callconv(.C) RocStr {
|
||||
var string = input_string;
|
||||
|
||||
if (string.isEmpty()) {
|
||||
|
@ -2583,22 +2583,22 @@ test "strTrim: small to small" {
|
|||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimLeft: empty" {
|
||||
const trimmedEmpty = strTrimLeft(RocStr.empty());
|
||||
test "strTrimStart: empty" {
|
||||
const trimmedEmpty = strTrimStart(RocStr.empty());
|
||||
try expect(trimmedEmpty.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimLeft: blank" {
|
||||
test "strTrimStart: blank" {
|
||||
const original_bytes = " ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
||||
const trimmed = strTrimLeft(original);
|
||||
const trimmed = strTrimStart(original);
|
||||
|
||||
try expect(trimmed.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimLeft: large to large" {
|
||||
test "strTrimStart: large to large" {
|
||||
const original_bytes = " hello even more giant world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
@ -2611,12 +2611,12 @@ test "strTrimLeft: large to large" {
|
|||
|
||||
try expect(!expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimLeft(original);
|
||||
const trimmed = strTrimStart(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
}
|
||||
|
||||
test "strTrimLeft: large to small" {
|
||||
test "strTrimStart: large to small" {
|
||||
// `original` will be consumed by the concat; do not free explicitly
|
||||
const original_bytes = " hello ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
|
@ -2629,14 +2629,14 @@ test "strTrimLeft: large to small" {
|
|||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimLeft(original);
|
||||
const trimmed = strTrimStart(original);
|
||||
defer trimmed.decref();
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(!trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimLeft: small to small" {
|
||||
test "strTrimStart: small to small" {
|
||||
const original_bytes = " hello ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
@ -2649,28 +2649,28 @@ test "strTrimLeft: small to small" {
|
|||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimLeft(original);
|
||||
const trimmed = strTrimStart(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: empty" {
|
||||
const trimmedEmpty = strTrimRight(RocStr.empty());
|
||||
test "strTrimEnd: empty" {
|
||||
const trimmedEmpty = strTrimEnd(RocStr.empty());
|
||||
try expect(trimmedEmpty.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: blank" {
|
||||
test "strTrimEnd: blank" {
|
||||
const original_bytes = " ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
const trimmed = strTrimEnd(original);
|
||||
|
||||
try expect(trimmed.eq(RocStr.empty()));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to large" {
|
||||
test "strTrimEnd: large to large" {
|
||||
const original_bytes = " hello even more giant world ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
@ -2683,12 +2683,12 @@ test "strTrimRight: large to large" {
|
|||
|
||||
try expect(!expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
const trimmed = strTrimEnd(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
}
|
||||
|
||||
test "strTrimRight: large to small" {
|
||||
test "strTrimEnd: large to small" {
|
||||
// `original` will be consumed by the concat; do not free explicitly
|
||||
const original_bytes = " hello ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
|
@ -2701,14 +2701,14 @@ test "strTrimRight: large to small" {
|
|||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
const trimmed = strTrimEnd(original);
|
||||
defer trimmed.decref();
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(!trimmed.isSmallStr());
|
||||
}
|
||||
|
||||
test "strTrimRight: small to small" {
|
||||
test "strTrimEnd: small to small" {
|
||||
const original_bytes = " hello ";
|
||||
const original = RocStr.init(original_bytes, original_bytes.len);
|
||||
defer original.decref();
|
||||
|
@ -2721,7 +2721,7 @@ test "strTrimRight: small to small" {
|
|||
|
||||
try expect(expected.isSmallStr());
|
||||
|
||||
const trimmed = strTrimRight(original);
|
||||
const trimmed = strTrimEnd(original);
|
||||
|
||||
try expect(trimmed.eq(expected));
|
||||
try expect(trimmed.isSmallStr());
|
||||
|
|
|
@ -220,6 +220,29 @@ pub fn increfDataPtrC(
|
|||
return increfRcPtrC(isizes, inc_amount);
|
||||
}
|
||||
|
||||
pub fn freeDataPtrC(
|
||||
bytes_or_null: ?[*]isize,
|
||||
alignment: u32,
|
||||
) callconv(.C) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
|
||||
const ptr = @ptrToInt(bytes);
|
||||
const tag_mask: usize = if (@sizeOf(usize) == 8) 0b111 else 0b11;
|
||||
const masked_ptr = ptr & ~tag_mask;
|
||||
|
||||
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
|
||||
|
||||
return freeRcPtrC(isizes - 1, alignment);
|
||||
}
|
||||
|
||||
pub fn freeRcPtrC(
|
||||
bytes_or_null: ?[*]isize,
|
||||
alignment: u32,
|
||||
) callconv(.C) void {
|
||||
var bytes = bytes_or_null orelse return;
|
||||
return free_ptr_to_refcount(bytes, alignment);
|
||||
}
|
||||
|
||||
pub fn decref(
|
||||
bytes_or_null: ?[*]u8,
|
||||
data_bytes: usize,
|
||||
|
@ -236,13 +259,23 @@ pub fn decref(
|
|||
decref_ptr_to_refcount(isizes - 1, alignment);
|
||||
}
|
||||
|
||||
inline fn decref_ptr_to_refcount(
|
||||
inline fn free_ptr_to_refcount(
|
||||
refcount_ptr: [*]isize,
|
||||
alignment: u32,
|
||||
) void {
|
||||
if (RC_TYPE == Refcount.none) return;
|
||||
const extra_bytes = std.math.max(alignment, @sizeOf(usize));
|
||||
|
||||
// NOTE: we don't even check whether the refcount is "infinity" here!
|
||||
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
|
||||
}
|
||||
|
||||
inline fn decref_ptr_to_refcount(
|
||||
refcount_ptr: [*]isize,
|
||||
alignment: u32,
|
||||
) void {
|
||||
if (RC_TYPE == Refcount.none) return;
|
||||
|
||||
if (DEBUG_INCDEC and builtin.target.cpu.arch != .wasm32) {
|
||||
std.debug.print("| decrement {*}: ", .{refcount_ptr});
|
||||
}
|
||||
|
@ -264,13 +297,13 @@ inline fn decref_ptr_to_refcount(
|
|||
}
|
||||
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
|
||||
free_ptr_to_refcount(refcount_ptr, alignment);
|
||||
}
|
||||
},
|
||||
Refcount.atomic => {
|
||||
var last = @atomicRmw(isize, &refcount_ptr[0], std.builtin.AtomicRmwOp.Sub, 1, Monotonic);
|
||||
if (last == REFCOUNT_ONE_ISIZE) {
|
||||
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
|
||||
free_ptr_to_refcount(refcount_ptr, alignment);
|
||||
}
|
||||
},
|
||||
Refcount.none => unreachable,
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## Most users won't need Box, it is used for:
|
||||
## - Holding unknown Roc types when developing [platforms](https://github.com/roc-lang/roc/wiki/Roc-concepts-explained#platform).
|
||||
## - To improve performance in rare cases.
|
||||
##
|
||||
interface Box
|
||||
exposes [box, unbox]
|
||||
imports []
|
||||
|
|
|
@ -7,6 +7,7 @@ interface Dict
|
|||
clear,
|
||||
capacity,
|
||||
len,
|
||||
isEmpty,
|
||||
get,
|
||||
contains,
|
||||
insert,
|
||||
|
@ -21,6 +22,8 @@ interface Dict
|
|||
insertAll,
|
||||
keepShared,
|
||||
removeAll,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool, Eq },
|
||||
|
@ -98,14 +101,14 @@ Dict k v := {
|
|||
data : List (k, v),
|
||||
size : Nat,
|
||||
} where k implements Hash & Eq
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashDict,
|
||||
},
|
||||
]
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashDict,
|
||||
},
|
||||
]
|
||||
|
||||
isEq : Dict k v, Dict k v -> Bool where k implements Hash & Eq, v implements Eq
|
||||
isEq = \xs, ys ->
|
||||
|
@ -139,12 +142,12 @@ empty = \{} ->
|
|||
## Returns the max number of elements the dictionary can hold before requiring a rehash.
|
||||
## ```
|
||||
## foodDict =
|
||||
## Dict.empty {}
|
||||
## |> Dict.insert "apple" "fruit"
|
||||
## Dict.empty {}
|
||||
## |> Dict.insert "apple" "fruit"
|
||||
##
|
||||
## capacityOfDict = Dict.capacity foodDict
|
||||
## ```
|
||||
capacity : Dict k v -> Nat where k implements Hash & Eq
|
||||
capacity : Dict * * -> Nat
|
||||
capacity = \@Dict { dataIndices } ->
|
||||
cap = List.len dataIndices
|
||||
|
||||
|
@ -192,10 +195,20 @@ fromList = \data ->
|
|||
## |> Dict.len
|
||||
## |> Bool.isEq 3
|
||||
## ```
|
||||
len : Dict k v -> Nat where k implements Hash & Eq
|
||||
len : Dict * * -> Nat
|
||||
len = \@Dict { size } ->
|
||||
size
|
||||
|
||||
## Check if the dictinoary is empty.
|
||||
## ```
|
||||
## Dict.isEmpty (Dict.empty {} |> Dict.insert "key" 42)
|
||||
##
|
||||
## Dict.isEmpty (Dict.empty {})
|
||||
## ```
|
||||
isEmpty : Dict * * -> Bool
|
||||
isEmpty = \@Dict { size } ->
|
||||
size == 0
|
||||
|
||||
## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
|
||||
## ```
|
||||
## songs =
|
||||
|
@ -225,6 +238,30 @@ clear = \@Dict { metadata, dataIndices, data } ->
|
|||
size: 0,
|
||||
}
|
||||
|
||||
## Convert each value in the dictionary to something new, by calling a conversion
|
||||
## function on each of them which receives both the key and the old value. Then return a
|
||||
## new dictionary containing the same keys and the converted values.
|
||||
map : Dict k a, (k, a -> b) -> Dict k b
|
||||
where k implements Hash & Eq, b implements Hash & Eq
|
||||
map = \dict, transform ->
|
||||
init = withCapacity (capacity dict)
|
||||
|
||||
walk dict init \answer, k, v ->
|
||||
insert answer k (transform k v)
|
||||
|
||||
## Like [Dict.map], except the transformation function wraps the return value
|
||||
## in a dictionary. At the end, all the dictionaries get joined together
|
||||
## (using [Dict.insertAll]) into one dictionary.
|
||||
##
|
||||
## You may know a similar function named `concatMap` in other languages.
|
||||
joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y
|
||||
where a implements Hash & Eq, x implements Hash & Eq
|
||||
joinMap = \dict, transform ->
|
||||
init = withCapacity (capacity dict) # Might be a pessimization
|
||||
|
||||
walk dict init \answer, k, v ->
|
||||
insertAll answer (transform k v)
|
||||
|
||||
## Iterate through the keys and values in the dictionary and call the provided
|
||||
## function with signature `state, k, v -> state` for each value, with an
|
||||
## initial `state` value provided for the first call.
|
||||
|
@ -976,16 +1013,16 @@ expect
|
|||
# TODO: Add a builtin to distinguish big endian systems and change loading orders.
|
||||
# TODO: Switch out Wymum on systems with slow 128bit multiplication.
|
||||
LowLevelHasher := { originalSeed : U64, state : U64 } implements [
|
||||
Hasher {
|
||||
addBytes,
|
||||
addU8,
|
||||
addU16,
|
||||
addU32,
|
||||
addU64,
|
||||
addU128,
|
||||
complete,
|
||||
},
|
||||
]
|
||||
Hasher {
|
||||
addBytes,
|
||||
addU8,
|
||||
addU16,
|
||||
addU32,
|
||||
addU64,
|
||||
addU128,
|
||||
complete,
|
||||
},
|
||||
]
|
||||
|
||||
# unsafe primitive that does not perform a bounds check
|
||||
# TODO hide behind an InternalList.roc module
|
||||
|
|
|
@ -208,6 +208,9 @@ interface List
|
|||
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
|
||||
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
|
||||
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
|
||||
|
||||
# separator so List.isEmpty doesn't absorb the above into its doc comment
|
||||
|
||||
## Check if the list is empty.
|
||||
## ```
|
||||
## List.isEmpty [1, 2, 3]
|
||||
|
@ -222,6 +225,13 @@ isEmpty = \list ->
|
|||
# but will cause a reference count increment on the value it got out of the list
|
||||
getUnsafe : List a, Nat -> a
|
||||
|
||||
## Returns an element from a list at the given index.
|
||||
##
|
||||
## Returns `Err OutOfBounds` if the given index exceeds the List's length
|
||||
## ```
|
||||
## expect List.get [100, 200, 300] 1 == Ok 200
|
||||
## expect List.get [100, 200, 300] 5 == Err OutOfBounds
|
||||
## ```
|
||||
get : List a, Nat -> Result a [OutOfBounds]
|
||||
get = \list, index ->
|
||||
if index < List.len list then
|
||||
|
@ -352,6 +362,10 @@ releaseExcessCapacity : List a -> List a
|
|||
concat : List a, List a -> List a
|
||||
|
||||
## Returns the last element in the list, or `ListWasEmpty` if it was empty.
|
||||
## ```
|
||||
## expect List.last [1, 2, 3] == Ok 3
|
||||
## expect List.last [] == Err ListWasEmpty
|
||||
## ```
|
||||
last : List a -> Result a [ListWasEmpty]
|
||||
last = \list ->
|
||||
when List.get list (Num.subSaturated (List.len list) 1) is
|
||||
|
@ -383,7 +397,7 @@ repeatHelp = \value, count, accum ->
|
|||
|
||||
## Returns the list with its elements reversed.
|
||||
## ```
|
||||
## List.reverse [1, 2, 3]
|
||||
## expect List.reverse [1, 2, 3] == [3, 2, 1]
|
||||
## ```
|
||||
reverse : List a -> List a
|
||||
reverse = \list ->
|
||||
|
@ -397,9 +411,9 @@ reverseHelp = \list, left, right ->
|
|||
|
||||
## Join the given lists together into one list.
|
||||
## ```
|
||||
## List.join [[1, 2, 3], [4, 5], [], [6, 7]]
|
||||
## List.join [[], []]
|
||||
## List.join []
|
||||
## expect List.join [[1], [2, 3], [], [4, 5]] == [1, 2, 3, 4, 5]
|
||||
## expect List.join [[], []] == []
|
||||
## expect List.join [] == []
|
||||
## ```
|
||||
join : List (List a) -> List a
|
||||
join = \lists ->
|
||||
|
@ -597,6 +611,10 @@ dropIf = \list, predicate ->
|
|||
|
||||
## Run the given function on each element of a list, and return the
|
||||
## number of elements for which the function returned `Bool.true`.
|
||||
## ```
|
||||
## expect List.countIf [1, -2, -3] Num.isNegative == 2
|
||||
## expect List.countIf [1, 2, 3] (\num -> num > 1 ) == 2
|
||||
## ```
|
||||
countIf : List a, (a -> Bool) -> Nat
|
||||
countIf = \list, predicate ->
|
||||
walkState = \state, elem ->
|
||||
|
@ -610,11 +628,12 @@ countIf = \list, predicate ->
|
|||
## This works like [List.map], except only the transformed values that are
|
||||
## wrapped in `Ok` are kept. Any that are wrapped in `Err` are dropped.
|
||||
## ```
|
||||
## List.keepOks [["a", "b"], [], [], ["c", "d", "e"]] List.last
|
||||
## expect List.keepOks ["1", "Two", "23", "Bird"] Str.toI32 == [1, 23]
|
||||
##
|
||||
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok (Str.len str)
|
||||
## expect List.keepOks [["a", "b"], [], ["c", "d", "e"], [] ] List.first == ["a", "c"]
|
||||
##
|
||||
## List.keepOks ["", "a", "bc", "", "d", "ef", ""]
|
||||
## fn = \str -> if Str.isEmpty str then Err StrWasEmpty else Ok str
|
||||
## expect List.keepOks ["", "a", "bc", "", "d", "ef", ""] fn == ["a", "bc", "d", "ef"]
|
||||
## ```
|
||||
keepOks : List before, (before -> Result after *) -> List after
|
||||
keepOks = \list, toResult ->
|
||||
|
@ -646,9 +665,9 @@ keepErrs = \list, toResult ->
|
|||
## Convert each element in the list to something new, by calling a conversion
|
||||
## function on each of them. Then return a new list of the converted values.
|
||||
## ```
|
||||
## List.map [1, 2, 3] (\num -> num + 1)
|
||||
## expect List.map [1, 2, 3] (\num -> num + 1) == [2, 3, 4]
|
||||
##
|
||||
## List.map ["", "a", "bc"] Str.isEmpty
|
||||
## expect List.map ["", "a", "bc"] Str.isEmpty == [Bool.true, Bool.false, Bool.false]
|
||||
## ```
|
||||
map : List a, (a -> b) -> List b
|
||||
|
||||
|
@ -675,6 +694,9 @@ map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
|
|||
|
||||
## This works like [List.map], except it also passes the index
|
||||
## of the element to the conversion function.
|
||||
## ```
|
||||
## expect List.mapWithIndex [10, 20, 30] (\num, index -> num + index) == [10, 21, 32]
|
||||
## ```
|
||||
mapWithIndex : List a, (a, Nat -> b) -> List b
|
||||
mapWithIndex = \src, func ->
|
||||
length = len src
|
||||
|
@ -1160,8 +1182,17 @@ mapTry = \list, toResult ->
|
|||
Result.map (toResult elem) \ok ->
|
||||
List.append state ok
|
||||
|
||||
## This is the same as `iterate` but with [Result] instead of `[Continue, Break]`.
|
||||
## Using `Result` saves a conditional in `mapTry`.
|
||||
## Same as [List.walk], except you can stop walking early by returning `Err`.
|
||||
##
|
||||
## ## Performance Details
|
||||
##
|
||||
## Compared to [List.walk], this can potentially visit fewer elements (which can
|
||||
## improve performance) at the cost of making each step take longer.
|
||||
## However, the added cost to each step is extremely small, and can easily
|
||||
## be outweighed if it results in skipping even a small number of elements.
|
||||
##
|
||||
## As such, it is typically better for performance to use this over [List.walk]
|
||||
## if returning `Break` earlier than the last element is expected to be common.
|
||||
walkTry : List elem, state, (state, elem -> Result state err) -> Result state err
|
||||
walkTry = \list, init, func ->
|
||||
walkTryHelp list init func 0 (List.len list)
|
||||
|
|
|
@ -33,12 +33,17 @@ interface Num
|
|||
Decimal,
|
||||
Binary32,
|
||||
Binary64,
|
||||
e,
|
||||
pi,
|
||||
tau,
|
||||
abs,
|
||||
absDiff,
|
||||
neg,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
min,
|
||||
max,
|
||||
isLt,
|
||||
isLte,
|
||||
isGt,
|
||||
|
@ -81,6 +86,7 @@ interface Num
|
|||
bitwiseAnd,
|
||||
bitwiseXor,
|
||||
bitwiseOr,
|
||||
bitwiseNot,
|
||||
shiftLeftBy,
|
||||
shiftRightBy,
|
||||
shiftRightZfBy,
|
||||
|
@ -492,6 +498,18 @@ F32 : Num (FloatingPoint Binary32)
|
|||
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
|
||||
Dec : Num (FloatingPoint Decimal)
|
||||
|
||||
## Euler's number (e)
|
||||
e : Frac *
|
||||
e = 2.71828182845904523536028747135266249775724709369995
|
||||
|
||||
## Archimedes' constant (π)
|
||||
pi : Frac *
|
||||
pi = 3.14159265358979323846264338327950288419716939937510
|
||||
|
||||
## Circle constant (τ)
|
||||
tau : Frac *
|
||||
tau = 2 * pi
|
||||
|
||||
# ------- Functions
|
||||
## Convert a number to a [Str].
|
||||
##
|
||||
|
@ -777,6 +795,34 @@ sub : Num a, Num a -> Num a
|
|||
## ∞ or -∞. For all other number types, overflow results in a panic.
|
||||
mul : Num a, Num a -> Num a
|
||||
|
||||
## Obtains the smaller between two numbers of the same type.
|
||||
##
|
||||
## ```
|
||||
## Num.min 100 0
|
||||
##
|
||||
## Num.min 3.0 -3.0
|
||||
## ```
|
||||
min : Num a, Num a -> Num a
|
||||
min = \a, b ->
|
||||
if a < b then
|
||||
a
|
||||
else
|
||||
b
|
||||
|
||||
## Obtains the greater between two numbers of the same type.
|
||||
##
|
||||
## ```
|
||||
## Num.max 100 0
|
||||
##
|
||||
## Num.max 3.0 -3.0
|
||||
## ```
|
||||
max : Num a, Num a -> Num a
|
||||
max = \a, b ->
|
||||
if a > b then
|
||||
a
|
||||
else
|
||||
b
|
||||
|
||||
sin : Frac a -> Frac a
|
||||
cos : Frac a -> Frac a
|
||||
|
||||
|
@ -930,10 +976,25 @@ remChecked = \a, b ->
|
|||
|
||||
isMultipleOf : Int a, Int a -> Bool
|
||||
|
||||
## Does a "bitwise and". Each bit of the output is 1 if the corresponding bit
|
||||
## of x AND of y is 1, otherwise it's 0.
|
||||
bitwiseAnd : Int a, Int a -> Int a
|
||||
|
||||
## Does a "bitwise exclusive or". Each bit of the output is the same as the
|
||||
## corresponding bit in x if that bit in y is 0, and it's the complement of
|
||||
## the bit in x if that bit in y is 1.
|
||||
bitwiseXor : Int a, Int a -> Int a
|
||||
|
||||
## Does a "bitwise or". Each bit of the output is 0 if the corresponding bit
|
||||
## of x OR of y is 0, otherwise it's 1.
|
||||
bitwiseOr : Int a, Int a -> Int a
|
||||
|
||||
## Returns the complement of x - the number you get by switching each 1 for a
|
||||
## 0 and each 0 for a 1. This is the same as -x - 1.
|
||||
bitwiseNot : Int a -> Int a
|
||||
bitwiseNot = \n ->
|
||||
bitwiseXor n (subWrap 0 1)
|
||||
|
||||
## Bitwise left shift of a number by another
|
||||
##
|
||||
## The least significant bits always become 0. This means that shifting left is
|
||||
|
|
|
@ -42,8 +42,8 @@ withDefault = \result, default ->
|
|||
## function on it. Then returns a new `Ok` holding the transformed value. If the
|
||||
## result is `Err`, this has no effect. Use [mapErr] to transform an `Err`.
|
||||
## ```
|
||||
## Result.map (Ok 12) Num.negate
|
||||
## Result.map (Err "yipes!") Num.negate
|
||||
## Result.map (Ok 12) Num.neg
|
||||
## Result.map (Err "yipes!") Num.neg
|
||||
## ```
|
||||
##
|
||||
## Functions like `map` are common in Roc; see for example [List.map],
|
||||
|
|
|
@ -7,6 +7,8 @@ interface Set
|
|||
walkUntil,
|
||||
insert,
|
||||
len,
|
||||
isEmpty,
|
||||
capacity,
|
||||
remove,
|
||||
contains,
|
||||
toList,
|
||||
|
@ -14,6 +16,8 @@ interface Set
|
|||
union,
|
||||
intersection,
|
||||
difference,
|
||||
map,
|
||||
joinMap,
|
||||
]
|
||||
imports [
|
||||
List,
|
||||
|
@ -26,14 +30,14 @@ interface Set
|
|||
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
|
||||
## type which stores a collection of unique values, without any ordering
|
||||
Set k := Dict.Dict k {} where k implements Hash & Eq
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashSet,
|
||||
},
|
||||
]
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashSet,
|
||||
},
|
||||
]
|
||||
|
||||
isEq : Set k, Set k -> Bool where k implements Hash & Eq
|
||||
isEq = \xs, ys ->
|
||||
|
@ -59,6 +63,13 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
|||
empty : {} -> Set k where k implements Hash & Eq
|
||||
empty = \{} -> @Set (Dict.empty {})
|
||||
|
||||
## Return a dictionary with space allocated for a number of entries. This
|
||||
## may provide a performance optimization if you know how many entries will be
|
||||
## inserted.
|
||||
withCapacity : Nat -> Set k where k implements Hash & Eq
|
||||
withCapacity = \cap ->
|
||||
@Set (Dict.withCapacity cap)
|
||||
|
||||
## Creates a new `Set` with a single value.
|
||||
## ```
|
||||
## singleItemSet = Set.single "Apple"
|
||||
|
@ -115,10 +126,32 @@ expect
|
|||
##
|
||||
## expect countValues == 3
|
||||
## ```
|
||||
len : Set k -> Nat where k implements Hash & Eq
|
||||
len : Set * -> Nat
|
||||
len = \@Set dict ->
|
||||
Dict.len dict
|
||||
|
||||
## Returns the max number of elements the set can hold before requiring a rehash.
|
||||
## ```
|
||||
## foodSet =
|
||||
## Set.empty {}
|
||||
## |> Set.insert "apple"
|
||||
##
|
||||
## capacityOfSet = Set.capacity foodSet
|
||||
## ```
|
||||
capacity : Set * -> Nat
|
||||
capacity = \@Set dict ->
|
||||
Dict.capacity dict
|
||||
|
||||
## Check if the set is empty.
|
||||
## ```
|
||||
## Set.isEmpty (Set.empty {} |> Set.insert 42)
|
||||
##
|
||||
## Set.isEmpty (Set.empty {})
|
||||
## ```
|
||||
isEmpty : Set * -> Bool
|
||||
isEmpty = \@Set dict ->
|
||||
Dict.isEmpty dict
|
||||
|
||||
# Inserting a duplicate key has no effect on length.
|
||||
expect
|
||||
actual =
|
||||
|
@ -261,6 +294,28 @@ walk : Set k, state, (state, k -> state) -> state where k implements Hash & Eq
|
|||
walk = \@Set dict, state, step ->
|
||||
Dict.walk dict state (\s, k, _ -> step s k)
|
||||
|
||||
## Convert each value in the set to something new, by calling a conversion
|
||||
## function on each of them which receives the old value. Then return a
|
||||
## new set containing the converted values.
|
||||
map : Set a, (a -> b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
map = \set, transform ->
|
||||
init = withCapacity (capacity set)
|
||||
|
||||
walk set init \answer, k ->
|
||||
insert answer (transform k)
|
||||
|
||||
## Like [Set.map], except the transformation function wraps the return value
|
||||
## in a set. At the end, all the sets get joined together
|
||||
## (using [Set.union]) into one set.
|
||||
##
|
||||
## You may know a similar function named `concatMap` in other languages.
|
||||
joinMap : Set a, (a -> Set b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
joinMap = \set, transform ->
|
||||
init = withCapacity (capacity set) # Might be a pessimization
|
||||
|
||||
walk set init \answer, k ->
|
||||
union answer (transform k)
|
||||
|
||||
## Iterate through the values of a given `Set` and build a value, can stop
|
||||
## iterating part way through the collection.
|
||||
## ```
|
||||
|
|
|
@ -103,8 +103,8 @@ interface Str
|
|||
startsWith,
|
||||
endsWith,
|
||||
trim,
|
||||
trimLeft,
|
||||
trimRight,
|
||||
trimStart,
|
||||
trimEnd,
|
||||
toDec,
|
||||
toF64,
|
||||
toF32,
|
||||
|
@ -448,15 +448,15 @@ trim : Str -> Str
|
|||
|
||||
## Return the [Str] with all whitespace removed from the beginning.
|
||||
## ```
|
||||
## expect Str.trimLeft " Hello \n\n" == "Hello \n\n"
|
||||
## expect Str.trimStart " Hello \n\n" == "Hello \n\n"
|
||||
## ```
|
||||
trimLeft : Str -> Str
|
||||
trimStart : Str -> Str
|
||||
|
||||
## Return the [Str] with all whitespace removed from the end.
|
||||
## ```
|
||||
## expect Str.trimRight " Hello \n\n" == " Hello"
|
||||
## expect Str.trimEnd " Hello \n\n" == " Hello"
|
||||
## ```
|
||||
trimRight : Str -> Str
|
||||
trimEnd : Str -> Str
|
||||
|
||||
## Encode a [Str] to a [Dec]. A [Dec] value is a 128-bit decimal
|
||||
## [fixed-point number](https://en.wikipedia.org/wiki/Fixed-point_arithmetic).
|
||||
|
@ -638,12 +638,13 @@ countUtf8Bytes : Str -> Nat
|
|||
substringUnsafe : Str, Nat, Nat -> Str
|
||||
|
||||
## Returns the given [Str] with each occurrence of a substring replaced.
|
||||
## Returns [Err NotFound] if the substring is not found.
|
||||
## If the substring is not found, returns the original string.
|
||||
##
|
||||
## ```
|
||||
## expect Str.replaceEach "foo/bar/baz" "/" "_" == Ok "foo_bar_baz"
|
||||
## expect Str.replaceEach "not here" "/" "_" == Err NotFound
|
||||
## expect Str.replaceEach "foo/bar/baz" "/" "_" == "foo_bar_baz"
|
||||
## expect Str.replaceEach "not here" "/" "_" == "not here"
|
||||
## ```
|
||||
replaceEach : Str, Str, Str -> Result Str [NotFound]
|
||||
replaceEach : Str, Str, Str -> Str
|
||||
replaceEach = \haystack, needle, flower ->
|
||||
when splitFirst haystack needle is
|
||||
Ok { before, after } ->
|
||||
|
@ -653,9 +654,8 @@ replaceEach = \haystack, needle, flower ->
|
|||
|> Str.concat before
|
||||
|> Str.concat flower
|
||||
|> replaceEachHelp after needle flower
|
||||
|> Ok
|
||||
|
||||
Err err -> Err err
|
||||
Err NotFound -> haystack
|
||||
|
||||
replaceEachHelp : Str, Str, Str, Str -> Str
|
||||
replaceEachHelp = \buf, haystack, needle, flower ->
|
||||
|
@ -668,39 +668,44 @@ replaceEachHelp = \buf, haystack, needle, flower ->
|
|||
|
||||
Err NotFound -> Str.concat buf haystack
|
||||
|
||||
expect Str.replaceEach "abXdeXghi" "X" "_" == Ok "ab_de_ghi"
|
||||
expect Str.replaceEach "abXdeXghi" "X" "_" == "ab_de_ghi"
|
||||
expect Str.replaceEach "abcdefg" "nothing" "_" == "abcdefg"
|
||||
|
||||
## Returns the given [Str] with the first occurrence of a substring replaced.
|
||||
## Returns [Err NotFound] if the substring is not found.
|
||||
## If the substring is not found, returns the original string.
|
||||
##
|
||||
## ```
|
||||
## expect Str.replaceFirst "foo/bar/baz" "/" "_" == Ok "foo_bar/baz"
|
||||
## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound
|
||||
## expect Str.replaceFirst "foo/bar/baz" "/" "_" == "foo_bar/baz"
|
||||
## expect Str.replaceFirst "no slashes here" "/" "_" == "no slashes here"
|
||||
## ```
|
||||
replaceFirst : Str, Str, Str -> Result Str [NotFound]
|
||||
replaceFirst : Str, Str, Str -> Str
|
||||
replaceFirst = \haystack, needle, flower ->
|
||||
when splitFirst haystack needle is
|
||||
Ok { before, after } ->
|
||||
Ok "\(before)\(flower)\(after)"
|
||||
"\(before)\(flower)\(after)"
|
||||
|
||||
Err err -> Err err
|
||||
Err NotFound -> haystack
|
||||
|
||||
expect Str.replaceFirst "abXdeXghi" "X" "_" == Ok "ab_deXghi"
|
||||
expect Str.replaceFirst "abXdeXghi" "X" "_" == "ab_deXghi"
|
||||
expect Str.replaceFirst "abcdefg" "nothing" "_" == "abcdefg"
|
||||
|
||||
## Returns the given [Str] with the last occurrence of a substring replaced.
|
||||
## Returns [Err NotFound] if the substring is not found.
|
||||
## If the substring is not found, returns the original string.
|
||||
##
|
||||
## ```
|
||||
## expect Str.replaceLast "foo/bar/baz" "/" "_" == Ok "foo/bar_baz"
|
||||
## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound
|
||||
## expect Str.replaceLast "foo/bar/baz" "/" "_" == "foo/bar_baz"
|
||||
## expect Str.replaceLast "no slashes here" "/" "_" == "no slashes here"
|
||||
## ```
|
||||
replaceLast : Str, Str, Str -> Result Str [NotFound]
|
||||
replaceLast : Str, Str, Str -> Str
|
||||
replaceLast = \haystack, needle, flower ->
|
||||
when splitLast haystack needle is
|
||||
Ok { before, after } ->
|
||||
Ok "\(before)\(flower)\(after)"
|
||||
"\(before)\(flower)\(after)"
|
||||
|
||||
Err err -> Err err
|
||||
Err NotFound -> haystack
|
||||
|
||||
expect Str.replaceLast "abXdeXghi" "X" "_" == Ok "abXde_ghi"
|
||||
expect Str.replaceLast "abXdeXghi" "X" "_" == "abXde_ghi"
|
||||
expect Str.replaceLast "abcdefg" "nothing" "_" == "abcdefg"
|
||||
|
||||
## Returns the given [Str] before the first occurrence of a [delimiter](https://www.computerhope.com/jargon/d/delimite.htm), as well
|
||||
## as the rest of the string after that occurrence.
|
||||
|
|
|
@ -44,49 +44,49 @@ interface TotallyNotJson
|
|||
## An opaque type with the `EncoderFormatting` and
|
||||
## `DecoderFormatting` abilities.
|
||||
Json := { fieldNameMapping : FieldNameMapping }
|
||||
implements [
|
||||
EncoderFormatting {
|
||||
u8: encodeU8,
|
||||
u16: encodeU16,
|
||||
u32: encodeU32,
|
||||
u64: encodeU64,
|
||||
u128: encodeU128,
|
||||
i8: encodeI8,
|
||||
i16: encodeI16,
|
||||
i32: encodeI32,
|
||||
i64: encodeI64,
|
||||
i128: encodeI128,
|
||||
f32: encodeF32,
|
||||
f64: encodeF64,
|
||||
dec: encodeDec,
|
||||
bool: encodeBool,
|
||||
string: encodeString,
|
||||
list: encodeList,
|
||||
record: encodeRecord,
|
||||
tuple: encodeTuple,
|
||||
tag: encodeTag,
|
||||
},
|
||||
DecoderFormatting {
|
||||
u8: decodeU8,
|
||||
u16: decodeU16,
|
||||
u32: decodeU32,
|
||||
u64: decodeU64,
|
||||
u128: decodeU128,
|
||||
i8: decodeI8,
|
||||
i16: decodeI16,
|
||||
i32: decodeI32,
|
||||
i64: decodeI64,
|
||||
i128: decodeI128,
|
||||
f32: decodeF32,
|
||||
f64: decodeF64,
|
||||
dec: decodeDec,
|
||||
bool: decodeBool,
|
||||
string: decodeString,
|
||||
list: decodeList,
|
||||
record: decodeRecord,
|
||||
tuple: decodeTuple,
|
||||
},
|
||||
]
|
||||
implements [
|
||||
EncoderFormatting {
|
||||
u8: encodeU8,
|
||||
u16: encodeU16,
|
||||
u32: encodeU32,
|
||||
u64: encodeU64,
|
||||
u128: encodeU128,
|
||||
i8: encodeI8,
|
||||
i16: encodeI16,
|
||||
i32: encodeI32,
|
||||
i64: encodeI64,
|
||||
i128: encodeI128,
|
||||
f32: encodeF32,
|
||||
f64: encodeF64,
|
||||
dec: encodeDec,
|
||||
bool: encodeBool,
|
||||
string: encodeString,
|
||||
list: encodeList,
|
||||
record: encodeRecord,
|
||||
tuple: encodeTuple,
|
||||
tag: encodeTag,
|
||||
},
|
||||
DecoderFormatting {
|
||||
u8: decodeU8,
|
||||
u16: decodeU16,
|
||||
u32: decodeU32,
|
||||
u64: decodeU64,
|
||||
u128: decodeU128,
|
||||
i8: decodeI8,
|
||||
i16: decodeI16,
|
||||
i32: decodeI32,
|
||||
i64: decodeI64,
|
||||
i128: decodeI128,
|
||||
f32: decodeF32,
|
||||
f64: decodeF64,
|
||||
dec: decodeDec,
|
||||
bool: decodeBool,
|
||||
string: decodeString,
|
||||
list: decodeList,
|
||||
record: decodeRecord,
|
||||
tuple: decodeTuple,
|
||||
},
|
||||
]
|
||||
|
||||
## Returns a JSON `Encoder` and `Decoder`
|
||||
json = @Json { fieldNameMapping: Default }
|
||||
|
|
|
@ -339,8 +339,8 @@ pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
|||
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
||||
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
|
||||
pub const STR_TRIM: &str = "roc_builtins.str.trim";
|
||||
pub const STR_TRIM_LEFT: &str = "roc_builtins.str.trim_left";
|
||||
pub const STR_TRIM_RIGHT: &str = "roc_builtins.str.trim_right";
|
||||
pub const STR_TRIM_START: &str = "roc_builtins.str.trim_start";
|
||||
pub const STR_TRIM_END: &str = "roc_builtins.str.trim_end";
|
||||
pub const STR_GET_UNSAFE: &str = "roc_builtins.str.get_unsafe";
|
||||
pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
|
||||
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
|
||||
|
@ -393,8 +393,10 @@ pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
|
|||
pub const UTILS_ALLOCATE_WITH_REFCOUNT: &str = "roc_builtins.utils.allocate_with_refcount";
|
||||
pub const UTILS_INCREF_RC_PTR: &str = "roc_builtins.utils.incref_rc_ptr";
|
||||
pub const UTILS_DECREF_RC_PTR: &str = "roc_builtins.utils.decref_rc_ptr";
|
||||
pub const UTILS_FREE_RC_PTR: &str = "roc_builtins.utils.free_rc_ptr";
|
||||
pub const UTILS_INCREF_DATA_PTR: &str = "roc_builtins.utils.incref_data_ptr";
|
||||
pub const UTILS_DECREF_DATA_PTR: &str = "roc_builtins.utils.decref_data_ptr";
|
||||
pub const UTILS_FREE_DATA_PTR: &str = "roc_builtins.utils.free_data_ptr";
|
||||
pub const UTILS_IS_UNIQUE: &str = "roc_builtins.utils.is_unique";
|
||||
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
|
||||
pub const UTILS_DICT_PSEUDO_SEED: &str = "roc_builtins.utils.dict_pseudo_seed";
|
||||
|
|
|
@ -469,8 +469,7 @@ impl IAbilitiesStore<Resolved> {
|
|||
|
||||
debug_assert!(
|
||||
old_specialization.is_none(),
|
||||
"Existing resolution: {:?}",
|
||||
old_specialization
|
||||
"Existing resolution: {old_specialization:?}"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -847,29 +847,17 @@ fn can_annotation_help(
|
|||
let alias = scope.lookup_alias(symbol).unwrap();
|
||||
local_aliases.insert(symbol, alias.clone());
|
||||
|
||||
if vars.is_empty() && env.home == symbol.module_id() {
|
||||
let actual_var = var_store.fresh();
|
||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||
Type::HostExposedAlias {
|
||||
name: symbol,
|
||||
type_arguments: vars,
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
actual_var,
|
||||
}
|
||||
} else {
|
||||
Type::Alias {
|
||||
symbol,
|
||||
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
infer_ext_in_output_types: alias
|
||||
.infer_ext_in_output_variables
|
||||
.iter()
|
||||
.map(|v| Type::Variable(*v))
|
||||
.collect(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
kind: alias.kind,
|
||||
}
|
||||
Type::Alias {
|
||||
symbol,
|
||||
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
infer_ext_in_output_types: alias
|
||||
.infer_ext_in_output_variables
|
||||
.iter()
|
||||
.map(|v| Type::Variable(*v))
|
||||
.collect(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
kind: alias.kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,13 +85,19 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
|||
// these are used internally and not tied to a symbol
|
||||
LowLevel::Hash => unimplemented!(),
|
||||
LowLevel::PtrCast => unimplemented!(),
|
||||
LowLevel::PtrWrite => unimplemented!(),
|
||||
LowLevel::PtrStore => unimplemented!(),
|
||||
LowLevel::PtrLoad => unimplemented!(),
|
||||
LowLevel::PtrClearTagId => unimplemented!(),
|
||||
LowLevel::RefCountIncRcPtr => unimplemented!(),
|
||||
LowLevel::RefCountDecRcPtr=> unimplemented!(),
|
||||
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
||||
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
||||
LowLevel::RefCountIsUnique => unimplemented!(),
|
||||
|
||||
LowLevel::SetJmp => unimplemented!(),
|
||||
LowLevel::LongJmp => unimplemented!(),
|
||||
LowLevel::SetLongJmpBuffer => unimplemented!(),
|
||||
|
||||
// these are not implemented, not sure why
|
||||
LowLevel::StrFromInt => unimplemented!(),
|
||||
LowLevel::StrFromFloat => unimplemented!(),
|
||||
|
@ -118,8 +124,8 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
StrToUtf8; STR_TO_UTF8; 1,
|
||||
StrRepeat; STR_REPEAT; 2,
|
||||
StrTrim; STR_TRIM; 1,
|
||||
StrTrimLeft; STR_TRIM_LEFT; 1,
|
||||
StrTrimRight; STR_TRIM_RIGHT; 1,
|
||||
StrTrimStart; STR_TRIM_START; 1,
|
||||
StrTrimEnd; STR_TRIM_END; 1,
|
||||
StrToScalars; STR_TO_SCALARS; 1,
|
||||
StrGetUnsafe; STR_GET_UNSAFE; 2,
|
||||
StrSubstringUnsafe; STR_SUBSTRING_UNSAFE; 3,
|
||||
|
|
|
@ -176,7 +176,7 @@ impl Constraints {
|
|||
|
||||
let mut buf = String::new();
|
||||
|
||||
writeln!(buf, "Constraints statistics for module {:?}:", module_id)?;
|
||||
writeln!(buf, "Constraints statistics for module {module_id:?}:")?;
|
||||
|
||||
writeln!(buf, " constraints length: {}:", self.constraints.len())?;
|
||||
writeln!(
|
||||
|
@ -833,16 +833,16 @@ impl std::fmt::Debug for Constraint {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Eq(Eq(arg0, arg1, arg2, arg3)) => {
|
||||
write!(f, "Eq({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
|
||||
write!(f, "Eq({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
|
||||
}
|
||||
Self::Store(arg0, arg1, arg2, arg3) => {
|
||||
write!(f, "Store({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
|
||||
write!(f, "Store({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
|
||||
}
|
||||
Self::Lookup(arg0, arg1, arg2) => {
|
||||
write!(f, "Lookup({:?}, {:?}, {:?})", arg0, arg1, arg2)
|
||||
write!(f, "Lookup({arg0:?}, {arg1:?}, {arg2:?})")
|
||||
}
|
||||
Self::Pattern(arg0, arg1, arg2, arg3) => {
|
||||
write!(f, "Pattern({:?}, {:?}, {:?}, {:?})", arg0, arg1, arg2, arg3)
|
||||
write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
|
||||
}
|
||||
Self::True => write!(f, "True"),
|
||||
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
|
||||
|
@ -851,27 +851,19 @@ impl std::fmt::Debug for Constraint {
|
|||
Self::IsOpenType(arg0) => f.debug_tuple("IsOpenType").field(arg0).finish(),
|
||||
Self::IncludesTag(arg0) => f.debug_tuple("IncludesTag").field(arg0).finish(),
|
||||
Self::PatternPresence(arg0, arg1, arg2, arg3) => {
|
||||
write!(
|
||||
f,
|
||||
"PatternPresence({:?}, {:?}, {:?}, {:?})",
|
||||
arg0, arg1, arg2, arg3
|
||||
)
|
||||
write!(f, "PatternPresence({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
|
||||
}
|
||||
Self::Exhaustive(arg0, arg1, arg2, arg3) => {
|
||||
write!(
|
||||
f,
|
||||
"Exhaustive({:?}, {:?}, {:?}, {:?})",
|
||||
arg0, arg1, arg2, arg3
|
||||
)
|
||||
write!(f, "Exhaustive({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
|
||||
}
|
||||
Self::Resolve(arg0) => {
|
||||
write!(f, "Resolve({:?})", arg0)
|
||||
write!(f, "Resolve({arg0:?})")
|
||||
}
|
||||
Self::CheckCycle(arg0, arg1) => {
|
||||
write!(f, "CheckCycle({:?}, {:?})", arg0, arg1)
|
||||
write!(f, "CheckCycle({arg0:?}, {arg1:?})")
|
||||
}
|
||||
Self::IngestedFile(arg0, arg1, arg2) => {
|
||||
write!(f, "IngestedFile({:?}, {:?}, {:?})", arg0, arg1, arg2)
|
||||
write!(f, "IngestedFile({arg0:?}, {arg1:?}, {arg2:?})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ trait CopyEnv {
|
|||
if descriptor.copy.into_variable().is_some() {
|
||||
descriptor.copy = OptVariable::NONE;
|
||||
} else {
|
||||
debug_assert!(false, "{:?} marked as copied but it wasn't", var);
|
||||
debug_assert!(false, "{var:?} marked as copied but it wasn't");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
|||
})
|
||||
})
|
||||
}
|
||||
ErasedLambda => ErasedLambda,
|
||||
|
||||
RangedNumber(range) => {
|
||||
perform_clone!(RangedNumber(range))
|
||||
|
@ -1320,7 +1321,7 @@ mod test {
|
|||
FlexVar(Some(name)) => {
|
||||
assert_eq!(subs[*name].as_str(), "a");
|
||||
}
|
||||
it => panic!("{:?}", it),
|
||||
it => panic!("{it:?}"),
|
||||
}
|
||||
assert_eq!(var, variant_var);
|
||||
assert!(matches!(
|
||||
|
@ -1337,7 +1338,7 @@ mod test {
|
|||
FlexVar(Some(name)) => {
|
||||
assert_eq!(subs[*name].as_str(), "b");
|
||||
}
|
||||
it => panic!("{:?}", it),
|
||||
it => panic!("{it:?}"),
|
||||
}
|
||||
|
||||
match arg.value {
|
||||
|
@ -1355,10 +1356,10 @@ mod test {
|
|||
assert_eq!(name.0.as_str(), "G");
|
||||
assert_eq!(arguments.len(), 0);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
e => panic!("{e:?}"),
|
||||
}
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
e => panic!("{e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1403,7 +1404,7 @@ mod test {
|
|||
FlexVar(Some(name)) => {
|
||||
assert_eq!(target[*name].as_str(), "a");
|
||||
}
|
||||
it => panic!("{:?}", it),
|
||||
it => panic!("{it:?}"),
|
||||
}
|
||||
assert_eq!(var, variant_var);
|
||||
assert!(matches!(
|
||||
|
@ -1418,7 +1419,7 @@ mod test {
|
|||
FlexVar(Some(name)) => {
|
||||
assert_eq!(target[*name].as_str(), "b");
|
||||
}
|
||||
it => panic!("{:?}", it),
|
||||
it => panic!("{it:?}"),
|
||||
}
|
||||
|
||||
match arg.value {
|
||||
|
@ -1436,10 +1437,10 @@ mod test {
|
|||
assert_eq!(name.0.as_str(), "G");
|
||||
assert_eq!(arguments.len(), 0);
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
e => panic!("{e:?}"),
|
||||
}
|
||||
}
|
||||
e => panic!("{:?}", e),
|
||||
e => panic!("{e:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,23 @@ pub struct Annotation {
|
|||
pub region: Region,
|
||||
}
|
||||
|
||||
impl Annotation {
|
||||
fn freshen(mut self, var_store: &mut VarStore) -> Self {
|
||||
let mut substitutions = MutMap::default();
|
||||
|
||||
for v in self.introduced_variables.lambda_sets.iter_mut() {
|
||||
let new = var_store.fresh();
|
||||
substitutions.insert(*v, new);
|
||||
|
||||
*v = new;
|
||||
}
|
||||
|
||||
self.signature.substitute_variables(&substitutions);
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CanDefs {
|
||||
defs: Vec<Option<Def>>,
|
||||
|
@ -1069,8 +1086,7 @@ fn canonicalize_value_defs<'a>(
|
|||
debug_assert_eq!(env.home, s.module_id());
|
||||
debug_assert!(
|
||||
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
|
||||
"{:?}",
|
||||
s
|
||||
"{s:?}"
|
||||
);
|
||||
|
||||
symbol_to_index.push((s.ident_id(), def_index as u32));
|
||||
|
@ -1639,6 +1655,14 @@ pub(crate) fn sort_can_defs_new(
|
|||
}
|
||||
};
|
||||
|
||||
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||
def.annotation
|
||||
.clone()
|
||||
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if is_initial && !exposed_symbols.contains(&symbol) {
|
||||
env.problem(Problem::DefsOnlyUsedInRecursion(1, def.region()));
|
||||
}
|
||||
|
@ -1650,6 +1674,7 @@ pub(crate) fn sort_can_defs_new(
|
|||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
specializes,
|
||||
);
|
||||
}
|
||||
|
@ -1659,55 +1684,80 @@ pub(crate) fn sort_can_defs_new(
|
|||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
specializes,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match def.loc_pattern.value {
|
||||
Pattern::Identifier(symbol) => match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
declarations.push_function_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
);
|
||||
Pattern::Identifier(symbol) => {
|
||||
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||
def.annotation
|
||||
.clone()
|
||||
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
declarations.push_function_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
None,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
declarations.push_value_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
declarations.push_value_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
Pattern::AbilityMemberSpecialization {
|
||||
ident: symbol,
|
||||
specializes,
|
||||
} => match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
declarations.push_function_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
Some(specializes),
|
||||
);
|
||||
} => {
|
||||
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||
def.annotation
|
||||
.clone()
|
||||
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
declarations.push_function_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
Some(specializes),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
declarations.push_value_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
Some(specializes),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
declarations.push_value_def(
|
||||
Loc::at(def.loc_pattern.region, symbol),
|
||||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
Some(specializes),
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
declarations.push_destructure_def(
|
||||
def.loc_pattern,
|
||||
|
@ -1750,6 +1800,14 @@ pub(crate) fn sort_can_defs_new(
|
|||
Some(r) => Some(Region::span_across(&r, &def.region())),
|
||||
};
|
||||
|
||||
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||
def.annotation
|
||||
.clone()
|
||||
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match def.loc_expr.value {
|
||||
Closure(closure_data) => {
|
||||
declarations.push_recursive_def(
|
||||
|
@ -1757,6 +1815,7 @@ pub(crate) fn sort_can_defs_new(
|
|||
Loc::at(def.loc_expr.region, closure_data),
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
specializes,
|
||||
);
|
||||
}
|
||||
|
@ -1766,6 +1825,7 @@ pub(crate) fn sort_can_defs_new(
|
|||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
specializes,
|
||||
);
|
||||
}
|
||||
|
@ -1838,7 +1898,7 @@ pub(crate) fn sort_can_defs(
|
|||
);
|
||||
|
||||
let declaration = if def_ordering.references.get_row_col(index, index) {
|
||||
debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {:?} now, that's a bug!", def);
|
||||
debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {def:?} now, that's a bug!");
|
||||
|
||||
if is_initial
|
||||
&& !def
|
||||
|
@ -2272,12 +2332,18 @@ fn canonicalize_pending_body<'a>(
|
|||
|
||||
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
|
||||
) -> DefOutput {
|
||||
let mut loc_value = &loc_expr.value;
|
||||
|
||||
while let ast::Expr::ParensAround(value) = loc_value {
|
||||
loc_value = value;
|
||||
}
|
||||
|
||||
// We treat closure definitions `foo = \a, b -> ...` differently from other body expressions,
|
||||
// because they need more bookkeeping (for tail calls, closure captures, etc.)
|
||||
//
|
||||
// Only defs of the form `foo = ...` can be closure declarations or self tail calls.
|
||||
let (loc_can_expr, def_references) = {
|
||||
match (&loc_can_pattern.value, &loc_expr.value) {
|
||||
match (&loc_can_pattern.value, &loc_value) {
|
||||
(
|
||||
Pattern::Identifier(defined_symbol)
|
||||
| Pattern::AbilityMemberSpecialization {
|
||||
|
|
|
@ -222,16 +222,16 @@ pub(crate) fn synthesize_member_impl<'a>(
|
|||
ability_member: Symbol,
|
||||
) -> (Symbol, Loc<Pattern>, &'a Loc<ast::Expr<'a>>) {
|
||||
// @Opaq
|
||||
let at_opaque = env.arena.alloc_str(&format!("@{}", opaque_name));
|
||||
let at_opaque = env.arena.alloc_str(&format!("@{opaque_name}"));
|
||||
|
||||
let (impl_name, def_body): (String, ast::Expr<'a>) = match ability_member {
|
||||
Symbol::ENCODE_TO_ENCODER => (
|
||||
format!("#{}_toEncoder", opaque_name),
|
||||
format!("#{opaque_name}_toEncoder"),
|
||||
to_encoder(env, at_opaque),
|
||||
),
|
||||
Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), decoder(env, at_opaque)),
|
||||
Symbol::HASH_HASH => (format!("#{}_hash", opaque_name), hash(env, at_opaque)),
|
||||
Symbol::BOOL_IS_EQ => (format!("#{}_isEq", opaque_name), is_eq(env, at_opaque)),
|
||||
Symbol::DECODE_DECODER => (format!("#{opaque_name}_decoder"), decoder(env, at_opaque)),
|
||||
Symbol::HASH_HASH => (format!("#{opaque_name}_hash"), hash(env, at_opaque)),
|
||||
Symbol::BOOL_IS_EQ => (format!("#{opaque_name}_isEq"), is_eq(env, at_opaque)),
|
||||
other => internal_error!("{:?} is not a derivable ability member!", other),
|
||||
};
|
||||
|
||||
|
|
|
@ -1362,7 +1362,7 @@ pub fn build_host_exposed_def(
|
|||
match typ.shallow_structural_dealias() {
|
||||
Type::Function(args, _, _) => {
|
||||
for i in 0..args.len() {
|
||||
let name = format!("closure_arg_{}_{}", ident, i);
|
||||
let name = format!("closure_arg_{ident}_{i}");
|
||||
|
||||
let arg_symbol = {
|
||||
let ident = name.clone().into();
|
||||
|
@ -1381,7 +1381,7 @@ pub fn build_host_exposed_def(
|
|||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
|
||||
}
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{}", ident);
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
|
@ -1389,7 +1389,7 @@ pub fn build_host_exposed_def(
|
|||
};
|
||||
|
||||
let effect_closure_symbol = {
|
||||
let name = format!("effect_closure_{}", ident);
|
||||
let name = format!("effect_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
|
@ -1435,7 +1435,7 @@ pub fn build_host_exposed_def(
|
|||
_ => {
|
||||
// not a function
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{}", ident);
|
||||
let foreign_symbol_name = format!("roc_fx_{ident}");
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
|
@ -1443,7 +1443,7 @@ pub fn build_host_exposed_def(
|
|||
};
|
||||
|
||||
let effect_closure_symbol = {
|
||||
let name = format!("effect_closure_{}", ident);
|
||||
let name = format!("effect_closure_{ident}");
|
||||
|
||||
let ident = name.into();
|
||||
scope.introduce(ident, Region::zero()).unwrap()
|
||||
|
|
|
@ -67,8 +67,7 @@ impl<'a> Env<'a> {
|
|||
) -> Result<Symbol, RuntimeError> {
|
||||
debug_assert!(
|
||||
!module_name_str.is_empty(),
|
||||
"Called env.qualified_lookup with an unqualified ident: {:?}",
|
||||
ident
|
||||
"Called env.qualified_lookup with an unqualified ident: {ident:?}"
|
||||
);
|
||||
|
||||
let module_name = ModuleName::from(module_name_str);
|
||||
|
|
|
@ -140,6 +140,7 @@ fn index_var(
|
|||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _)
|
||||
| Content::LambdaSet(_)
|
||||
| Content::ErasedLambda
|
||||
| Content::RangedNumber(..) => return Err(TypeError),
|
||||
Content::Error => return Err(TypeError),
|
||||
Content::RecursionVar {
|
||||
|
|
|
@ -18,7 +18,7 @@ use roc_module::called_via::CalledVia;
|
|||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{self, Defs, StrLiteral};
|
||||
use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral};
|
||||
use roc_parse::ident::Accessor;
|
||||
use roc_parse::pattern::PatternType::*;
|
||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
||||
|
@ -1403,14 +1403,13 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
(answer, Output::default())
|
||||
}
|
||||
&ast::Expr::ParensAround(sub_expr) => {
|
||||
let (loc_expr, output) = canonicalize_expr(env, var_store, scope, region, sub_expr);
|
||||
|
||||
(loc_expr.value, output)
|
||||
}
|
||||
// Below this point, we shouln't see any of these nodes anymore because
|
||||
// operator desugaring should have removed them!
|
||||
bad_expr @ ast::Expr::ParensAround(_) => {
|
||||
internal_error!(
|
||||
"A ParensAround did not get removed during operator desugaring somehow: {:#?}",
|
||||
bad_expr
|
||||
);
|
||||
}
|
||||
bad_expr @ ast::Expr::SpaceBefore(_, _) => {
|
||||
internal_error!(
|
||||
"A SpaceBefore did not get removed during operator desugaring somehow: {:#?}",
|
||||
|
@ -2377,11 +2376,115 @@ fn flatten_str_literal<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Comments, newlines, and nested interpolation are disallowed inside interpolation
|
||||
pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||
match expr {
|
||||
ast::Expr::Var { .. } => true,
|
||||
ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
|
||||
_ => false,
|
||||
// These definitely contain neither comments nor newlines, so they are valid
|
||||
ast::Expr::Var { .. }
|
||||
| ast::Expr::SingleQuote(_)
|
||||
| ast::Expr::Str(StrLiteral::PlainLine(_))
|
||||
| ast::Expr::Float(_)
|
||||
| ast::Expr::Num(_)
|
||||
| ast::Expr::NonBase10Int { .. }
|
||||
| ast::Expr::AccessorFunction(_)
|
||||
| ast::Expr::Crash
|
||||
| ast::Expr::Underscore(_)
|
||||
| ast::Expr::MalformedIdent(_, _)
|
||||
| ast::Expr::Tag(_)
|
||||
| ast::Expr::OpaqueRef(_)
|
||||
| ast::Expr::MalformedClosure => true,
|
||||
// Newlines are disallowed inside interpolation, and these all require newlines
|
||||
ast::Expr::Dbg(_, _)
|
||||
| ast::Expr::Defs(_, _)
|
||||
| ast::Expr::Expect(_, _)
|
||||
| ast::Expr::When(_, _)
|
||||
| ast::Expr::Backpassing(_, _, _)
|
||||
| ast::Expr::IngestedFile(_, _)
|
||||
| ast::Expr::SpaceBefore(_, _)
|
||||
| ast::Expr::Str(StrLiteral::Block(_))
|
||||
| ast::Expr::SpaceAfter(_, _) => false,
|
||||
// These can contain subexpressions, so we need to recursively check those
|
||||
ast::Expr::Str(StrLiteral::Line(segments)) => {
|
||||
segments.iter().all(|segment| match segment {
|
||||
ast::StrSegment::EscapedChar(_)
|
||||
| ast::StrSegment::Unicode(_)
|
||||
| ast::StrSegment::Plaintext(_) => true,
|
||||
// Disallow nested interpolation. Alternatively, we could allow it but require
|
||||
// a comment above it apologizing to the next person who has to read the code.
|
||||
ast::StrSegment::Interpolated(_) => false,
|
||||
})
|
||||
}
|
||||
ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
|
||||
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
ast::Expr::Tuple(fields) => fields
|
||||
.iter()
|
||||
.all(|loc_field| is_valid_interpolation(&loc_field.value)),
|
||||
ast::Expr::MultipleRecordBuilders(loc_expr)
|
||||
| ast::Expr::UnappliedRecordBuilder(loc_expr)
|
||||
| ast::Expr::PrecedenceConflict(PrecedenceConflict { expr: loc_expr, .. })
|
||||
| ast::Expr::UnaryOp(loc_expr, _)
|
||||
| ast::Expr::Closure(_, loc_expr) => is_valid_interpolation(&loc_expr.value),
|
||||
ast::Expr::TupleAccess(sub_expr, _)
|
||||
| ast::Expr::ParensAround(sub_expr)
|
||||
| ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
|
||||
ast::Expr::Apply(loc_expr, args, _called_via) => {
|
||||
is_valid_interpolation(&loc_expr.value)
|
||||
&& args
|
||||
.iter()
|
||||
.all(|loc_arg| is_valid_interpolation(&loc_arg.value))
|
||||
}
|
||||
ast::Expr::BinOps(loc_exprs, loc_expr) => {
|
||||
is_valid_interpolation(&loc_expr.value)
|
||||
&& loc_exprs
|
||||
.iter()
|
||||
.all(|(loc_expr, _binop)| is_valid_interpolation(&loc_expr.value))
|
||||
}
|
||||
ast::Expr::If(branches, final_branch) => {
|
||||
is_valid_interpolation(&final_branch.value)
|
||||
&& branches.iter().all(|(loc_before, loc_after)| {
|
||||
is_valid_interpolation(&loc_before.value)
|
||||
&& is_valid_interpolation(&loc_after.value)
|
||||
})
|
||||
}
|
||||
ast::Expr::List(elems) => elems
|
||||
.iter()
|
||||
.all(|loc_expr| is_valid_interpolation(&loc_expr.value)),
|
||||
ast::Expr::RecordUpdate { update, fields } => {
|
||||
is_valid_interpolation(&update.value)
|
||||
&& fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
|
||||
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
}
|
||||
ast::Expr::RecordBuilder(fields) => fields.iter().all(|loc_field| match loc_field.value {
|
||||
ast::RecordBuilderField::Value(_label, comments, loc_expr) => {
|
||||
comments.is_empty() && is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::RecordBuilderField::ApplyValue(
|
||||
_label,
|
||||
comments_before,
|
||||
comments_after,
|
||||
loc_expr,
|
||||
) => {
|
||||
comments_before.is_empty()
|
||||
&& comments_after.is_empty()
|
||||
&& is_valid_interpolation(&loc_expr.value)
|
||||
}
|
||||
ast::RecordBuilderField::Malformed(_) | ast::RecordBuilderField::LabelOnly(_) => true,
|
||||
ast::RecordBuilderField::SpaceBefore(_, _)
|
||||
| ast::RecordBuilderField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2536,6 +2639,8 @@ pub struct Declarations {
|
|||
// used for ability member specializatons.
|
||||
pub specializes: VecMap<usize, Symbol>,
|
||||
|
||||
pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>,
|
||||
|
||||
pub function_bodies: Vec<Loc<FunctionDef>>,
|
||||
pub expressions: Vec<Loc<Expr>>,
|
||||
pub destructs: Vec<DestructureDef>,
|
||||
|
@ -2557,6 +2662,7 @@ impl Declarations {
|
|||
variables: Vec::with_capacity(capacity),
|
||||
symbols: Vec::with_capacity(capacity),
|
||||
annotations: Vec::with_capacity(capacity),
|
||||
host_exposed_annotations: VecMap::new(),
|
||||
function_bodies: Vec::with_capacity(capacity),
|
||||
expressions: Vec::with_capacity(capacity),
|
||||
specializes: VecMap::default(), // number of specializations is probably low
|
||||
|
@ -2587,6 +2693,7 @@ impl Declarations {
|
|||
loc_closure_data: Loc<ClosureData>,
|
||||
expr_var: Variable,
|
||||
annotation: Option<Annotation>,
|
||||
host_annotation: Option<(Variable, Annotation)>,
|
||||
specializes: Option<Symbol>,
|
||||
) -> usize {
|
||||
let index = self.declarations.len();
|
||||
|
@ -2609,6 +2716,11 @@ impl Declarations {
|
|||
Recursive::TailRecursive => DeclarationTag::TailRecursive(function_def_index),
|
||||
};
|
||||
|
||||
if let Some(annotation) = host_annotation {
|
||||
self.host_exposed_annotations
|
||||
.insert(self.declarations.len(), annotation);
|
||||
}
|
||||
|
||||
self.declarations.push(tag);
|
||||
self.variables.push(expr_var);
|
||||
self.symbols.push(symbol);
|
||||
|
@ -2629,6 +2741,7 @@ impl Declarations {
|
|||
loc_closure_data: Loc<ClosureData>,
|
||||
expr_var: Variable,
|
||||
annotation: Option<Annotation>,
|
||||
host_annotation: Option<(Variable, Annotation)>,
|
||||
specializes: Option<Symbol>,
|
||||
) -> usize {
|
||||
let index = self.declarations.len();
|
||||
|
@ -2644,6 +2757,11 @@ impl Declarations {
|
|||
|
||||
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||
|
||||
if let Some(annotation) = host_annotation {
|
||||
self.host_exposed_annotations
|
||||
.insert(self.declarations.len(), annotation);
|
||||
}
|
||||
|
||||
self.declarations
|
||||
.push(DeclarationTag::Function(function_def_index));
|
||||
self.variables.push(expr_var);
|
||||
|
@ -2701,10 +2819,16 @@ impl Declarations {
|
|||
loc_expr: Loc<Expr>,
|
||||
expr_var: Variable,
|
||||
annotation: Option<Annotation>,
|
||||
host_annotation: Option<(Variable, Annotation)>,
|
||||
specializes: Option<Symbol>,
|
||||
) -> usize {
|
||||
let index = self.declarations.len();
|
||||
|
||||
if let Some(annotation) = host_annotation {
|
||||
self.host_exposed_annotations
|
||||
.insert(self.declarations.len(), annotation);
|
||||
}
|
||||
|
||||
self.declarations.push(DeclarationTag::Value);
|
||||
self.variables.push(expr_var);
|
||||
self.symbols.push(symbol);
|
||||
|
@ -2759,6 +2883,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2769,6 +2894,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -2779,6 +2905,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -347,9 +347,7 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// the symbol should already be added to the scope when this module is canonicalized
|
||||
debug_assert!(
|
||||
scope.contains_alias(symbol) || scope.abilities_store.is_ability(symbol),
|
||||
"The {:?} is not a type alias or ability known in {:?}",
|
||||
symbol,
|
||||
home
|
||||
"The {symbol:?} is not a type alias or ability known in {home:?}"
|
||||
);
|
||||
|
||||
// but now we know this symbol by a different identifier, so we still need to add it to
|
||||
|
|
|
@ -220,8 +220,7 @@ fn from_str_radix(src: &str, radix: u32) -> Result<ParsedNumResult, IntErrorKind
|
|||
|
||||
assert!(
|
||||
(2..=36).contains(&radix),
|
||||
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
|
||||
radix
|
||||
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}"
|
||||
);
|
||||
|
||||
let (opt_exact_bound, src) = parse_literal_suffix(src);
|
||||
|
|
|
@ -7,7 +7,9 @@ use roc_module::called_via::BinOp::Pizza;
|
|||
use roc_module::called_via::{BinOp, CalledVia};
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
use roc_parse::ast::{AssignedField, Collection, RecordBuilderField, ValueDef, WhenBranch};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, RecordBuilderField, StrLiteral, StrSegment, ValueDef, WhenBranch,
|
||||
};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
// BinOp precedence logic adapted from Gluon by Markus Westerlind
|
||||
|
@ -129,7 +131,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
Float(..)
|
||||
| Num(..)
|
||||
| NonBase10Int { .. }
|
||||
| Str(_)
|
||||
| SingleQuote(_)
|
||||
| AccessorFunction(_)
|
||||
| Var { .. }
|
||||
|
@ -144,6 +145,28 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
| IngestedFile(_, _)
|
||||
| Crash => loc_expr,
|
||||
|
||||
Str(str_literal) => match str_literal {
|
||||
StrLiteral::PlainLine(_) => loc_expr,
|
||||
StrLiteral::Line(segments) => {
|
||||
let region = loc_expr.region;
|
||||
let value = Str(StrLiteral::Line(desugar_str_segments(arena, segments)));
|
||||
|
||||
arena.alloc(Loc { region, value })
|
||||
}
|
||||
StrLiteral::Block(lines) => {
|
||||
let region = loc_expr.region;
|
||||
let new_lines = Vec::from_iter_in(
|
||||
lines
|
||||
.iter()
|
||||
.map(|segments| desugar_str_segments(arena, segments)),
|
||||
arena,
|
||||
);
|
||||
let value = Str(StrLiteral::Block(new_lines.into_bump_slice()));
|
||||
|
||||
arena.alloc(Loc { region, value })
|
||||
}
|
||||
},
|
||||
|
||||
TupleAccess(sub_expr, paths) => {
|
||||
let region = loc_expr.region;
|
||||
let loc_sub_expr = Loc {
|
||||
|
@ -288,7 +311,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
|
||||
break builder_arg.closure;
|
||||
}
|
||||
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
|
||||
SpaceBefore(expr, _) | SpaceAfter(expr, _) => {
|
||||
current = *expr;
|
||||
}
|
||||
_ => break loc_arg,
|
||||
|
@ -382,7 +405,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => {
|
||||
SpaceBefore(expr, _) | SpaceAfter(expr, _) => {
|
||||
// Since we've already begun canonicalization, spaces and parens
|
||||
// are no longer needed and should be dropped.
|
||||
desugar_expr(
|
||||
|
@ -393,6 +416,20 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}),
|
||||
)
|
||||
}
|
||||
ParensAround(expr) => {
|
||||
let desugared = desugar_expr(
|
||||
arena,
|
||||
arena.alloc(Loc {
|
||||
value: **expr,
|
||||
region: loc_expr.region,
|
||||
}),
|
||||
);
|
||||
|
||||
arena.alloc(Loc {
|
||||
value: ParensAround(&desugared.value),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
If(if_thens, final_else_branch) => {
|
||||
// If does not get desugared into `when` so we can give more targeted error messages during type checking.
|
||||
let desugared_final_else = &*arena.alloc(desugar_expr(arena, final_else_branch));
|
||||
|
@ -430,6 +467,34 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}
|
||||
}
|
||||
|
||||
fn desugar_str_segments<'a>(
|
||||
arena: &'a Bump,
|
||||
segments: &'a [StrSegment<'a>],
|
||||
) -> &'a [StrSegment<'a>] {
|
||||
Vec::from_iter_in(
|
||||
segments.iter().map(|segment| match segment {
|
||||
StrSegment::Plaintext(_) | StrSegment::Unicode(_) | StrSegment::EscapedChar(_) => {
|
||||
*segment
|
||||
}
|
||||
StrSegment::Interpolated(loc_expr) => {
|
||||
let loc_desugared = desugar_expr(
|
||||
arena,
|
||||
arena.alloc(Loc {
|
||||
region: loc_expr.region,
|
||||
value: *loc_expr.value,
|
||||
}),
|
||||
);
|
||||
StrSegment::Interpolated(Loc {
|
||||
region: loc_desugared.region,
|
||||
value: arena.alloc(loc_desugared.value),
|
||||
})
|
||||
}
|
||||
}),
|
||||
arena,
|
||||
)
|
||||
.into_bump_slice()
|
||||
}
|
||||
|
||||
fn desugar_field<'a>(
|
||||
arena: &'a Bump,
|
||||
field: &'a AssignedField<'a, Expr<'a>>,
|
||||
|
|
|
@ -531,7 +531,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
use std::ops::Neg;
|
||||
|
||||
let sign_str = if is_negative { "-" } else { "" };
|
||||
let int_str = format!("{}{}", sign_str, int).into_boxed_str();
|
||||
let int_str = format!("{sign_str}{int}").into_boxed_str();
|
||||
let i = match int {
|
||||
// Safety: this is fine because I128::MAX = |I128::MIN| - 1
|
||||
IntValue::I128(n) if is_negative => {
|
||||
|
|
|
@ -37,8 +37,7 @@ pub struct CanExprOut {
|
|||
pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut {
|
||||
let loc_expr = roc_parse::test_helpers::parse_loc_with(arena, expr_str).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"can_expr_with() got a parse error when attempting to canonicalize:\n\n{:?} {:?}",
|
||||
expr_str, e
|
||||
"can_expr_with() got a parse error when attempting to canonicalize:\n\n{expr_str:?} {e:?}"
|
||||
)
|
||||
});
|
||||
|
||||
|
|
|
@ -422,7 +422,7 @@ mod test_can {
|
|||
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||
|
||||
assert_eq!(problems.len(), 2);
|
||||
println!("{:#?}", problems);
|
||||
println!("{problems:#?}");
|
||||
assert!(problems.iter().any(|problem| matches!(
|
||||
problem,
|
||||
Problem::RuntimeError(RuntimeError::Shadowing { .. })
|
||||
|
|
19
crates/compiler/checkmate/Cargo.toml
Normal file
19
crates/compiler/checkmate/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "roc_checkmate"
|
||||
description = "A framework for debugging the solver."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
roc_checkmate_schema = { path = "../checkmate_schema" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_solve_schema = { path = "../solve_schema" }
|
||||
roc_types = { path = "../types" }
|
||||
chrono.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
roc_checkmate_schema = { path = "../checkmate_schema" }
|
||||
serde_json.workspace = true
|
5
crates/compiler/checkmate/README.md
Normal file
5
crates/compiler/checkmate/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# `checkmate`
|
||||
|
||||
A tool to debug the solver (checker + inference + specialization engine).
|
||||
|
||||
See [the document](https://rwx.notion.site/Type-debugging-tools-de42260060784cacbaf08ea4d61e0eb9?pvs=4).
|
13
crates/compiler/checkmate/build.rs
Normal file
13
crates/compiler/checkmate/build.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use std::fs;
|
||||
|
||||
use roc_checkmate_schema::AllEvents;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../checkmate_schema");
|
||||
let schema = AllEvents::schema();
|
||||
fs::write(
|
||||
"schema.json",
|
||||
serde_json::to_string_pretty(&schema).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
907
crates/compiler/checkmate/schema.json
Normal file
907
crates/compiler/checkmate/schema.json
Normal file
|
@ -0,0 +1,907 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AllEvents",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Event"
|
||||
},
|
||||
"definitions": {
|
||||
"AliasKind": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Structural"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Opaque"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"AliasTypeVariables": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"infer_ext_in_output_position_variables",
|
||||
"lambda_set_variables",
|
||||
"type_variables"
|
||||
],
|
||||
"properties": {
|
||||
"infer_ext_in_output_position_variables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
},
|
||||
"lambda_set_variables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
},
|
||||
"type_variables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ClosureType": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"environment",
|
||||
"function"
|
||||
],
|
||||
"properties": {
|
||||
"environment": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
},
|
||||
"function": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Content": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Flex"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Rigid"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"abilities",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"abilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"FlexAble"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"abilities",
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"abilities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RigidAble"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"structure",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"structure": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Recursive"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ambient_function",
|
||||
"solved",
|
||||
"type",
|
||||
"unspecialized"
|
||||
],
|
||||
"properties": {
|
||||
"ambient_function": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"recursion_var": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"solved": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ClosureType"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"LambdaSet"
|
||||
]
|
||||
},
|
||||
"unspecialized": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/UnspecializedClosureType"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ErasedLambda"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"name",
|
||||
"real_variable",
|
||||
"type",
|
||||
"variables"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"$ref": "#/definitions/AliasKind"
|
||||
},
|
||||
"name": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
},
|
||||
"real_variable": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Alias"
|
||||
]
|
||||
},
|
||||
"variables": {
|
||||
"$ref": "#/definitions/AliasTypeVariables"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"symbol",
|
||||
"type",
|
||||
"variables"
|
||||
],
|
||||
"properties": {
|
||||
"symbol": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Apply"
|
||||
]
|
||||
},
|
||||
"variables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"arguments",
|
||||
"lambda_type",
|
||||
"ret",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"arguments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
},
|
||||
"lambda_type": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"ret": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Function"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"extension",
|
||||
"fields",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"extension": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/RecordField"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Record"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"elements",
|
||||
"extension",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"elements": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
},
|
||||
"extension": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Tuple"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"extension",
|
||||
"tags",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"extension": {
|
||||
"$ref": "#/definitions/TagUnionExtension"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"TagUnion"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"extension",
|
||||
"functions",
|
||||
"tags",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"extension": {
|
||||
"$ref": "#/definitions/TagUnionExtension"
|
||||
},
|
||||
"functions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"FunctionOrTagUnion"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"extension",
|
||||
"recursion_var",
|
||||
"tags",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"extension": {
|
||||
"$ref": "#/definitions/TagUnionExtension"
|
||||
},
|
||||
"recursion_var": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RecursiveTagUnion"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"EmptyRecord"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"EmptyTuple"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"EmptyTagUnion"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"range",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"range": {
|
||||
"$ref": "#/definitions/NumericRange"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"RangedNumber"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Error"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Event": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"left",
|
||||
"mode",
|
||||
"right",
|
||||
"subevents",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"left": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"mode": {
|
||||
"$ref": "#/definitions/UnificationMode"
|
||||
},
|
||||
"right": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"subevents": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Event"
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Unification"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"from",
|
||||
"to",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"from": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"to": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"VariableUnified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"variable"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Content"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rank": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Rank"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"VariableSetDescriptor"
|
||||
]
|
||||
},
|
||||
"variable": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"NumericRange": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"min_width",
|
||||
"signed"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"$ref": "#/definitions/NumericRangeKind"
|
||||
},
|
||||
"min_width": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"signed": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NumericRangeKind": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Int"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"AnyNum"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Rank": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"RecordField": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"field_type",
|
||||
"kind"
|
||||
],
|
||||
"properties": {
|
||||
"field_type": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
},
|
||||
"kind": {
|
||||
"$ref": "#/definitions/RecordFieldKind"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RecordFieldKind": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Demanded"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rigid",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"rigid": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Required"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"rigid",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"rigid": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Optional"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Symbol": {
|
||||
"type": "string"
|
||||
},
|
||||
"TagUnionExtension": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"variable"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Openness"
|
||||
]
|
||||
},
|
||||
"variable": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"variable"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Any"
|
||||
]
|
||||
},
|
||||
"variable": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"UnificationMode": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Eq"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Present"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"LambdaSetSpecialization"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"UnspecializedClosureType": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ability_member",
|
||||
"lambda_set_region",
|
||||
"specialization"
|
||||
],
|
||||
"properties": {
|
||||
"ability_member": {
|
||||
"$ref": "#/definitions/Symbol"
|
||||
},
|
||||
"lambda_set_region": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"specialization": {
|
||||
"$ref": "#/definitions/Variable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Variable": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
}
|
175
crates/compiler/checkmate/src/collector.rs
Normal file
175
crates/compiler/checkmate/src/collector.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
use std::error::Error;
|
||||
|
||||
use roc_checkmate_schema::{AllEvents, Event};
|
||||
use roc_types::subs as s;
|
||||
|
||||
use crate::convert::AsSchema;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Collector {
|
||||
events: AllEvents,
|
||||
current_event_path: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Default for Collector {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Collector {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
events: AllEvents(Vec::new()),
|
||||
current_event_path: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unify(&mut self, subs: &s::Subs, from: s::Variable, to: s::Variable) {
|
||||
let to = to.as_schema(subs);
|
||||
let from = from.as_schema(subs);
|
||||
self.add_event(Event::VariableUnified { to, from });
|
||||
}
|
||||
|
||||
pub fn set_content(&mut self, subs: &s::Subs, var: s::Variable, content: s::Content) {
|
||||
let variable = var.as_schema(subs);
|
||||
let content = content.as_schema(subs);
|
||||
self.add_event(Event::VariableSetDescriptor {
|
||||
variable,
|
||||
content: Some(content),
|
||||
rank: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_rank(&mut self, subs: &s::Subs, var: s::Variable, rank: s::Rank) {
|
||||
let variable = var.as_schema(subs);
|
||||
let rank = rank.as_schema(subs);
|
||||
self.add_event(Event::VariableSetDescriptor {
|
||||
variable,
|
||||
rank: Some(rank),
|
||||
content: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_descriptor(&mut self, subs: &s::Subs, var: s::Variable, descriptor: s::Descriptor) {
|
||||
let variable = var.as_schema(subs);
|
||||
let rank = descriptor.rank.as_schema(subs);
|
||||
let content = descriptor.content.as_schema(subs);
|
||||
self.add_event(Event::VariableSetDescriptor {
|
||||
variable,
|
||||
rank: Some(rank),
|
||||
content: Some(content),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_unification(
|
||||
&mut self,
|
||||
subs: &s::Subs,
|
||||
left: s::Variable,
|
||||
right: s::Variable,
|
||||
mode: roc_solve_schema::UnificationMode,
|
||||
) {
|
||||
let left = left.as_schema(subs);
|
||||
let right = right.as_schema(subs);
|
||||
let mode = mode.as_schema(subs);
|
||||
let subevents = Vec::new();
|
||||
self.add_event(Event::Unification {
|
||||
left,
|
||||
right,
|
||||
mode,
|
||||
subevents,
|
||||
success: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn end_unification(
|
||||
&mut self,
|
||||
subs: &s::Subs,
|
||||
left: s::Variable,
|
||||
right: s::Variable,
|
||||
success: bool,
|
||||
) {
|
||||
let current_event = self.get_path_event();
|
||||
match current_event {
|
||||
EventW::Sub(Event::Unification {
|
||||
left: l,
|
||||
right: r,
|
||||
success: s,
|
||||
..
|
||||
}) => {
|
||||
assert_eq!(left.as_schema(subs), *l);
|
||||
assert_eq!(right.as_schema(subs), *r);
|
||||
assert!(s.is_none());
|
||||
*s = Some(success);
|
||||
}
|
||||
_ => panic!("end_unification called when not in a unification"),
|
||||
}
|
||||
self.current_event_path.pop();
|
||||
}
|
||||
|
||||
pub fn write(&self, writer: impl std::io::Write) -> Result<(), Box<dyn Error>> {
|
||||
self.events.write(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_event(&mut self, event: impl Into<Event>) {
|
||||
let mut event = event.into();
|
||||
let is_appendable = EventW::Sub(&mut event).appendable();
|
||||
let event = event;
|
||||
|
||||
let path_event = self.get_path_event();
|
||||
let new_event_index = path_event.append(event);
|
||||
if is_appendable {
|
||||
self.current_event_path.push(new_event_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_event(&mut self) -> EventW {
|
||||
let mut event = EventW::Top(&mut self.events);
|
||||
for i in &self.current_event_path {
|
||||
event = event.index(*i);
|
||||
}
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
enum EventW<'a> {
|
||||
Top(&'a mut AllEvents),
|
||||
Sub(&'a mut Event),
|
||||
}
|
||||
|
||||
impl<'a> EventW<'a> {
|
||||
fn append(self, event: Event) -> usize {
|
||||
let list = self.subevents_mut().unwrap();
|
||||
let index = list.len();
|
||||
list.push(event);
|
||||
index
|
||||
}
|
||||
|
||||
fn appendable(self) -> bool {
|
||||
self.subevents().is_some()
|
||||
}
|
||||
|
||||
fn index(self, index: usize) -> EventW<'a> {
|
||||
Self::Sub(&mut self.subevents_mut().unwrap()[index])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EventW<'a> {
|
||||
fn subevents(self) -> Option<&'a Vec<Event>> {
|
||||
use EventW::*;
|
||||
match self {
|
||||
Top(events) => Some(&events.0),
|
||||
Sub(Event::Unification { subevents, .. }) => Some(subevents),
|
||||
Sub(Event::VariableUnified { .. } | Event::VariableSetDescriptor { .. }) => None,
|
||||
}
|
||||
}
|
||||
fn subevents_mut(self) -> Option<&'a mut Vec<Event>> {
|
||||
use EventW::*;
|
||||
match self {
|
||||
Top(events) => Some(&mut events.0),
|
||||
Sub(Event::Unification { subevents, .. }) => Some(subevents),
|
||||
Sub(Event::VariableUnified { .. } | Event::VariableSetDescriptor { .. }) => None,
|
||||
}
|
||||
}
|
||||
}
|
323
crates/compiler/checkmate/src/convert.rs
Normal file
323
crates/compiler/checkmate/src/convert.rs
Normal file
|
@ -0,0 +1,323 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use roc_module::{ident, symbol};
|
||||
use roc_types::{
|
||||
num,
|
||||
subs::{self, GetSubsSlice, Subs, SubsIndex, SubsSlice, UnionLabels},
|
||||
types,
|
||||
};
|
||||
|
||||
use roc_checkmate_schema::{
|
||||
AliasKind, AliasTypeVariables, ClosureType, Content, NumericRange, NumericRangeKind, Rank,
|
||||
RecordField, RecordFieldKind, Symbol, TagUnionExtension, UnificationMode,
|
||||
UnspecializedClosureType, Variable,
|
||||
};
|
||||
|
||||
pub trait AsSchema<T> {
|
||||
fn as_schema(&self, subs: &Subs) -> T;
|
||||
}
|
||||
|
||||
impl<T, U> AsSchema<Option<U>> for Option<T>
|
||||
where
|
||||
T: AsSchema<U>,
|
||||
T: Copy,
|
||||
{
|
||||
fn as_schema(&self, subs: &Subs) -> Option<U> {
|
||||
self.map(|i| i.as_schema(subs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> AsSchema<Vec<U>> for &[T]
|
||||
where
|
||||
T: AsSchema<U>,
|
||||
{
|
||||
fn as_schema(&self, subs: &Subs) -> Vec<U> {
|
||||
self.iter().map(|i| i.as_schema(subs)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> AsSchema<U> for SubsIndex<T>
|
||||
where
|
||||
Subs: std::ops::Index<SubsIndex<T>, Output = T>,
|
||||
T: AsSchema<U>,
|
||||
{
|
||||
fn as_schema(&self, subs: &Subs) -> U {
|
||||
subs[*self].as_schema(subs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> AsSchema<Vec<U>> for SubsSlice<T>
|
||||
where
|
||||
Subs: GetSubsSlice<T>,
|
||||
T: AsSchema<U>,
|
||||
{
|
||||
fn as_schema(&self, subs: &Subs) -> Vec<U> {
|
||||
subs.get_subs_slice(*self)
|
||||
.iter()
|
||||
.map(|i| i.as_schema(subs))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Content> for subs::Content {
|
||||
fn as_schema(&self, subs: &Subs) -> Content {
|
||||
use {subs::Content as A, Content as B};
|
||||
match self {
|
||||
A::FlexVar(name) => B::Flex(name.as_schema(subs)),
|
||||
A::RigidVar(name) => B::Rigid(name.as_schema(subs)),
|
||||
A::FlexAbleVar(name, abilities) => {
|
||||
B::FlexAble(name.as_schema(subs), abilities.as_schema(subs))
|
||||
}
|
||||
A::RigidAbleVar(name, abilities) => {
|
||||
B::RigidAble(name.as_schema(subs), abilities.as_schema(subs))
|
||||
}
|
||||
A::RecursionVar {
|
||||
structure,
|
||||
opt_name,
|
||||
} => B::Recursive(opt_name.as_schema(subs), structure.as_schema(subs)),
|
||||
A::LambdaSet(lambda_set) => lambda_set.as_schema(subs),
|
||||
A::ErasedLambda => B::ErasedLambda(),
|
||||
A::Structure(flat_type) => flat_type.as_schema(subs),
|
||||
A::Alias(name, type_vars, real_var, kind) => B::Alias(
|
||||
name.as_schema(subs),
|
||||
type_vars.as_schema(subs),
|
||||
real_var.as_schema(subs),
|
||||
kind.as_schema(subs),
|
||||
),
|
||||
A::RangedNumber(range) => B::RangedNumber(range.as_schema(subs)),
|
||||
A::Error => B::Error(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Content> for subs::FlatType {
|
||||
fn as_schema(&self, subs: &Subs) -> Content {
|
||||
match self {
|
||||
subs::FlatType::Apply(symbol, variables) => {
|
||||
Content::Apply(symbol.as_schema(subs), variables.as_schema(subs))
|
||||
}
|
||||
subs::FlatType::Func(arguments, closure, ret) => Content::Function(
|
||||
arguments.as_schema(subs),
|
||||
closure.as_schema(subs),
|
||||
ret.as_schema(subs),
|
||||
),
|
||||
subs::FlatType::Record(fields, ext) => {
|
||||
Content::Record(fields.as_schema(subs), ext.as_schema(subs))
|
||||
}
|
||||
subs::FlatType::Tuple(elems, ext) => {
|
||||
Content::Tuple(elems.as_schema(subs), ext.as_schema(subs))
|
||||
}
|
||||
subs::FlatType::TagUnion(tags, ext) => {
|
||||
Content::TagUnion(tags.as_schema(subs), ext.as_schema(subs))
|
||||
}
|
||||
subs::FlatType::FunctionOrTagUnion(tags, functions, ext) => {
|
||||
Content::FunctionOrTagUnion(
|
||||
functions.as_schema(subs),
|
||||
tags.as_schema(subs),
|
||||
ext.as_schema(subs),
|
||||
)
|
||||
}
|
||||
subs::FlatType::RecursiveTagUnion(rec_var, tags, ext) => Content::RecursiveTagUnion(
|
||||
rec_var.as_schema(subs),
|
||||
tags.as_schema(subs),
|
||||
ext.as_schema(subs),
|
||||
),
|
||||
subs::FlatType::EmptyRecord => Content::EmptyRecord(),
|
||||
subs::FlatType::EmptyTuple => Content::EmptyTuple(),
|
||||
subs::FlatType::EmptyTagUnion => Content::EmptyTagUnion(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Content> for subs::LambdaSet {
|
||||
fn as_schema(&self, subs: &Subs) -> Content {
|
||||
let subs::LambdaSet {
|
||||
solved,
|
||||
unspecialized,
|
||||
recursion_var,
|
||||
ambient_function,
|
||||
} = self;
|
||||
|
||||
Content::LambdaSet(
|
||||
solved.as_schema(subs),
|
||||
unspecialized.as_schema(subs),
|
||||
recursion_var.as_schema(subs),
|
||||
ambient_function.as_schema(subs),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<String> for ident::Lowercase {
|
||||
fn as_schema(&self, _subs: &Subs) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Symbol> for symbol::Symbol {
|
||||
fn as_schema(&self, _subs: &Subs) -> Symbol {
|
||||
Symbol(format!("{:#?}", self))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Variable> for subs::Variable {
|
||||
fn as_schema(&self, _subs: &Subs) -> Variable {
|
||||
Variable(self.index())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Option<Variable>> for subs::OptVariable {
|
||||
fn as_schema(&self, _subs: &Subs) -> Option<Variable> {
|
||||
self.into_variable().map(|i| i.as_schema(_subs))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Vec<ClosureType>> for UnionLabels<symbol::Symbol> {
|
||||
fn as_schema(&self, subs: &Subs) -> Vec<ClosureType> {
|
||||
self.iter_from_subs(subs)
|
||||
.map(|(function, environment)| ClosureType {
|
||||
function: function.as_schema(subs),
|
||||
environment: environment.as_schema(subs),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<UnspecializedClosureType> for types::Uls {
|
||||
fn as_schema(&self, subs: &Subs) -> UnspecializedClosureType {
|
||||
let types::Uls(specialization, ability_member, lambda_set_region) = self;
|
||||
|
||||
UnspecializedClosureType {
|
||||
specialization: specialization.as_schema(subs),
|
||||
ability_member: ability_member.as_schema(subs),
|
||||
lambda_set_region: *lambda_set_region,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<AliasTypeVariables> for subs::AliasVariables {
|
||||
fn as_schema(&self, subs: &Subs) -> AliasTypeVariables {
|
||||
let type_variables = self.type_variables().as_schema(subs);
|
||||
let lambda_set_variables = self.lambda_set_variables().as_schema(subs);
|
||||
let infer_ext_in_output_position_variables =
|
||||
self.infer_ext_in_output_variables().as_schema(subs);
|
||||
|
||||
AliasTypeVariables {
|
||||
type_variables,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_position_variables,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<AliasKind> for types::AliasKind {
|
||||
fn as_schema(&self, _subs: &Subs) -> AliasKind {
|
||||
match self {
|
||||
types::AliasKind::Structural => AliasKind::Structural,
|
||||
types::AliasKind::Opaque => AliasKind::Opaque,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<HashMap<String, RecordField>> for subs::RecordFields {
|
||||
fn as_schema(&self, subs: &Subs) -> HashMap<String, RecordField> {
|
||||
let mut map = HashMap::new();
|
||||
for (name, var, field) in self.iter_all() {
|
||||
let name = name.as_schema(subs);
|
||||
let field_type = var.as_schema(subs);
|
||||
let kind = field.as_schema(subs);
|
||||
map.insert(name, RecordField { field_type, kind });
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<RecordFieldKind> for types::RecordField<()> {
|
||||
fn as_schema(&self, _subs: &Subs) -> RecordFieldKind {
|
||||
match self {
|
||||
types::RecordField::Demanded(_) => RecordFieldKind::Demanded,
|
||||
types::RecordField::Required(_) => RecordFieldKind::Required { rigid: false },
|
||||
types::RecordField::Optional(_) => RecordFieldKind::Optional { rigid: false },
|
||||
types::RecordField::RigidRequired(_) => RecordFieldKind::Required { rigid: true },
|
||||
types::RecordField::RigidOptional(_) => RecordFieldKind::Optional { rigid: true },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<HashMap<u32, Variable>> for subs::TupleElems {
|
||||
fn as_schema(&self, subs: &Subs) -> HashMap<u32, Variable> {
|
||||
let mut map = HashMap::new();
|
||||
for (index, var) in self.iter_all() {
|
||||
let name = subs[index] as _;
|
||||
let var = var.as_schema(subs);
|
||||
map.insert(name, var);
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<HashMap<String, Vec<Variable>>> for subs::UnionTags {
|
||||
fn as_schema(&self, subs: &Subs) -> HashMap<String, Vec<Variable>> {
|
||||
let mut map = HashMap::new();
|
||||
for (tag, payloads) in self.iter_from_subs(subs) {
|
||||
map.insert(tag.as_schema(subs), payloads.as_schema(subs));
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<TagUnionExtension> for subs::TagExt {
|
||||
fn as_schema(&self, subs: &Subs) -> TagUnionExtension {
|
||||
match self {
|
||||
subs::TagExt::Openness(var) => TagUnionExtension::Openness(var.as_schema(subs)),
|
||||
subs::TagExt::Any(var) => TagUnionExtension::Any(var.as_schema(subs)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<NumericRange> for num::NumericRange {
|
||||
fn as_schema(&self, _subs: &Subs) -> NumericRange {
|
||||
let kind =
|
||||
match self {
|
||||
num::NumericRange::IntAtLeastSigned(_)
|
||||
| num::NumericRange::IntAtLeastEitherSign(_) => NumericRangeKind::Int,
|
||||
num::NumericRange::NumAtLeastSigned(_)
|
||||
| num::NumericRange::NumAtLeastEitherSign(_) => NumericRangeKind::AnyNum,
|
||||
};
|
||||
|
||||
let min_width = self.min_width();
|
||||
let (signedness, width) = min_width.signedness_and_width();
|
||||
let signed = signedness.is_signed();
|
||||
|
||||
NumericRange {
|
||||
kind,
|
||||
signed,
|
||||
min_width: width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<String> for ident::TagName {
|
||||
fn as_schema(&self, _subs: &Subs) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<Rank> for subs::Rank {
|
||||
fn as_schema(&self, _subs: &Subs) -> Rank {
|
||||
Rank(self.into_usize() as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSchema<UnificationMode> for roc_solve_schema::UnificationMode {
|
||||
fn as_schema(&self, _subs: &Subs) -> UnificationMode {
|
||||
if self.is_eq() {
|
||||
UnificationMode::Eq
|
||||
} else if self.is_present() {
|
||||
UnificationMode::Present
|
||||
} else if self.is_lambda_set_specialization() {
|
||||
UnificationMode::LambdaSetSpecialization
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue