mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge branch 'main' into main
This commit is contained in:
commit
dbd1100a20
243 changed files with 29812 additions and 3149 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
|
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
|
||||
|
||||
|
|
3
.github/workflows/nix_macos_x86_64.yml
vendored
3
.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
|
||||
|
||||
|
|
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.67
|
||||
run: rustup install nightly-2022-12-09
|
||||
- name: install rust nightly 1.70.0
|
||||
run: rustup install nightly-2023-04-15
|
||||
|
||||
- name: set up llvm 13
|
||||
run: |
|
||||
|
|
7
.github/workflows/windows_tests.yml
vendored
7
.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.67
|
||||
run: rustup install nightly-2022-12-09
|
||||
- name: install rust nightly 1.70.0
|
||||
run: rustup install nightly-2023-04-15
|
||||
|
||||
- name: set up llvm 13
|
||||
run: |
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -85,3 +85,6 @@ www/src/roc-tutorial
|
|||
|
||||
# snapshot tests temp file
|
||||
*.pending-snap
|
||||
|
||||
# checkmate
|
||||
checkmate_*.json
|
||||
|
|
179
Cargo.lock
generated
179
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",
|
||||
|
@ -3686,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",
|
||||
]
|
||||
|
@ -3794,6 +3886,7 @@ dependencies = [
|
|||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
|
@ -3849,6 +3942,7 @@ dependencies = [
|
|||
"roc_parse",
|
||||
"roc_repl_eval",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"tempfile",
|
||||
|
@ -3912,6 +4006,7 @@ dependencies = [
|
|||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_checkmate",
|
||||
"roc_collections",
|
||||
"roc_debug_flags",
|
||||
"roc_derive",
|
||||
|
@ -3927,6 +4022,7 @@ dependencies = [
|
|||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_problem",
|
||||
"roc_solve_schema",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
|
@ -3947,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"
|
||||
|
@ -4006,11 +4109,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",
|
||||
]
|
||||
|
@ -4156,6 +4260,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"
|
||||
|
@ -4244,6 +4372,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"
|
||||
|
@ -4798,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"
|
||||
|
@ -4942,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",
|
||||
]
|
||||
|
||||
|
@ -5252,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"
|
||||
|
@ -5576,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"
|
||||
|
@ -5860,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
|
|
@ -29,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" }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
@ -718,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 {
|
||||
|
@ -1489,6 +1502,8 @@ fn adjust_rank_content(
|
|||
rank
|
||||
}
|
||||
|
||||
ErasedLambda => group_rank,
|
||||
|
||||
RangedNumber(_vars) => group_rank,
|
||||
}
|
||||
}
|
||||
|
@ -1669,6 +1684,7 @@ fn instantiate_rigids_help(
|
|||
}
|
||||
}
|
||||
|
||||
ErasedLambda => {}
|
||||
RangedNumber(_vars) => {}
|
||||
}
|
||||
|
||||
|
@ -1981,6 +1997,12 @@ fn deep_copy_var_help(
|
|||
copy
|
||||
}
|
||||
|
||||
ErasedLambda => {
|
||||
subs.set(copy, make_descriptor(ErasedLambda));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
RangedNumber(vars) => {
|
||||
let new_content = RangedNumber(vars);
|
||||
|
||||
|
|
|
@ -385,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;
|
||||
|
||||
|
@ -427,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,
|
||||
|
@ -1169,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;
|
||||
|
|
|
@ -11,7 +11,7 @@ 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};
|
||||
|
@ -123,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)) => {
|
||||
|
|
|
@ -24,19 +24,15 @@ 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]
|
||||
|
||||
|
|
|
@ -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, HostExposedLayouts,
|
||||
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
|
||||
|
@ -181,6 +181,7 @@ where
|
|||
|
||||
let mut type_definitions = MutSet::default();
|
||||
let mut host_exposed_functions = Vec::new();
|
||||
let mut erased_functions = Vec::new();
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
|
@ -201,6 +202,17 @@ where
|
|||
|
||||
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,
|
||||
|
@ -226,6 +238,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 +270,7 @@ where
|
|||
entry_point_layout,
|
||||
Some(roc_main),
|
||||
&host_exposed_functions,
|
||||
&erased_functions,
|
||||
)?;
|
||||
|
||||
type_definitions.extend(env.type_names);
|
||||
|
@ -279,8 +297,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);
|
||||
|
||||
|
@ -364,6 +388,7 @@ fn build_entry_point<'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 +419,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 {
|
||||
|
@ -788,6 +813,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,
|
||||
|
@ -1517,6 +1552,28 @@ 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, interner.get_repr(layout))?;
|
||||
|
||||
|
@ -1527,6 +1584,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1631,6 +1698,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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1707,3 +1777,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -530,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-12-09", "cargo"]);
|
||||
cmd.args(["run", "nightly-2023-04-15", "cargo"]);
|
||||
|
||||
cmd
|
||||
} else {
|
||||
|
@ -1078,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(),
|
||||
])
|
||||
|
|
|
@ -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;
|
||||
|
@ -740,8 +740,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,
|
||||
|
@ -1205,6 +1217,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,
|
||||
|
|
|
@ -1182,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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -88,13 +88,16 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
|||
LowLevel::PtrStore => unimplemented!(),
|
||||
LowLevel::PtrLoad => unimplemented!(),
|
||||
LowLevel::PtrClearTagId => unimplemented!(),
|
||||
LowLevel::Alloca => 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!(),
|
||||
|
|
|
@ -64,26 +64,6 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
|
|||
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
|
||||
|
||||
impl Constraints {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
constraints: Default::default(),
|
||||
type_slices: Default::default(),
|
||||
variables: Default::default(),
|
||||
loc_symbols: Default::default(),
|
||||
let_constraints: Default::default(),
|
||||
categories: Default::default(),
|
||||
pattern_categories: Default::default(),
|
||||
expectations: Default::default(),
|
||||
pattern_expectations: Default::default(),
|
||||
includes_tags: Default::default(),
|
||||
strings: Default::default(),
|
||||
sketched_rows: Default::default(),
|
||||
eq: Default::default(),
|
||||
pattern_eq: Default::default(),
|
||||
cycles: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
let constraints = Vec::new();
|
||||
let type_slices = Vec::with_capacity(16);
|
||||
|
|
|
@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
|||
})
|
||||
})
|
||||
}
|
||||
ErasedLambda => ErasedLambda,
|
||||
|
||||
RangedNumber(range) => {
|
||||
perform_clone!(RangedNumber(range))
|
||||
|
|
|
@ -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>>,
|
||||
|
@ -1638,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()));
|
||||
}
|
||||
|
@ -1649,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,
|
||||
);
|
||||
}
|
||||
|
@ -1658,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,
|
||||
|
@ -1749,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(
|
||||
|
@ -1756,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,
|
||||
);
|
||||
}
|
||||
|
@ -1765,6 +1825,7 @@ pub(crate) fn sort_can_defs_new(
|
|||
def.loc_expr,
|
||||
def.expr_var,
|
||||
def.annotation,
|
||||
host_annotation,
|
||||
specializes,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
@ -2376,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,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2535,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>,
|
||||
|
@ -2556,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
|
||||
|
@ -2586,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();
|
||||
|
@ -2608,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);
|
||||
|
@ -2628,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();
|
||||
|
@ -2643,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);
|
||||
|
@ -2700,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);
|
||||
|
@ -2758,6 +2883,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2768,6 +2894,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -2778,6 +2905,7 @@ impl Declarations {
|
|||
def.expr_var,
|
||||
def.annotation,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
@ -444,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>>,
|
||||
|
|
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!()
|
||||
}
|
||||
}
|
||||
}
|
62
crates/compiler/checkmate/src/lib.rs
Normal file
62
crates/compiler/checkmate/src/lib.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
mod collector;
|
||||
mod convert;
|
||||
|
||||
pub use collector::Collector;
|
||||
|
||||
pub fn is_checkmate_enabled() -> bool {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let flag = std::env::var("ROC_CHECKMATE");
|
||||
flag.as_deref() == Ok("1")
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug_checkmate {
|
||||
($opt_collector:expr, $cm:ident => $expr:expr) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if let Some($cm) = $opt_collector.as_mut() {
|
||||
$expr
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dump_checkmate {
|
||||
($opt_collector:expr) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if let Some(cm) = $opt_collector.as_ref() {
|
||||
$crate::dump_checkmate(cm);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dump_checkmate(collector: &Collector) {
|
||||
let timestamp = chrono::Local::now().format("%Y%m%d_%H-%M-%S");
|
||||
let filename = format!("checkmate_{timestamp}.json");
|
||||
let fi = std::fs::File::create(&filename).unwrap();
|
||||
collector.write(fi).unwrap();
|
||||
eprintln!("Wrote checkmate output to {filename}");
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! with_checkmate {
|
||||
({ on => $on:expr, off => $off:expr, }) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
$on
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
$off
|
||||
}
|
||||
}};
|
||||
}
|
17
crates/compiler/checkmate/www/.gitignore
vendored
Normal file
17
crates/compiler/checkmate/www/.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
/coverage
|
||||
|
||||
/build
|
||||
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
4
crates/compiler/checkmate/www/.vim/coc-settings.json
Normal file
4
crates/compiler/checkmate/www/.vim/coc-settings.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tsserver.useLocalTsdk": true,
|
||||
"tsserver.tsdk": "${workspaceFolder}/node_modules/typescript/lib"
|
||||
}
|
19105
crates/compiler/checkmate/www/package-lock.json
generated
Normal file
19105
crates/compiler/checkmate/www/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
54
crates/compiler/checkmate/www/package.json
Normal file
54
crates/compiler/checkmate/www/package.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "checkmate",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@dagrejs/dagre": "^1.0.2",
|
||||
"elkjs": "^0.8.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.14.2",
|
||||
"react-router-hash-link": "^2.4.3",
|
||||
"reactflow": "^11.7.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "build:codegen && react-scripts build",
|
||||
"build:codegen": "node ./scripts/gen_schema_dts.js",
|
||||
"check": "tsc",
|
||||
"lint": "eslint src/ scripts/",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.38",
|
||||
"@types/react": "^18.2.15",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/react-router-hash-link": "^2.4.6",
|
||||
"clsx": "^2.0.0",
|
||||
"json-schema-to-typescript": "^13.0.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tiny-typed-emitter": "^2.1.0",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
14
crates/compiler/checkmate/www/public/index.html
Normal file
14
crates/compiler/checkmate/www/public/index.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="checkmate in N" />
|
||||
<title>Checkmate</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
24
crates/compiler/checkmate/www/scripts/gen_schema_dts.js
Normal file
24
crates/compiler/checkmate/www/scripts/gen_schema_dts.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
const {compileFromFile} = require("json-schema-to-typescript");
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
|
||||
const SCHEMA_PATH = path.resolve(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"schema.json"
|
||||
);
|
||||
|
||||
const DTS_PATH = path.resolve(
|
||||
__dirname,
|
||||
"..",
|
||||
"src",
|
||||
"schema.d.ts"
|
||||
);
|
||||
|
||||
async function main() {
|
||||
const result = await compileFromFile(SCHEMA_PATH);
|
||||
await fs.writeFile(DTS_PATH, result);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
42
crates/compiler/checkmate/www/src/App.tsx
Normal file
42
crates/compiler/checkmate/www/src/App.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import React from "react";
|
||||
import FileInput, { LoadedEvents } from "./components/FileInput";
|
||||
import Ui from "./components/Ui";
|
||||
import data from "./checkmate.json";
|
||||
import { AllEvents } from "./schema";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
export default function App() {
|
||||
const [events, setEvents] = React.useState<LoadedEvents | null>({
|
||||
kind: "ok",
|
||||
events: data as AllEvents,
|
||||
});
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<div className="w-screen h-screen p-2 bg-gray-100 flex flex-col">
|
||||
<div>
|
||||
<FileInput setResult={setEvents} />
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<EventsWrapper events={events} />
|
||||
</div>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
interface EventsWrapperProps {
|
||||
events: LoadedEvents | null;
|
||||
}
|
||||
|
||||
function EventsWrapper({ events }: EventsWrapperProps): JSX.Element {
|
||||
if (events === null) {
|
||||
return <div></div>;
|
||||
}
|
||||
switch (events.kind) {
|
||||
case "ok":
|
||||
return <Ui events={events.events} />;
|
||||
case "err":
|
||||
return <div className="text-red-400 text-lg">{events.error}</div>;
|
||||
}
|
||||
}
|
545
crates/compiler/checkmate/www/src/checkmate.json
Normal file
545
crates/compiler/checkmate/www/src/checkmate.json
Normal file
|
@ -0,0 +1,545 @@
|
|||
[
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1540,
|
||||
"right": 71,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 71,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Function",
|
||||
"arguments": [1543, 1544],
|
||||
"lambda_type": 1542,
|
||||
"ret": 1541
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1540, "to": 71 },
|
||||
{ "type": "VariableUnified", "from": 71, "to": 71 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 71,
|
||||
"right": 1546,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1543,
|
||||
"right": 67,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 67,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [1545],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1545,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1543, "to": 67 },
|
||||
{ "type": "VariableUnified", "from": 67, "to": 67 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1544,
|
||||
"right": 69,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 69,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [1545],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1545,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1544, "to": 69 },
|
||||
{ "type": "VariableUnified", "from": 69, "to": 69 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1541,
|
||||
"right": 73,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 73,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [1545],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1545,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1541, "to": 73 },
|
||||
{ "type": "VariableUnified", "from": 73, "to": 73 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1542,
|
||||
"right": 72,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 72,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "LambdaSet",
|
||||
"solved": [{ "function": "`Num.add`", "environment": [] }],
|
||||
"unspecialized": [],
|
||||
"recursion_var": null,
|
||||
"ambient_function": 1540
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1542, "to": 72 },
|
||||
{ "type": "VariableUnified", "from": 72, "to": 72 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1546,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Function",
|
||||
"arguments": [67, 69],
|
||||
"lambda_type": 1542,
|
||||
"ret": 73
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 71, "to": 1546 },
|
||||
{ "type": "VariableUnified", "from": 1546, "to": 1546 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 66,
|
||||
"right": 1547,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1547,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "RangedNumber",
|
||||
"range": {
|
||||
"kind": { "type": "AnyNum" },
|
||||
"signed": true,
|
||||
"min_width": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 66, "to": 1547 },
|
||||
{ "type": "VariableUnified", "from": 1547, "to": 1547 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1548,
|
||||
"right": 67,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 66,
|
||||
"right": 1545,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1545,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "RangedNumber",
|
||||
"range": {
|
||||
"kind": { "type": "AnyNum" },
|
||||
"signed": true,
|
||||
"min_width": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1547, "to": 1545 },
|
||||
{ "type": "VariableUnified", "from": 1545, "to": 1545 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 67,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [66],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 66,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1548, "to": 67 },
|
||||
{ "type": "VariableUnified", "from": 67, "to": 67 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 68,
|
||||
"right": 1549,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1549,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "RangedNumber",
|
||||
"range": {
|
||||
"kind": { "type": "AnyNum" },
|
||||
"signed": true,
|
||||
"min_width": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 68, "to": 1549 },
|
||||
{ "type": "VariableUnified", "from": 1549, "to": 1549 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1550,
|
||||
"right": 69,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 68,
|
||||
"right": 1545,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1545,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "RangedNumber",
|
||||
"range": {
|
||||
"kind": { "type": "AnyNum" },
|
||||
"signed": true,
|
||||
"min_width": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1549, "to": 1545 },
|
||||
{ "type": "VariableUnified", "from": 1545, "to": 1545 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 69,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [68],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 68,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1550, "to": 69 },
|
||||
{ "type": "VariableUnified", "from": 69, "to": 69 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 73,
|
||||
"right": 1551,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 73,
|
||||
"right": 1552,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1545,
|
||||
"right": 1553,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1556,
|
||||
"right": 1554,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1554,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Unsigned8`",
|
||||
"variables": {
|
||||
"type_variables": [],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1555,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1556, "to": 1554 },
|
||||
{ "type": "VariableUnified", "from": 1554, "to": 1554 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1545,
|
||||
"right": 1553,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1553,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Integer`",
|
||||
"variables": {
|
||||
"type_variables": [1556],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1556,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1545, "to": 1553 },
|
||||
{ "type": "VariableUnified", "from": 1553, "to": 1553 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 1552,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.Num`",
|
||||
"variables": {
|
||||
"type_variables": [1545],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1545,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 73, "to": 1552 },
|
||||
{ "type": "VariableUnified", "from": 1552, "to": 1552 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1551,
|
||||
"right": 75,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 75,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.U8`",
|
||||
"variables": {
|
||||
"type_variables": [],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1552,
|
||||
"kind": { "type": "Structural" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1551, "to": 75 },
|
||||
{ "type": "VariableUnified", "from": 75, "to": 75 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 80,
|
||||
"right": 75,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 75,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Num.U8`",
|
||||
"variables": {
|
||||
"type_variables": [],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1552,
|
||||
"kind": { "type": "Structural" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 80, "to": 75 },
|
||||
{ "type": "VariableUnified", "from": 75, "to": 75 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1557,
|
||||
"right": 79,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 79,
|
||||
"rank": 1,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Bool.Bool`",
|
||||
"variables": {
|
||||
"type_variables": [],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1558,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1557, "to": 79 },
|
||||
{ "type": "VariableUnified", "from": 79, "to": 79 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 79,
|
||||
"right": 5,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 1558,
|
||||
"right": 4,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "Unification",
|
||||
"left": 84,
|
||||
"right": 3,
|
||||
"mode": { "type": "Eq" },
|
||||
"success": true,
|
||||
"subevents": [
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 3,
|
||||
"rank": 0,
|
||||
"content": { "type": "EmptyTagUnion" }
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 84, "to": 3 },
|
||||
{ "type": "VariableUnified", "from": 3, "to": 3 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 4,
|
||||
"rank": 0,
|
||||
"content": {
|
||||
"type": "TagUnion",
|
||||
"tags": { "False": [], "True": [] },
|
||||
"extension": { "type": "Any", "variable": 84 }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 1558, "to": 4 },
|
||||
{ "type": "VariableUnified", "from": 4, "to": 4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "VariableSetDescriptor",
|
||||
"variable": 5,
|
||||
"rank": 0,
|
||||
"content": {
|
||||
"type": "Alias",
|
||||
"name": "`Bool.Bool`",
|
||||
"variables": {
|
||||
"type_variables": [],
|
||||
"lambda_set_variables": [],
|
||||
"infer_ext_in_output_position_variables": []
|
||||
},
|
||||
"real_variable": 1558,
|
||||
"kind": { "type": "Opaque" }
|
||||
}
|
||||
},
|
||||
{ "type": "VariableUnified", "from": 79, "to": 5 },
|
||||
{ "type": "VariableUnified", "from": 5, "to": 5 }
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,60 @@
|
|||
import clsx from "clsx";
|
||||
import { EventEpoch } from "../../engine/engine";
|
||||
import { HashLink } from "react-router-hash-link";
|
||||
|
||||
export enum EpochCellView {
|
||||
Events,
|
||||
Graph,
|
||||
}
|
||||
|
||||
function invert(cell: EpochCellView): EpochCellView {
|
||||
if (cell === EpochCellView.Events) {
|
||||
return EpochCellView.Graph;
|
||||
}
|
||||
return EpochCellView.Events;
|
||||
}
|
||||
|
||||
function asStr(cell: EpochCellView): string {
|
||||
switch (cell) {
|
||||
case EpochCellView.Events:
|
||||
return "events";
|
||||
case EpochCellView.Graph:
|
||||
return "graph";
|
||||
}
|
||||
}
|
||||
|
||||
interface EpochCellProps {
|
||||
view: EpochCellView;
|
||||
epoch: EventEpoch;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const EPOCH_STYLES_ARRAY = [
|
||||
"text-slate-900",
|
||||
"font-mono",
|
||||
"bg-slate-200",
|
||||
"p-1",
|
||||
"py-0",
|
||||
"rounded-sm",
|
||||
"ring-1",
|
||||
"ring-slate-500",
|
||||
"text-sm",
|
||||
];
|
||||
|
||||
export const EPOCH_STYLES = clsx(...EPOCH_STYLES_ARRAY);
|
||||
|
||||
export default function EpochCell({ epoch, className, view }: EpochCellProps) {
|
||||
const invertedView = invert(view);
|
||||
|
||||
return (
|
||||
<HashLink smooth to={`#${asStr(invertedView)}-${epoch}`}>
|
||||
<div
|
||||
id={`${asStr(view)}-${epoch}`}
|
||||
className={clsx(EPOCH_STYLES, className)}
|
||||
>
|
||||
{view === EpochCellView.Graph ? "Epoch " : ""}
|
||||
{epoch}
|
||||
</div>
|
||||
</HashLink>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import { ComponentProps } from "react";
|
||||
import clsx from "clsx";
|
||||
import { QuerySubs, TypeDescriptor } from "../../engine/subs";
|
||||
import { Variable } from "../../schema";
|
||||
import DrawHeadConstructor from "../Content/HeadConstructor";
|
||||
import { contentStyles } from "./../Content";
|
||||
|
||||
interface VariableElProps {
|
||||
variable: Variable;
|
||||
subs: QuerySubs;
|
||||
onClick?: (variable: Variable) => void;
|
||||
nested?: boolean;
|
||||
raw?: boolean;
|
||||
}
|
||||
|
||||
export function VariableElPretty(props: VariableElProps): JSX.Element {
|
||||
const { variable, subs } = props;
|
||||
const desc = subs.get_root(variable);
|
||||
const content = (
|
||||
<DrawHeadConstructor
|
||||
desc={desc}
|
||||
drawVariablePretty={(variable) => (
|
||||
<VariableElPretty {...props} variable={variable} nested />
|
||||
)}
|
||||
drawVariableRaw={(variable) => (
|
||||
<VariableElRaw {...props} variable={variable} nested raw />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Helper {...props} desc={desc}>
|
||||
{content}
|
||||
</Helper>
|
||||
);
|
||||
}
|
||||
|
||||
function VariableElRaw(props: VariableElProps): JSX.Element {
|
||||
const desc = props.subs.get_root(props.variable);
|
||||
return <Helper {...props} desc={desc}></Helper>;
|
||||
}
|
||||
|
||||
function Helper({
|
||||
children,
|
||||
variable,
|
||||
desc,
|
||||
onClick,
|
||||
nested,
|
||||
raw,
|
||||
}: VariableElProps &
|
||||
Pick<ComponentProps<"div">, "children"> & {
|
||||
desc: TypeDescriptor | undefined;
|
||||
}): JSX.Element {
|
||||
const { bg } = contentStyles(desc);
|
||||
const varHeader =
|
||||
!nested || raw ? (
|
||||
<span
|
||||
className={clsx(
|
||||
"ring-1 ring-inset ring-black-100 px-1 bg-white rounded-md cursor",
|
||||
nested ? "text-md" : "p-0.5"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClick?.(variable);
|
||||
}}
|
||||
>
|
||||
{variable}
|
||||
</span>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
return (
|
||||
<span
|
||||
className={clsx(
|
||||
"rounded-md whitespace-nowrap",
|
||||
bg,
|
||||
nested ? "text-sm" : "p-0.5 pl-0 text-base"
|
||||
)}
|
||||
>
|
||||
{varHeader}
|
||||
{children ? <span className="px-1">{children}</span> : <></>}
|
||||
</span>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
import { TypeDescriptor } from "../../engine/subs";
|
||||
import {
|
||||
ClosureType,
|
||||
RecordFieldKind,
|
||||
UnspecializedClosureType,
|
||||
Variable,
|
||||
} from "../../schema";
|
||||
|
||||
type DrawVariable = (variable: Variable) => JSX.Element;
|
||||
|
||||
export interface DrawHeadConstructorProps {
|
||||
desc: TypeDescriptor | undefined;
|
||||
drawVariablePretty: DrawVariable;
|
||||
drawVariableRaw: DrawVariable;
|
||||
}
|
||||
|
||||
export default function DrawHeadConstructor({
|
||||
desc,
|
||||
drawVariablePretty,
|
||||
drawVariableRaw,
|
||||
}: DrawHeadConstructorProps): JSX.Element {
|
||||
if (!desc) {
|
||||
return <>???</>;
|
||||
}
|
||||
const content = desc.content;
|
||||
switch (content.type) {
|
||||
case "Flex":
|
||||
case "Rigid": {
|
||||
const { name } = content;
|
||||
return name ? <>{name}</> : <>_</>;
|
||||
}
|
||||
case "FlexAble":
|
||||
case "RigidAble": {
|
||||
const { name, abilities } = content;
|
||||
const nameEl = name ? <>{name}</> : <>_</>;
|
||||
return (
|
||||
<>
|
||||
{nameEl} has {abilities.join(", ")}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "Recursive": {
|
||||
const { name, structure } = content;
|
||||
const structureEl = drawVariableRaw(structure);
|
||||
const nameEl = name ? <>{name} to </> : <></>;
|
||||
return (
|
||||
<>
|
||||
<{nameEl}
|
||||
{structureEl}>
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "LambdaSet": {
|
||||
const { ambient_function, solved, unspecialized, recursion_var } =
|
||||
content;
|
||||
const ambientFunctionEl = drawVariableRaw(ambient_function);
|
||||
const solvedEl = (
|
||||
<DrawSolved drawVariableRaw={drawVariableRaw} solved={solved} />
|
||||
);
|
||||
const unspecializedEl = (
|
||||
<DrawUnspecialized
|
||||
drawVariableRaw={drawVariableRaw}
|
||||
unspecialized={unspecialized}
|
||||
/>
|
||||
);
|
||||
const recursionVarEl = recursion_var ? (
|
||||
<> as <{drawVariableRaw(recursion_var)}></>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
[{solvedEl}
|
||||
{unspecializedEl}]{recursionVarEl} ^{ambientFunctionEl}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "ErasedLambda": {
|
||||
return <>?</>;
|
||||
}
|
||||
case "Alias": {
|
||||
const { kind, name, variables } = content;
|
||||
const prefix = kind.type === "Opaque" ? "@" : "";
|
||||
const variablesEl = (
|
||||
<DrawVarArgumentsList
|
||||
drawVariableRaw={drawVariableRaw}
|
||||
variables={variables.type_variables}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<span>
|
||||
{prefix}
|
||||
{sym(name)}
|
||||
{variablesEl}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
case "Apply": {
|
||||
const { symbol, variables } = content;
|
||||
const variablesEl = (
|
||||
<DrawVarArgumentsList
|
||||
drawVariableRaw={drawVariableRaw}
|
||||
variables={variables}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{sym(symbol)}
|
||||
{variablesEl}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "Function": {
|
||||
const { arguments: args, lambda_type, ret } = content;
|
||||
const argsEl = args.map((arg, i) => (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
{drawVariablePretty(arg)}
|
||||
</span>
|
||||
));
|
||||
const lambdaTypeEl = drawVariablePretty(lambda_type);
|
||||
const retEl = drawVariablePretty(ret);
|
||||
return (
|
||||
<>
|
||||
{argsEl}
|
||||
{" -"}
|
||||
{lambdaTypeEl}
|
||||
{"-> "}
|
||||
{retEl}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "Record": {
|
||||
const { fields, extension } = content;
|
||||
const fieldsEl = Object.entries(fields).map(([key, value], i) => {
|
||||
const { field_type, kind } = value;
|
||||
return (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
{key} {<DrawFieldKind kind={kind} />}{" "}
|
||||
{drawVariablePretty(field_type)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{"{"}
|
||||
{fieldsEl}
|
||||
{"}"}
|
||||
{drawVariablePretty(extension)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "Tuple": {
|
||||
const { elements, extension } = content;
|
||||
const elemsEl = Object.entries(elements).map(([key, value], i) => {
|
||||
return (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
{key}: {drawVariablePretty(value)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<>
|
||||
({elemsEl}){drawVariablePretty(extension)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "TagUnion": {
|
||||
const { tags, extension } = content;
|
||||
return (
|
||||
<>
|
||||
<DrawTags tags={tags} drawVariableRaw={drawVariableRaw} />
|
||||
{drawVariablePretty(extension.variable)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "RecursiveTagUnion": {
|
||||
const { tags, extension, recursion_var } = content;
|
||||
return (
|
||||
<>
|
||||
(<DrawTags tags={tags} drawVariableRaw={drawVariableRaw} />
|
||||
{drawVariablePretty(extension.variable)} as <
|
||||
{drawVariableRaw(recursion_var)}>)
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "FunctionOrTagUnion": {
|
||||
const { functions, tags, extension } = content;
|
||||
const functionsEl = functions.map((f, i) => (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
{sym(f)}
|
||||
</span>
|
||||
));
|
||||
const tagsEl = tags.map((t, i) => (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
{t}
|
||||
</span>
|
||||
));
|
||||
return (
|
||||
<>
|
||||
[{functionsEl} | {tagsEl}]{drawVariablePretty(extension.variable)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "RangedNumber": {
|
||||
const {
|
||||
range: { kind, min_width, signed },
|
||||
} = content;
|
||||
switch (kind.type) {
|
||||
case "AnyNum":
|
||||
return <>ℚ{min_width}+</>;
|
||||
case "Int":
|
||||
return signed ? <>ℤ{min_width}+</> : <>ℕ{min_width}+</>;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "EmptyRecord": {
|
||||
return <>{"{}"}</>;
|
||||
}
|
||||
case "EmptyTuple": {
|
||||
return <>()</>;
|
||||
}
|
||||
case "EmptyTagUnion": {
|
||||
return <>[]</>;
|
||||
}
|
||||
case "Error": {
|
||||
return <>⊥</>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function DrawVarArgumentsList({
|
||||
variables,
|
||||
drawVariableRaw,
|
||||
}: {
|
||||
variables: Variable[];
|
||||
drawVariableRaw: DrawVariable;
|
||||
}): JSX.Element {
|
||||
return variables.length !== 0 ? (
|
||||
<>
|
||||
{" "}
|
||||
{variables.map((v, i) => (
|
||||
<span key={i}>{drawVariableRaw(v)}</span>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawSolved({
|
||||
solved,
|
||||
drawVariableRaw,
|
||||
}: {
|
||||
solved: ClosureType[];
|
||||
drawVariableRaw: DrawVariable;
|
||||
}): JSX.Element {
|
||||
const tags = solved.map(({ environment, function: fn }, i) => (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
<DrawTag
|
||||
tag={sym(fn)}
|
||||
variables={environment}
|
||||
drawVariableRaw={drawVariableRaw}
|
||||
/>
|
||||
</span>
|
||||
));
|
||||
return <>[{tags}]</>;
|
||||
}
|
||||
|
||||
function DrawTags({
|
||||
tags,
|
||||
drawVariableRaw,
|
||||
}: {
|
||||
tags: Record<string, Variable[]>;
|
||||
drawVariableRaw: DrawVariable;
|
||||
}): JSX.Element {
|
||||
const tagsEl = Object.entries(tags).map(([tag, vars], i) => (
|
||||
<span key={i}>
|
||||
{i !== 0 ? ", " : ""}
|
||||
<DrawTag tag={tag} variables={vars} drawVariableRaw={drawVariableRaw} />
|
||||
</span>
|
||||
));
|
||||
return <>[{tagsEl}]</>;
|
||||
}
|
||||
|
||||
function DrawUnspecialized({
|
||||
unspecialized,
|
||||
drawVariableRaw,
|
||||
}: {
|
||||
unspecialized: UnspecializedClosureType[];
|
||||
drawVariableRaw: DrawVariable;
|
||||
}): JSX.Element {
|
||||
const unspecs = unspecialized.map(
|
||||
({ ability_member, lambda_set_region, specialization }, i) => (
|
||||
<span key={i}>
|
||||
{" + "}
|
||||
{drawVariableRaw(specialization)}:{sym(ability_member)}:
|
||||
{lambda_set_region}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
return <>{unspecs}</>;
|
||||
}
|
||||
|
||||
function DrawTag({
|
||||
tag,
|
||||
variables,
|
||||
drawVariableRaw,
|
||||
}: {
|
||||
tag: string;
|
||||
variables: Variable[];
|
||||
drawVariableRaw: DrawVariable;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{tag}
|
||||
<DrawVarArgumentsList
|
||||
drawVariableRaw={drawVariableRaw}
|
||||
variables={variables}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawFieldKind({ kind }: { kind: RecordFieldKind }): JSX.Element {
|
||||
switch (kind.type) {
|
||||
case "Required":
|
||||
case "Demanded":
|
||||
return <>:</>;
|
||||
case "Optional":
|
||||
return <>?</>;
|
||||
}
|
||||
}
|
||||
|
||||
function sym(symbol: string): string {
|
||||
if (symbol.startsWith("`")) symbol = symbol.slice(1);
|
||||
if (symbol.endsWith("`")) symbol = symbol.slice(0, -1);
|
||||
return symbol.split(".").at(-1)!;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import { TypeDescriptor } from "../../engine/subs";
|
||||
import { assertExhaustive } from "../../utils/exhaustive";
|
||||
|
||||
export interface ContentStyles {
|
||||
name: string;
|
||||
bg: string;
|
||||
}
|
||||
|
||||
export function contentStyles(desc: TypeDescriptor | undefined): ContentStyles {
|
||||
if (!desc) {
|
||||
return { name: "???", bg: "bg-red-500" };
|
||||
}
|
||||
|
||||
const content = desc.content;
|
||||
switch (content.type) {
|
||||
case "Flex":
|
||||
return { name: "Flex", bg: "bg-blue-300" };
|
||||
case "FlexAble":
|
||||
return { name: "FlexAble", bg: "bg-blue-400" };
|
||||
case "Rigid":
|
||||
return { name: "Rigid", bg: "bg-indigo-300" };
|
||||
case "RigidAble":
|
||||
return { name: "RigidAble", bg: "bg-indigo-400" };
|
||||
case "Recursive":
|
||||
return { name: "Rec", bg: "bg-blue-grey-500" };
|
||||
case "LambdaSet":
|
||||
return { name: "LambdaSet", bg: "bg-green-500" };
|
||||
case "ErasedLambda":
|
||||
return { name: "ErasedLambda", bg: "bg-green-700" };
|
||||
case "Alias": {
|
||||
switch (content.kind.type) {
|
||||
case "Structural":
|
||||
return { name: "Alias", bg: "bg-yellow-300" };
|
||||
case "Opaque":
|
||||
return { name: "Opaque", bg: "bg-amber-400" };
|
||||
default:
|
||||
assertExhaustive(content.kind);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Apply":
|
||||
return { name: "Apply", bg: "bg-orange-500" };
|
||||
case "Function":
|
||||
return { name: "Func", bg: "bg-teal-400" };
|
||||
case "Record":
|
||||
return { name: "Record", bg: "bg-purple-400" };
|
||||
case "Tuple":
|
||||
return { name: "Tuple", bg: "bg-deep-purple-400" };
|
||||
case "TagUnion":
|
||||
return { name: "Tags", bg: "bg-cyan-200" };
|
||||
case "FunctionOrTagUnion":
|
||||
return { name: "Func|Tags", bg: "bg-cyan-300" };
|
||||
case "RecursiveTagUnion":
|
||||
return { name: "RecTags", bg: "bg-cyan-400" };
|
||||
case "RangedNumber":
|
||||
return { name: "ℕ", bg: "bg-lime-400" };
|
||||
case "EmptyRecord":
|
||||
return { name: "{}", bg: "bg-purple-400" };
|
||||
case "EmptyTuple":
|
||||
return { name: "()", bg: "bg-deep-purple-400" };
|
||||
case "EmptyTagUnion":
|
||||
return { name: "[]", bg: "bg-cyan-200" };
|
||||
case "Error":
|
||||
return { name: "Error", bg: "bg-red-400" };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { EventEpoch } from "../../engine/engine";
|
||||
import { Variable } from "../../schema";
|
||||
import { VariableElPretty } from "../Common/Variable";
|
||||
import { CommonProps } from "./types";
|
||||
|
||||
interface VariableProps extends CommonProps {
|
||||
epoch: EventEpoch;
|
||||
variable: Variable;
|
||||
}
|
||||
|
||||
export function VariableEl({
|
||||
engine,
|
||||
toggleVariableVis,
|
||||
epoch,
|
||||
variable,
|
||||
}: VariableProps): JSX.Element {
|
||||
engine.stepTo(epoch);
|
||||
return (
|
||||
<VariableElPretty
|
||||
variable={variable}
|
||||
subs={engine.subs}
|
||||
onClick={(variable: Variable) => {
|
||||
toggleVariableVis(variable);
|
||||
}}
|
||||
></VariableElPretty>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import type { Engine, EventEpoch } from "../../engine/engine";
|
||||
import type { Variable } from "../../schema";
|
||||
|
||||
export interface CommonProps {
|
||||
currentEpoch: EventEpoch;
|
||||
engine: Engine;
|
||||
toggleVariableVis: (variable: Variable) => void;
|
||||
}
|
234
crates/compiler/checkmate/www/src/components/EventList.tsx
Normal file
234
crates/compiler/checkmate/www/src/components/EventList.tsx
Normal file
|
@ -0,0 +1,234 @@
|
|||
import clsx from "clsx";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { EventEpoch } from "../engine/engine";
|
||||
import { lastSubEvent } from "../engine/event_util";
|
||||
import { UnificationMode, Event } from "../schema";
|
||||
import { Refine } from "../utils/refine";
|
||||
import EpochCell, { EpochCellView } from "./Common/EpochCell";
|
||||
import { CommonProps } from "./EventItem/types";
|
||||
import { VariableEl } from "./EventItem/Variable";
|
||||
|
||||
interface EventListProps extends CommonProps {
|
||||
events: Event[];
|
||||
root?: boolean;
|
||||
}
|
||||
|
||||
const MT = "mt-2.5";
|
||||
const UNFOCUSED = "opacity-40";
|
||||
|
||||
export default function EventList(props: EventListProps): JSX.Element {
|
||||
const { events, root } = props;
|
||||
return (
|
||||
<ul className={clsx(MT, root ? "ml-2" : "ml-[1.5em]")}>
|
||||
{events.map((event, i) => (
|
||||
<li key={i} className={MT}>
|
||||
<OneEvent {...props} event={event} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
interface OneEventProps extends CommonProps {
|
||||
event: Event;
|
||||
}
|
||||
|
||||
function OneEvent(props: OneEventProps): JSX.Element {
|
||||
const { event } = props;
|
||||
switch (event.type) {
|
||||
case "Unification":
|
||||
return <Unification {...props} event={event} />;
|
||||
case "VariableUnified":
|
||||
return <></>;
|
||||
case "VariableSetDescriptor":
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
const DROPDOWN_CLOSED = "▶";
|
||||
const DROPDOWN_OPEN = "▼";
|
||||
|
||||
const UN_UNKNOWN = "💭";
|
||||
const UN_SUCCESS = "✅";
|
||||
const UN_FAILURE = "❌";
|
||||
|
||||
function epochInRange(
|
||||
epoch: EventEpoch,
|
||||
[start, end]: [EventEpoch, EventEpoch]
|
||||
): boolean {
|
||||
return epoch >= start && epoch <= end;
|
||||
}
|
||||
|
||||
interface UnificationProps extends CommonProps {
|
||||
event: Refine<Event, "Unification">;
|
||||
}
|
||||
|
||||
function Unification(props: UnificationProps): JSX.Element {
|
||||
const { engine, event, currentEpoch } = props;
|
||||
const { mode, subevents, success } = event;
|
||||
|
||||
const beforeUnificationEpoch = engine.getEventIndex(event);
|
||||
const afterUnificationEpoch = engine.getEventIndex(lastSubEvent(event));
|
||||
|
||||
const containsCurrentEpoch = epochInRange(currentEpoch, [
|
||||
beforeUnificationEpoch,
|
||||
afterUnificationEpoch,
|
||||
]);
|
||||
|
||||
const leftVar = useMemo(
|
||||
() => (epoch: EventEpoch) =>
|
||||
<VariableEl {...props} epoch={epoch} variable={event.left} />,
|
||||
[event.left, props]
|
||||
);
|
||||
const rightVar = useMemo(
|
||||
() => (epoch: EventEpoch) =>
|
||||
<VariableEl {...props} epoch={epoch} variable={event.right} />,
|
||||
[event.right, props]
|
||||
);
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const modeIcon = useMemo(() => <UnificationModeIcon mode={mode} />, [mode]);
|
||||
|
||||
const resultIcon = success ? UN_SUCCESS : UN_FAILURE;
|
||||
const resultHeadline = <Headline icon={resultIcon}></Headline>;
|
||||
|
||||
const epochCell = useMemo(() => {
|
||||
if (!containsCurrentEpoch) return null;
|
||||
return (
|
||||
<EpochCell
|
||||
view={EpochCellView.Events}
|
||||
epoch={currentEpoch}
|
||||
className="inline-block align-middle mr-2"
|
||||
></EpochCell>
|
||||
);
|
||||
}, [containsCurrentEpoch, currentEpoch]);
|
||||
|
||||
const getHeadline = useCallback(
|
||||
({
|
||||
epoch,
|
||||
includeEpochIfInRange,
|
||||
}: {
|
||||
epoch: EventEpoch;
|
||||
includeEpochIfInRange: boolean;
|
||||
}) => {
|
||||
const topHeadline = (
|
||||
<Headline icon={isOpen ? UN_UNKNOWN : resultIcon}></Headline>
|
||||
);
|
||||
|
||||
const optEpochCell =
|
||||
includeEpochIfInRange && containsCurrentEpoch && epochCell;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-full text-left whitespace-nowrap h-full"
|
||||
>
|
||||
{optEpochCell}
|
||||
<span
|
||||
className={clsx(
|
||||
"mr-2",
|
||||
isOpen ? "text-slate-500" : "text-slate-400"
|
||||
)}
|
||||
>
|
||||
{isOpen ? DROPDOWN_OPEN : DROPDOWN_CLOSED}
|
||||
</span>
|
||||
{topHeadline} {leftVar(epoch)} {modeIcon} {rightVar(epoch)}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
[
|
||||
isOpen,
|
||||
resultIcon,
|
||||
containsCurrentEpoch,
|
||||
epochCell,
|
||||
leftVar,
|
||||
modeIcon,
|
||||
rightVar,
|
||||
]
|
||||
);
|
||||
|
||||
if (!isOpen) {
|
||||
const headLine = getHeadline({
|
||||
epoch: afterUnificationEpoch,
|
||||
includeEpochIfInRange: true,
|
||||
});
|
||||
return (
|
||||
<div className={clsx(!containsCurrentEpoch && UNFOCUSED)}>{headLine}</div>
|
||||
);
|
||||
} else {
|
||||
const optEpochCellAfter =
|
||||
afterUnificationEpoch === currentEpoch && epochCell;
|
||||
const optEpochCellBefore =
|
||||
beforeUnificationEpoch === currentEpoch && epochCell;
|
||||
|
||||
const headlineBefore = getHeadline({
|
||||
epoch: beforeUnificationEpoch,
|
||||
includeEpochIfInRange: false,
|
||||
});
|
||||
|
||||
const dropdownTransparent = (
|
||||
<span className="text-transparent mr-2">{DROPDOWN_OPEN}</span>
|
||||
);
|
||||
|
||||
const headlineAfter = (
|
||||
<div className={clsx("whitespace-nowrap")}>
|
||||
{dropdownTransparent}
|
||||
{resultHeadline} {leftVar(afterUnificationEpoch)} {modeIcon}{" "}
|
||||
{rightVar(afterUnificationEpoch)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid gap-0 grid-cols-[min-content_min-content_auto]">
|
||||
{/* Row 1: unification start */}
|
||||
<div className="row-start-1 col-start-1">{optEpochCellBefore}</div>
|
||||
<div className="row-start-1 col-start-3">{headlineBefore}</div>
|
||||
|
||||
{/* Row 2: inner traces */}
|
||||
<div className="row-start-2 col-start-1"></div>
|
||||
<div className="row-start-2 col-start-3">
|
||||
<EventList
|
||||
{...props}
|
||||
root={false}
|
||||
engine={engine}
|
||||
events={subevents}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Row 3: inner traces */}
|
||||
<div className="row-start-3 col-start-1">{optEpochCellAfter}</div>
|
||||
<div className="row-start-3 col-start-3">{headlineAfter}</div>
|
||||
|
||||
{/* Col 2: dropdown line */}
|
||||
<div
|
||||
className={clsx(
|
||||
"row-start-1 row-end-4 col-start-2 h-full",
|
||||
"relative z-[1] h-full",
|
||||
"before:content-[''] before:border-l before:border-slate-500 before:z-[-1]",
|
||||
"before:absolute before:w-0 before:h-[calc(100%-1.5rem)] before:top-[1rem] before:left-[0.3rem]"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Headline({ icon }: { icon: string }): JSX.Element {
|
||||
return (
|
||||
<div className="inline-block align-middle">
|
||||
<div className="">{icon}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UnificationModeIcon({ mode }: { mode: UnificationMode }): JSX.Element {
|
||||
switch (mode.type) {
|
||||
case "Eq":
|
||||
return <>~</>;
|
||||
case "Present":
|
||||
return <>+=</>;
|
||||
case "LambdaSetSpecialization":
|
||||
return <>|~|</>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import { Variable } from "../../schema";
|
||||
|
||||
export type ToggleVariableHandler = (variable: Variable) => void;
|
||||
export type KeydownHandler = (key: string) => Promise<void>;
|
48
crates/compiler/checkmate/www/src/components/FileInput.tsx
Normal file
48
crates/compiler/checkmate/www/src/components/FileInput.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { AllEvents } from "../schema";
|
||||
|
||||
export type EventsOk = {
|
||||
kind: "ok";
|
||||
events: AllEvents;
|
||||
};
|
||||
|
||||
export type EventsErr = {
|
||||
kind: "err";
|
||||
error: string;
|
||||
};
|
||||
|
||||
export type LoadedEvents = EventsOk | EventsErr;
|
||||
|
||||
interface FileInputProps {
|
||||
setResult(result: LoadedEvents): void;
|
||||
}
|
||||
|
||||
export default function FileInput({ setResult }: FileInputProps) {
|
||||
async function setFile(e: React.ChangeEvent<HTMLInputElement>) {
|
||||
e.preventDefault();
|
||||
const files = e.target.files;
|
||||
if (!files) {
|
||||
setResult({ kind: "err", error: "Please choose a checkmate file." });
|
||||
return;
|
||||
}
|
||||
const file = files[0];
|
||||
const buf = await file.text();
|
||||
try {
|
||||
const events: AllEvents = JSON.parse(buf);
|
||||
setResult({ kind: "ok", events });
|
||||
} catch (e) {
|
||||
setResult({ kind: "err", error: "Invalid checkmate file." });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="file"
|
||||
name="small-file-input"
|
||||
id="small-file-input"
|
||||
onChange={(e) => setFile(e)}
|
||||
className="block w-full border border-gray-200 shadow-sm rounded-md text-sm
|
||||
file:bg-roc-purple-bg file:border-0 file:mr-4 file:py-2 file:px-4 cursor-pointer"
|
||||
></input>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
import clsx from "clsx";
|
||||
import { Handle, Position } from "reactflow";
|
||||
import { Variable } from "../../schema";
|
||||
import { assertExhaustive } from "../../utils/exhaustive";
|
||||
import { contentStyles } from "../Content";
|
||||
import { VariableElPretty } from "../Common/Variable";
|
||||
import { SubsSnapshot, TypeDescriptor } from "../../engine/subs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { TypedEmitter } from "tiny-typed-emitter";
|
||||
|
||||
type AddSubVariableLink = (from: Variable, subVariable: Variable) => void;
|
||||
|
||||
export interface VariableMessageEvents {
|
||||
focus: (variable: Variable) => void;
|
||||
}
|
||||
|
||||
export interface VariableNodeProps {
|
||||
data: {
|
||||
subs: SubsSnapshot;
|
||||
variable: Variable;
|
||||
addSubVariableLink: AddSubVariableLink;
|
||||
isOutlined: boolean;
|
||||
ee: TypedEmitter<VariableMessageEvents>;
|
||||
};
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
}
|
||||
|
||||
export default function VariableNode({
|
||||
data,
|
||||
targetPosition,
|
||||
sourcePosition,
|
||||
}: VariableNodeProps): JSX.Element {
|
||||
const {
|
||||
variable,
|
||||
subs,
|
||||
addSubVariableLink,
|
||||
isOutlined: isOutlinedProp,
|
||||
ee: eeProp,
|
||||
} = data;
|
||||
|
||||
const [isOutlined, setIsOutlined] = useState(isOutlinedProp);
|
||||
|
||||
useEffect(() => {
|
||||
eeProp.on("focus", (focusVar: Variable) => {
|
||||
if (focusVar !== variable) return;
|
||||
setIsOutlined(true);
|
||||
});
|
||||
}, [eeProp, variable]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOutlined) return;
|
||||
const timer = setTimeout(() => {
|
||||
setIsOutlined(false);
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [isOutlined]);
|
||||
|
||||
const desc = subs.get_root(variable);
|
||||
const styles = contentStyles(desc);
|
||||
const basis: BasisProps = {
|
||||
subs,
|
||||
origin: variable,
|
||||
addSubVariableLink,
|
||||
};
|
||||
|
||||
const content = Object.entries(
|
||||
VariableNodeContent(variable, desc, basis)
|
||||
).filter((el): el is [string, JSX.Element] => !!el[1]);
|
||||
|
||||
let expandedContent = <></>;
|
||||
if (content.length > 0) {
|
||||
expandedContent = (
|
||||
<ul className="text-sm text-left mt-2 space-y-1">
|
||||
{content.map(([key, value], i) => (
|
||||
<li key={i} className="space-x-2">
|
||||
{key}: {value}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
styles.bg,
|
||||
"bg-opacity-50 py-2 px-4 rounded-lg border transition ease-in-out duration-700",
|
||||
isOutlined && "ring-2 ring-blue-500",
|
||||
"text-center font-mono"
|
||||
)}
|
||||
>
|
||||
<Handle
|
||||
type="target"
|
||||
position={targetPosition ?? Position.Top}
|
||||
isConnectable={false}
|
||||
/>
|
||||
<div>
|
||||
<VariableElPretty variable={variable} subs={subs} />
|
||||
</div>
|
||||
{expandedContent}
|
||||
<Handle
|
||||
type="source"
|
||||
position={sourcePosition ?? Position.Bottom}
|
||||
isConnectable={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function VariableNodeContent(
|
||||
variable: Variable,
|
||||
desc: TypeDescriptor | undefined,
|
||||
basis: BasisProps
|
||||
): Record<string, JSX.Element | null> {
|
||||
if (!desc) return {};
|
||||
const { content } = desc;
|
||||
|
||||
switch (content.type) {
|
||||
case "Flex":
|
||||
case "Rigid": {
|
||||
const { name } = content;
|
||||
return { name: name ? <>{name}</> : null };
|
||||
}
|
||||
case "FlexAble":
|
||||
case "RigidAble": {
|
||||
const { name, abilities } = content;
|
||||
return {
|
||||
name: <>{name}</>,
|
||||
abilities: <>[{abilities.join(", ")}]</>,
|
||||
};
|
||||
}
|
||||
case "Recursive": {
|
||||
const { name, structure } = content;
|
||||
return {
|
||||
name: <>{name}</>,
|
||||
structure: <SubVariable {...basis} variable={structure} />,
|
||||
};
|
||||
}
|
||||
case "LambdaSet": {
|
||||
const { ambient_function, solved, unspecialized, recursion_var } =
|
||||
content;
|
||||
return {
|
||||
"^": <SubVariable {...basis} variable={ambient_function} />,
|
||||
as: recursion_var ? (
|
||||
<SubVariable {...basis} variable={recursion_var} />
|
||||
) : null,
|
||||
};
|
||||
}
|
||||
case "ErasedLambda": {
|
||||
return {};
|
||||
}
|
||||
case "Alias": {
|
||||
const { name, real_variable, variables } = content;
|
||||
return {
|
||||
name: <>{name}</>,
|
||||
};
|
||||
}
|
||||
case "Apply": {
|
||||
const { name, variables } = content;
|
||||
return {
|
||||
name: <>{name}</>,
|
||||
};
|
||||
}
|
||||
case "Function": {
|
||||
const { arguments: args, lambda_type, ret } = content;
|
||||
return {
|
||||
args: (
|
||||
<>
|
||||
{args.map((arg, i) => (
|
||||
<SubVariable key={i} {...basis} variable={arg} />
|
||||
))}
|
||||
</>
|
||||
),
|
||||
"||": <SubVariable {...basis} variable={lambda_type} />,
|
||||
ret: <SubVariable {...basis} variable={ret} />,
|
||||
};
|
||||
}
|
||||
case "FunctionOrTagUnion": {
|
||||
const { tags, functions, extension } = content;
|
||||
return {
|
||||
tags: <>[{tags.join(", ")}]</>,
|
||||
fns: <>[{functions.join(", ")}]</>,
|
||||
};
|
||||
}
|
||||
case "TagUnion": {
|
||||
const { tags, extension } = content;
|
||||
return {};
|
||||
}
|
||||
case "RecursiveTagUnion": {
|
||||
const { recursion_var, extension, tags } = content;
|
||||
return {
|
||||
as: <SubVariable {...basis} variable={recursion_var} />,
|
||||
};
|
||||
}
|
||||
case "Record": {
|
||||
const { fields, extension } = content;
|
||||
return {};
|
||||
}
|
||||
case "Tuple": {
|
||||
const { elements, extension } = content;
|
||||
return {};
|
||||
}
|
||||
case "RangedNumber": {
|
||||
const { range } = content;
|
||||
return {};
|
||||
}
|
||||
case "EmptyRecord":
|
||||
case "EmptyTuple":
|
||||
case "EmptyTagUnion":
|
||||
case "Error": {
|
||||
return {};
|
||||
}
|
||||
default: {
|
||||
return assertExhaustive(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BasisProps {
|
||||
subs: SubsSnapshot;
|
||||
origin: Variable;
|
||||
addSubVariableLink: AddSubVariableLink;
|
||||
}
|
||||
|
||||
function SubVariable({
|
||||
subs,
|
||||
origin,
|
||||
variable,
|
||||
addSubVariableLink,
|
||||
}: {
|
||||
variable: Variable;
|
||||
} & BasisProps): JSX.Element {
|
||||
return (
|
||||
<VariableElPretty
|
||||
variable={variable}
|
||||
subs={subs}
|
||||
onClick={() => addSubVariableLink(origin, variable)}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,502 @@
|
|||
import ELK, {
|
||||
type ElkNode,
|
||||
type LayoutOptions,
|
||||
} from "elkjs/lib/elk.bundled.js";
|
||||
import ReactFlow, {
|
||||
Node,
|
||||
Edge,
|
||||
Background,
|
||||
BackgroundVariant,
|
||||
useReactFlow,
|
||||
ReactFlowProvider,
|
||||
NodeChange,
|
||||
applyNodeChanges,
|
||||
EdgeChange,
|
||||
applyEdgeChanges,
|
||||
Panel,
|
||||
NodeTypes,
|
||||
useStore,
|
||||
ReactFlowState,
|
||||
Position,
|
||||
} from "reactflow";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Variable } from "../../schema";
|
||||
|
||||
import "reactflow/dist/style.css";
|
||||
import clsx from "clsx";
|
||||
import VariableNode, {
|
||||
VariableMessageEvents,
|
||||
VariableNodeProps,
|
||||
} from "./VariableNode";
|
||||
import { SubsSnapshot } from "../../engine/subs";
|
||||
import { KeydownHandler } from "../Events";
|
||||
import { TypedEmitter } from "tiny-typed-emitter";
|
||||
import EpochCell, { EpochCellView } from "../Common/EpochCell";
|
||||
|
||||
export interface VariablesGraphProps {
|
||||
subs: SubsSnapshot;
|
||||
onVariable: (handler: (variable: Variable) => void) => void;
|
||||
onKeydown: (handler: KeydownHandler) => void;
|
||||
}
|
||||
|
||||
function horizontalityToPositions(isHorizontal: boolean): {
|
||||
targetPosition: Position;
|
||||
sourcePosition: Position;
|
||||
} {
|
||||
return {
|
||||
targetPosition: isHorizontal ? Position.Left : Position.Top,
|
||||
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
|
||||
};
|
||||
}
|
||||
|
||||
interface LayoutedElements {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
}
|
||||
|
||||
const LAYOUT_CONFIG_DOWN = {
|
||||
keypress: "j",
|
||||
emoji: "⬇️",
|
||||
elkLayoutOptions: {
|
||||
"elk.algorithm": "layered",
|
||||
"elk.direction": "DOWN",
|
||||
},
|
||||
isHorizontal: false,
|
||||
} as const;
|
||||
const LAYOUT_CONFIG_RIGHT = {
|
||||
keypress: "l",
|
||||
emoji: "➡️",
|
||||
elkLayoutOptions: {
|
||||
"elk.algorithm": "layered",
|
||||
"elk.direction": "RIGHT",
|
||||
},
|
||||
isHorizontal: true,
|
||||
} as const;
|
||||
const LAYOUT_CONFIG_RADIAL = {
|
||||
keypress: "r",
|
||||
emoji: "🌐",
|
||||
elkLayoutOptions: {
|
||||
"elk.algorithm": "radial",
|
||||
},
|
||||
isHorizontal: false,
|
||||
} as const;
|
||||
const LAYOUT_CONFIG_FORCE = {
|
||||
keypress: "f",
|
||||
emoji: "🧲",
|
||||
elkLayoutOptions: {
|
||||
"elk.algorithm": "force",
|
||||
},
|
||||
isHorizontal: false,
|
||||
} as const;
|
||||
|
||||
type LayoutConfiguration =
|
||||
| typeof LAYOUT_CONFIG_DOWN
|
||||
| typeof LAYOUT_CONFIG_RIGHT
|
||||
| typeof LAYOUT_CONFIG_RADIAL
|
||||
| typeof LAYOUT_CONFIG_FORCE;
|
||||
|
||||
const LAYOUT_CONFIGURATIONS: LayoutConfiguration[] = [
|
||||
LAYOUT_CONFIG_DOWN,
|
||||
LAYOUT_CONFIG_RIGHT,
|
||||
LAYOUT_CONFIG_RADIAL,
|
||||
LAYOUT_CONFIG_FORCE,
|
||||
];
|
||||
|
||||
type ComputeElkLayoutOptions = Pick<
|
||||
LayoutConfiguration,
|
||||
"elkLayoutOptions" | "isHorizontal"
|
||||
>;
|
||||
|
||||
interface ComputeLayoutedElementsProps
|
||||
extends LayoutedElements,
|
||||
ComputeElkLayoutOptions {}
|
||||
|
||||
// Elk has a *huge* amount of options to configure. To see everything you can
|
||||
// tweak check out:
|
||||
//
|
||||
// - https://www.eclipse.org/elk/reference/algorithms.html
|
||||
// - https://www.eclipse.org/elk/reference/options.html
|
||||
const baseElkOptions: LayoutOptions = {
|
||||
"elk.layered.spacing.nodeNodeBetweenLayers": "100",
|
||||
"elk.spacing.nodeNode": "80",
|
||||
};
|
||||
|
||||
async function computeLayoutedElements({
|
||||
nodes,
|
||||
edges,
|
||||
elkLayoutOptions,
|
||||
isHorizontal,
|
||||
}: ComputeLayoutedElementsProps): Promise<LayoutedElements> {
|
||||
if (nodes.length === 0) {
|
||||
return Promise.resolve({
|
||||
nodes: [],
|
||||
edges: [],
|
||||
});
|
||||
}
|
||||
|
||||
const elk = new ELK();
|
||||
const graph: ElkNode = {
|
||||
id: "root",
|
||||
layoutOptions: {
|
||||
...baseElkOptions,
|
||||
...elkLayoutOptions,
|
||||
},
|
||||
//@ts-ignore
|
||||
children: nodes.map((node) => ({
|
||||
...node,
|
||||
// Adjust the target and source handle positions based on the layout
|
||||
// direction.
|
||||
targetPosition: isHorizontal ? "left" : "top",
|
||||
sourcePosition: isHorizontal ? "right" : "bottom",
|
||||
|
||||
// Hardcode a width and height for elk to use when layouting.
|
||||
//width: 150,
|
||||
//height: 50,
|
||||
})),
|
||||
//@ts-ignore
|
||||
edges: edges,
|
||||
};
|
||||
|
||||
const layoutedGraph = await elk.layout(graph);
|
||||
|
||||
if (!layoutedGraph.children || !layoutedGraph.edges) {
|
||||
throw new Error("Elk did not return a valid graph");
|
||||
}
|
||||
|
||||
return {
|
||||
//@ts-ignore
|
||||
nodes: layoutedGraph.children.map((node) => ({
|
||||
...node,
|
||||
// React Flow expects a position property on the node instead of `x`
|
||||
// and `y` fields.
|
||||
position: { x: node.x, y: node.y },
|
||||
})),
|
||||
//@ts-ignore
|
||||
edges: layoutedGraph.edges,
|
||||
};
|
||||
}
|
||||
|
||||
const NODE_TYPES: NodeTypes = {
|
||||
variable: VariableNode,
|
||||
};
|
||||
|
||||
type VariableNodeData = VariableNodeProps["data"];
|
||||
type RFVariableNode = Node<VariableNodeData>;
|
||||
|
||||
function newVariable(
|
||||
id: string,
|
||||
data: VariableNodeData,
|
||||
isHorizontal: boolean
|
||||
): RFVariableNode {
|
||||
return {
|
||||
id,
|
||||
position: { x: 0, y: 0 },
|
||||
type: "variable",
|
||||
data,
|
||||
...horizontalityToPositions(isHorizontal),
|
||||
};
|
||||
}
|
||||
|
||||
function addNodeChange(node: Node, existingNodes: Node[]): NodeChange | null {
|
||||
if (existingNodes.some((n) => n.id === node.id)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "add",
|
||||
item: node,
|
||||
};
|
||||
}
|
||||
|
||||
function addEdgeChange(edge: Edge, existingEdges: Edge[]): EdgeChange | null {
|
||||
if (existingEdges.some((e) => e.id === edge.id)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "add",
|
||||
item: edge,
|
||||
};
|
||||
}
|
||||
|
||||
// Auto-layout logic due in part to the `feldera/dbsp` project, licensed under
|
||||
// the MIT license.
|
||||
//
|
||||
// The source code for the original project can be found at
|
||||
// https://github.com/feldera/dbsp/blob/585a1926a6d3a0f8176dc80db5e906ec7d095400/web-ui/src/streaming/builder/hooks/useAutoLayout.ts#L215
|
||||
// and its license at
|
||||
// https://github.com/feldera/dbsp/blob/585a1926a6d3a0f8176dc80db5e906ec7d095400/LICENSE
|
||||
const nodeCountSelector = (state: ReactFlowState) =>
|
||||
state.nodeInternals.size + state.edges.length;
|
||||
const nodesSetInViewSelector = (state: ReactFlowState) =>
|
||||
Array.from(state.nodeInternals.values()).every(
|
||||
(node) => node.width && node.height
|
||||
);
|
||||
|
||||
type RedoLayoutFn = () => Promise<void>;
|
||||
function useRedoLayout(options: ComputeElkLayoutOptions): RedoLayoutFn {
|
||||
const nodeCount = useStore(nodeCountSelector);
|
||||
const nodesInitialized = useStore(nodesSetInViewSelector);
|
||||
const { getNodes, setNodes, getEdges } = useReactFlow();
|
||||
const instance = useReactFlow();
|
||||
|
||||
return useCallback(async () => {
|
||||
if (!nodeCount || !nodesInitialized) {
|
||||
return;
|
||||
}
|
||||
const { nodes } = await computeLayoutedElements({
|
||||
nodes: getNodes(),
|
||||
edges: getEdges(),
|
||||
...options,
|
||||
});
|
||||
setNodes(nodes);
|
||||
window.requestAnimationFrame(() => {
|
||||
instance.fitView();
|
||||
});
|
||||
}, [
|
||||
nodeCount,
|
||||
nodesInitialized,
|
||||
getNodes,
|
||||
getEdges,
|
||||
options,
|
||||
setNodes,
|
||||
instance,
|
||||
]);
|
||||
}
|
||||
|
||||
// Does positioning of the nodes in the graph.
|
||||
function useAutoLayout(options: ComputeElkLayoutOptions) {
|
||||
const redoLayout = useRedoLayout(options);
|
||||
|
||||
useEffect(() => {
|
||||
// This wrapping is of course redundant, but exercised for the purpose of
|
||||
// explicitness.
|
||||
async function inner() {
|
||||
await redoLayout();
|
||||
}
|
||||
inner();
|
||||
}, [redoLayout]);
|
||||
}
|
||||
|
||||
function useKeydown({
|
||||
layoutConfig,
|
||||
setLayoutConfig,
|
||||
onKeydown,
|
||||
}: {
|
||||
layoutConfig: LayoutConfiguration;
|
||||
setLayoutConfig: React.Dispatch<React.SetStateAction<LayoutConfiguration>>;
|
||||
onKeydown: (handler: KeydownHandler) => void;
|
||||
}) {
|
||||
const redoLayout = useRedoLayout(layoutConfig);
|
||||
|
||||
const keyDownHandler = useCallback(
|
||||
async (key: string) => {
|
||||
switch (key) {
|
||||
case "c": {
|
||||
await redoLayout();
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const config of LAYOUT_CONFIGURATIONS) {
|
||||
if (key === config.keypress) {
|
||||
setLayoutConfig(config);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
[redoLayout, setLayoutConfig]
|
||||
);
|
||||
onKeydown(async (key) => {
|
||||
await keyDownHandler(key);
|
||||
});
|
||||
}
|
||||
|
||||
function Graph({
|
||||
subs,
|
||||
onVariable,
|
||||
onKeydown,
|
||||
}: VariablesGraphProps): JSX.Element {
|
||||
const initialNodes: Node[] = [];
|
||||
const initialEdges: Edge[] = [];
|
||||
|
||||
const ee = useRef(new TypedEmitter<VariableMessageEvents>());
|
||||
|
||||
const [layoutConfig, setLayoutConfig] =
|
||||
useState<LayoutConfiguration>(LAYOUT_CONFIG_DOWN);
|
||||
const [elements, setElements] = useState<LayoutedElements>({
|
||||
nodes: initialNodes,
|
||||
edges: initialEdges,
|
||||
});
|
||||
|
||||
useAutoLayout(layoutConfig);
|
||||
useKeydown({
|
||||
layoutConfig,
|
||||
setLayoutConfig,
|
||||
onKeydown,
|
||||
});
|
||||
|
||||
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
||||
setElements(({ nodes, edges }) => {
|
||||
return {
|
||||
nodes: applyNodeChanges(changes, nodes),
|
||||
edges,
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onEdgesChange = useCallback((changes: EdgeChange[]) => {
|
||||
setElements(({ nodes, edges }) => {
|
||||
return {
|
||||
nodes,
|
||||
edges: applyEdgeChanges(changes, edges),
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const addSubVariableLink = useCallback(
|
||||
(fromN: Variable, subLinkN: Variable) => {
|
||||
fromN = subs.get_root_key(fromN);
|
||||
subLinkN = subs.get_root_key(subLinkN);
|
||||
const from = fromN.toString();
|
||||
const to = subLinkN.toString();
|
||||
|
||||
setElements(({ nodes, edges }) => {
|
||||
const optNewNode = addNodeChange(
|
||||
newVariable(
|
||||
to,
|
||||
{
|
||||
subs,
|
||||
variable: subLinkN,
|
||||
addSubVariableLink,
|
||||
isOutlined: true,
|
||||
ee: ee.current,
|
||||
},
|
||||
layoutConfig.isHorizontal
|
||||
),
|
||||
nodes
|
||||
);
|
||||
const newNodes = optNewNode
|
||||
? applyNodeChanges([optNewNode], nodes)
|
||||
: nodes;
|
||||
|
||||
const optNewEdge = addEdgeChange(
|
||||
{ id: `${from}->${to}`, source: from, target: to },
|
||||
edges
|
||||
);
|
||||
const newEdges = optNewEdge
|
||||
? applyEdgeChanges([optNewEdge], edges)
|
||||
: edges;
|
||||
|
||||
return { nodes: newNodes, edges: newEdges };
|
||||
});
|
||||
|
||||
ee.current.emit("focus", subLinkN);
|
||||
},
|
||||
[layoutConfig, subs]
|
||||
);
|
||||
|
||||
const addNode = useCallback(
|
||||
(variableN: Variable) => {
|
||||
variableN = subs.get_root_key(variableN);
|
||||
const variable = variableN.toString();
|
||||
|
||||
setElements(({ nodes, edges }) => {
|
||||
const optNewNode = addNodeChange(
|
||||
newVariable(
|
||||
variable,
|
||||
{
|
||||
subs,
|
||||
variable: variableN,
|
||||
addSubVariableLink,
|
||||
isOutlined: true,
|
||||
ee: ee.current,
|
||||
},
|
||||
layoutConfig.isHorizontal
|
||||
),
|
||||
nodes
|
||||
);
|
||||
const newNodes = optNewNode
|
||||
? applyNodeChanges([optNewNode], nodes)
|
||||
: nodes;
|
||||
|
||||
return { nodes: newNodes, edges: edges };
|
||||
});
|
||||
|
||||
ee.current.emit("focus", variableN);
|
||||
},
|
||||
[subs, addSubVariableLink, layoutConfig]
|
||||
);
|
||||
|
||||
onVariable(addNode);
|
||||
|
||||
return (
|
||||
<ReactFlow
|
||||
nodes={elements.nodes}
|
||||
edges={elements.edges}
|
||||
onNodesChange={(e) => onNodesChange(e)}
|
||||
onEdgesChange={(e) => onEdgesChange(e)}
|
||||
fitView
|
||||
nodesDraggable
|
||||
nodesConnectable={false}
|
||||
nodeTypes={NODE_TYPES}
|
||||
proOptions={{
|
||||
// https://reactflow.dev/docs/guides/remove-attribution/
|
||||
hideAttribution: true,
|
||||
}}
|
||||
>
|
||||
<Panel position="top-left">
|
||||
<EpochCell view={EpochCellView.Graph} epoch={subs.epoch}></EpochCell>
|
||||
</Panel>
|
||||
<Panel position="top-right">
|
||||
<LayoutPanel
|
||||
layoutConfig={layoutConfig}
|
||||
setLayoutConfig={setLayoutConfig}
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<Background variant={BackgroundVariant.Dots} />
|
||||
</ReactFlow>
|
||||
);
|
||||
}
|
||||
|
||||
interface LayoutPanelProps {
|
||||
layoutConfig: LayoutConfiguration;
|
||||
setLayoutConfig: React.Dispatch<React.SetStateAction<LayoutConfiguration>>;
|
||||
}
|
||||
|
||||
function LayoutPanel({
|
||||
layoutConfig,
|
||||
setLayoutConfig,
|
||||
}: LayoutPanelProps): JSX.Element {
|
||||
const commonStyle = "rounded cursor-pointer text-2xl select-none";
|
||||
|
||||
return (
|
||||
<>
|
||||
{LAYOUT_CONFIGURATIONS.map((config, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className={clsx(
|
||||
commonStyle,
|
||||
i !== 0 ? "ml-2" : "",
|
||||
config !== layoutConfig ? "opacity-50" : ""
|
||||
)}
|
||||
onClick={() => {
|
||||
setLayoutConfig(config);
|
||||
}}
|
||||
>
|
||||
{config.emoji}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VariablesGraph(props: VariablesGraphProps) {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<Graph {...props} />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
68
crates/compiler/checkmate/www/src/components/Ui.tsx
Normal file
68
crates/compiler/checkmate/www/src/components/Ui.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import React, { useState } from "react";
|
||||
import { AllEvents, Variable } from "../schema";
|
||||
import { Engine } from "../engine/engine";
|
||||
import EventList from "./EventList";
|
||||
import VariablesGraph from "./Graph/VariablesGraph";
|
||||
import { TypedEmitter } from "tiny-typed-emitter";
|
||||
import { KeydownHandler, ToggleVariableHandler } from "./Events";
|
||||
|
||||
interface UiProps {
|
||||
events: AllEvents;
|
||||
}
|
||||
|
||||
interface MessageEvents {
|
||||
toggleVariable: ToggleVariableHandler;
|
||||
keydown: KeydownHandler;
|
||||
}
|
||||
|
||||
export default function Ui({ events }: UiProps): JSX.Element {
|
||||
const engine = new Engine(events);
|
||||
|
||||
const ee = new TypedEmitter<MessageEvents>();
|
||||
const toggleVariableHandlers: ToggleVariableHandler[] = [];
|
||||
const keydownHandlers: KeydownHandler[] = [];
|
||||
ee.on("toggleVariable", (variable: Variable) => {
|
||||
toggleVariableHandlers.forEach((handler) => handler(variable));
|
||||
});
|
||||
ee.on("keydown", async (key: string) => {
|
||||
await Promise.all(keydownHandlers.map((handler) => handler(key)));
|
||||
});
|
||||
|
||||
engine.stepTo(engine.lastEventIndex());
|
||||
const subs = engine.subsSnapshot();
|
||||
|
||||
// _setEpoch to be used in the future!
|
||||
const [epoch, _setEpoch] = useState(subs.epoch);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col md:flex-row gap-0 w-full h-full"
|
||||
onKeyDown={(e) => {
|
||||
ee.emit("keydown", e.key);
|
||||
}}
|
||||
>
|
||||
<div className="font-mono mt-2 text-lg md:flex-1 overflow-scroll">
|
||||
<EventList
|
||||
engine={engine}
|
||||
root
|
||||
events={events}
|
||||
toggleVariableVis={(variable: Variable) =>
|
||||
ee.emit("toggleVariable", variable)
|
||||
}
|
||||
currentEpoch={epoch}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-h-[50%] h-full">
|
||||
<VariablesGraph
|
||||
subs={subs}
|
||||
onVariable={(handler) => {
|
||||
toggleVariableHandlers.push(handler);
|
||||
}}
|
||||
onKeydown={(handler) => {
|
||||
keydownHandlers.push(handler);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
157
crates/compiler/checkmate/www/src/engine/engine.tsx
Normal file
157
crates/compiler/checkmate/www/src/engine/engine.tsx
Normal file
|
@ -0,0 +1,157 @@
|
|||
import { Event, Variable } from "../schema";
|
||||
import { assertExhaustive } from "../utils/exhaustive";
|
||||
import {
|
||||
ChangeEvent,
|
||||
makeDeleteVariable,
|
||||
makeRevertVariable,
|
||||
RollbackChange,
|
||||
Subs,
|
||||
SubsSnapshot,
|
||||
} from "./subs";
|
||||
|
||||
export type EventEpoch = number & { __eventIndex: never };
|
||||
|
||||
function* flattenEvents(events: Event[]): Generator<Event> {
|
||||
for (const event of events) {
|
||||
yield event;
|
||||
switch (event.type) {
|
||||
case "Unification": {
|
||||
yield* flattenEvents(event.subevents);
|
||||
break;
|
||||
}
|
||||
case "VariableUnified":
|
||||
case "VariableSetDescriptor":
|
||||
break;
|
||||
default:
|
||||
assertExhaustive(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getFlatEvents(events: Event[]): {
|
||||
flatEvents: Event[];
|
||||
map: Map<Event, EventEpoch>;
|
||||
} {
|
||||
const map = new Map<Event, EventEpoch>();
|
||||
const flatEvents = Array.from(flattenEvents(events));
|
||||
let i = 0;
|
||||
for (const event of flatEvents) {
|
||||
map.set(event, i as EventEpoch);
|
||||
i++;
|
||||
}
|
||||
return { flatEvents, map };
|
||||
}
|
||||
|
||||
export class Engine {
|
||||
#eventIndexMap: Map<Event, EventEpoch>;
|
||||
#events: Event[];
|
||||
#subs: Subs = Subs.new();
|
||||
#reverseEvents: Map<EventEpoch, RollbackChange> = new Map();
|
||||
|
||||
#nextIndexForward: EventEpoch = 0 as EventEpoch;
|
||||
|
||||
constructor(events: Event[]) {
|
||||
const { flatEvents, map } = getFlatEvents(events);
|
||||
this.#eventIndexMap = map;
|
||||
this.#events = flatEvents;
|
||||
}
|
||||
|
||||
getEventIndex(event: Event): EventEpoch {
|
||||
const index = this.#eventIndexMap.get(event);
|
||||
if (index === undefined) {
|
||||
throw new Error("Event not found");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
get step(): EventEpoch {
|
||||
return this.#nextIndexForward;
|
||||
}
|
||||
|
||||
stepTo(eventIndex: EventEpoch): void {
|
||||
while (this.#nextIndexForward <= eventIndex) {
|
||||
this.stepForward(this.#nextIndexForward);
|
||||
++this.#nextIndexForward;
|
||||
}
|
||||
while (this.#nextIndexForward > eventIndex + 1) {
|
||||
--this.#nextIndexForward;
|
||||
this.stepBackward(this.#nextIndexForward);
|
||||
}
|
||||
|
||||
if (this.#nextIndexForward !== eventIndex + 1) {
|
||||
throw new Error("Invalid event index");
|
||||
}
|
||||
}
|
||||
|
||||
get subs(): Readonly<Subs> {
|
||||
return this.#subs;
|
||||
}
|
||||
|
||||
subsSnapshot(): SubsSnapshot {
|
||||
return this.#subs.snapshot({
|
||||
epoch: (this.#nextIndexForward - 1) as EventEpoch,
|
||||
});
|
||||
}
|
||||
|
||||
lastEventIndex(): EventEpoch {
|
||||
return (this.#events.length - 1) as EventEpoch;
|
||||
}
|
||||
|
||||
private stepForward(eventIndex: EventEpoch): void {
|
||||
const event = this.#events[eventIndex];
|
||||
if (!isApplicable(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#reverseEvents.has(eventIndex)) {
|
||||
const variable = applicableVariable(event);
|
||||
const current = this.#subs.get(variable);
|
||||
let revert: RollbackChange;
|
||||
if (!current) {
|
||||
revert = makeDeleteVariable({ variable });
|
||||
} else {
|
||||
revert = makeRevertVariable({ variable, to: current });
|
||||
}
|
||||
this.#reverseEvents.set(eventIndex, revert);
|
||||
}
|
||||
|
||||
this.#subs.apply(event);
|
||||
}
|
||||
|
||||
private stepBackward(eventIndex: EventEpoch): void {
|
||||
const event = this.#events[eventIndex];
|
||||
if (!isApplicable(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const revert = this.#reverseEvents.get(eventIndex);
|
||||
if (!revert) {
|
||||
throw new Error("No revert found");
|
||||
}
|
||||
|
||||
this.#subs.apply(revert);
|
||||
}
|
||||
}
|
||||
|
||||
function isApplicable(event: Event): event is ChangeEvent {
|
||||
switch (event.type) {
|
||||
case "VariableUnified":
|
||||
case "VariableSetDescriptor":
|
||||
return true;
|
||||
case "Unification":
|
||||
return false;
|
||||
default:
|
||||
assertExhaustive(event);
|
||||
}
|
||||
}
|
||||
|
||||
function applicableVariable(event: ChangeEvent): Variable {
|
||||
switch (event.type) {
|
||||
case "VariableUnified":
|
||||
return event.from;
|
||||
case "VariableSetDescriptor":
|
||||
return event.variable;
|
||||
default:
|
||||
assertExhaustive(event);
|
||||
}
|
||||
}
|
19
crates/compiler/checkmate/www/src/engine/event_util.tsx
Normal file
19
crates/compiler/checkmate/www/src/engine/event_util.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Event } from "../schema";
|
||||
|
||||
export function lastSubEvent(event: Event): Event {
|
||||
switch (event.type) {
|
||||
case "Unification": {
|
||||
const subevents = event.subevents;
|
||||
if (subevents.length === 0) {
|
||||
return event;
|
||||
}
|
||||
return lastSubEvent(event.subevents[event.subevents.length - 1]);
|
||||
}
|
||||
case "VariableUnified": {
|
||||
return event;
|
||||
}
|
||||
case "VariableSetDescriptor": {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
179
crates/compiler/checkmate/www/src/engine/subs.tsx
Normal file
179
crates/compiler/checkmate/www/src/engine/subs.tsx
Normal file
|
@ -0,0 +1,179 @@
|
|||
import { Content, Rank, Variable, Event } from "../schema";
|
||||
import { assertExhaustive } from "../utils/exhaustive";
|
||||
import { Refine } from "../utils/refine";
|
||||
import { EventEpoch } from "./engine";
|
||||
|
||||
export type TypeLink = {
|
||||
type: "link";
|
||||
to: Variable;
|
||||
};
|
||||
|
||||
function link({ to }: Omit<TypeLink, "type">): TypeLink {
|
||||
return { type: "link", to };
|
||||
}
|
||||
|
||||
export type TypeDescriptor = {
|
||||
type: "descriptor";
|
||||
rank: Rank;
|
||||
content: Content;
|
||||
};
|
||||
|
||||
function descriptor({
|
||||
rank,
|
||||
content,
|
||||
}: Omit<TypeDescriptor, "type">): TypeDescriptor {
|
||||
return { type: "descriptor", rank, content };
|
||||
}
|
||||
|
||||
export type VarType = TypeLink | TypeDescriptor;
|
||||
|
||||
export type RevertVariableChange = {
|
||||
type: "revertTo";
|
||||
variable: Variable;
|
||||
to: VarType;
|
||||
};
|
||||
|
||||
export type DeleteVariableChange = {
|
||||
type: "delete";
|
||||
variable: Variable;
|
||||
};
|
||||
|
||||
export type RollbackChange = RevertVariableChange | DeleteVariableChange;
|
||||
|
||||
export function makeRevertVariable({
|
||||
variable,
|
||||
to,
|
||||
}: Omit<RevertVariableChange, "type">): RevertVariableChange {
|
||||
return { type: "revertTo", variable, to: { ...to } };
|
||||
}
|
||||
|
||||
export function makeDeleteVariable({
|
||||
variable,
|
||||
}: Omit<DeleteVariableChange, "type">): DeleteVariableChange {
|
||||
return { type: "delete", variable };
|
||||
}
|
||||
|
||||
export type ChangeEvent =
|
||||
| Refine<Event, "VariableUnified">
|
||||
| Refine<Event, "VariableSetDescriptor">;
|
||||
|
||||
export type Change = ChangeEvent | RollbackChange;
|
||||
|
||||
export class Subs implements QuerySubs {
|
||||
#map: Map<Variable, VarType>;
|
||||
|
||||
private constructor(map: Map<Variable, VarType>) {
|
||||
this.#map = map;
|
||||
}
|
||||
|
||||
static new(): Subs {
|
||||
return new Subs(new Map());
|
||||
}
|
||||
|
||||
get(variable: Variable): VarType | undefined {
|
||||
return this.#map.get(variable);
|
||||
}
|
||||
|
||||
get_root(variable: Variable): TypeDescriptor | undefined {
|
||||
const type = this.get(variable);
|
||||
if (type === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
switch (type.type) {
|
||||
case "descriptor":
|
||||
return type;
|
||||
case "link":
|
||||
return this.get_root(type.to);
|
||||
default:
|
||||
assertExhaustive(type);
|
||||
}
|
||||
}
|
||||
|
||||
get_root_key(variable: Variable): Variable {
|
||||
const type = this.get(variable);
|
||||
if (type === undefined) {
|
||||
return variable;
|
||||
}
|
||||
switch (type.type) {
|
||||
case "descriptor":
|
||||
return variable;
|
||||
case "link":
|
||||
return this.get_root_key(type.to);
|
||||
default:
|
||||
assertExhaustive(type);
|
||||
}
|
||||
}
|
||||
|
||||
snapshot({ epoch }: { epoch: EventEpoch }): SubsSnapshot {
|
||||
const snapshotMap = new Map<Variable, VarType>();
|
||||
for (const [key, value] of this.#map) {
|
||||
snapshotMap.set(key, { ...value });
|
||||
}
|
||||
const snapshot = new Subs(snapshotMap);
|
||||
return {
|
||||
epoch,
|
||||
get(variable: Variable): VarType | undefined {
|
||||
return snapshot.get(variable);
|
||||
},
|
||||
get_root(variable: Variable): TypeDescriptor | undefined {
|
||||
return snapshot.get_root(variable);
|
||||
},
|
||||
get_root_key(variable: Variable): Variable {
|
||||
return snapshot.get_root_key(variable);
|
||||
},
|
||||
__snapshot__: SnapshotSymbol,
|
||||
};
|
||||
}
|
||||
|
||||
apply(change: Change): void {
|
||||
switch (change.type) {
|
||||
case "VariableUnified": {
|
||||
const { from, to } = change;
|
||||
if (from !== to) {
|
||||
this.#map.set(from, link({ to }));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "VariableSetDescriptor": {
|
||||
const { variable, rank, content } = change;
|
||||
const existing = this.get_root(variable);
|
||||
if (existing !== undefined) {
|
||||
const nu = descriptor({ ...existing });
|
||||
if (rank) nu.rank = rank;
|
||||
if (content) nu.content = content;
|
||||
this.#map.set(variable, nu);
|
||||
} else {
|
||||
if (typeof rank !== "number") throw new Error("rank is required");
|
||||
if (!content) throw new Error("content is required");
|
||||
this.#map.set(variable, descriptor({ rank, content }));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "revertTo": {
|
||||
const { variable, to } = change;
|
||||
this.#map.set(variable, { ...to });
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
const { variable } = change;
|
||||
this.#map.delete(variable);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertExhaustive(change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SnapshotSymbol = Symbol("Snapshot");
|
||||
|
||||
export interface QuerySubs {
|
||||
get(variable: Variable): VarType | undefined;
|
||||
get_root(variable: Variable): TypeDescriptor | undefined;
|
||||
get_root_key(variable: Variable): Variable;
|
||||
}
|
||||
|
||||
export interface SubsSnapshot extends QuerySubs {
|
||||
readonly epoch: EventEpoch;
|
||||
__snapshot__: typeof SnapshotSymbol;
|
||||
}
|
3
crates/compiler/checkmate/www/src/index.css
Normal file
3
crates/compiler/checkmate/www/src/index.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
13
crates/compiler/checkmate/www/src/index.tsx
Normal file
13
crates/compiler/checkmate/www/src/index.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
243
crates/compiler/checkmate/www/src/schema.d.ts
vendored
Normal file
243
crates/compiler/checkmate/www/src/schema.d.ts
vendored
Normal file
|
@ -0,0 +1,243 @@
|
|||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by json-schema-to-typescript.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
||||
* and run json-schema-to-typescript to regenerate this file.
|
||||
*/
|
||||
|
||||
export type Event =
|
||||
| {
|
||||
left: Variable;
|
||||
mode: UnificationMode;
|
||||
right: Variable;
|
||||
subevents: Event[];
|
||||
success?: boolean | null;
|
||||
type: "Unification";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
from: Variable;
|
||||
to: Variable;
|
||||
type: "VariableUnified";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
content?: Content | null;
|
||||
rank?: Rank | null;
|
||||
type: "VariableSetDescriptor";
|
||||
variable: Variable;
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type Variable = number;
|
||||
export type UnificationMode =
|
||||
| {
|
||||
type: "Eq";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "Present";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "LambdaSetSpecialization";
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type Content =
|
||||
| {
|
||||
name?: string | null;
|
||||
type: "Flex";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
name: string;
|
||||
type: "Rigid";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
abilities: Symbol[];
|
||||
name?: string | null;
|
||||
type: "FlexAble";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
abilities: Symbol[];
|
||||
name: string;
|
||||
type: "RigidAble";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
name?: string | null;
|
||||
structure: Variable;
|
||||
type: "Recursive";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
ambient_function: Variable;
|
||||
recursion_var?: Variable | null;
|
||||
solved: ClosureType[];
|
||||
type: "LambdaSet";
|
||||
unspecialized: UnspecializedClosureType[];
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "ErasedLambda";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
kind: AliasKind;
|
||||
name: Symbol;
|
||||
real_variable: Variable;
|
||||
type: "Alias";
|
||||
variables: AliasTypeVariables;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
symbol: Symbol;
|
||||
type: "Apply";
|
||||
variables: Variable[];
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
arguments: Variable[];
|
||||
lambda_type: Variable;
|
||||
ret: Variable;
|
||||
type: "Function";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
extension: Variable;
|
||||
fields: {
|
||||
[k: string]: RecordField;
|
||||
};
|
||||
type: "Record";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
elements: {
|
||||
[k: string]: Variable;
|
||||
};
|
||||
extension: Variable;
|
||||
type: "Tuple";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
extension: TagUnionExtension;
|
||||
tags: {
|
||||
[k: string]: Variable[];
|
||||
};
|
||||
type: "TagUnion";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
extension: TagUnionExtension;
|
||||
functions: Symbol[];
|
||||
tags: string[];
|
||||
type: "FunctionOrTagUnion";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
extension: TagUnionExtension;
|
||||
recursion_var: Variable;
|
||||
tags: {
|
||||
[k: string]: Variable[];
|
||||
};
|
||||
type: "RecursiveTagUnion";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "EmptyRecord";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "EmptyTuple";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "EmptyTagUnion";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
range: NumericRange;
|
||||
type: "RangedNumber";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "Error";
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type Symbol = string;
|
||||
export type AliasKind =
|
||||
| {
|
||||
type: "Structural";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "Opaque";
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type RecordFieldKind =
|
||||
| {
|
||||
type: "Demanded";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
rigid: boolean;
|
||||
type: "Required";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
rigid: boolean;
|
||||
type: "Optional";
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type TagUnionExtension =
|
||||
| {
|
||||
type: "Openness";
|
||||
variable: Variable;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "Any";
|
||||
variable: Variable;
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type NumericRangeKind =
|
||||
| {
|
||||
type: "Int";
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| {
|
||||
type: "AnyNum";
|
||||
[k: string]: unknown;
|
||||
};
|
||||
export type Rank = number;
|
||||
export type AllEvents = Event[];
|
||||
|
||||
export interface ClosureType {
|
||||
environment: Variable[];
|
||||
function: Symbol;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface UnspecializedClosureType {
|
||||
ability_member: Symbol;
|
||||
lambda_set_region: number;
|
||||
specialization: Variable;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface AliasTypeVariables {
|
||||
infer_ext_in_output_position_variables: Variable[];
|
||||
lambda_set_variables: Variable[];
|
||||
type_variables: Variable[];
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface RecordField {
|
||||
field_type: Variable;
|
||||
kind: RecordFieldKind;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface NumericRange {
|
||||
kind: NumericRangeKind;
|
||||
min_width: number;
|
||||
signed: boolean;
|
||||
[k: string]: unknown;
|
||||
}
|
3
crates/compiler/checkmate/www/src/utils/exhaustive.ts
Normal file
3
crates/compiler/checkmate/www/src/utils/exhaustive.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function assertExhaustive(_: never): never {
|
||||
throw new Error("Exhaustive switch");
|
||||
}
|
3
crates/compiler/checkmate/www/src/utils/refine.ts
Normal file
3
crates/compiler/checkmate/www/src/utils/refine.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export type Refine<T extends { type: string }, Type extends string> = T & {
|
||||
type: Type;
|
||||
};
|
17
crates/compiler/checkmate/www/tailwind.config.js
Normal file
17
crates/compiler/checkmate/www/tailwind.config.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'roc-purple': '#7c38f5',
|
||||
'roc-purple-bg': '#ece2fd',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
20
crates/compiler/checkmate/www/tsconfig.json
Normal file
20
crates/compiler/checkmate/www/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
13
crates/compiler/checkmate_schema/Cargo.toml
Normal file
13
crates/compiler/checkmate_schema/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "roc_checkmate_schema"
|
||||
description = "Schema for checkmate."
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
schemars.workspace = true
|
225
crates/compiler/checkmate_schema/src/lib.rs
Normal file
225
crates/compiler/checkmate_schema/src/lib.rs
Normal file
|
@ -0,0 +1,225 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use schemars::{schema::RootSchema, schema_for, JsonSchema};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Constraint {}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug, PartialEq)]
|
||||
pub struct Variable(pub u32);
|
||||
|
||||
macro_rules! impl_content {
|
||||
($($name:ident { $($arg:ident: $ty:ty,)* },)*) => {
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Content {
|
||||
$(
|
||||
$name {
|
||||
$($arg: $ty),*
|
||||
},
|
||||
)*
|
||||
}
|
||||
|
||||
impl Content {
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
pub fn $name($($arg: $ty),*) -> Self {
|
||||
Self::$name { $($arg),* }
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_content! {
|
||||
Flex {
|
||||
name: Option<String>,
|
||||
},
|
||||
Rigid {
|
||||
name: String,
|
||||
},
|
||||
FlexAble {
|
||||
name: Option<String>,
|
||||
abilities: Vec<Symbol>,
|
||||
},
|
||||
RigidAble {
|
||||
name: String,
|
||||
abilities: Vec<Symbol>,
|
||||
},
|
||||
Recursive {
|
||||
name: Option<String>,
|
||||
structure: Variable,
|
||||
},
|
||||
LambdaSet {
|
||||
solved: Vec<ClosureType>,
|
||||
unspecialized: Vec<UnspecializedClosureType>,
|
||||
recursion_var: Option<Variable>,
|
||||
ambient_function: Variable,
|
||||
},
|
||||
ErasedLambda {},
|
||||
Alias {
|
||||
name: Symbol,
|
||||
variables: AliasTypeVariables,
|
||||
real_variable: Variable,
|
||||
kind: AliasKind,
|
||||
},
|
||||
Apply {
|
||||
symbol: Symbol,
|
||||
variables: Vec<Variable>,
|
||||
},
|
||||
Function {
|
||||
arguments: Vec<Variable>,
|
||||
lambda_type: Variable,
|
||||
ret: Variable,
|
||||
},
|
||||
Record {
|
||||
fields: HashMap<String, RecordField>,
|
||||
extension: Variable,
|
||||
},
|
||||
Tuple {
|
||||
elements: HashMap<u32, Variable>,
|
||||
extension: Variable,
|
||||
},
|
||||
TagUnion {
|
||||
tags: HashMap<String, Vec<Variable>>,
|
||||
extension: TagUnionExtension,
|
||||
},
|
||||
FunctionOrTagUnion {
|
||||
functions: Vec<Symbol>,
|
||||
tags: Vec<String>,
|
||||
extension: TagUnionExtension,
|
||||
},
|
||||
RecursiveTagUnion {
|
||||
recursion_var: Variable,
|
||||
tags: HashMap<String, Vec<Variable>>,
|
||||
extension: TagUnionExtension,
|
||||
},
|
||||
EmptyRecord {},
|
||||
EmptyTuple {},
|
||||
EmptyTagUnion {},
|
||||
RangedNumber {
|
||||
range: NumericRange,
|
||||
},
|
||||
Error {},
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct ClosureType {
|
||||
pub function: Symbol,
|
||||
pub environment: Vec<Variable>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct UnspecializedClosureType {
|
||||
pub specialization: Variable,
|
||||
pub ability_member: Symbol,
|
||||
pub lambda_set_region: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum AliasKind {
|
||||
Structural,
|
||||
Opaque,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct AliasTypeVariables {
|
||||
pub type_variables: Vec<Variable>,
|
||||
pub lambda_set_variables: Vec<Variable>,
|
||||
pub infer_ext_in_output_position_variables: Vec<Variable>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct RecordField {
|
||||
pub kind: RecordFieldKind,
|
||||
pub field_type: Variable,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum RecordFieldKind {
|
||||
Demanded,
|
||||
Required { rigid: bool },
|
||||
Optional { rigid: bool },
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type", content = "variable")]
|
||||
pub enum TagUnionExtension {
|
||||
Openness(Variable),
|
||||
Any(Variable),
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct NumericRange {
|
||||
pub kind: NumericRangeKind,
|
||||
pub signed: bool,
|
||||
pub min_width: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum NumericRangeKind {
|
||||
Int,
|
||||
AnyNum,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct Rank(pub u32);
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct Descriptor {
|
||||
pub content: Content,
|
||||
pub rank: Rank,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct Symbol(
|
||||
// TODO: should this be module ID + symbol?
|
||||
pub String,
|
||||
);
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum UnificationMode {
|
||||
Eq,
|
||||
Present,
|
||||
LambdaSetSpecialization,
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Event {
|
||||
Unification {
|
||||
left: Variable,
|
||||
right: Variable,
|
||||
mode: UnificationMode,
|
||||
success: Option<bool>,
|
||||
subevents: Vec<Event>,
|
||||
},
|
||||
VariableUnified {
|
||||
from: Variable,
|
||||
to: Variable,
|
||||
},
|
||||
VariableSetDescriptor {
|
||||
variable: Variable,
|
||||
rank: Option<Rank>,
|
||||
content: Option<Content>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, JsonSchema, Debug)]
|
||||
pub struct AllEvents(pub Vec<Event>);
|
||||
|
||||
impl AllEvents {
|
||||
pub fn schema() -> RootSchema {
|
||||
schema_for!(AllEvents)
|
||||
}
|
||||
|
||||
pub fn write(&self, writer: impl std::io::Write) -> Result<(), serde_json::Error> {
|
||||
serde_json::to_writer(writer, self)
|
||||
}
|
||||
}
|
|
@ -2472,6 +2472,26 @@ fn constrain_empty_record(
|
|||
constraints.equal_types(record_type_index, expected, Category::Record, region)
|
||||
}
|
||||
|
||||
fn add_host_annotation(
|
||||
types: &mut Types,
|
||||
constraints: &mut Constraints,
|
||||
host_exposed_annotation: Option<&(Variable, roc_can::def::Annotation)>,
|
||||
constraint: Constraint,
|
||||
) -> Constraint {
|
||||
if let Some((var, ann)) = host_exposed_annotation {
|
||||
let host_annotation = {
|
||||
let type_index = types.from_old_type(&ann.signature);
|
||||
constraints.push_type(types, type_index)
|
||||
};
|
||||
|
||||
let store_constr = constraints.store(host_annotation, *var, file!(), line!());
|
||||
|
||||
constraints.and_constraint([store_constr, constraint])
|
||||
} else {
|
||||
constraint
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrain top-level module declarations
|
||||
#[inline(always)]
|
||||
pub fn constrain_decls(
|
||||
|
@ -2498,6 +2518,7 @@ pub fn constrain_decls(
|
|||
|
||||
use roc_can::expr::DeclarationTag::*;
|
||||
let tag = declarations.declarations[index];
|
||||
|
||||
match tag {
|
||||
Value => {
|
||||
constraint = constrain_value_def(
|
||||
|
@ -2508,6 +2529,77 @@ pub fn constrain_decls(
|
|||
index,
|
||||
constraint,
|
||||
);
|
||||
|
||||
constraint = add_host_annotation(
|
||||
types,
|
||||
constraints,
|
||||
declarations.host_exposed_annotations.get(&index),
|
||||
constraint,
|
||||
);
|
||||
}
|
||||
Function(function_def_index) => {
|
||||
constraint = constrain_function_def(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index,
|
||||
function_def_index,
|
||||
constraint,
|
||||
);
|
||||
|
||||
constraint = add_host_annotation(
|
||||
types,
|
||||
constraints,
|
||||
declarations.host_exposed_annotations.get(&index),
|
||||
constraint,
|
||||
);
|
||||
}
|
||||
Recursive(_) | TailRecursive(_) => {
|
||||
constraint = add_host_annotation(
|
||||
types,
|
||||
constraints,
|
||||
declarations.host_exposed_annotations.get(&index),
|
||||
constraint,
|
||||
);
|
||||
|
||||
// for the type it does not matter that a recursive call is a tail call
|
||||
constraint = constrain_recursive_declarations(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index..index + 1,
|
||||
constraint,
|
||||
IllegalCycleMark::empty(),
|
||||
);
|
||||
}
|
||||
Destructure(destructure_def_index) => {
|
||||
constraint = constrain_destructure_def(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index,
|
||||
destructure_def_index,
|
||||
constraint,
|
||||
);
|
||||
}
|
||||
MutualRecursion { length, cycle_mark } => {
|
||||
// the next `length` defs belong to this group
|
||||
let length = length as usize;
|
||||
|
||||
constraint = constrain_recursive_declarations(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index + 1..index + 1 + length,
|
||||
constraint,
|
||||
cycle_mark,
|
||||
);
|
||||
|
||||
index += length;
|
||||
}
|
||||
Expectation => {
|
||||
let loc_expr = &declarations.expressions[index];
|
||||
|
@ -2565,56 +2657,6 @@ pub fn constrain_decls(
|
|||
Generalizable(false),
|
||||
)
|
||||
}
|
||||
Function(function_def_index) => {
|
||||
constraint = constrain_function_def(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index,
|
||||
function_def_index,
|
||||
constraint,
|
||||
);
|
||||
}
|
||||
Recursive(_) | TailRecursive(_) => {
|
||||
// for the type it does not matter that a recursive call is a tail call
|
||||
constraint = constrain_recursive_declarations(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index..index + 1,
|
||||
constraint,
|
||||
IllegalCycleMark::empty(),
|
||||
);
|
||||
}
|
||||
Destructure(destructure_def_index) => {
|
||||
constraint = constrain_destructure_def(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index,
|
||||
destructure_def_index,
|
||||
constraint,
|
||||
);
|
||||
}
|
||||
MutualRecursion { length, cycle_mark } => {
|
||||
// the next `length` defs belong to this group
|
||||
let length = length as usize;
|
||||
|
||||
constraint = constrain_recursive_declarations(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
declarations,
|
||||
index + 1..index + 1 + length,
|
||||
constraint,
|
||||
cycle_mark,
|
||||
);
|
||||
|
||||
index += length;
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
|
|
|
@ -9,11 +9,13 @@ version.workspace = true
|
|||
|
||||
[dependencies]
|
||||
roc_can = { path = "../can" }
|
||||
roc_checkmate = { path = "../checkmate" }
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_derive_key = { path = "../derive_key" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_solve_schema = { path = "../solve_schema" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_unify = { path = "../unify" }
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use roc_can::{abilities::SpecializationLambdaSets, module::ExposedByModule};
|
||||
use roc_checkmate::with_checkmate;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{IdentIds, Symbol};
|
||||
use roc_solve_schema::UnificationMode;
|
||||
use roc_types::{
|
||||
subs::{instantiate_rigids, Subs, Variable},
|
||||
types::Polarity,
|
||||
|
@ -71,13 +73,20 @@ impl Env<'_> {
|
|||
}
|
||||
|
||||
pub fn unify(&mut self, left: Variable, right: Variable) {
|
||||
use roc_unify::unify::{unify, Env, Mode, Unified};
|
||||
use roc_unify::{
|
||||
unify::{unify, Unified},
|
||||
Env,
|
||||
};
|
||||
|
||||
let unified = unify(
|
||||
&mut Env::new(self.subs),
|
||||
// TODO(checkmate): pass checkmate through
|
||||
&mut with_checkmate!({
|
||||
on => Env::new(self.subs, None),
|
||||
off => Env::new(self.subs),
|
||||
}),
|
||||
left,
|
||||
right,
|
||||
Mode::EQ,
|
||||
UnificationMode::EQ,
|
||||
Polarity::OF_PATTERN,
|
||||
);
|
||||
|
||||
|
@ -103,15 +112,22 @@ impl Env<'_> {
|
|||
specialization_type: Variable,
|
||||
ability_member: Symbol,
|
||||
) -> SpecializationLambdaSets {
|
||||
use roc_unify::unify::{unify_introduced_ability_specialization, Env, Mode, Unified};
|
||||
use roc_unify::{
|
||||
unify::{unify_introduced_ability_specialization, Unified},
|
||||
Env,
|
||||
};
|
||||
|
||||
let member_signature = self.import_builtin_symbol_var(ability_member);
|
||||
|
||||
let unified = unify_introduced_ability_specialization(
|
||||
&mut Env::new(self.subs),
|
||||
// TODO(checkmate): pass checkmate through
|
||||
&mut with_checkmate!({
|
||||
on => Env::new(self.subs, None),
|
||||
off => Env::new(self.subs),
|
||||
}),
|
||||
member_signature,
|
||||
specialization_type,
|
||||
Mode::EQ,
|
||||
UnificationMode::EQ,
|
||||
);
|
||||
|
||||
match unified {
|
||||
|
|
|
@ -103,7 +103,7 @@ impl FlatDecodable {
|
|||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ impl FlatEncodable {
|
|||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ impl FlatHash {
|
|||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||
Content::LambdaSet(_) => Err(Underivable),
|
||||
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix {
|
|||
.collect();
|
||||
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, n - 1);
|
||||
|
||||
let last: _ = alt_list
|
||||
let last = alt_list
|
||||
.iter()
|
||||
.filter_map(|r| is_missing(alts.clone(), &ctors, r));
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
use bumpalo::collections::{CollectIn, Vec};
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp};
|
||||
use roc_mono::ir::{
|
||||
|
@ -3007,11 +3007,17 @@ impl<
|
|||
ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg);
|
||||
}
|
||||
|
||||
fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) {
|
||||
fn build_alloca(&mut self, sym: Symbol, value: Option<Symbol>, element_layout: InLayout<'a>) {
|
||||
// 1. acquire some stack space
|
||||
let element_width = self.interner().stack_size(element_layout);
|
||||
let allocation = self.debug_symbol("stack_allocation");
|
||||
let ptr = self.debug_symbol("ptr");
|
||||
|
||||
if element_width == 0 {
|
||||
self.storage_manager.claim_pointer_stack_area(sym);
|
||||
return;
|
||||
}
|
||||
|
||||
let base_offset = self
|
||||
.storage_manager
|
||||
.claim_stack_area(&allocation, element_width);
|
||||
|
@ -3021,7 +3027,13 @@ impl<
|
|||
ASM::mov_reg64_reg64(&mut self.buf, ptr_reg, CC::BASE_PTR_REG);
|
||||
ASM::add_reg64_reg64_imm32(&mut self.buf, ptr_reg, ptr_reg, base_offset);
|
||||
|
||||
self.build_ptr_store(sym, ptr, value, element_layout);
|
||||
if let Some(value) = value {
|
||||
self.build_ptr_store(sym, ptr, value, element_layout);
|
||||
} else {
|
||||
// this is now a pointer to uninitialized memory!
|
||||
let r = self.storage_manager.claim_general_reg(&mut self.buf, &sym);
|
||||
ASM::mov_reg64_reg64(&mut self.buf, r, ptr_reg);
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_box(
|
||||
|
@ -3630,7 +3642,8 @@ impl<
|
|||
}
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive(_))
|
||||
| LayoutRepr::Builtin(_)
|
||||
| LayoutRepr::Struct(_) => {
|
||||
| LayoutRepr::Struct(_)
|
||||
| LayoutRepr::Erased(_) => {
|
||||
internal_error!("All primitive values should fit in a single register");
|
||||
}
|
||||
}
|
||||
|
@ -4306,6 +4319,8 @@ impl<
|
|||
dst,
|
||||
);
|
||||
}
|
||||
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4527,5 +4542,6 @@ macro_rules! pointer_layouts {
|
|||
| UnionLayout::NullableWrapped { .. }
|
||||
| UnionLayout::NullableUnwrapped { .. },
|
||||
)
|
||||
| LayoutRepr::FunctionPointer(_)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
use bumpalo::collections::Vec;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::{
|
||||
ir::{JoinPointId, Param},
|
||||
|
@ -830,6 +830,7 @@ impl<
|
|||
|
||||
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
||||
}
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
pointer_layouts!() => {
|
||||
// like a 64-bit integer
|
||||
debug_assert_eq!(to_offset % 8, 0);
|
||||
|
@ -1168,12 +1169,7 @@ impl<
|
|||
) {
|
||||
let mut param_storage = bumpalo::vec![in self.env.arena];
|
||||
param_storage.reserve(params.len());
|
||||
for Param {
|
||||
symbol,
|
||||
ownership: _,
|
||||
layout,
|
||||
} in params
|
||||
{
|
||||
for Param { symbol, layout } in params {
|
||||
// Claim a location for every join point parameter to be loaded at.
|
||||
// Put everything on the stack for simplicity.
|
||||
self.joinpoint_argument_stack_storage(layout_interner, *symbol, *layout);
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
|
|||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
@ -149,6 +149,13 @@ impl<'a> LastSeenMap<'a> {
|
|||
self.set_last_seen(*sym, stmt);
|
||||
}
|
||||
}
|
||||
Expr::ErasedMake { value, callee } => {
|
||||
value.map(|v| self.set_last_seen(v, stmt));
|
||||
self.set_last_seen(*callee, stmt);
|
||||
}
|
||||
Expr::ErasedLoad { symbol, field: _ } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::Struct(syms) => {
|
||||
for sym in *syms {
|
||||
self.set_last_seen(*sym, stmt);
|
||||
|
@ -176,8 +183,14 @@ impl<'a> LastSeenMap<'a> {
|
|||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
||||
self.set_last_seen(*symbol, stmt);
|
||||
}
|
||||
Expr::EmptyArray => {}
|
||||
Expr::Alloca { initializer, .. } => {
|
||||
if let Some(initializer) = initializer {
|
||||
self.set_last_seen(*initializer, stmt);
|
||||
}
|
||||
}
|
||||
Expr::RuntimeErrorFunction(_) => {}
|
||||
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||
Expr::EmptyArray => {}
|
||||
}
|
||||
self.scan_ast_help(following);
|
||||
}
|
||||
|
@ -265,6 +278,7 @@ impl<'a> LastSeenMap<'a> {
|
|||
|
||||
match call_type {
|
||||
CallType::ByName { .. } => {}
|
||||
CallType::ByPointer { .. } => {}
|
||||
CallType::LowLevel { .. } => {}
|
||||
CallType::HigherOrder { .. } => {}
|
||||
CallType::Foreign { .. } => {}
|
||||
|
@ -729,6 +743,10 @@ trait Backend<'a> {
|
|||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
}
|
||||
|
||||
CallType::ByPointer { .. } => {
|
||||
todo_lambda_erasure!()
|
||||
}
|
||||
|
||||
CallType::LowLevel { op: lowlevel, .. } => {
|
||||
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
||||
bumpalo::vec![in self.env().arena];
|
||||
|
@ -839,6 +857,9 @@ trait Backend<'a> {
|
|||
Expr::NullPointer => {
|
||||
self.load_literal_i64(sym, 0);
|
||||
}
|
||||
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedMake { .. } => todo_lambda_erasure!(),
|
||||
Expr::ErasedLoad { .. } => todo_lambda_erasure!(),
|
||||
Expr::Reset { symbol, .. } => {
|
||||
let layout = *self.layout_map().get(symbol).unwrap();
|
||||
|
||||
|
@ -879,6 +900,12 @@ trait Backend<'a> {
|
|||
|
||||
self.build_expr(sym, &new_expr, &Layout::BOOL)
|
||||
}
|
||||
Expr::Alloca {
|
||||
initializer,
|
||||
element_layout,
|
||||
} => {
|
||||
self.build_alloca(*sym, *initializer, *element_layout);
|
||||
}
|
||||
Expr::RuntimeErrorFunction(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -1599,10 +1626,6 @@ trait Backend<'a> {
|
|||
self.build_ptr_clear_tag_id(*sym, args[0]);
|
||||
}
|
||||
|
||||
LowLevel::Alloca => {
|
||||
self.build_alloca(*sym, args[0], arg_layouts[0]);
|
||||
}
|
||||
|
||||
LowLevel::RefCountDecRcPtr => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::UTILS_DECREF_RC_PTR.to_string(),
|
||||
|
@ -2243,7 +2266,7 @@ trait Backend<'a> {
|
|||
|
||||
fn build_ptr_clear_tag_id(&mut self, sym: Symbol, ptr: Symbol);
|
||||
|
||||
fn build_alloca(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>);
|
||||
fn build_alloca(&mut self, sym: Symbol, value: Option<Symbol>, element_layout: InLayout<'a>);
|
||||
|
||||
/// literal_map gets the map from symbol to literal and layout, used for lazy loading and literal folding.
|
||||
fn literal_map(&mut self) -> &mut MutMap<Symbol, (*const Literal<'a>, *const InLayout<'a>)>;
|
||||
|
|
|
@ -444,6 +444,7 @@ fn build_exposed_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'a>) -> P
|
|||
ret_layout: proc.ret_layout,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,6 +526,7 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'
|
|||
ret_layout: roc_mono::layout::Layout::UNIT,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,8 +119,9 @@ impl<'a> LlvmAlignment<'a> for LayoutRepr<'a> {
|
|||
.runtime_representation()
|
||||
.llvm_alignment_bytes(interner),
|
||||
Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) | Erased(_) => {
|
||||
interner.target_info().ptr_width() as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -296,7 +296,7 @@ fn build_transform_caller_help<'a, 'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
let result = crate::llvm::build::call_roc_function(
|
||||
let result = crate::llvm::build::call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::llvm::refcounting::{
|
|||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||
};
|
||||
use crate::llvm::struct_::{struct_from_fields, RocStruct};
|
||||
use crate::llvm::{erased, fn_ptr};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
|
@ -23,11 +24,11 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
|
|||
use inkwell::types::{
|
||||
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
|
||||
};
|
||||
use inkwell::values::BasicValueEnum;
|
||||
use inkwell::values::{
|
||||
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
|
||||
StructValue,
|
||||
};
|
||||
use inkwell::values::{BasicValueEnum, CallableValue};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use morphic_lib::{
|
||||
|
@ -38,6 +39,7 @@ use roc_collections::all::{MutMap, MutSet};
|
|||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet,
|
||||
|
@ -604,8 +606,7 @@ fn promote_to_main_function<'a, 'ctx>(
|
|||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
||||
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
|
@ -654,8 +655,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
|
|||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
||||
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let output_type = match roc_main_fn.get_type().get_return_type() {
|
||||
Some(return_type) => {
|
||||
|
@ -690,7 +690,7 @@ fn promote_to_wasm_test_wrapper<'a, 'ctx>(
|
|||
let entry = context.append_basic_block(c_function, "entry");
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let roc_main_fn_result = call_roc_function(
|
||||
let roc_main_fn_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_main_fn,
|
||||
|
@ -912,7 +912,6 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
|||
CallType::ByName {
|
||||
name,
|
||||
specialization_id,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
..
|
||||
} => {
|
||||
|
@ -927,17 +926,39 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
|||
let callee_var = CalleeSpecVar(&bytes);
|
||||
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||
|
||||
roc_call_with_args(
|
||||
roc_call_direct_with_args(
|
||||
env,
|
||||
layout_interner,
|
||||
arg_layouts,
|
||||
*ret_layout,
|
||||
*name,
|
||||
func_spec,
|
||||
FuncBorrowSpec::Some(func_spec),
|
||||
arg_tuples.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
|
||||
CallType::ByPointer {
|
||||
pointer,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
} => {
|
||||
let mut args: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
|
||||
for symbol in arguments.iter() {
|
||||
args.push(scope.load_symbol(symbol));
|
||||
}
|
||||
|
||||
let pointer = scope.load_symbol(pointer).into_pointer_value();
|
||||
|
||||
roc_call_erased_with_args(
|
||||
env,
|
||||
layout_interner,
|
||||
pointer,
|
||||
arg_layouts,
|
||||
*ret_layout,
|
||||
args.into_bump_slice(),
|
||||
)
|
||||
}
|
||||
|
||||
CallType::LowLevel { op, update_mode } => {
|
||||
let bytes = update_mode.to_bytes();
|
||||
let update_var = UpdateModeVar(&bytes);
|
||||
|
@ -1085,6 +1106,24 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
|||
)
|
||||
}
|
||||
|
||||
FunctionPointer { lambda_name } => {
|
||||
let alloca = fn_ptr::build(env, *lambda_name);
|
||||
alloca.into()
|
||||
}
|
||||
ErasedMake { value, callee } => {
|
||||
let value = value.map(|sym| scope.load_symbol(&sym).into_pointer_value());
|
||||
let callee = scope.load_symbol(callee).into_pointer_value();
|
||||
erased::build(env, value, callee).into()
|
||||
}
|
||||
ErasedLoad { symbol, field } => {
|
||||
let value = scope.load_symbol(symbol).into_struct_value();
|
||||
let wanted_llvm_type =
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout))
|
||||
.into_pointer_type();
|
||||
|
||||
erased::load(env, value, *field, wanted_llvm_type).into()
|
||||
}
|
||||
|
||||
Reset {
|
||||
symbol,
|
||||
update_mode,
|
||||
|
@ -1561,6 +1600,24 @@ pub(crate) fn build_exp_expr<'a, 'ctx>(
|
|||
|
||||
get_tag_id(env, layout_interner, parent, union_layout, argument).into()
|
||||
}
|
||||
|
||||
Alloca {
|
||||
initializer,
|
||||
element_layout,
|
||||
} => {
|
||||
let element_type = basic_type_from_layout(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_interner.get_repr(*element_layout),
|
||||
);
|
||||
let ptr = entry_block_alloca_zerofill(env, element_type, "stack_value");
|
||||
|
||||
if let Some(initializer) = initializer {
|
||||
env.builder.build_store(ptr, scope.load_symbol(initializer));
|
||||
}
|
||||
|
||||
ptr.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1851,7 +1908,7 @@ fn build_tag<'a, 'ctx>(
|
|||
let roc_union =
|
||||
RocUnion::untagged_from_slices(layout_interner, env.context, &[other_fields]);
|
||||
|
||||
if tag_id == *nullable_id as _ {
|
||||
if tag_id == *nullable_id as u16 {
|
||||
let output_type = roc_union.struct_type().ptr_type(AddressSpace::default());
|
||||
|
||||
return output_type.const_null().into();
|
||||
|
@ -2833,8 +2890,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
DecRef(symbol) => {
|
||||
let (value, layout) = scope.load_symbol_and_layout(symbol);
|
||||
|
||||
let lay = layout_interner.get_repr(layout);
|
||||
match lay {
|
||||
match layout_interner.runtime_representation(layout) {
|
||||
LayoutRepr::Builtin(Builtin::Str) => todo!(),
|
||||
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
||||
debug_assert!(value.is_struct_value());
|
||||
|
@ -2844,16 +2900,19 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
build_list::decref(env, value.into_struct_value(), alignment);
|
||||
}
|
||||
|
||||
_ if lay.is_refcounted() => {
|
||||
other_layout if other_layout.is_refcounted(layout_interner) => {
|
||||
if value.is_pointer_value() {
|
||||
let value_ptr = match lay {
|
||||
LayoutRepr::Union(union_layout)
|
||||
if union_layout
|
||||
.stores_tag_id_in_pointer(env.target_info) =>
|
||||
{
|
||||
tag_pointer_clear_tag_id(env, value.into_pointer_value())
|
||||
let clear_tag_id = match other_layout {
|
||||
LayoutRepr::Union(union_layout) => {
|
||||
union_layout.stores_tag_id_in_pointer(env.target_info)
|
||||
}
|
||||
_ => value.into_pointer_value(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let value_ptr = if clear_tag_id {
|
||||
tag_pointer_clear_tag_id(env, value.into_pointer_value())
|
||||
} else {
|
||||
value.into_pointer_value()
|
||||
};
|
||||
|
||||
let then_block = env.context.append_basic_block(parent, "then");
|
||||
|
@ -2906,7 +2965,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
debug_assert!(value.is_pointer_value());
|
||||
let value = value.into_pointer_value();
|
||||
|
||||
let clear_tag_id = match layout_interner.chase_recursive(layout) {
|
||||
let clear_tag_id = match layout_interner.runtime_representation(layout) {
|
||||
LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target_info),
|
||||
_ => false,
|
||||
};
|
||||
|
@ -3849,7 +3908,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
|||
builder.position_at_end(entry);
|
||||
|
||||
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
@ -3857,7 +3916,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
|||
arguments_for_call,
|
||||
)
|
||||
} else {
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
@ -3995,7 +4054,7 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>(
|
|||
|
||||
let wrapper_result = roc_call_result_layout(env.arena, return_layout);
|
||||
|
||||
let roc_value = call_roc_function(
|
||||
let roc_value = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_wrapper_function,
|
||||
|
@ -4248,7 +4307,7 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx>(
|
|||
|
||||
let arguments = Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let value = call_roc_function(
|
||||
let value = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
@ -4558,7 +4617,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
|||
{
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
let call_result = call_roc_function(
|
||||
let call_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
roc_function,
|
||||
|
@ -4826,15 +4885,19 @@ pub(crate) fn build_proc_headers<'a, 'r, 'ctx>(
|
|||
|
||||
let it = func_solutions.specs();
|
||||
let mut function_values = std::vec::Vec::with_capacity(it.size_hint().0);
|
||||
|
||||
let is_erased = proc.is_erased;
|
||||
debug_assert!(!is_erased || func_solutions.specs().count() == 1);
|
||||
|
||||
for specialization in it {
|
||||
let fn_val = build_proc_header(
|
||||
env,
|
||||
layout_interner,
|
||||
*specialization,
|
||||
symbol,
|
||||
&proc,
|
||||
layout_ids,
|
||||
);
|
||||
let func_spec = if is_erased {
|
||||
FuncBorrowSpec::Erased
|
||||
} else {
|
||||
FuncBorrowSpec::Some(*specialization)
|
||||
};
|
||||
|
||||
let fn_val =
|
||||
build_proc_header(env, layout_interner, func_spec, symbol, &proc, layout_ids);
|
||||
|
||||
if proc.args.is_empty() {
|
||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
||||
|
@ -4889,8 +4952,7 @@ pub fn build_procedures<'a>(
|
|||
);
|
||||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let getter_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
|
||||
let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let name = getter_fn.get_name().to_str().unwrap();
|
||||
let getter_name = symbol.as_str(&env.interns);
|
||||
|
@ -5006,7 +5068,7 @@ pub fn build_procedures_expose_expects<'a>(
|
|||
|
||||
// NOTE fake layout; it is only used for debug prints
|
||||
let roc_main_fn =
|
||||
function_value_by_func_spec(env, *func_spec, symbol, &[], captures_niche, Layout::UNIT);
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||
|
||||
let name = roc_main_fn.get_name().to_str().unwrap();
|
||||
|
||||
|
@ -5133,11 +5195,19 @@ fn build_procedures_help<'a>(
|
|||
mod_solutions
|
||||
}
|
||||
|
||||
pub enum FuncBorrowSpec {
|
||||
/// This function has an specialization due to alias analysis.
|
||||
Some(FuncSpec),
|
||||
/// This function does not have a specialization due to alias analysis,
|
||||
/// because it is type-erased, and thus has no statically determined AA specialization.
|
||||
Erased,
|
||||
}
|
||||
|
||||
fn func_spec_name<'a>(
|
||||
arena: &'a Bump,
|
||||
interns: &Interns,
|
||||
symbol: Symbol,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
) -> bumpalo::collections::String<'a> {
|
||||
use std::fmt::Write;
|
||||
|
||||
|
@ -5147,8 +5217,13 @@ fn func_spec_name<'a>(
|
|||
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
||||
write!(buf, "{module_string}_{ident_string}_").unwrap();
|
||||
|
||||
for byte in func_spec.0.iter() {
|
||||
write!(buf, "{byte:x?}").unwrap();
|
||||
match func_spec {
|
||||
FuncBorrowSpec::Some(func_spec) => {
|
||||
for byte in func_spec.0.iter() {
|
||||
write!(buf, "{byte:x?}").unwrap();
|
||||
}
|
||||
}
|
||||
FuncBorrowSpec::Erased => write!(buf, "erased").unwrap(),
|
||||
}
|
||||
|
||||
buf
|
||||
|
@ -5157,7 +5232,7 @@ fn func_spec_name<'a>(
|
|||
fn build_proc_header<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
symbol: Symbol,
|
||||
proc: &roc_mono::ir::Proc<'a>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -5260,14 +5335,7 @@ fn expose_alias_to_host<'a>(
|
|||
"we expect only one specialization of this symbol"
|
||||
);
|
||||
|
||||
function_value_by_func_spec(
|
||||
env,
|
||||
*func_spec,
|
||||
hels.symbol,
|
||||
hels.proc_layout.arguments,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
)
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), hels.symbol)
|
||||
}
|
||||
None => {
|
||||
// morphic did not generate a specialization for this function,
|
||||
|
@ -5290,6 +5358,7 @@ fn expose_alias_to_host<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
RawFunctionLayout::ErasedFunction(..) => todo_lambda_erasure!(),
|
||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||
// Define only the return value size, since this is a thunk
|
||||
//
|
||||
|
@ -5400,7 +5469,7 @@ fn build_closure_caller<'a, 'ctx>(
|
|||
|
||||
builder.build_store(output, call_result);
|
||||
} else {
|
||||
let call_result = call_roc_function(
|
||||
let call_result = call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
evaluator,
|
||||
|
@ -5571,73 +5640,43 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn function_value_by_func_spec<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
func_spec: FuncSpec,
|
||||
pub(crate) fn function_value_by_func_spec<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
func_spec: FuncBorrowSpec,
|
||||
symbol: Symbol,
|
||||
arguments: &[InLayout<'a>],
|
||||
niche: Niche<'a>,
|
||||
result: InLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||
let fn_name = fn_name.as_str();
|
||||
|
||||
function_value_by_name_help(env, arguments, niche, result, symbol, fn_name)
|
||||
function_value_by_name_help(env, symbol, fn_name)
|
||||
}
|
||||
|
||||
fn function_value_by_name_help<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
arguments: &[InLayout<'a>],
|
||||
_niche: Niche<'a>,
|
||||
result: InLayout<'a>,
|
||||
fn function_value_by_name_help<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
symbol: Symbol,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
env.module.get_function(fn_name).unwrap_or_else(|| {
|
||||
if symbol.is_builtin() {
|
||||
eprintln!(
|
||||
"Unrecognized builtin function: {:?}\nLayout: {:?}\n",
|
||||
fn_name,
|
||||
(arguments, result)
|
||||
);
|
||||
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
|
||||
|
||||
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})",)
|
||||
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||
} else {
|
||||
// Unrecognized non-builtin function:
|
||||
eprintln!(
|
||||
"Unrecognized non-builtin function: {:?}\n\nSymbol: {:?}\nLayout: {:?}\n",
|
||||
fn_name,
|
||||
symbol,
|
||||
(arguments, result)
|
||||
);
|
||||
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
|
||||
|
||||
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})",)
|
||||
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn roc_call_with_args<'a, 'ctx>(
|
||||
fn roc_call_direct_with_args<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
argument_layouts: &[InLayout<'a>],
|
||||
result_layout: InLayout<'a>,
|
||||
name: LambdaName<'a>,
|
||||
func_spec: FuncSpec,
|
||||
func_spec: FuncBorrowSpec,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let fn_val = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
name.name(),
|
||||
argument_layouts,
|
||||
name.niche(),
|
||||
result_layout,
|
||||
);
|
||||
let fn_val = function_value_by_func_spec(env, func_spec, name.name());
|
||||
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
fn_val,
|
||||
|
@ -5646,14 +5685,70 @@ fn roc_call_with_args<'a, 'ctx>(
|
|||
)
|
||||
}
|
||||
|
||||
pub fn call_roc_function<'a, 'ctx>(
|
||||
#[inline(always)]
|
||||
fn roc_call_erased_with_args<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
argument_layouts: &[InLayout<'a>],
|
||||
result_layout: InLayout<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let function_type =
|
||||
fn_ptr::function_type(env, layout_interner, argument_layouts, result_layout);
|
||||
let function_ptr_type = function_type.ptr_type(AddressSpace::default());
|
||||
|
||||
let function_pointer = fn_ptr::cast_to_function_ptr_type(env, pointer, function_ptr_type);
|
||||
let callable_function_pointer = CallableValue::try_from(function_pointer).unwrap();
|
||||
|
||||
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
|
||||
env.builder
|
||||
.build_call(callable_function_pointer, arguments, "call")
|
||||
};
|
||||
|
||||
call_roc_function_help(
|
||||
env,
|
||||
layout_interner,
|
||||
build_call,
|
||||
function_type,
|
||||
layout_interner.get_repr(result_layout),
|
||||
arguments,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn call_direct_roc_function<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
result_layout: LayoutRepr<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let pass_by_pointer = roc_function.get_type().get_param_types().len() == arguments.len() + 1;
|
||||
let function_type = roc_function.get_type();
|
||||
|
||||
let build_call = |arguments: &[BasicMetadataValueEnum<'ctx>]| {
|
||||
env.builder.build_call(roc_function, arguments, "call")
|
||||
};
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
|
||||
call_roc_function_help(
|
||||
env,
|
||||
layout_interner,
|
||||
build_call,
|
||||
function_type,
|
||||
result_layout,
|
||||
arguments,
|
||||
)
|
||||
}
|
||||
|
||||
fn call_roc_function_help<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
build_call: impl FnOnce(&[BasicMetadataValueEnum<'ctx>]) -> CallSiteValue<'ctx>,
|
||||
roc_function_type: FunctionType<'ctx>,
|
||||
result_layout: LayoutRepr<'a>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let pass_by_pointer = roc_function_type.get_param_types().len() == arguments.len() + 1;
|
||||
|
||||
match RocReturn::from_layout(layout_interner, result_layout) {
|
||||
RocReturn::ByPointer if !pass_by_pointer => {
|
||||
|
@ -5667,14 +5762,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
|||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
env.builder
|
||||
|
@ -5689,14 +5780,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
|||
|
||||
arguments.push(result_alloca.into());
|
||||
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
if result_layout.is_passed_by_reference(layout_interner) {
|
||||
|
@ -5710,25 +5797,18 @@ pub fn call_roc_function<'a, 'ctx>(
|
|||
}
|
||||
}
|
||||
RocReturn::Return => {
|
||||
debug_assert_eq!(
|
||||
roc_function.get_type().get_param_types().len(),
|
||||
arguments.len()
|
||||
);
|
||||
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||
let it = arguments.iter().map(|x| (*x).into());
|
||||
let arguments = Vec::from_iter_in(it, env.arena);
|
||||
|
||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
||||
let call = build_call(&arguments);
|
||||
|
||||
// roc functions should have the fast calling convention
|
||||
debug_assert_eq!(roc_function.get_call_conventions(), FAST_CALL_CONV);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value().left().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"LLVM error: Invalid call by name for name {:?}",
|
||||
roc_function.get_name()
|
||||
)
|
||||
})
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| internal_error!("LLVM error: Invalid call by name",))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5905,7 +5985,7 @@ pub enum CCReturn {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FunctionSpec<'ctx> {
|
||||
pub(crate) struct FunctionSpec<'ctx> {
|
||||
/// The function type
|
||||
pub typ: FunctionType<'ctx>,
|
||||
call_conv: u32,
|
||||
|
@ -5975,7 +6055,7 @@ impl<'ctx> FunctionSpec<'ctx> {
|
|||
}
|
||||
|
||||
/// Fastcc calling convention
|
||||
fn fastcc<'a, 'env>(
|
||||
pub fn fastcc<'a, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_return: RocReturn,
|
||||
return_type: BasicTypeEnum<'ctx>,
|
||||
|
@ -6223,7 +6303,7 @@ fn build_foreign_symbol<'a, 'ctx>(
|
|||
}
|
||||
};
|
||||
|
||||
call_roc_function(
|
||||
call_direct_roc_function(
|
||||
env,
|
||||
layout_interner,
|
||||
fastcc_function,
|
||||
|
@ -6366,7 +6446,7 @@ fn get_foreign_symbol<'ctx>(
|
|||
/// Add a function to a module, after asserting that the function is unique.
|
||||
/// We never want to define the same function twice in the same module!
|
||||
/// The result can be bugs that are difficult to track down.
|
||||
pub fn add_func<'ctx>(
|
||||
pub(crate) fn add_func<'ctx>(
|
||||
ctx: &Context,
|
||||
module: &Module<'ctx>,
|
||||
name: &str,
|
||||
|
|
|
@ -168,6 +168,8 @@ fn build_eq<'a, 'ctx>(
|
|||
),
|
||||
|
||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closures"),
|
||||
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
|
||||
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
|
||||
|
||||
LayoutRepr::Union(union_layout) => build_tag_eq(
|
||||
env,
|
||||
|
@ -399,6 +401,8 @@ fn build_neq<'a, 'ctx>(
|
|||
unreachable!("recursion pointers should never be compared directly")
|
||||
}
|
||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closure"),
|
||||
LayoutRepr::FunctionPointer(_) => unreachable!("cannot compare function pointers"),
|
||||
LayoutRepr::Erased(_) => unreachable!("cannot compare erased types"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1364,7 +1368,7 @@ fn build_box_eq<'a, 'ctx>(
|
|||
env.builder.set_current_debug_location(di_location);
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(function, &[tag1.into(), tag2.into()], "tag_eq");
|
||||
.build_call(function, &[tag1.into(), tag2.into()], "box_eq");
|
||||
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
|
@ -1429,12 +1433,47 @@ fn build_box_eq_help<'a, 'ctx>(
|
|||
"compare_pointers",
|
||||
);
|
||||
|
||||
let compare_inner_value = ctx.append_basic_block(parent, "compare_inner_value");
|
||||
let check_null_then_compare_inner_values =
|
||||
ctx.append_basic_block(parent, "check_null_then_compare_inner_values");
|
||||
|
||||
env.builder.build_conditional_branch(
|
||||
ptr_equal,
|
||||
return_true,
|
||||
check_null_then_compare_inner_values,
|
||||
);
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(ptr_equal, return_true, compare_inner_value);
|
||||
.position_at_end(check_null_then_compare_inner_values);
|
||||
|
||||
env.builder.position_at_end(compare_inner_value);
|
||||
// Check for nullability, then compare inner values
|
||||
|
||||
let box1_is_null = env
|
||||
.builder
|
||||
.build_is_null(box1.into_pointer_value(), "box1_is_null");
|
||||
let check_box2_is_null = ctx.append_basic_block(parent, "check_if_box2_is_null");
|
||||
let return_false = ctx.append_basic_block(parent, "return_false");
|
||||
let compare_inner_values = ctx.append_basic_block(parent, "compare_inner_values");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(box1_is_null, return_false, check_box2_is_null);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(check_box2_is_null);
|
||||
let box2_is_null = env
|
||||
.builder
|
||||
.build_is_null(box2.into_pointer_value(), "box2_is_null");
|
||||
env.builder
|
||||
.build_conditional_branch(box2_is_null, return_false, compare_inner_values);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(return_false);
|
||||
env.builder
|
||||
.build_return(Some(&env.context.bool_type().const_zero()));
|
||||
}
|
||||
|
||||
// Compare the inner values.
|
||||
env.builder.position_at_end(compare_inner_values);
|
||||
|
||||
// clear the tag_id so we get a pointer to the actual data
|
||||
let box1 = box1.into_pointer_value();
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::llvm::build::{BuilderExt, Env};
|
||||
use crate::llvm::build::{BuilderExt, Env, FunctionSpec, RocReturn};
|
||||
use crate::llvm::erased;
|
||||
use crate::llvm::memcpy::build_memcpy;
|
||||
use bumpalo::collections::Vec as AVec;
|
||||
use bumpalo::collections::{CollectIn, Vec as AVec};
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||
use inkwell::values::PointerValue;
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_mono::layout::{
|
||||
round_up_to_alignment, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
|
||||
UnionLayout,
|
||||
round_up_to_alignment, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr,
|
||||
STLayoutInterner, UnionLayout,
|
||||
};
|
||||
use roc_target::TargetInfo;
|
||||
|
||||
|
@ -48,6 +49,22 @@ pub fn basic_type_from_layout<'a, 'ctx>(
|
|||
.ptr_type(AddressSpace::default())
|
||||
.as_basic_type_enum(),
|
||||
|
||||
FunctionPointer(self::FunctionPointer { args, ret }) => {
|
||||
let args = args.iter().map(|arg| {
|
||||
basic_type_from_layout(env, layout_interner, layout_interner.get_repr(*arg))
|
||||
});
|
||||
|
||||
let ret_repr = layout_interner.get_repr(ret);
|
||||
let ret = basic_type_from_layout(env, layout_interner, ret_repr);
|
||||
|
||||
let roc_return = RocReturn::from_layout(layout_interner, ret_repr);
|
||||
|
||||
let fn_spec = FunctionSpec::fastcc(env, roc_return, ret, args.collect_in(env.arena));
|
||||
|
||||
fn_spec.typ.ptr_type(AddressSpace::default()).into()
|
||||
}
|
||||
Erased(_) => erased::basic_type(env).into(),
|
||||
|
||||
Builtin(builtin) => basic_type_from_builtin(env, &builtin),
|
||||
}
|
||||
}
|
||||
|
|
131
crates/compiler/gen_llvm/src/llvm/erased.rs
Normal file
131
crates/compiler/gen_llvm/src/llvm/erased.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use inkwell::{
|
||||
types::{PointerType, StructType},
|
||||
values::{PointerValue, StructValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use roc_mono::ir::ErasedField;
|
||||
|
||||
use super::build::Env;
|
||||
|
||||
pub fn opaque_ptr_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerType<'ctx> {
|
||||
env.context.i8_type().ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
fn refcounter_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerType<'ctx> {
|
||||
let return_void = env.context.void_type();
|
||||
let arg_ty = opaque_ptr_type(env);
|
||||
|
||||
return_void
|
||||
.fn_type(&[arg_ty.into()], false)
|
||||
.ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
/// Erased is laid out like
|
||||
///
|
||||
/// ```text
|
||||
/// struct Erased {
|
||||
/// value: void*,
|
||||
/// callee: void*,
|
||||
/// refcounter_inc: (void* -> void) *,
|
||||
/// refcounter_dec: (void* -> void) *,
|
||||
/// }
|
||||
/// ```
|
||||
pub fn basic_type<'ctx>(env: &Env<'_, 'ctx, '_>) -> StructType<'ctx> {
|
||||
let opaque_ptr_ty = opaque_ptr_type(env);
|
||||
let refcounter_ptr_ty = refcounter_type(env);
|
||||
|
||||
env.context.struct_type(
|
||||
&[
|
||||
opaque_ptr_ty.into(),
|
||||
opaque_ptr_ty.into(),
|
||||
refcounter_ptr_ty.into(),
|
||||
refcounter_ptr_ty.into(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
fn bitcast_to_opaque_ptr<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
value: PointerValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
env.builder
|
||||
.build_bitcast(
|
||||
value,
|
||||
env.context.i8_type().ptr_type(AddressSpace::default()),
|
||||
"to_opaque_ptr",
|
||||
)
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
pub fn build<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
value: Option<PointerValue<'ctx>>,
|
||||
callee: PointerValue<'ctx>,
|
||||
) -> StructValue<'ctx> {
|
||||
let struct_type = basic_type(env);
|
||||
|
||||
let struct_value = struct_type.const_zero().into();
|
||||
|
||||
let struct_value = match value {
|
||||
Some(value) => {
|
||||
let value = bitcast_to_opaque_ptr(env, value);
|
||||
env.builder
|
||||
.build_insert_value(struct_value, value, 0, "insert_value")
|
||||
.unwrap()
|
||||
}
|
||||
None => struct_value,
|
||||
};
|
||||
|
||||
let callee = bitcast_to_opaque_ptr(env, callee);
|
||||
let struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, callee, 1, "insert_callee")
|
||||
.unwrap();
|
||||
|
||||
// TODO: insert refcounter
|
||||
|
||||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
pub fn load<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
erasure: StructValue<'ctx>,
|
||||
field: ErasedField,
|
||||
as_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let index = match field {
|
||||
ErasedField::Value => 0,
|
||||
ErasedField::ValuePtr => 0,
|
||||
ErasedField::Callee => 1,
|
||||
};
|
||||
|
||||
let value = env
|
||||
.builder
|
||||
.build_extract_value(erasure, index, "extract_erased_value")
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
let value = env
|
||||
.builder
|
||||
.build_bitcast(value, as_type, "bitcast_to_type")
|
||||
.into_pointer_value();
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
pub fn load_refcounter<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
erasure: StructValue<'ctx>,
|
||||
mode: super::refcounting::Mode,
|
||||
) -> PointerValue<'ctx> {
|
||||
let index = match mode {
|
||||
super::refcounting::Mode::Inc => 2,
|
||||
super::refcounting::Mode::Dec => 3,
|
||||
};
|
||||
|
||||
env.builder
|
||||
.build_extract_value(erasure, index, "extract_refcounter")
|
||||
.unwrap()
|
||||
.into_pointer_value()
|
||||
}
|
|
@ -9,7 +9,7 @@ use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum};
|
|||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::LookupType;
|
||||
use roc_mono::layout::{
|
||||
|
@ -387,6 +387,8 @@ fn build_clone<'a, 'ctx>(
|
|||
union_layout,
|
||||
)
|
||||
}
|
||||
LayoutRepr::FunctionPointer(_) => todo_lambda_erasure!(),
|
||||
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -798,7 +800,7 @@ fn build_clone_tag_help<'a, 'ctx>(
|
|||
let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena);
|
||||
|
||||
for i in 0..other_tags.len() + 1 {
|
||||
if i == nullable_id as _ {
|
||||
if i == nullable_id as usize {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
48
crates/compiler/gen_llvm/src/llvm/fn_ptr.rs
Normal file
48
crates/compiler/gen_llvm/src/llvm/fn_ptr.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use bumpalo::collections::CollectIn;
|
||||
use inkwell::{
|
||||
types::{FunctionType, PointerType},
|
||||
values::{FunctionValue, PointerValue},
|
||||
};
|
||||
|
||||
use roc_mono::layout::{InLayout, LambdaName, LayoutInterner, STLayoutInterner};
|
||||
|
||||
use super::{
|
||||
build::{function_value_by_func_spec, Env, FuncBorrowSpec, FunctionSpec, RocReturn},
|
||||
convert::{argument_type_from_layout, basic_type_from_layout},
|
||||
};
|
||||
|
||||
pub fn function_type<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
arguments: &[InLayout<'a>],
|
||||
return_type: InLayout<'a>,
|
||||
) -> FunctionType<'ctx> {
|
||||
let args = arguments
|
||||
.iter()
|
||||
.map(|arg| argument_type_from_layout(env, layout_interner, layout_interner.get_repr(*arg)));
|
||||
|
||||
let ret_repr = layout_interner.get_repr(return_type);
|
||||
let ret = basic_type_from_layout(env, layout_interner, ret_repr);
|
||||
|
||||
let roc_return = RocReturn::from_layout(layout_interner, ret_repr);
|
||||
|
||||
let fn_spec = FunctionSpec::fastcc(env, roc_return, ret, args.collect_in(env.arena));
|
||||
|
||||
fn_spec.typ
|
||||
}
|
||||
|
||||
pub fn build<'a, 'ctx>(env: &Env<'a, 'ctx, '_>, lambda_name: LambdaName<'a>) -> PointerValue<'ctx> {
|
||||
let func_value: FunctionValue<'ctx> =
|
||||
function_value_by_func_spec(env, FuncBorrowSpec::Erased, lambda_name.name());
|
||||
func_value.as_global_value().as_pointer_value()
|
||||
}
|
||||
|
||||
pub fn cast_to_function_ptr_type<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
function_pointer_type: PointerType<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
env.builder
|
||||
.build_bitcast(pointer, function_pointer_type, "cast_to_function_ptr")
|
||||
.into_pointer_value()
|
||||
}
|
|
@ -30,8 +30,8 @@ use crate::llvm::{
|
|||
},
|
||||
build::{
|
||||
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
|
||||
entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value,
|
||||
roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn,
|
||||
function_value_by_func_spec, load_roc_value, roc_function_call, tag_pointer_clear_tag_id,
|
||||
BuilderExt, FuncBorrowSpec, RocReturn,
|
||||
},
|
||||
build_list::{
|
||||
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
|
||||
|
@ -1331,16 +1331,6 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
tag_pointer_clear_tag_id(env, ptr.into_pointer_value()).into()
|
||||
}
|
||||
|
||||
Alloca => {
|
||||
arguments!(initial_value);
|
||||
|
||||
let ptr = entry_block_alloca_zerofill(env, initial_value.get_type(), "stack_value");
|
||||
|
||||
env.builder.build_store(ptr, initial_value);
|
||||
|
||||
ptr.into()
|
||||
}
|
||||
|
||||
RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr => {
|
||||
unreachable!("Not used in LLVM backend: {:?}", op);
|
||||
}
|
||||
|
@ -1354,7 +1344,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
"cast_to_i8_ptr",
|
||||
);
|
||||
|
||||
let value_ptr = match layout_interner.get_repr(data_layout) {
|
||||
let value_ptr = match layout_interner.runtime_representation(data_layout) {
|
||||
LayoutRepr::Union(union_layout)
|
||||
if union_layout.stores_tag_id_in_pointer(env.target_info) =>
|
||||
{
|
||||
|
@ -1396,6 +1386,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
|
||||
call_bitcode_fn(env, &[], bitcode::UTILS_DICT_PSEUDO_SEED)
|
||||
}
|
||||
|
||||
SetJmp | LongJmp | SetLongJmpBuffer => unreachable!("only inserted in dev backend codegen"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2611,7 +2603,7 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
|||
} = higher_order;
|
||||
|
||||
let PassedFunction {
|
||||
argument_layouts,
|
||||
argument_layouts: _,
|
||||
return_layout: result_layout,
|
||||
owns_captured_environment: function_owns_closure_data,
|
||||
name: function_name,
|
||||
|
@ -2624,11 +2616,8 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
|||
() => {{
|
||||
let function = function_value_by_func_spec(
|
||||
env,
|
||||
func_spec,
|
||||
FuncBorrowSpec::Some(func_spec),
|
||||
function_name.name(),
|
||||
argument_layouts,
|
||||
function_name.niche(),
|
||||
return_layout,
|
||||
);
|
||||
|
||||
let (closure, closure_layout) =
|
||||
|
|
|
@ -11,6 +11,8 @@ mod lowlevel;
|
|||
pub mod refcounting;
|
||||
|
||||
mod align;
|
||||
mod erased;
|
||||
mod fn_ptr;
|
||||
mod memcpy;
|
||||
mod scope;
|
||||
mod struct_;
|
||||
|
|
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