mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59: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
|
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
|
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
|
name: Nix linux x86_64 cargo test
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
on: [pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
name: Nix apple silicon cargo test
|
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
|
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
|
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:
|
on:
|
||||||
|
#pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
||||||
|
|
||||||
env:
|
|
||||||
ZIG_VERSION: 0.9.1
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-nightly:
|
test-nightly:
|
||||||
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
||||||
|
@ -17,6 +15,9 @@ jobs:
|
||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- 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)
|
- name: get the latest release archive for linux (x86_64)
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
|
@ -26,44 +27,19 @@ jobs:
|
||||||
if: startsWith(matrix.os, 'macos')
|
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
|
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: zig version
|
||||||
run: ls | grep -v "roc_nightly.*tar\.gz" | xargs rm -rf
|
|
||||||
|
|
||||||
- name: decompress the tar
|
- name: prep and run basic tests
|
||||||
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
|
|
||||||
run: |
|
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
|
./ci/basic_nightly_test.sh
|
||||||
echo "${GITHUB_WORKSPACE}/zig-${{env.OS_TYPE}}-x86_64-${ZIG_VERSION}" >> $GITHUB_PATH
|
|
||||||
- name: zig version
|
- name: clean up, get old linux release (x86_64), run tests
|
||||||
run: zig version
|
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
|
name: CI
|
||||||
|
|
||||||
|
@ -61,7 +62,6 @@ jobs:
|
||||||
run: ./ci/www-repl.sh && sccache --show-stats
|
run: ./ci/www-repl.sh && sccache --show-stats
|
||||||
|
|
||||||
#TODO i386 (32-bit linux) cli tests
|
#TODO i386 (32-bit linux) cli tests
|
||||||
|
|
||||||
#TODO verify-no-git-changes
|
#TODO verify-no-git-changes
|
||||||
|
|
||||||
- name: test website build script
|
- 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
|
name: windows - release build
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ jobs:
|
||||||
- name: zig version
|
- name: zig version
|
||||||
run: zig version
|
run: zig version
|
||||||
|
|
||||||
- name: install rust nightly 1.67
|
- name: install rust nightly 1.70.0
|
||||||
run: rustup install nightly-2022-12-09
|
run: rustup install nightly-2023-04-15
|
||||||
|
|
||||||
- name: set up llvm 13
|
- name: set up llvm 13
|
||||||
run: |
|
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
|
name: windows - subset of tests
|
||||||
|
|
||||||
|
@ -36,8 +37,8 @@ jobs:
|
||||||
- name: zig version
|
- name: zig version
|
||||||
run: zig version
|
run: zig version
|
||||||
|
|
||||||
- name: install rust nightly 1.67
|
- name: install rust nightly 1.70.0
|
||||||
run: rustup install nightly-2022-12-09
|
run: rustup install nightly-2023-04-15
|
||||||
|
|
||||||
- name: set up llvm 13
|
- name: set up llvm 13
|
||||||
run: |
|
run: |
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -85,3 +85,6 @@ www/src/roc-tutorial
|
||||||
|
|
||||||
# snapshot tests temp file
|
# snapshot tests temp file
|
||||||
*.pending-snap
|
*.pending-snap
|
||||||
|
|
||||||
|
# checkmate
|
||||||
|
checkmate_*.json
|
||||||
|
|
179
Cargo.lock
generated
179
Cargo.lock
generated
|
@ -88,6 +88,21 @@ dependencies = [
|
||||||
"alloc-no-stdlib",
|
"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]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -473,6 +488,21 @@ dependencies = [
|
||||||
"num-traits",
|
"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]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "2.34.0"
|
version = "2.34.0"
|
||||||
|
@ -1094,6 +1124,12 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
|
@ -1394,7 +1430,7 @@ dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1653,6 +1689,29 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"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]]
|
[[package]]
|
||||||
name = "iced-x86"
|
name = "iced-x86"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
|
@ -1975,7 +2034,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time 0.3.21",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2106,7 +2165,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3095,6 +3154,7 @@ dependencies = [
|
||||||
"page_size",
|
"page_size",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_load",
|
"roc_load",
|
||||||
|
@ -3105,6 +3165,7 @@ dependencies = [
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
|
@ -3203,6 +3264,27 @@ dependencies = [
|
||||||
"ven_pretty",
|
"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]]
|
[[package]]
|
||||||
name = "roc_cli"
|
name = "roc_cli"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -3311,11 +3393,13 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_derive_key",
|
"roc_derive_key",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
]
|
]
|
||||||
|
@ -3351,6 +3435,7 @@ dependencies = [
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_solve",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"snafu",
|
"snafu",
|
||||||
|
@ -3563,11 +3648,13 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_derive",
|
"roc_derive",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
]
|
]
|
||||||
|
@ -3591,6 +3678,7 @@ dependencies = [
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
"roc_packaging",
|
"roc_packaging",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_solve",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"serde",
|
"serde",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
@ -3611,6 +3699,7 @@ dependencies = [
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_packaging",
|
"roc_packaging",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_solve",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
@ -3627,6 +3716,7 @@ dependencies = [
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
"roc_debug_flags",
|
"roc_debug_flags",
|
||||||
|
@ -3686,10 +3776,12 @@ dependencies = [
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_tracing",
|
"roc_tracing",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
|
"roc_unify",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"ven_pretty",
|
"ven_pretty",
|
||||||
]
|
]
|
||||||
|
@ -3794,6 +3886,7 @@ dependencies = [
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_solve",
|
||||||
"roc_std",
|
"roc_std",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
|
@ -3849,6 +3942,7 @@ dependencies = [
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_repl_eval",
|
"roc_repl_eval",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_solve",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
@ -3912,6 +4006,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_debug_flags",
|
"roc_debug_flags",
|
||||||
"roc_derive",
|
"roc_derive",
|
||||||
|
@ -3927,6 +4022,7 @@ dependencies = [
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
"roc_solve_problem",
|
"roc_solve_problem",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
|
@ -3947,6 +4043,13 @@ dependencies = [
|
||||||
"roc_types",
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_solve_schema"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
@ -4006,11 +4109,12 @@ dependencies = [
|
||||||
name = "roc_unify"
|
name = "roc_unify"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"roc_checkmate",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_debug_flags",
|
"roc_debug_flags",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
|
"roc_solve_schema",
|
||||||
"roc_tracing",
|
"roc_tracing",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
@ -4156,6 +4260,30 @@ dependencies = [
|
||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -4244,6 +4372,17 @@ dependencies = [
|
||||||
"syn 2.0.16",
|
"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]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.96"
|
version = "1.0.96"
|
||||||
|
@ -4798,6 +4937,17 @@ dependencies = [
|
||||||
"num_cpus",
|
"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]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -4942,7 +5092,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
|
checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"time",
|
"time 0.3.21",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -5252,6 +5402,12 @@ dependencies = [
|
||||||
"try-lock",
|
"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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
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]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.42.0"
|
version = "0.42.0"
|
||||||
|
@ -5860,9 +6025,9 @@ checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "xml-rs"
|
||||||
version = "0.8.11"
|
version = "0.8.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1690519550bfa95525229b9ca2350c63043a4857b3b0013811b2ccf4a2420b01"
|
checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
|
|
|
@ -82,6 +82,7 @@ bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||||
capstone = { version = "0.11.0", default-features = false }
|
capstone = { version = "0.11.0", default-features = false }
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
|
chrono = "0.4.26"
|
||||||
clap = { version = "4.2.7", default-features = false, features = ["std", "color", "suggestions", "help", "usage", "error-context"] }
|
clap = { version = "4.2.7", default-features = false, features = ["std", "color", "suggestions", "help", "usage", "error-context"] }
|
||||||
colored = "2.0.0"
|
colored = "2.0.0"
|
||||||
console_error_panic_hook = "0.1.7"
|
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"
|
rlimit = "0.9.1"
|
||||||
rustyline = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
rustyline = { git = "https://github.com/roc-lang/rustyline", rev = "e74333c" }
|
||||||
rustyline-derive = { 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 = { version = "1.0.153", features = ["derive"] } # update roc_std/Cargo.toml on change
|
||||||
serde-xml-rs = "0.6.0"
|
serde-xml-rs = "0.6.0"
|
||||||
serde_json = "1.0.94" # update roc_std/Cargo.toml on change
|
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:
|
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)
|
* [Lucas Rosa](https://github.com/rvcas)
|
||||||
* [Jonas Schell](https://github.com/Ocupe)
|
* [Jonas Schell](https://github.com/Ocupe)
|
||||||
* [Christopher Dolan](https://github.com/cdolan)
|
* [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]
|
[dependencies]
|
||||||
roc_builtins = { path = "../compiler/builtins" }
|
roc_builtins = { path = "../compiler/builtins" }
|
||||||
roc_can = { path = "../compiler/can" }
|
roc_can = { path = "../compiler/can" }
|
||||||
|
roc_checkmate = { path = "../compiler/checkmate" }
|
||||||
roc_collections = { path = "../compiler/collections" }
|
roc_collections = { path = "../compiler/collections" }
|
||||||
roc_error_macros = { path = "../error_macros" }
|
roc_error_macros = { path = "../error_macros" }
|
||||||
roc_load = { path = "../compiler/load" }
|
roc_load = { path = "../compiler/load" }
|
||||||
|
@ -20,6 +21,7 @@ roc_problem = { path = "../compiler/problem" }
|
||||||
roc_region = { path = "../compiler/region" }
|
roc_region = { path = "../compiler/region" }
|
||||||
roc_reporting = { path = "../reporting" }
|
roc_reporting = { path = "../reporting" }
|
||||||
roc_solve = { path = "../compiler/solve" }
|
roc_solve = { path = "../compiler/solve" }
|
||||||
|
roc_solve_schema = { path = "../compiler/solve_schema" }
|
||||||
roc_target = { path = "../compiler/roc_target" }
|
roc_target = { path = "../compiler/roc_target" }
|
||||||
roc_types = { path = "../compiler/types" }
|
roc_types = { path = "../compiler/types" }
|
||||||
roc_unify = { path = "../compiler/unify" }
|
roc_unify = { path = "../compiler/unify" }
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl<T> Clone for NodeId<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
NodeId {
|
NodeId {
|
||||||
index: self.index,
|
index: self.index,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ impl Pool {
|
||||||
|
|
||||||
NodeId {
|
NodeId {
|
||||||
index,
|
index,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
} else {
|
} 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.");
|
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 {
|
PoolStr {
|
||||||
first_node_id: NodeId {
|
first_node_id: NodeId {
|
||||||
index: 0,
|
index: 0,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
},
|
},
|
||||||
len: 0,
|
len: 0,
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
|
||||||
PoolVec {
|
PoolVec {
|
||||||
first_node_id: NodeId {
|
first_node_id: NodeId {
|
||||||
index: 0,
|
index: 0,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
},
|
},
|
||||||
len: 0,
|
len: 0,
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ where
|
||||||
// Advance the node pointer to the next node in the current page
|
// Advance the node pointer to the next node in the current page
|
||||||
self.current_node_id = NodeId {
|
self.current_node_id = NodeId {
|
||||||
index: index + 1,
|
index: index + 1,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
};
|
};
|
||||||
self.len_remaining = len_remaining - 1;
|
self.len_remaining = len_remaining - 1;
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ where
|
||||||
// Advance the node pointer to the next node in the current page
|
// Advance the node pointer to the next node in the current page
|
||||||
self.current_node_id = NodeId {
|
self.current_node_id = NodeId {
|
||||||
index: index + 1,
|
index: index + 1,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
};
|
};
|
||||||
self.len_remaining = len_remaining - 1;
|
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
|
// Advance the node pointer to the next node in the current page
|
||||||
self.current_node_id = NodeId {
|
self.current_node_id = NodeId {
|
||||||
index: index + 1,
|
index: index + 1,
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData,
|
||||||
};
|
};
|
||||||
self.len_remaining = len_remaining - 1;
|
self.len_remaining = len_remaining - 1;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub fn load_module(
|
||||||
) -> LoadedModule {
|
) -> LoadedModule {
|
||||||
let load_config = LoadConfig {
|
let load_config = LoadConfig {
|
||||||
target_info: TargetInfo::default_x86_64(), // editor only needs type info, so this is unused
|
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,
|
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||||
palette: DEFAULT_PALETTE,
|
palette: DEFAULT_PALETTE,
|
||||||
threading,
|
threading,
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
|
use roc_checkmate::with_checkmate;
|
||||||
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{BumpMap, BumpMapDefault, MutMap};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_solve::module::Solved;
|
use roc_solve::module::Solved;
|
||||||
|
use roc_solve_schema::UnificationMode;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
self, AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields,
|
self, AliasVariables, Content, Descriptor, FlatType, Mark, OptVariable, Rank, RecordFields,
|
||||||
Subs, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
Subs, SubsSlice, TagExt, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
|
||||||
|
@ -17,9 +19,8 @@ use roc_types::types::{
|
||||||
RecordField,
|
RecordField,
|
||||||
};
|
};
|
||||||
use roc_unify::unify::unify;
|
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::unify::Unified::*;
|
||||||
|
use roc_unify::Env as UEnv;
|
||||||
|
|
||||||
use crate::constrain::{Constraint, PresenceConstraint};
|
use crate::constrain::{Constraint, PresenceConstraint};
|
||||||
use crate::lang::core::types::Type2;
|
use crate::lang::core::types::Type2;
|
||||||
|
@ -228,10 +229,13 @@ fn solve<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
match unify(
|
match unify(
|
||||||
&mut UEnv::new(subs),
|
&mut with_checkmate!({
|
||||||
|
on => UEnv::new(subs, None),
|
||||||
|
off => UEnv::new(subs),
|
||||||
|
}),
|
||||||
actual,
|
actual,
|
||||||
expected,
|
expected,
|
||||||
Mode::EQ,
|
UnificationMode::EQ,
|
||||||
Polarity::OF_VALUE,
|
Polarity::OF_VALUE,
|
||||||
) {
|
) {
|
||||||
Success {
|
Success {
|
||||||
|
@ -326,10 +330,13 @@ fn solve<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
match unify(
|
match unify(
|
||||||
&mut UEnv::new(subs),
|
&mut with_checkmate!({
|
||||||
|
on => UEnv::new(subs, None),
|
||||||
|
off => UEnv::new(subs),
|
||||||
|
}),
|
||||||
actual,
|
actual,
|
||||||
expected,
|
expected,
|
||||||
Mode::EQ,
|
UnificationMode::EQ,
|
||||||
Polarity::OF_VALUE,
|
Polarity::OF_VALUE,
|
||||||
) {
|
) {
|
||||||
Success {
|
Success {
|
||||||
|
@ -402,10 +409,13 @@ fn solve<'a>(
|
||||||
|
|
||||||
// TODO(ayazhafiz): presence constraints for Expr2/Type2
|
// TODO(ayazhafiz): presence constraints for Expr2/Type2
|
||||||
match unify(
|
match unify(
|
||||||
&mut UEnv::new(subs),
|
&mut with_checkmate!({
|
||||||
|
on => UEnv::new(subs, None),
|
||||||
|
off => UEnv::new(subs),
|
||||||
|
}),
|
||||||
actual,
|
actual,
|
||||||
expected,
|
expected,
|
||||||
Mode::EQ,
|
UnificationMode::EQ,
|
||||||
Polarity::OF_PATTERN,
|
Polarity::OF_PATTERN,
|
||||||
) {
|
) {
|
||||||
Success {
|
Success {
|
||||||
|
@ -718,10 +728,13 @@ fn solve<'a>(
|
||||||
let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty);
|
let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty);
|
||||||
|
|
||||||
match unify(
|
match unify(
|
||||||
&mut UEnv::new(subs),
|
&mut with_checkmate!({
|
||||||
|
on => UEnv::new(subs, None),
|
||||||
|
off => UEnv::new(subs),
|
||||||
|
}),
|
||||||
actual,
|
actual,
|
||||||
includes,
|
includes,
|
||||||
Mode::PRESENT,
|
UnificationMode::PRESENT,
|
||||||
Polarity::OF_PATTERN,
|
Polarity::OF_PATTERN,
|
||||||
) {
|
) {
|
||||||
Success {
|
Success {
|
||||||
|
@ -1489,6 +1502,8 @@ fn adjust_rank_content(
|
||||||
rank
|
rank
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErasedLambda => group_rank,
|
||||||
|
|
||||||
RangedNumber(_vars) => group_rank,
|
RangedNumber(_vars) => group_rank,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1669,6 +1684,7 @@ fn instantiate_rigids_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErasedLambda => {}
|
||||||
RangedNumber(_vars) => {}
|
RangedNumber(_vars) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,6 +1997,12 @@ fn deep_copy_var_help(
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErasedLambda => {
|
||||||
|
subs.set(copy, make_descriptor(ErasedLambda));
|
||||||
|
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
|
||||||
RangedNumber(vars) => {
|
RangedNumber(vars) => {
|
||||||
let new_content = RangedNumber(vars);
|
let new_content = RangedNumber(vars);
|
||||||
|
|
||||||
|
|
|
@ -385,7 +385,7 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||||
use roc_build::program::report_problems_monomorphized;
|
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_packaging::cache;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
|
||||||
|
@ -427,10 +427,13 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
|
||||||
let target = &triple;
|
let target = &triple;
|
||||||
let opt_level = opt_level;
|
let opt_level = opt_level;
|
||||||
let target_info = TargetInfo::from(target);
|
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
|
// Step 1: compile the app and generate the .o file
|
||||||
let load_config = LoadConfig {
|
let load_config = LoadConfig {
|
||||||
target_info,
|
target_info,
|
||||||
|
function_kind,
|
||||||
// TODO: expose this from CLI?
|
// TODO: expose this from CLI?
|
||||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
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))
|
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> {
|
fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<ExecutableFile> {
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
|
@ -11,7 +11,7 @@ use roc_docs::generate_docs_html;
|
||||||
use roc_error_macros::user_error;
|
use roc_error_macros::user_error;
|
||||||
use roc_gen_dev::AssemblyBackendMode;
|
use roc_gen_dev::AssemblyBackendMode;
|
||||||
use roc_gen_llvm::llvm::build::LlvmBackendMode;
|
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_packaging::cache::{self, RocCacheDir};
|
||||||
use roc_target::Target;
|
use roc_target::Target;
|
||||||
use std::fs::{self, FileType};
|
use std::fs::{self, FileType};
|
||||||
|
@ -123,10 +123,12 @@ fn main() -> io::Result<()> {
|
||||||
.get_one::<String>(FLAG_TARGET)
|
.get_one::<String>(FLAG_TARGET)
|
||||||
.and_then(|s| Target::from_str(s).ok())
|
.and_then(|s| Target::from_str(s).ok())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
let function_kind = FunctionKind::LambdaSet;
|
||||||
roc_linker::generate_stub_lib(
|
roc_linker::generate_stub_lib(
|
||||||
input_path,
|
input_path,
|
||||||
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
|
||||||
&target.to_triple(),
|
&target.to_triple(),
|
||||||
|
function_kind,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some((CMD_BUILD, matches)) => {
|
Some((CMD_BUILD, matches)) => {
|
||||||
|
|
|
@ -24,19 +24,15 @@ main =
|
||||||
Err GetIntError ->
|
Err GetIntError ->
|
||||||
Task.putLine "Error: Failed to get Integer from stdin."
|
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 : (I64, Expr -> IO Expr), I64, Expr -> IO Expr
|
||||||
nest = \f, n, e -> Task.loop { s: n, f, m: n, x: e } nestHelp
|
nest = \f, n, e -> nestHelp n f n e
|
||||||
|
|
||||||
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 })
|
|
||||||
|
|
||||||
Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr]
|
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_module::symbol::Symbol;
|
||||||
|
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
|
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLayouts,
|
||||||
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
|
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, Niche, RawFunctionLayout,
|
||||||
|
@ -181,6 +181,7 @@ where
|
||||||
|
|
||||||
let mut type_definitions = MutSet::default();
|
let mut type_definitions = MutSet::default();
|
||||||
let mut host_exposed_functions = Vec::new();
|
let mut host_exposed_functions = Vec::new();
|
||||||
|
let mut erased_functions = Vec::new();
|
||||||
|
|
||||||
// all other functions
|
// all other functions
|
||||||
for proc in procs {
|
for proc in procs {
|
||||||
|
@ -201,6 +202,17 @@ where
|
||||||
|
|
||||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
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(_) => {
|
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
let bytes = func_name_bytes_help(
|
let bytes = func_name_bytes_help(
|
||||||
hels.symbol,
|
hels.symbol,
|
||||||
|
@ -226,6 +238,11 @@ where
|
||||||
|
|
||||||
let (spec, type_names) = proc_spec(arena, interner, proc)?;
|
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);
|
type_definitions.extend(type_names);
|
||||||
|
|
||||||
m.add_func(func_name, spec)?;
|
m.add_func(func_name, spec)?;
|
||||||
|
@ -253,6 +270,7 @@ where
|
||||||
entry_point_layout,
|
entry_point_layout,
|
||||||
Some(roc_main),
|
Some(roc_main),
|
||||||
&host_exposed_functions,
|
&host_exposed_functions,
|
||||||
|
&erased_functions,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
type_definitions.extend(env.type_names);
|
type_definitions.extend(env.type_names);
|
||||||
|
@ -279,8 +297,14 @@ where
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut env = Env::new();
|
let mut env = Env::new();
|
||||||
let entry_point_function =
|
let entry_point_function = build_entry_point(
|
||||||
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
|
&mut env,
|
||||||
|
interner,
|
||||||
|
layout,
|
||||||
|
None,
|
||||||
|
&host_exposed,
|
||||||
|
&erased_functions,
|
||||||
|
)?;
|
||||||
|
|
||||||
type_definitions.extend(env.type_names);
|
type_definitions.extend(env.type_names);
|
||||||
|
|
||||||
|
@ -364,6 +388,7 @@ fn build_entry_point<'a>(
|
||||||
layout: roc_mono::ir::ProcLayout<'a>,
|
layout: roc_mono::ir::ProcLayout<'a>,
|
||||||
entry_point_function: Option<FuncName>,
|
entry_point_function: Option<FuncName>,
|
||||||
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
host_exposed_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||||
|
erased_functions: &[([u8; SIZE], &'a [InLayout<'a>])],
|
||||||
) -> Result<FuncDef> {
|
) -> Result<FuncDef> {
|
||||||
let mut builder = FuncDefBuilder::new();
|
let mut builder = FuncDefBuilder::new();
|
||||||
let outer_block = builder.add_block();
|
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
|
// 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);
|
let host_exposed_func_name = FuncName(name_bytes);
|
||||||
|
|
||||||
if Some(host_exposed_func_name) == entry_point_function {
|
if Some(host_exposed_func_name) == entry_point_function {
|
||||||
|
@ -788,6 +813,16 @@ fn call_spec<'a>(
|
||||||
let module = MOD_APP;
|
let module = MOD_APP;
|
||||||
builder.add_call(block, spec_var, module, name, arg_value_id)
|
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 {
|
||||||
foreign_symbol: _,
|
foreign_symbol: _,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -1517,6 +1552,28 @@ fn expr_spec<'a>(
|
||||||
let value = with_new_heap_cell(builder, block, union_data)?;
|
let value = with_new_heap_cell(builder, block, union_data)?;
|
||||||
builder.add_make_named(block, MOD_APP, type_name, value)
|
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(_) => {
|
RuntimeErrorFunction(_) => {
|
||||||
let type_id = layout_spec(env, builder, interner, interner.get_repr(layout))?;
|
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, &[])
|
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"),
|
_ => 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
|
// we model all our numbers as unit values
|
||||||
builder.add_make_tuple(block, &[])
|
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`
|
// on windows, we need the nightly toolchain so we can use `-Z export-executable-symbols`
|
||||||
// using `+nightly` only works when running cargo through rustup
|
// using `+nightly` only works when running cargo through rustup
|
||||||
let mut cmd = rustup();
|
let mut cmd = rustup();
|
||||||
cmd.args(["run", "nightly-2022-12-09", "cargo"]);
|
cmd.args(["run", "nightly-2023-04-15", "cargo"]);
|
||||||
|
|
||||||
cmd
|
cmd
|
||||||
} else {
|
} else {
|
||||||
|
@ -1078,6 +1078,12 @@ fn link_macos(
|
||||||
// "--gc-sections",
|
// "--gc-sections",
|
||||||
"-arch",
|
"-arch",
|
||||||
&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",
|
"-macos_version_min",
|
||||||
&get_macos_version(),
|
&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::build::{module_from_builtins, LlvmBackendMode};
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_load::{
|
use roc_load::{
|
||||||
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
|
EntryPoint, ExecutionMode, ExpectMetadata, FunctionKind, LoadConfig, LoadMonomorphizedError,
|
||||||
LoadingProblem, MonomorphizedModule, Threading,
|
LoadedModule, LoadingProblem, MonomorphizedModule, Threading,
|
||||||
};
|
};
|
||||||
use roc_mono::ir::{OptLevel, SingleEntryPoint};
|
use roc_mono::ir::{OptLevel, SingleEntryPoint};
|
||||||
use roc_packaging::cache::RocCacheDir;
|
use roc_packaging::cache::RocCacheDir;
|
||||||
|
@ -740,8 +740,20 @@ pub fn standard_load_config(
|
||||||
BuildOrdering::AlwaysBuild => ExecutionMode::Executable,
|
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 {
|
LoadConfig {
|
||||||
target_info,
|
target_info,
|
||||||
|
function_kind,
|
||||||
render: RenderTarget::ColorTerminal,
|
render: RenderTarget::ColorTerminal,
|
||||||
palette: DEFAULT_PALETTE,
|
palette: DEFAULT_PALETTE,
|
||||||
threading,
|
threading,
|
||||||
|
@ -1205,6 +1217,8 @@ pub fn check_file<'a>(
|
||||||
|
|
||||||
let load_config = LoadConfig {
|
let load_config = LoadConfig {
|
||||||
target_info,
|
target_info,
|
||||||
|
// TODO: we may not want this for just checking.
|
||||||
|
function_kind: FunctionKind::LambdaSet,
|
||||||
// TODO: expose this from CLI?
|
// TODO: expose this from CLI?
|
||||||
render: RenderTarget::ColorTerminal,
|
render: RenderTarget::ColorTerminal,
|
||||||
palette: DEFAULT_PALETTE,
|
palette: DEFAULT_PALETTE,
|
||||||
|
|
|
@ -1182,8 +1182,17 @@ mapTry = \list, toResult ->
|
||||||
Result.map (toResult elem) \ok ->
|
Result.map (toResult elem) \ok ->
|
||||||
List.append state ok
|
List.append state ok
|
||||||
|
|
||||||
## This is the same as `iterate` but with [Result] instead of `[Continue, Break]`.
|
## Same as [List.walk], except you can stop walking early by returning `Err`.
|
||||||
## Using `Result` saves a conditional in `mapTry`.
|
##
|
||||||
|
## ## 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 elem, state, (state, elem -> Result state err) -> Result state err
|
||||||
walkTry = \list, init, func ->
|
walkTry = \list, init, func ->
|
||||||
walkTryHelp list init func 0 (List.len list)
|
walkTryHelp list init func 0 (List.len list)
|
||||||
|
|
|
@ -638,12 +638,13 @@ countUtf8Bytes : Str -> Nat
|
||||||
substringUnsafe : Str, Nat, Nat -> Str
|
substringUnsafe : Str, Nat, Nat -> Str
|
||||||
|
|
||||||
## Returns the given [Str] with each occurrence of a substring replaced.
|
## 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 "foo/bar/baz" "/" "_" == "foo_bar_baz"
|
||||||
## expect Str.replaceEach "not here" "/" "_" == Err NotFound
|
## expect Str.replaceEach "not here" "/" "_" == "not here"
|
||||||
## ```
|
## ```
|
||||||
replaceEach : Str, Str, Str -> Result Str [NotFound]
|
replaceEach : Str, Str, Str -> Str
|
||||||
replaceEach = \haystack, needle, flower ->
|
replaceEach = \haystack, needle, flower ->
|
||||||
when splitFirst haystack needle is
|
when splitFirst haystack needle is
|
||||||
Ok { before, after } ->
|
Ok { before, after } ->
|
||||||
|
@ -653,9 +654,8 @@ replaceEach = \haystack, needle, flower ->
|
||||||
|> Str.concat before
|
|> Str.concat before
|
||||||
|> Str.concat flower
|
|> Str.concat flower
|
||||||
|> replaceEachHelp after needle flower
|
|> replaceEachHelp after needle flower
|
||||||
|> Ok
|
|
||||||
|
|
||||||
Err err -> Err err
|
Err NotFound -> haystack
|
||||||
|
|
||||||
replaceEachHelp : Str, Str, Str, Str -> Str
|
replaceEachHelp : Str, Str, Str, Str -> Str
|
||||||
replaceEachHelp = \buf, haystack, needle, flower ->
|
replaceEachHelp = \buf, haystack, needle, flower ->
|
||||||
|
@ -668,39 +668,44 @@ replaceEachHelp = \buf, haystack, needle, flower ->
|
||||||
|
|
||||||
Err NotFound -> Str.concat buf haystack
|
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 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 "foo/bar/baz" "/" "_" == "foo_bar/baz"
|
||||||
## expect Str.replaceFirst "no slashes here" "/" "_" == Err NotFound
|
## 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 ->
|
replaceFirst = \haystack, needle, flower ->
|
||||||
when splitFirst haystack needle is
|
when splitFirst haystack needle is
|
||||||
Ok { before, after } ->
|
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 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 "foo/bar/baz" "/" "_" == "foo/bar_baz"
|
||||||
## expect Str.replaceLast "no slashes here" "/" "_" == Err NotFound
|
## 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 ->
|
replaceLast = \haystack, needle, flower ->
|
||||||
when splitLast haystack needle is
|
when splitLast haystack needle is
|
||||||
Ok { before, after } ->
|
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
|
## 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.
|
## 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::PtrStore => unimplemented!(),
|
||||||
LowLevel::PtrLoad => unimplemented!(),
|
LowLevel::PtrLoad => unimplemented!(),
|
||||||
LowLevel::PtrClearTagId => unimplemented!(),
|
LowLevel::PtrClearTagId => unimplemented!(),
|
||||||
LowLevel::Alloca => unimplemented!(),
|
|
||||||
LowLevel::RefCountIncRcPtr => unimplemented!(),
|
LowLevel::RefCountIncRcPtr => unimplemented!(),
|
||||||
LowLevel::RefCountDecRcPtr=> unimplemented!(),
|
LowLevel::RefCountDecRcPtr=> unimplemented!(),
|
||||||
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
LowLevel::RefCountIncDataPtr => unimplemented!(),
|
||||||
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
LowLevel::RefCountDecDataPtr=> unimplemented!(),
|
||||||
LowLevel::RefCountIsUnique => unimplemented!(),
|
LowLevel::RefCountIsUnique => unimplemented!(),
|
||||||
|
|
||||||
|
LowLevel::SetJmp => unimplemented!(),
|
||||||
|
LowLevel::LongJmp => unimplemented!(),
|
||||||
|
LowLevel::SetLongJmpBuffer => unimplemented!(),
|
||||||
|
|
||||||
// these are not implemented, not sure why
|
// these are not implemented, not sure why
|
||||||
LowLevel::StrFromInt => unimplemented!(),
|
LowLevel::StrFromInt => unimplemented!(),
|
||||||
LowLevel::StrFromFloat => unimplemented!(),
|
LowLevel::StrFromFloat => unimplemented!(),
|
||||||
|
|
|
@ -64,26 +64,6 @@ pub type PExpectedTypeIndex = Index<PExpected<TypeOrVar>>;
|
||||||
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
|
pub type TypeOrVar = EitherIndex<TypeTag, Variable>;
|
||||||
|
|
||||||
impl Constraints {
|
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 {
|
pub fn new() -> Self {
|
||||||
let constraints = Vec::new();
|
let constraints = Vec::new();
|
||||||
let type_slices = Vec::with_capacity(16);
|
let type_slices = Vec::with_capacity(16);
|
||||||
|
|
|
@ -1148,6 +1148,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ErasedLambda => ErasedLambda,
|
||||||
|
|
||||||
RangedNumber(range) => {
|
RangedNumber(range) => {
|
||||||
perform_clone!(RangedNumber(range))
|
perform_clone!(RangedNumber(range))
|
||||||
|
|
|
@ -88,6 +88,23 @@ pub struct Annotation {
|
||||||
pub region: Region,
|
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)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CanDefs {
|
pub(crate) struct CanDefs {
|
||||||
defs: Vec<Option<Def>>,
|
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) {
|
if is_initial && !exposed_symbols.contains(&symbol) {
|
||||||
env.problem(Problem::DefsOnlyUsedInRecursion(1, def.region()));
|
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),
|
Loc::at(def.loc_expr.region, closure_data),
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
|
host_annotation,
|
||||||
specializes,
|
specializes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1658,55 +1684,80 @@ pub(crate) fn sort_can_defs_new(
|
||||||
def.loc_expr,
|
def.loc_expr,
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
|
host_annotation,
|
||||||
specializes,
|
specializes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match def.loc_pattern.value {
|
match def.loc_pattern.value {
|
||||||
Pattern::Identifier(symbol) => match def.loc_expr.value {
|
Pattern::Identifier(symbol) => {
|
||||||
Closure(closure_data) => {
|
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||||
declarations.push_function_def(
|
def.annotation
|
||||||
Loc::at(def.loc_pattern.region, symbol),
|
.clone()
|
||||||
Loc::at(def.loc_expr.region, closure_data),
|
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||||
def.expr_var,
|
} else {
|
||||||
def.annotation,
|
None
|
||||||
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 {
|
Pattern::AbilityMemberSpecialization {
|
||||||
ident: symbol,
|
ident: symbol,
|
||||||
specializes,
|
specializes,
|
||||||
} => match def.loc_expr.value {
|
} => {
|
||||||
Closure(closure_data) => {
|
let host_annotation = if exposed_symbols.contains(&symbol) {
|
||||||
declarations.push_function_def(
|
def.annotation
|
||||||
Loc::at(def.loc_pattern.region, symbol),
|
.clone()
|
||||||
Loc::at(def.loc_expr.region, closure_data),
|
.map(|a| (var_store.fresh(), a.freshen(var_store)))
|
||||||
def.expr_var,
|
} else {
|
||||||
def.annotation,
|
None
|
||||||
Some(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,
|
||||||
|
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(
|
declarations.push_destructure_def(
|
||||||
def.loc_pattern,
|
def.loc_pattern,
|
||||||
|
@ -1749,6 +1800,14 @@ pub(crate) fn sort_can_defs_new(
|
||||||
Some(r) => Some(Region::span_across(&r, &def.region())),
|
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 {
|
match def.loc_expr.value {
|
||||||
Closure(closure_data) => {
|
Closure(closure_data) => {
|
||||||
declarations.push_recursive_def(
|
declarations.push_recursive_def(
|
||||||
|
@ -1756,6 +1815,7 @@ pub(crate) fn sort_can_defs_new(
|
||||||
Loc::at(def.loc_expr.region, closure_data),
|
Loc::at(def.loc_expr.region, closure_data),
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
|
host_annotation,
|
||||||
specializes,
|
specializes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1765,6 +1825,7 @@ pub(crate) fn sort_can_defs_new(
|
||||||
def.loc_expr,
|
def.loc_expr,
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
|
host_annotation,
|
||||||
specializes,
|
specializes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,7 @@ fn index_var(
|
||||||
| Content::FlexAbleVar(_, _)
|
| Content::FlexAbleVar(_, _)
|
||||||
| Content::RigidAbleVar(_, _)
|
| Content::RigidAbleVar(_, _)
|
||||||
| Content::LambdaSet(_)
|
| Content::LambdaSet(_)
|
||||||
|
| Content::ErasedLambda
|
||||||
| Content::RangedNumber(..) => return Err(TypeError),
|
| Content::RangedNumber(..) => return Err(TypeError),
|
||||||
Content::Error => return Err(TypeError),
|
Content::Error => return Err(TypeError),
|
||||||
Content::RecursionVar {
|
Content::RecursionVar {
|
||||||
|
|
|
@ -18,7 +18,7 @@ use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::Symbol;
|
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::ident::Accessor;
|
||||||
use roc_parse::pattern::PatternType::*;
|
use roc_parse::pattern::PatternType::*;
|
||||||
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
|
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 {
|
pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::Var { .. } => true,
|
// These definitely contain neither comments nor newlines, so they are valid
|
||||||
ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
|
ast::Expr::Var { .. }
|
||||||
_ => false,
|
| 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.
|
// used for ability member specializatons.
|
||||||
pub specializes: VecMap<usize, Symbol>,
|
pub specializes: VecMap<usize, Symbol>,
|
||||||
|
|
||||||
|
pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>,
|
||||||
|
|
||||||
pub function_bodies: Vec<Loc<FunctionDef>>,
|
pub function_bodies: Vec<Loc<FunctionDef>>,
|
||||||
pub expressions: Vec<Loc<Expr>>,
|
pub expressions: Vec<Loc<Expr>>,
|
||||||
pub destructs: Vec<DestructureDef>,
|
pub destructs: Vec<DestructureDef>,
|
||||||
|
@ -2556,6 +2662,7 @@ impl Declarations {
|
||||||
variables: Vec::with_capacity(capacity),
|
variables: Vec::with_capacity(capacity),
|
||||||
symbols: Vec::with_capacity(capacity),
|
symbols: Vec::with_capacity(capacity),
|
||||||
annotations: Vec::with_capacity(capacity),
|
annotations: Vec::with_capacity(capacity),
|
||||||
|
host_exposed_annotations: VecMap::new(),
|
||||||
function_bodies: Vec::with_capacity(capacity),
|
function_bodies: Vec::with_capacity(capacity),
|
||||||
expressions: Vec::with_capacity(capacity),
|
expressions: Vec::with_capacity(capacity),
|
||||||
specializes: VecMap::default(), // number of specializations is probably low
|
specializes: VecMap::default(), // number of specializations is probably low
|
||||||
|
@ -2586,6 +2693,7 @@ impl Declarations {
|
||||||
loc_closure_data: Loc<ClosureData>,
|
loc_closure_data: Loc<ClosureData>,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
annotation: Option<Annotation>,
|
annotation: Option<Annotation>,
|
||||||
|
host_annotation: Option<(Variable, Annotation)>,
|
||||||
specializes: Option<Symbol>,
|
specializes: Option<Symbol>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let index = self.declarations.len();
|
let index = self.declarations.len();
|
||||||
|
@ -2608,6 +2716,11 @@ impl Declarations {
|
||||||
Recursive::TailRecursive => DeclarationTag::TailRecursive(function_def_index),
|
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.declarations.push(tag);
|
||||||
self.variables.push(expr_var);
|
self.variables.push(expr_var);
|
||||||
self.symbols.push(symbol);
|
self.symbols.push(symbol);
|
||||||
|
@ -2628,6 +2741,7 @@ impl Declarations {
|
||||||
loc_closure_data: Loc<ClosureData>,
|
loc_closure_data: Loc<ClosureData>,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
annotation: Option<Annotation>,
|
annotation: Option<Annotation>,
|
||||||
|
host_annotation: Option<(Variable, Annotation)>,
|
||||||
specializes: Option<Symbol>,
|
specializes: Option<Symbol>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let index = self.declarations.len();
|
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);
|
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
|
self.declarations
|
||||||
.push(DeclarationTag::Function(function_def_index));
|
.push(DeclarationTag::Function(function_def_index));
|
||||||
self.variables.push(expr_var);
|
self.variables.push(expr_var);
|
||||||
|
@ -2700,10 +2819,16 @@ impl Declarations {
|
||||||
loc_expr: Loc<Expr>,
|
loc_expr: Loc<Expr>,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
annotation: Option<Annotation>,
|
annotation: Option<Annotation>,
|
||||||
|
host_annotation: Option<(Variable, Annotation)>,
|
||||||
specializes: Option<Symbol>,
|
specializes: Option<Symbol>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let index = self.declarations.len();
|
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.declarations.push(DeclarationTag::Value);
|
||||||
self.variables.push(expr_var);
|
self.variables.push(expr_var);
|
||||||
self.symbols.push(symbol);
|
self.symbols.push(symbol);
|
||||||
|
@ -2758,6 +2883,7 @@ impl Declarations {
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2768,6 +2894,7 @@ impl Declarations {
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2778,6 +2905,7 @@ impl Declarations {
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
def.annotation,
|
def.annotation,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,9 @@ use roc_module::called_via::BinOp::Pizza;
|
||||||
use roc_module::called_via::{BinOp, CalledVia};
|
use roc_module::called_via::{BinOp, CalledVia};
|
||||||
use roc_module::ident::ModuleName;
|
use roc_module::ident::ModuleName;
|
||||||
use roc_parse::ast::Expr::{self, *};
|
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};
|
use roc_region::all::{Loc, Region};
|
||||||
|
|
||||||
// BinOp precedence logic adapted from Gluon by Markus Westerlind
|
// 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(..)
|
Float(..)
|
||||||
| Num(..)
|
| Num(..)
|
||||||
| NonBase10Int { .. }
|
| NonBase10Int { .. }
|
||||||
| Str(_)
|
|
||||||
| SingleQuote(_)
|
| SingleQuote(_)
|
||||||
| AccessorFunction(_)
|
| AccessorFunction(_)
|
||||||
| Var { .. }
|
| Var { .. }
|
||||||
|
@ -144,6 +145,28 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
| IngestedFile(_, _)
|
| IngestedFile(_, _)
|
||||||
| Crash => loc_expr,
|
| 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) => {
|
TupleAccess(sub_expr, paths) => {
|
||||||
let region = loc_expr.region;
|
let region = loc_expr.region;
|
||||||
let loc_sub_expr = Loc {
|
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>(
|
fn desugar_field<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
field: &'a AssignedField<'a, Expr<'a>>,
|
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)
|
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
|
/// Constrain top-level module declarations
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn constrain_decls(
|
pub fn constrain_decls(
|
||||||
|
@ -2498,6 +2518,7 @@ pub fn constrain_decls(
|
||||||
|
|
||||||
use roc_can::expr::DeclarationTag::*;
|
use roc_can::expr::DeclarationTag::*;
|
||||||
let tag = declarations.declarations[index];
|
let tag = declarations.declarations[index];
|
||||||
|
|
||||||
match tag {
|
match tag {
|
||||||
Value => {
|
Value => {
|
||||||
constraint = constrain_value_def(
|
constraint = constrain_value_def(
|
||||||
|
@ -2508,6 +2529,77 @@ pub fn constrain_decls(
|
||||||
index,
|
index,
|
||||||
constraint,
|
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 => {
|
Expectation => {
|
||||||
let loc_expr = &declarations.expressions[index];
|
let loc_expr = &declarations.expressions[index];
|
||||||
|
@ -2565,56 +2657,6 @@ pub fn constrain_decls(
|
||||||
Generalizable(false),
|
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;
|
index += 1;
|
||||||
|
|
|
@ -9,11 +9,13 @@ version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
|
roc_checkmate = { path = "../checkmate" }
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_derive_key = { path = "../derive_key" }
|
roc_derive_key = { path = "../derive_key" }
|
||||||
roc_error_macros = { path = "../../error_macros" }
|
roc_error_macros = { path = "../../error_macros" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
|
roc_solve_schema = { path = "../solve_schema" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use roc_can::{abilities::SpecializationLambdaSets, module::ExposedByModule};
|
use roc_can::{abilities::SpecializationLambdaSets, module::ExposedByModule};
|
||||||
|
use roc_checkmate::with_checkmate;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{IdentIds, Symbol};
|
use roc_module::symbol::{IdentIds, Symbol};
|
||||||
|
use roc_solve_schema::UnificationMode;
|
||||||
use roc_types::{
|
use roc_types::{
|
||||||
subs::{instantiate_rigids, Subs, Variable},
|
subs::{instantiate_rigids, Subs, Variable},
|
||||||
types::Polarity,
|
types::Polarity,
|
||||||
|
@ -71,13 +73,20 @@ impl Env<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unify(&mut self, left: Variable, right: Variable) {
|
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(
|
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,
|
left,
|
||||||
right,
|
right,
|
||||||
Mode::EQ,
|
UnificationMode::EQ,
|
||||||
Polarity::OF_PATTERN,
|
Polarity::OF_PATTERN,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -103,15 +112,22 @@ impl Env<'_> {
|
||||||
specialization_type: Variable,
|
specialization_type: Variable,
|
||||||
ability_member: Symbol,
|
ability_member: Symbol,
|
||||||
) -> SpecializationLambdaSets {
|
) -> 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 member_signature = self.import_builtin_symbol_var(ability_member);
|
||||||
|
|
||||||
let unified = unify_introduced_ability_specialization(
|
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,
|
member_signature,
|
||||||
specialization_type,
|
specialization_type,
|
||||||
Mode::EQ,
|
UnificationMode::EQ,
|
||||||
);
|
);
|
||||||
|
|
||||||
match unified {
|
match unified {
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl FlatDecodable {
|
||||||
| Content::RigidVar(_)
|
| Content::RigidVar(_)
|
||||||
| Content::FlexAbleVar(_, _)
|
| Content::FlexAbleVar(_, _)
|
||||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||||
Content::LambdaSet(_) => Err(Underivable),
|
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ impl FlatEncodable {
|
||||||
| Content::RigidVar(_)
|
| Content::RigidVar(_)
|
||||||
| Content::FlexAbleVar(_, _)
|
| Content::FlexAbleVar(_, _)
|
||||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
||||||
Content::LambdaSet(_) => Err(Underivable),
|
Content::LambdaSet(_) | Content::ErasedLambda => Err(Underivable),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ impl FlatHash {
|
||||||
| Content::RigidVar(_)
|
| Content::RigidVar(_)
|
||||||
| Content::FlexAbleVar(_, _)
|
| Content::FlexAbleVar(_, _)
|
||||||
| Content::RigidAbleVar(_, _) => Err(UnboundVar),
|
| 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();
|
.collect();
|
||||||
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, n - 1);
|
let rest: Vec<Vec<Pattern>> = is_exhaustive(&new_matrix, n - 1);
|
||||||
|
|
||||||
let last: _ = alt_list
|
let last = alt_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|r| is_missing(alts.clone(), &ctors, r));
|
.filter_map(|r| is_missing(alts.clone(), &ctors, r));
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::{internal_error, todo_lambda_erasure};
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp};
|
use roc_mono::code_gen_help::{CallerProc, CodeGenHelp, HelperOp};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
|
@ -3007,11 +3007,17 @@ impl<
|
||||||
ASM::and_reg64_reg64_reg64(buf, sym_reg, sym_reg, ptr_reg);
|
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
|
// 1. acquire some stack space
|
||||||
let element_width = self.interner().stack_size(element_layout);
|
let element_width = self.interner().stack_size(element_layout);
|
||||||
let allocation = self.debug_symbol("stack_allocation");
|
let allocation = self.debug_symbol("stack_allocation");
|
||||||
let ptr = self.debug_symbol("ptr");
|
let ptr = self.debug_symbol("ptr");
|
||||||
|
|
||||||
|
if element_width == 0 {
|
||||||
|
self.storage_manager.claim_pointer_stack_area(sym);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let base_offset = self
|
let base_offset = self
|
||||||
.storage_manager
|
.storage_manager
|
||||||
.claim_stack_area(&allocation, element_width);
|
.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::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);
|
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(
|
fn expr_box(
|
||||||
|
@ -3630,7 +3642,8 @@ impl<
|
||||||
}
|
}
|
||||||
LayoutRepr::Union(UnionLayout::NonRecursive(_))
|
LayoutRepr::Union(UnionLayout::NonRecursive(_))
|
||||||
| LayoutRepr::Builtin(_)
|
| LayoutRepr::Builtin(_)
|
||||||
| LayoutRepr::Struct(_) => {
|
| LayoutRepr::Struct(_)
|
||||||
|
| LayoutRepr::Erased(_) => {
|
||||||
internal_error!("All primitive values should fit in a single register");
|
internal_error!("All primitive values should fit in a single register");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4306,6 +4319,8 @@ impl<
|
||||||
dst,
|
dst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4527,5 +4542,6 @@ macro_rules! pointer_layouts {
|
||||||
| UnionLayout::NullableWrapped { .. }
|
| UnionLayout::NullableWrapped { .. }
|
||||||
| UnionLayout::NullableUnwrapped { .. },
|
| UnionLayout::NullableUnwrapped { .. },
|
||||||
)
|
)
|
||||||
|
| LayoutRepr::FunctionPointer(_)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
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_module::symbol::Symbol;
|
||||||
use roc_mono::{
|
use roc_mono::{
|
||||||
ir::{JoinPointId, Param},
|
ir::{JoinPointId, Param},
|
||||||
|
@ -830,6 +830,7 @@ impl<
|
||||||
|
|
||||||
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
||||||
}
|
}
|
||||||
|
LayoutRepr::Erased(_) => todo_lambda_erasure!(),
|
||||||
pointer_layouts!() => {
|
pointer_layouts!() => {
|
||||||
// like a 64-bit integer
|
// like a 64-bit integer
|
||||||
debug_assert_eq!(to_offset % 8, 0);
|
debug_assert_eq!(to_offset % 8, 0);
|
||||||
|
@ -1168,12 +1169,7 @@ impl<
|
||||||
) {
|
) {
|
||||||
let mut param_storage = bumpalo::vec![in self.env.arena];
|
let mut param_storage = bumpalo::vec![in self.env.arena];
|
||||||
param_storage.reserve(params.len());
|
param_storage.reserve(params.len());
|
||||||
for Param {
|
for Param { symbol, layout } in params {
|
||||||
symbol,
|
|
||||||
ownership: _,
|
|
||||||
layout,
|
|
||||||
} in params
|
|
||||||
{
|
|
||||||
// Claim a location for every join point parameter to be loaded at.
|
// Claim a location for every join point parameter to be loaded at.
|
||||||
// Put everything on the stack for simplicity.
|
// Put everything on the stack for simplicity.
|
||||||
self.joinpoint_argument_stack_storage(layout_interner, *symbol, *layout);
|
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 bumpalo::{collections::Vec, Bump};
|
||||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
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::ident::ModuleName;
|
||||||
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
use roc_module::low_level::{LowLevel, LowLevelWrapperType};
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
@ -149,6 +149,13 @@ impl<'a> LastSeenMap<'a> {
|
||||||
self.set_last_seen(*sym, stmt);
|
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) => {
|
Expr::Struct(syms) => {
|
||||||
for sym in *syms {
|
for sym in *syms {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt);
|
||||||
|
@ -176,8 +183,14 @@ impl<'a> LastSeenMap<'a> {
|
||||||
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
Expr::Reset { symbol, .. } | Expr::ResetRef { symbol, .. } => {
|
||||||
self.set_last_seen(*symbol, stmt);
|
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::RuntimeErrorFunction(_) => {}
|
||||||
|
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||||
|
Expr::EmptyArray => {}
|
||||||
}
|
}
|
||||||
self.scan_ast_help(following);
|
self.scan_ast_help(following);
|
||||||
}
|
}
|
||||||
|
@ -265,6 +278,7 @@ impl<'a> LastSeenMap<'a> {
|
||||||
|
|
||||||
match call_type {
|
match call_type {
|
||||||
CallType::ByName { .. } => {}
|
CallType::ByName { .. } => {}
|
||||||
|
CallType::ByPointer { .. } => {}
|
||||||
CallType::LowLevel { .. } => {}
|
CallType::LowLevel { .. } => {}
|
||||||
CallType::HigherOrder { .. } => {}
|
CallType::HigherOrder { .. } => {}
|
||||||
CallType::Foreign { .. } => {}
|
CallType::Foreign { .. } => {}
|
||||||
|
@ -729,6 +743,10 @@ trait Backend<'a> {
|
||||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CallType::ByPointer { .. } => {
|
||||||
|
todo_lambda_erasure!()
|
||||||
|
}
|
||||||
|
|
||||||
CallType::LowLevel { op: lowlevel, .. } => {
|
CallType::LowLevel { op: lowlevel, .. } => {
|
||||||
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
let mut arg_layouts: bumpalo::collections::Vec<InLayout<'a>> =
|
||||||
bumpalo::vec![in self.env().arena];
|
bumpalo::vec![in self.env().arena];
|
||||||
|
@ -839,6 +857,9 @@ trait Backend<'a> {
|
||||||
Expr::NullPointer => {
|
Expr::NullPointer => {
|
||||||
self.load_literal_i64(sym, 0);
|
self.load_literal_i64(sym, 0);
|
||||||
}
|
}
|
||||||
|
Expr::FunctionPointer { .. } => todo_lambda_erasure!(),
|
||||||
|
Expr::ErasedMake { .. } => todo_lambda_erasure!(),
|
||||||
|
Expr::ErasedLoad { .. } => todo_lambda_erasure!(),
|
||||||
Expr::Reset { symbol, .. } => {
|
Expr::Reset { symbol, .. } => {
|
||||||
let layout = *self.layout_map().get(symbol).unwrap();
|
let layout = *self.layout_map().get(symbol).unwrap();
|
||||||
|
|
||||||
|
@ -879,6 +900,12 @@ trait Backend<'a> {
|
||||||
|
|
||||||
self.build_expr(sym, &new_expr, &Layout::BOOL)
|
self.build_expr(sym, &new_expr, &Layout::BOOL)
|
||||||
}
|
}
|
||||||
|
Expr::Alloca {
|
||||||
|
initializer,
|
||||||
|
element_layout,
|
||||||
|
} => {
|
||||||
|
self.build_alloca(*sym, *initializer, *element_layout);
|
||||||
|
}
|
||||||
Expr::RuntimeErrorFunction(_) => todo!(),
|
Expr::RuntimeErrorFunction(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1599,10 +1626,6 @@ trait Backend<'a> {
|
||||||
self.build_ptr_clear_tag_id(*sym, args[0]);
|
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(
|
LowLevel::RefCountDecRcPtr => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::UTILS_DECREF_RC_PTR.to_string(),
|
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_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.
|
/// 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>)>;
|
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,
|
ret_layout: proc.ret_layout,
|
||||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
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,
|
ret_layout: roc_mono::layout::Layout::UNIT,
|
||||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
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()
|
.runtime_representation()
|
||||||
.llvm_alignment_bytes(interner),
|
.llvm_alignment_bytes(interner),
|
||||||
Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
|
Builtin(builtin) => builtin.llvm_alignment_bytes(interner),
|
||||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
RecursivePointer(_) | Ptr(_) | FunctionPointer(_) | Erased(_) => {
|
||||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
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,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_function,
|
roc_function,
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::llvm::refcounting::{
|
||||||
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
|
||||||
};
|
};
|
||||||
use crate::llvm::struct_::{struct_from_fields, RocStruct};
|
use crate::llvm::struct_::{struct_from_fields, RocStruct};
|
||||||
|
use crate::llvm::{erased, fn_ptr};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||||
|
@ -23,11 +24,11 @@ use inkwell::passes::{PassManager, PassManagerBuilder};
|
||||||
use inkwell::types::{
|
use inkwell::types::{
|
||||||
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
|
AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StructType,
|
||||||
};
|
};
|
||||||
use inkwell::values::BasicValueEnum;
|
|
||||||
use inkwell::values::{
|
use inkwell::values::{
|
||||||
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
|
BasicMetadataValueEnum, CallSiteValue, FunctionValue, InstructionValue, IntValue, PointerValue,
|
||||||
StructValue,
|
StructValue,
|
||||||
};
|
};
|
||||||
|
use inkwell::values::{BasicValueEnum, CallableValue};
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::{AddressSpace, IntPredicate};
|
||||||
use morphic_lib::{
|
use morphic_lib::{
|
||||||
|
@ -38,6 +39,7 @@ use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_debug_flags::dbg_do;
|
use roc_debug_flags::dbg_do;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
|
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_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet,
|
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
|
// NOTE fake layout; it is only used for debug prints
|
||||||
let roc_main_fn =
|
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
|
||||||
|
|
||||||
let main_fn_name = "$Test.main";
|
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
|
// NOTE fake layout; it is only used for debug prints
|
||||||
let roc_main_fn =
|
let roc_main_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||||
function_value_by_func_spec(env, *func_spec, symbol, &[], Niche::NONE, Layout::UNIT);
|
|
||||||
|
|
||||||
let output_type = match roc_main_fn.get_type().get_return_type() {
|
let output_type = match roc_main_fn.get_type().get_return_type() {
|
||||||
Some(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");
|
let entry = context.append_basic_block(c_function, "entry");
|
||||||
builder.position_at_end(entry);
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
let roc_main_fn_result = call_roc_function(
|
let roc_main_fn_result = call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_main_fn,
|
roc_main_fn,
|
||||||
|
@ -912,7 +912,6 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
||||||
CallType::ByName {
|
CallType::ByName {
|
||||||
name,
|
name,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
arg_layouts,
|
|
||||||
ret_layout,
|
ret_layout,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
@ -927,17 +926,39 @@ pub(crate) fn build_exp_call<'a, 'ctx>(
|
||||||
let callee_var = CalleeSpecVar(&bytes);
|
let callee_var = CalleeSpecVar(&bytes);
|
||||||
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
let func_spec = func_spec_solutions.callee_spec(callee_var).unwrap();
|
||||||
|
|
||||||
roc_call_with_args(
|
roc_call_direct_with_args(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
arg_layouts,
|
|
||||||
*ret_layout,
|
*ret_layout,
|
||||||
*name,
|
*name,
|
||||||
func_spec,
|
FuncBorrowSpec::Some(func_spec),
|
||||||
arg_tuples.into_bump_slice(),
|
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 } => {
|
CallType::LowLevel { op, update_mode } => {
|
||||||
let bytes = update_mode.to_bytes();
|
let bytes = update_mode.to_bytes();
|
||||||
let update_var = UpdateModeVar(&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 {
|
Reset {
|
||||||
symbol,
|
symbol,
|
||||||
update_mode,
|
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()
|
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 =
|
let roc_union =
|
||||||
RocUnion::untagged_from_slices(layout_interner, env.context, &[other_fields]);
|
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());
|
let output_type = roc_union.struct_type().ptr_type(AddressSpace::default());
|
||||||
|
|
||||||
return output_type.const_null().into();
|
return output_type.const_null().into();
|
||||||
|
@ -2833,8 +2890,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
||||||
DecRef(symbol) => {
|
DecRef(symbol) => {
|
||||||
let (value, layout) = scope.load_symbol_and_layout(symbol);
|
let (value, layout) = scope.load_symbol_and_layout(symbol);
|
||||||
|
|
||||||
let lay = layout_interner.get_repr(layout);
|
match layout_interner.runtime_representation(layout) {
|
||||||
match lay {
|
|
||||||
LayoutRepr::Builtin(Builtin::Str) => todo!(),
|
LayoutRepr::Builtin(Builtin::Str) => todo!(),
|
||||||
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
LayoutRepr::Builtin(Builtin::List(element_layout)) => {
|
||||||
debug_assert!(value.is_struct_value());
|
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);
|
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() {
|
if value.is_pointer_value() {
|
||||||
let value_ptr = match lay {
|
let clear_tag_id = match other_layout {
|
||||||
LayoutRepr::Union(union_layout)
|
LayoutRepr::Union(union_layout) => {
|
||||||
if union_layout
|
union_layout.stores_tag_id_in_pointer(env.target_info)
|
||||||
.stores_tag_id_in_pointer(env.target_info) =>
|
|
||||||
{
|
|
||||||
tag_pointer_clear_tag_id(env, value.into_pointer_value())
|
|
||||||
}
|
}
|
||||||
_ => 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");
|
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());
|
debug_assert!(value.is_pointer_value());
|
||||||
let value = value.into_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),
|
LayoutRepr::Union(union) => union.stores_tag_id_in_pointer(env.target_info),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
@ -3849,7 +3908,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
||||||
builder.position_at_end(entry);
|
builder.position_at_end(entry);
|
||||||
|
|
||||||
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
|
let wrapped_layout = roc_call_result_layout(env.arena, return_layout);
|
||||||
call_roc_function(
|
call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_function,
|
roc_function,
|
||||||
|
@ -3857,7 +3916,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx>(
|
||||||
arguments_for_call,
|
arguments_for_call,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
call_roc_function(
|
call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_function,
|
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 wrapper_result = roc_call_result_layout(env.arena, return_layout);
|
||||||
|
|
||||||
let roc_value = call_roc_function(
|
let roc_value = call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_wrapper_function,
|
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 arguments = Vec::from_iter_in(it, env.arena);
|
||||||
|
|
||||||
let value = call_roc_function(
|
let value = call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_function,
|
roc_function,
|
||||||
|
@ -4558,7 +4617,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>(
|
||||||
{
|
{
|
||||||
builder.position_at_end(then_block);
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
let call_result = call_roc_function(
|
let call_result = call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
roc_function,
|
roc_function,
|
||||||
|
@ -4826,15 +4885,19 @@ pub(crate) fn build_proc_headers<'a, 'r, 'ctx>(
|
||||||
|
|
||||||
let it = func_solutions.specs();
|
let it = func_solutions.specs();
|
||||||
let mut function_values = std::vec::Vec::with_capacity(it.size_hint().0);
|
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 {
|
for specialization in it {
|
||||||
let fn_val = build_proc_header(
|
let func_spec = if is_erased {
|
||||||
env,
|
FuncBorrowSpec::Erased
|
||||||
layout_interner,
|
} else {
|
||||||
*specialization,
|
FuncBorrowSpec::Some(*specialization)
|
||||||
symbol,
|
};
|
||||||
&proc,
|
|
||||||
layout_ids,
|
let fn_val =
|
||||||
);
|
build_proc_header(env, layout_interner, func_spec, symbol, &proc, layout_ids);
|
||||||
|
|
||||||
if proc.args.is_empty() {
|
if proc.args.is_empty() {
|
||||||
// this is a 0-argument thunk, i.e. a top-level constant definition
|
// 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
|
// NOTE fake layout; it is only used for debug prints
|
||||||
let getter_fn =
|
let getter_fn = function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), symbol);
|
||||||
function_value_by_func_spec(env, *func_spec, symbol, &[], niche, Layout::UNIT);
|
|
||||||
|
|
||||||
let name = getter_fn.get_name().to_str().unwrap();
|
let name = getter_fn.get_name().to_str().unwrap();
|
||||||
let getter_name = symbol.as_str(&env.interns);
|
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
|
// NOTE fake layout; it is only used for debug prints
|
||||||
let roc_main_fn =
|
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();
|
let name = roc_main_fn.get_name().to_str().unwrap();
|
||||||
|
|
||||||
|
@ -5133,11 +5195,19 @@ fn build_procedures_help<'a>(
|
||||||
mod_solutions
|
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>(
|
fn func_spec_name<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
interns: &Interns,
|
interns: &Interns,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
func_spec: FuncSpec,
|
func_spec: FuncBorrowSpec,
|
||||||
) -> bumpalo::collections::String<'a> {
|
) -> bumpalo::collections::String<'a> {
|
||||||
use std::fmt::Write;
|
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();
|
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
||||||
write!(buf, "{module_string}_{ident_string}_").unwrap();
|
write!(buf, "{module_string}_{ident_string}_").unwrap();
|
||||||
|
|
||||||
for byte in func_spec.0.iter() {
|
match func_spec {
|
||||||
write!(buf, "{byte:x?}").unwrap();
|
FuncBorrowSpec::Some(func_spec) => {
|
||||||
|
for byte in func_spec.0.iter() {
|
||||||
|
write!(buf, "{byte:x?}").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FuncBorrowSpec::Erased => write!(buf, "erased").unwrap(),
|
||||||
}
|
}
|
||||||
|
|
||||||
buf
|
buf
|
||||||
|
@ -5157,7 +5232,7 @@ fn func_spec_name<'a>(
|
||||||
fn build_proc_header<'a, 'ctx>(
|
fn build_proc_header<'a, 'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &STLayoutInterner<'a>,
|
layout_interner: &STLayoutInterner<'a>,
|
||||||
func_spec: FuncSpec,
|
func_spec: FuncBorrowSpec,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
proc: &roc_mono::ir::Proc<'a>,
|
proc: &roc_mono::ir::Proc<'a>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -5260,14 +5335,7 @@ fn expose_alias_to_host<'a>(
|
||||||
"we expect only one specialization of this symbol"
|
"we expect only one specialization of this symbol"
|
||||||
);
|
);
|
||||||
|
|
||||||
function_value_by_func_spec(
|
function_value_by_func_spec(env, FuncBorrowSpec::Some(*func_spec), hels.symbol)
|
||||||
env,
|
|
||||||
*func_spec,
|
|
||||||
hels.symbol,
|
|
||||||
hels.proc_layout.arguments,
|
|
||||||
Niche::NONE,
|
|
||||||
hels.proc_layout.result,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// morphic did not generate a specialization for this function,
|
// 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) => {
|
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||||
// Define only the return value size, since this is a thunk
|
// 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);
|
builder.build_store(output, call_result);
|
||||||
} else {
|
} else {
|
||||||
let call_result = call_roc_function(
|
let call_result = call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
evaluator,
|
evaluator,
|
||||||
|
@ -5571,73 +5640,43 @@ pub fn verify_fn(fn_val: FunctionValue<'_>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn function_value_by_func_spec<'a, 'ctx>(
|
pub(crate) fn function_value_by_func_spec<'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'_, 'ctx, '_>,
|
||||||
func_spec: FuncSpec,
|
func_spec: FuncBorrowSpec,
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
arguments: &[InLayout<'a>],
|
|
||||||
niche: Niche<'a>,
|
|
||||||
result: InLayout<'a>,
|
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
let fn_name = func_spec_name(env.arena, &env.interns, symbol, func_spec);
|
||||||
let fn_name = fn_name.as_str();
|
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>(
|
fn function_value_by_name_help<'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'_, 'ctx, '_>,
|
||||||
arguments: &[InLayout<'a>],
|
|
||||||
_niche: Niche<'a>,
|
|
||||||
result: InLayout<'a>,
|
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
) -> FunctionValue<'ctx> {
|
) -> FunctionValue<'ctx> {
|
||||||
env.module.get_function(fn_name).unwrap_or_else(|| {
|
env.module.get_function(fn_name).unwrap_or_else(|| {
|
||||||
if symbol.is_builtin() {
|
if symbol.is_builtin() {
|
||||||
eprintln!(
|
panic!("Unrecognized builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||||
"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:?})",)
|
|
||||||
} else {
|
} else {
|
||||||
// Unrecognized non-builtin function:
|
panic!("Unrecognized non-builtin function: {fn_name:?} (symbol: {symbol:?})")
|
||||||
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:?})",)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn roc_call_with_args<'a, 'ctx>(
|
fn roc_call_direct_with_args<'a, 'ctx>(
|
||||||
env: &Env<'a, 'ctx, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &STLayoutInterner<'a>,
|
layout_interner: &STLayoutInterner<'a>,
|
||||||
argument_layouts: &[InLayout<'a>],
|
|
||||||
result_layout: InLayout<'a>,
|
result_layout: InLayout<'a>,
|
||||||
name: LambdaName<'a>,
|
name: LambdaName<'a>,
|
||||||
func_spec: FuncSpec,
|
func_spec: FuncBorrowSpec,
|
||||||
arguments: &[BasicValueEnum<'ctx>],
|
arguments: &[BasicValueEnum<'ctx>],
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let fn_val = function_value_by_func_spec(
|
let fn_val = function_value_by_func_spec(env, func_spec, name.name());
|
||||||
env,
|
|
||||||
func_spec,
|
|
||||||
name.name(),
|
|
||||||
argument_layouts,
|
|
||||||
name.niche(),
|
|
||||||
result_layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
call_roc_function(
|
call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
fn_val,
|
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, '_>,
|
env: &Env<'a, 'ctx, '_>,
|
||||||
layout_interner: &STLayoutInterner<'a>,
|
layout_interner: &STLayoutInterner<'a>,
|
||||||
roc_function: FunctionValue<'ctx>,
|
roc_function: FunctionValue<'ctx>,
|
||||||
result_layout: LayoutRepr<'a>,
|
result_layout: LayoutRepr<'a>,
|
||||||
arguments: &[BasicValueEnum<'ctx>],
|
arguments: &[BasicValueEnum<'ctx>],
|
||||||
) -> 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) {
|
match RocReturn::from_layout(layout_interner, result_layout) {
|
||||||
RocReturn::ByPointer if !pass_by_pointer => {
|
RocReturn::ByPointer if !pass_by_pointer => {
|
||||||
|
@ -5667,14 +5762,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||||
|
|
||||||
arguments.push(result_alloca.into());
|
arguments.push(result_alloca.into());
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||||
roc_function.get_type().get_param_types().len(),
|
let call = build_call(&arguments);
|
||||||
arguments.len()
|
|
||||||
);
|
|
||||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
|
||||||
|
|
||||||
// roc functions should have the fast calling convention
|
// 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.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
env.builder
|
env.builder
|
||||||
|
@ -5689,14 +5780,10 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||||
|
|
||||||
arguments.push(result_alloca.into());
|
arguments.push(result_alloca.into());
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||||
roc_function.get_type().get_param_types().len(),
|
let call = build_call(&arguments);
|
||||||
arguments.len()
|
|
||||||
);
|
|
||||||
let call = env.builder.build_call(roc_function, &arguments, "call");
|
|
||||||
|
|
||||||
// roc functions should have the fast calling convention
|
// 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.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
if result_layout.is_passed_by_reference(layout_interner) {
|
if result_layout.is_passed_by_reference(layout_interner) {
|
||||||
|
@ -5710,25 +5797,18 @@ pub fn call_roc_function<'a, 'ctx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RocReturn::Return => {
|
RocReturn::Return => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(roc_function_type.get_param_types().len(), arguments.len());
|
||||||
roc_function.get_type().get_param_types().len(),
|
|
||||||
arguments.len()
|
|
||||||
);
|
|
||||||
let it = arguments.iter().map(|x| (*x).into());
|
let it = arguments.iter().map(|x| (*x).into());
|
||||||
let arguments = Vec::from_iter_in(it, env.arena);
|
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
|
// 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.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
call.try_as_basic_value().left().unwrap_or_else(|| {
|
call.try_as_basic_value()
|
||||||
panic!(
|
.left()
|
||||||
"LLVM error: Invalid call by name for name {:?}",
|
.unwrap_or_else(|| internal_error!("LLVM error: Invalid call by name",))
|
||||||
roc_function.get_name()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5905,7 +5985,7 @@ pub enum CCReturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct FunctionSpec<'ctx> {
|
pub(crate) struct FunctionSpec<'ctx> {
|
||||||
/// The function type
|
/// The function type
|
||||||
pub typ: FunctionType<'ctx>,
|
pub typ: FunctionType<'ctx>,
|
||||||
call_conv: u32,
|
call_conv: u32,
|
||||||
|
@ -5975,7 +6055,7 @@ impl<'ctx> FunctionSpec<'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fastcc calling convention
|
/// Fastcc calling convention
|
||||||
fn fastcc<'a, 'env>(
|
pub fn fastcc<'a, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
roc_return: RocReturn,
|
roc_return: RocReturn,
|
||||||
return_type: BasicTypeEnum<'ctx>,
|
return_type: BasicTypeEnum<'ctx>,
|
||||||
|
@ -6223,7 +6303,7 @@ fn build_foreign_symbol<'a, 'ctx>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
call_roc_function(
|
call_direct_roc_function(
|
||||||
env,
|
env,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
fastcc_function,
|
fastcc_function,
|
||||||
|
@ -6366,7 +6446,7 @@ fn get_foreign_symbol<'ctx>(
|
||||||
/// Add a function to a module, after asserting that the function is unique.
|
/// 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!
|
/// We never want to define the same function twice in the same module!
|
||||||
/// The result can be bugs that are difficult to track down.
|
/// The result can be bugs that are difficult to track down.
|
||||||
pub fn add_func<'ctx>(
|
pub(crate) fn add_func<'ctx>(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
|
@ -168,6 +168,8 @@ fn build_eq<'a, 'ctx>(
|
||||||
),
|
),
|
||||||
|
|
||||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closures"),
|
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(
|
LayoutRepr::Union(union_layout) => build_tag_eq(
|
||||||
env,
|
env,
|
||||||
|
@ -399,6 +401,8 @@ fn build_neq<'a, 'ctx>(
|
||||||
unreachable!("recursion pointers should never be compared directly")
|
unreachable!("recursion pointers should never be compared directly")
|
||||||
}
|
}
|
||||||
LayoutRepr::LambdaSet(_) => unreachable!("cannot compare closure"),
|
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);
|
env.builder.set_current_debug_location(di_location);
|
||||||
let call = env
|
let call = env
|
||||||
.builder
|
.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);
|
call.set_call_convention(FAST_CALL_CONV);
|
||||||
|
|
||||||
|
@ -1429,12 +1433,47 @@ fn build_box_eq_help<'a, 'ctx>(
|
||||||
"compare_pointers",
|
"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
|
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
|
// clear the tag_id so we get a pointer to the actual data
|
||||||
let box1 = box1.into_pointer_value();
|
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 crate::llvm::memcpy::build_memcpy;
|
||||||
use bumpalo::collections::Vec as AVec;
|
use bumpalo::collections::{CollectIn, Vec as AVec};
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
use inkwell::types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType};
|
||||||
use inkwell::values::PointerValue;
|
use inkwell::values::PointerValue;
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
round_up_to_alignment, Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner,
|
round_up_to_alignment, Builtin, FunctionPointer, InLayout, Layout, LayoutInterner, LayoutRepr,
|
||||||
UnionLayout,
|
STLayoutInterner, UnionLayout,
|
||||||
};
|
};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
|
||||||
|
@ -48,6 +49,22 @@ pub fn basic_type_from_layout<'a, 'ctx>(
|
||||||
.ptr_type(AddressSpace::default())
|
.ptr_type(AddressSpace::default())
|
||||||
.as_basic_type_enum(),
|
.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),
|
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::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use roc_builtins::bitcode;
|
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_module::symbol::Symbol;
|
||||||
use roc_mono::ir::LookupType;
|
use roc_mono::ir::LookupType;
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
|
@ -387,6 +387,8 @@ fn build_clone<'a, 'ctx>(
|
||||||
union_layout,
|
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);
|
let mut cases = Vec::with_capacity_in(other_tags.len(), env.arena);
|
||||||
|
|
||||||
for i in 0..other_tags.len() + 1 {
|
for i in 0..other_tags.len() + 1 {
|
||||||
if i == nullable_id as _ {
|
if i == nullable_id as usize {
|
||||||
continue;
|
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::{
|
build::{
|
||||||
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
|
cast_basic_basic, complex_bitcast_check_size, create_entry_block_alloca,
|
||||||
entry_block_alloca_zerofill, function_value_by_func_spec, load_roc_value,
|
function_value_by_func_spec, load_roc_value, roc_function_call, tag_pointer_clear_tag_id,
|
||||||
roc_function_call, tag_pointer_clear_tag_id, BuilderExt, RocReturn,
|
BuilderExt, FuncBorrowSpec, RocReturn,
|
||||||
},
|
},
|
||||||
build_list::{
|
build_list::{
|
||||||
list_append_unsafe, list_concat, list_drop_at, list_get_unsafe, list_len, list_map,
|
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()
|
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 => {
|
RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr | RefCountDecDataPtr => {
|
||||||
unreachable!("Not used in LLVM backend: {:?}", op);
|
unreachable!("Not used in LLVM backend: {:?}", op);
|
||||||
}
|
}
|
||||||
|
@ -1354,7 +1344,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
||||||
"cast_to_i8_ptr",
|
"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)
|
LayoutRepr::Union(union_layout)
|
||||||
if union_layout.stores_tag_id_in_pointer(env.target_info) =>
|
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)
|
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;
|
} = higher_order;
|
||||||
|
|
||||||
let PassedFunction {
|
let PassedFunction {
|
||||||
argument_layouts,
|
argument_layouts: _,
|
||||||
return_layout: result_layout,
|
return_layout: result_layout,
|
||||||
owns_captured_environment: function_owns_closure_data,
|
owns_captured_environment: function_owns_closure_data,
|
||||||
name: function_name,
|
name: function_name,
|
||||||
|
@ -2624,11 +2616,8 @@ pub(crate) fn run_higher_order_low_level<'a, 'ctx>(
|
||||||
() => {{
|
() => {{
|
||||||
let function = function_value_by_func_spec(
|
let function = function_value_by_func_spec(
|
||||||
env,
|
env,
|
||||||
func_spec,
|
FuncBorrowSpec::Some(func_spec),
|
||||||
function_name.name(),
|
function_name.name(),
|
||||||
argument_layouts,
|
|
||||||
function_name.niche(),
|
|
||||||
return_layout,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let (closure, closure_layout) =
|
let (closure, closure_layout) =
|
||||||
|
|
|
@ -11,6 +11,8 @@ mod lowlevel;
|
||||||
pub mod refcounting;
|
pub mod refcounting;
|
||||||
|
|
||||||
mod align;
|
mod align;
|
||||||
|
mod erased;
|
||||||
|
mod fn_ptr;
|
||||||
mod memcpy;
|
mod memcpy;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod struct_;
|
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