Merge remote-tracking branch 'remote/main' into upgrade-llvm-zig

This commit is contained in:
Luke Boswell 2024-09-05 12:11:51 +10:00
commit 99e2bc2038
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
253 changed files with 7403 additions and 4217 deletions

View file

@ -11,7 +11,7 @@ env:
# use .tar.gz for quick testing # use .tar.gz for quick testing
ARCHIVE_FORMAT: .tar.br ARCHIVE_FORMAT: .tar.br
# Make a new basic-cli git tag and set it here before starting this workflow # Make a new basic-cli git tag and set it here before starting this workflow
RELEASE_TAG: 0.12.0 RELEASE_TAG: 0.14.0
jobs: jobs:
prepare: prepare:
@ -34,14 +34,14 @@ jobs:
fi fi
# get latest nightly releases # get latest nightly releases
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-latest.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-latest.tar.gz
- 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
- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz #- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-latest.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-TESTING.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_arm64-TESTING.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-TESTING.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_x86_64-TESTING.tar.gz
#- run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-TESTING.tar.gz - run: curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-macos_apple_silicon-TESTING.tar.gz
- name: Save roc_nightly archives - name: Save roc_nightly archives
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@ -60,16 +60,16 @@ jobs:
- name: build basic-cli with surgical linker and also with legacy linker - name: build basic-cli with surgical linker and also with legacy linker
env: env:
CARGO_BUILD_TARGET: x86_64-unknown-linux-musl CARGO_BUILD_TARGET: x86_64-unknown-linux-musl
run: ./ci/build_basic_cli.sh linux_x86_64 "--linker legacy" run: ./ci/build_basic_cli.sh linux_x86_64
- name: Save .rh, .rm and .o file - name: Save .rh, .rm and .a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: linux-x86_64-files name: linux-x86_64-files
path: | path: |
basic-cli/platform/metadata_linux-x64.rm basic-cli/platform/metadata_linux-x64.rm
basic-cli/platform/linux-x64.rh basic-cli/platform/linux-x64.rh
basic-cli/platform/linux-x64.o basic-cli/platform/linux-x64.a
build-linux-arm64-files: build-linux-arm64-files:
@ -89,12 +89,12 @@ jobs:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-Clink-self-contained=yes -Clinker=rust-lld" CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-Clink-self-contained=yes -Clinker=rust-lld"
run: ./ci/build_basic_cli.sh linux_arm64 run: ./ci/build_basic_cli.sh linux_arm64
- name: Save .o file - name: Save .a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: linux-arm64-files name: linux-arm64-files
path: | path: |
basic-cli/platform/linux-arm64.o basic-cli/platform/linux-arm64.a
build-macos-x86_64-files: build-macos-x86_64-files:
runs-on: [macos-12] # I expect the generated files to work on macOS 12 and up runs-on: [macos-12] # I expect the generated files to work on macOS 12 and up
@ -107,15 +107,15 @@ jobs:
- run: ./ci/build_basic_cli.sh macos_x86_64 - run: ./ci/build_basic_cli.sh macos_x86_64
- name: Save .o files - name: Save .a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: macos-x86_64-files name: macos-x86_64-files
path: | path: |
basic-cli/platform/macos-x64.o basic-cli/platform/macos-x64.a
build-macos-apple-silicon-files: build-macos-apple-silicon-files:
name: build apple silicon .o file name: build apple silicon .a file
runs-on: [self-hosted, macOS, ARM64] runs-on: [self-hosted, macOS, ARM64]
needs: [prepare] needs: [prepare]
steps: steps:
@ -126,12 +126,12 @@ jobs:
- run: ./ci/build_basic_cli.sh macos_apple_silicon - run: ./ci/build_basic_cli.sh macos_apple_silicon
- name: Save macos-arm64.o file - name: Save macos-arm64.a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: macos-apple-silicon-files name: macos-apple-silicon-files
path: | path: |
basic-cli/platform/macos-arm64.o basic-cli/platform/macos-arm64.a
create-release-archive: create-release-archive:
needs: [build-linux-x86_64-files, build-linux-arm64-files, build-macos-x86_64-files, build-macos-apple-silicon-files] needs: [build-linux-x86_64-files, build-linux-arm64-files, build-macos-x86_64-files, build-macos-apple-silicon-files]
@ -250,10 +250,11 @@ jobs:
git checkout ${{ env.RELEASE_TAG }} git checkout ${{ env.RELEASE_TAG }}
cp -r examples ../.. cp -r examples ../..
cp -r ci ../.. cp -r ci ../..
cp -r LICENSE ../..
# LICENSE is necessary for command test # LICENSE is necessary for command test
cp -r LICENSE ../..
- name: run tests - name: run tests
run: | run: |
cd basic-cli-platform cd basic-cli-platform
ROC=./roc_nightly/roc EXAMPLES_DIR=./examples/ ROC_BUILD_FLAGS=--prebuilt-platform ./ci/all_tests.sh # no need to build platform anymore
NO_BUILD=1 ROC=./roc_nightly/roc EXAMPLES_DIR=./examples/ ./ci/all_tests.sh

View file

@ -10,7 +10,7 @@ concurrency:
env: env:
# use .tar.gz for quick testing # use .tar.gz for quick testing
ARCHIVE_FORMAT: .tar.br ARCHIVE_FORMAT: .tar.br
BASIC_WEBSERVER_BRANCH: main RELEASE_TAG: 0.8.0
jobs: jobs:
fetch-releases: fetch-releases:
@ -46,16 +46,16 @@ jobs:
- name: build basic-webserver with legacy linker - name: build basic-webserver with legacy linker
env: env:
CARGO_BUILD_TARGET: x86_64-unknown-linux-musl CARGO_BUILD_TARGET: x86_64-unknown-linux-musl
run: ./ci/build_basic_webserver.sh linux_x86_64 "--linker legacy" run: ./ci/build_basic_webserver.sh linux_x86_64
- name: Save .rh, .rm and .o file - name: Save .rh, .rm and .a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: linux-x86_64-files name: linux-x86_64-files
path: | path: |
basic-webserver/platform/metadata_linux-x64.rm basic-webserver/platform/metadata_linux-x64.rm
basic-webserver/platform/linux-x64.rh basic-webserver/platform/linux-x64.rh
basic-webserver/platform/linux-x64.o basic-webserver/platform/linux-x64.a
build-linux-arm64-files: build-linux-arm64-files:
@ -75,12 +75,12 @@ jobs:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-Clink-self-contained=yes -Clinker=rust-lld" CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_RUSTFLAGS: "-Clink-self-contained=yes -Clinker=rust-lld"
run: ./ci/build_basic_webserver.sh linux_arm64 run: ./ci/build_basic_webserver.sh linux_arm64
- name: Save .o file - name: Save .a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: linux-arm64-files name: linux-arm64-files
path: | path: |
basic-webserver/platform/linux-arm64.o basic-webserver/platform/linux-arm64.a
build-macos-x86_64-files: build-macos-x86_64-files:
runs-on: [macos-12] # I expect the generated files to work on macOS 12 and up runs-on: [macos-12] # I expect the generated files to work on macOS 12 and up
@ -93,15 +93,15 @@ jobs:
- run: ./ci/build_basic_webserver.sh macos_x86_64 - run: ./ci/build_basic_webserver.sh macos_x86_64
- name: Save .o files - name: Save .a files
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: macos-x86_64-files name: macos-x86_64-files
path: | path: |
basic-webserver/platform/macos-x64.o basic-webserver/platform/macos-x64.a
build-macos-apple-silicon-files: build-macos-apple-silicon-files:
name: build apple silicon .o file name: build apple silicon .a file
runs-on: [self-hosted, macOS, ARM64] runs-on: [self-hosted, macOS, ARM64]
needs: [fetch-releases] needs: [fetch-releases]
steps: steps:
@ -112,12 +112,12 @@ jobs:
- run: ./ci/build_basic_webserver.sh macos_apple_silicon - run: ./ci/build_basic_webserver.sh macos_apple_silicon
- name: Save macos-arm64.o file - name: Save macos-arm64.a file
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: macos-apple-silicon-files name: macos-apple-silicon-files
path: | path: |
basic-webserver/platform/macos-arm64.o basic-webserver/platform/macos-arm64.a
create-release-archive: create-release-archive:
needs: [build-linux-x86_64-files, build-linux-arm64-files, build-macos-x86_64-files, build-macos-apple-silicon-files] needs: [build-linux-x86_64-files, build-linux-arm64-files, build-macos-x86_64-files, build-macos-apple-silicon-files]
@ -147,7 +147,7 @@ jobs:
- run: | - run: |
git clone https://github.com/roc-lang/basic-webserver.git git clone https://github.com/roc-lang/basic-webserver.git
cd basic-webserver cd basic-webserver
git checkout ${{ env.BASIC_WEBSERVER_BRANCH }} git checkout ${{ env.RELEASE_TAG }}
cd .. cd ..
- run: cp macos-apple-silicon-files/* ./basic-webserver/platform - run: cp macos-apple-silicon-files/* ./basic-webserver/platform

View file

@ -1,5 +1,7 @@
on: on:
workflow_dispatch: workflow_dispatch:
# pull_request:
# TODO remove pull_request trigger
name: Docker images tests name: Docker images tests
@ -15,10 +17,10 @@ jobs:
run: cp docker/nightly-ubuntu-latest/docker-compose.example.yml docker/nightly-ubuntu-latest/docker-compose.yml run: cp docker/nightly-ubuntu-latest/docker-compose.example.yml docker/nightly-ubuntu-latest/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-ubuntu-latest/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc
nightly-ubuntu-2204: nightly-ubuntu-2204:
@ -32,10 +34,10 @@ jobs:
run: cp docker/nightly-ubuntu-2204/docker-compose.example.yml docker/nightly-ubuntu-2204/docker-compose.yml run: cp docker/nightly-ubuntu-2204/docker-compose.example.yml docker/nightly-ubuntu-2204/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-ubuntu-2204/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc
nightly-ubuntu-2004: nightly-ubuntu-2004:
name: nightly-ubuntu-2004 name: nightly-ubuntu-2004
@ -48,10 +50,10 @@ jobs:
run: cp docker/nightly-ubuntu-2004/docker-compose.example.yml docker/nightly-ubuntu-2004/docker-compose.yml run: cp docker/nightly-ubuntu-2004/docker-compose.example.yml docker/nightly-ubuntu-2004/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-ubuntu-2004/docker-compose.yml build run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc
nightly-debian-latest: nightly-debian-latest:
name: nightly-debian-latest name: nightly-debian-latest
@ -64,10 +66,10 @@ jobs:
run: cp docker/nightly-debian-latest/docker-compose.example.yml docker/nightly-debian-latest/docker-compose.yml run: cp docker/nightly-debian-latest/docker-compose.example.yml docker/nightly-debian-latest/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-debian-latest/docker-compose.yml build run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc
nightly-debian-bookworm: nightly-debian-bookworm:
name: nightly-debian-bookworm name: nightly-debian-bookworm
@ -80,10 +82,10 @@ jobs:
run: cp docker/nightly-debian-bookworm/docker-compose.example.yml docker/nightly-debian-bookworm/docker-compose.yml run: cp docker/nightly-debian-bookworm/docker-compose.example.yml docker/nightly-debian-bookworm/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-debian-bookworm/docker-compose.yml build run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc
nightly-debian-buster: nightly-debian-buster:
name: nightly-debian-buster name: nightly-debian-buster
@ -96,7 +98,7 @@ jobs:
run: cp docker/nightly-debian-buster/docker-compose.example.yml docker/nightly-debian-buster/docker-compose.yml run: cp docker/nightly-debian-buster/docker-compose.example.yml docker/nightly-debian-buster/docker-compose.yml
- name: Build image - name: Build image
run: docker-compose -f docker/nightly-debian-buster/docker-compose.yml build run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build
- name: Run hello world test - name: Run hello world test
run: docker-compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc run: docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc

View file

@ -9,7 +9,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ macos-12, macos-13, ubuntu-20.04, ubuntu-22.04 ] os: [ macos-12, macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 90 timeout-minutes: 90
steps: steps:

View file

@ -94,6 +94,21 @@ If so, you can fix it like so:
sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
``` ```
### libz libzstd libraries
You may see an error like this during builds:
```text
/usr/bin/ld: cannot find -lz: No such file or directory
/usr/bin/ld: cannot find -lzstd: No such file or directory
```
If so, you can fix it like so:
```sh
sudo apt-get install libz-dev libzstd-dev
```
### Zig ### Zig
**version: 0.11.0** **version: 0.11.0**

83
Cargo.lock generated
View file

@ -737,17 +737,6 @@ dependencies = [
"powerfmt", "powerfmt",
] ]
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@ -798,12 +787,6 @@ 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 = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "dunce" name = "dunce"
version = "1.0.4" version = "1.0.4"
@ -1715,11 +1698,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"flate2",
"hashbrown", "hashbrown",
"indexmap", "indexmap",
"memchr", "memchr",
"ruzstd",
] ]
[[package]] [[package]]
@ -2537,7 +2518,6 @@ dependencies = [
"roc_solve", "roc_solve",
"roc_target", "roc_target",
"roc_types", "roc_types",
"snafu",
"ven_pretty", "ven_pretty",
] ]
@ -2557,9 +2537,6 @@ version = "0.0.1"
[[package]] [[package]]
name = "roc_error_utils" name = "roc_error_utils"
version = "0.0.1" version = "0.0.1"
dependencies = [
"snafu",
]
[[package]] [[package]]
name = "roc_exhaustive" name = "roc_exhaustive"
@ -2650,7 +2627,6 @@ dependencies = [
name = "roc_glue" name = "roc_glue"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"backtrace",
"bumpalo", "bumpalo",
"cli_utils", "cli_utils",
"dircpy", "dircpy",
@ -2816,6 +2792,7 @@ dependencies = [
"roc_derive_key", "roc_derive_key",
"roc_error_macros", "roc_error_macros",
"roc_late_solve", "roc_late_solve",
"roc_lower_params",
"roc_module", "roc_module",
"roc_mono", "roc_mono",
"roc_packaging", "roc_packaging",
@ -2836,6 +2813,19 @@ dependencies = [
"ven_pretty", "ven_pretty",
] ]
[[package]]
name = "roc_lower_params"
version = "0.0.1"
dependencies = [
"bumpalo",
"roc_can",
"roc_collections",
"roc_module",
"roc_region",
"roc_solve_problem",
"roc_types",
]
[[package]] [[package]]
name = "roc_module" name = "roc_module"
version = "0.0.1" version = "0.0.1"
@ -2845,7 +2835,6 @@ dependencies = [
"roc_error_macros", "roc_error_macros",
"roc_ident", "roc_ident",
"roc_region", "roc_region",
"snafu",
"static_assertions", "static_assertions",
] ]
@ -3377,17 +3366,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "ruzstd"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58c4eb8a81997cf040a091d1f7e1938aeab6749d3a0dfa73af43cdc32393483d"
dependencies = [
"byteorder",
"derive_more",
"twox-hash",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.15" version = "1.0.15"
@ -3620,29 +3598,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "snafu"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
dependencies = [
"backtrace",
"doc-comment",
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.4.9" version = "0.4.9"
@ -4248,16 +4203,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "twox-hash"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"static_assertions",
]
[[package]] [[package]]
name = "typed-arena" name = "typed-arena"
version = "2.0.2" version = "2.0.2"

View file

@ -117,7 +117,7 @@ maplit = "1.0.2"
memmap2 = "0.5.10" memmap2 = "0.5.10"
mimalloc = { version = "0.1.34", default-features = false } mimalloc = { version = "0.1.34", default-features = false }
nonempty = "0.8.1" nonempty = "0.8.1"
object = { version = "0.32.2", features = ["read", "write"] } object = { version = "0.32.2", default-features = false, features = ["read", "write"] }
packed_struct = "0.10.1" packed_struct = "0.10.1"
page_size = "0.5.0" page_size = "0.5.0"
palette = "0.6.1" palette = "0.6.1"
@ -151,7 +151,6 @@ serde_json = "1.0.94" # update roc_std/Cargo.toml on change
serial_test = "1.0.0" serial_test = "1.0.0"
signal-hook = "0.3.15" signal-hook = "0.3.15"
smallvec = { version = "1.10.0", features = ["const_generics", "const_new"] } smallvec = { version = "1.10.0", features = ["const_generics", "const_new"] }
snafu = { version = "0.7.4", features = ["backtraces"] }
static_assertions = "1.1.0" # update roc_std/Cargo.toml on change static_assertions = "1.1.0" # update roc_std/Cargo.toml on change
strip-ansi-escapes = "0.1.1" strip-ansi-escapes = "0.1.1"
strum = { version = "0.24.1", features = ["derive"] } strum = { version = "0.24.1", features = ["derive"] }

View file

@ -17,10 +17,6 @@ if [ -n "$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$")" ]; then
done done
fi fi
if [[ "$(uname)" == "Darwin" ]]; then
brew install z3 # used by llvm
fi
# decompress the tar # decompress the tar
ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf

View file

@ -37,16 +37,23 @@ rm roc_nightly.tar.gz
mv roc_nightly* roc_nightly mv roc_nightly* roc_nightly
cd roc_nightly cd roc_nightly
export PATH="$(pwd -P):$PATH"
cd ..
# temp test
roc version
cd basic-cli
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if [[ $(uname -m) == "aarch64" ]]; then
target_arch="aarch64-unknown-linux-musl"
else
target_arch="x86_64-unknown-linux-musl"
fi
fi
./jump-start.sh
# build the basic cli platform # build the basic cli platform
./roc build ../basic-cli/examples/countdown.roc --optimize roc build.roc --prebuilt-platform
# We need this extra variable so we can safely check if $2 is empty later
EXTRA_ARGS=${2:-}
# In some rare cases it's nice to be able to use the legacy linker, so we produce the .o file to be able to do that
if [ -n "${EXTRA_ARGS}" ];
then ./roc build $EXTRA_ARGS ../basic-cli/examples/countdown.roc --optimize
fi
cd .. cd ..

View file

@ -4,6 +4,9 @@
set -euxo pipefail set -euxo pipefail
git clone https://github.com/roc-lang/basic-webserver.git git clone https://github.com/roc-lang/basic-webserver.git
cd basic-webserver
git checkout $RELEASE_TAG
cd ..
OS=$(uname -s) OS=$(uname -s)
ARCH=$(uname -m) ARCH=$(uname -m)
@ -37,20 +40,13 @@ rm roc_nightly.tar.gz
# simplify dir name # simplify dir name
mv roc_nightly* roc_nightly mv roc_nightly* roc_nightly
# add roc to PATH
cd roc_nightly cd roc_nightly
export PATH="$(pwd -P):$PATH"
cd ..
# prevent https://github.com/roc-lang/basic-webserver/issues/9 cd basic-webserver
if [ "$OS" != "Linux" ] || [ "$ARCH" != "x86_64" ]; then
# build the basic-webserver platform
./roc build ../basic-webserver/examples/echo.roc --optimize
fi
# We need this extra variable so we can safely check if $2 is empty later roc build.roc --prebuilt-platform
EXTRA_ARGS=${2:-}
# In some rare cases it's nice to be able to use the legacy linker, so we produce the .o file to be able to do that
if [ -n "${EXTRA_ARGS}" ];
then ./roc build $EXTRA_ARGS ../basic-webserver/examples/echo.roc --optimize
fi
cd .. cd ..

View file

@ -263,7 +263,7 @@ mod tests {
use std::io::Write; use std::io::Write;
use tempfile::{tempdir, TempDir}; use tempfile::{tempdir, TempDir};
const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import pf.Task import pf.Task
@ -271,7 +271,7 @@ import pf.Task
main = main =
Stdout.line! "I'm a Roc application!""#; Stdout.line! "I'm a Roc application!""#;
const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout

View file

@ -1279,8 +1279,8 @@ fn roc_dev_native(
break if libc::WIFEXITED(status) { break if libc::WIFEXITED(status) {
libc::WEXITSTATUS(status) libc::WEXITSTATUS(status)
} else { } else {
// we don't have an exit code, so we probably shouldn't make one up // we don't have an exit code, but something went wrong if we're in this else
0 1
}; };
} }
ChildProcessMsg::Expect => { ChildProcessMsg::Expect => {

View file

@ -1,6 +1,6 @@
interface AStar module [findPath, Model, initialModel, cheapestOpen, reconstructPath]
exposes [findPath, Model, initialModel, cheapestOpen, reconstructPath]
imports [Quicksort] import Quicksort
findPath = \costFn, moveFn, start, end -> findPath = \costFn, moveFn, start, end ->
astar costFn moveFn end (initialModel start) astar costFn moveFn end (initialModel start)
@ -92,9 +92,8 @@ astar = \costFn, moveFn, goal, model ->
modelWithNeighbors : Model position modelWithNeighbors : Model position
modelWithNeighbors = modelWithNeighbors =
{ modelPopped & modelPopped
openSet: Set.union modelPopped.openSet newNeighbors, |> &openSet (Set.union modelPopped.openSet newNeighbors)
}
walker : Model position, position -> Model position walker : Model position, position -> Model position
walker = \amodel, n -> updateCost current n amodel walker = \amodel, n -> updateCost current n amodel

View file

@ -1,4 +1,4 @@
interface Base64 exposes [fromBytes, fromStr, toBytes, toStr] imports [] module [fromBytes, fromStr, toBytes, toStr]
import Base64.Decode import Base64.Decode
import Base64.Encode import Base64.Encode

View file

@ -1,4 +1,4 @@
interface Base64.Decode exposes [fromBytes] imports [] module [fromBytes]
import Bytes.Decode exposing [ByteDecoder, DecodeProblem] import Bytes.Decode exposing [ByteDecoder, DecodeProblem]
@ -12,40 +12,39 @@ decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string:
loopHelp : { remaining : U64, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : U64, string : Str } Str) loopHelp : { remaining : U64, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : U64, string : Str } Str)
loopHelp = \{ remaining, string } -> loopHelp = \{ remaining, string } ->
if remaining >= 3 then if remaining >= 3 then
x, y, z <- Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8 \x, y, z ->
a : U32
a = Num.intCast x
b : U32
b = Num.intCast y
c : U32
c = Num.intCast z
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c
a : U32 Loop {
a = Num.intCast x remaining: remaining - 3,
b : U32 string: Str.concat string (bitsToChars combined 0),
b = Num.intCast y }
c : U32
c = Num.intCast z
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c
Loop {
remaining: remaining - 3,
string: Str.concat string (bitsToChars combined 0),
}
else if remaining == 0 then else if remaining == 0 then
Bytes.Decode.succeed (Done string) Bytes.Decode.succeed (Done string)
else if remaining == 2 then else if remaining == 2 then
x, y <- Bytes.Decode.map2 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.map2 Bytes.Decode.u8 Bytes.Decode.u8 \x, y ->
a : U32 a : U32
a = Num.intCast x a = Num.intCast x
b : U32 b : U32
b = Num.intCast y b = Num.intCast y
combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8) combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)
Done (Str.concat string (bitsToChars combined 1)) Done (Str.concat string (bitsToChars combined 1))
else else
# remaining = 1 # remaining = 1
x <- Bytes.Decode.map Bytes.Decode.u8 Bytes.Decode.map Bytes.Decode.u8 \x ->
a : U32 a : U32
a = Num.intCast x a = Num.intCast x
Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2)) Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2))
bitsToChars : U32, Int * -> Str bitsToChars : U32, Int * -> Str
bitsToChars = \bits, missing -> bitsToChars = \bits, missing ->

View file

@ -1,7 +1,4 @@
interface Base64.Encode module [toBytes]
exposes [toBytes]
imports []
import Bytes.Encode exposing [ByteEncoder] import Bytes.Encode exposing [ByteEncoder]

View file

@ -1,4 +1,4 @@
interface Bytes.Decode exposes [ByteDecoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3] imports [] module [ByteDecoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3]
State : { bytes : List U8, cursor : U64 } State : { bytes : List U8, cursor : U64 }

View file

@ -1,4 +1,4 @@
interface Bytes.Encode exposes [ByteEncoder, sequence, u8, u16, bytes, empty, encode] imports [] module [ByteEncoder, sequence, u8, u16, bytes, empty, encode]
Endianness : [BE, LE] Endianness : [BE, LE]

View file

@ -1,6 +1,4 @@
interface Issue2279Help module [text, asText]
exposes [text, asText]
imports []
text = "Hello, world!" text = "Hello, world!"

View file

@ -1,4 +1,4 @@
interface Quicksort exposes [sortBy, sortWith, show] imports [] module [sortBy, sortWith, show]
show : List I64 -> Str show : List I64 -> Str
show = \list -> show = \list ->

View file

@ -1,12 +1,16 @@
app "cfold" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task] import pf.PlatformTasks
provides [main] to pf
# adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs # adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs
main : Task.Task {} [] main : Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
@ -18,10 +22,10 @@ main =
|> Num.toStr |> Num.toStr
|> Str.concat " & " |> Str.concat " & "
|> Str.concat (Num.toStr optimized) |> Str.concat (Num.toStr optimized)
|> Task.putLine |> PlatformTasks.putLine
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
Expr : [ Expr : [
Add Expr Expr, Add Expr Expr,

View file

@ -1,18 +1,15 @@
app "closure" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task]
provides [main] to pf
# see https://github.com/roc-lang/roc/issues/985 # see https://github.com/roc-lang/roc/issues/985
main : Task.Task {} [] main : Task {} []
main = closure1 {} main = closure1 {}
# |> Task.after (\_ -> closure2 {}) # |> Task.after (\_ -> closure2 {})
# |> Task.after (\_ -> closure3 {}) # |> Task.after (\_ -> closure3 {})
# |> Task.after (\_ -> closure4 {}) # |> Task.after (\_ -> closure4 {})
# --- # ---
closure1 : {} -> Task.Task {} [] closure1 : {} -> Task {} []
closure1 = \_ -> closure1 = \_ ->
Task.succeed (foo toUnitBorrowed "a long string such that it's malloced") Task.ok (foo toUnitBorrowed "a long string such that it's malloced")
|> Task.map \_ -> {} |> Task.map \_ -> {}
toUnitBorrowed = \x -> Str.countUtf8Bytes x toUnitBorrowed = \x -> Str.countUtf8Bytes x

View file

@ -1,14 +1,18 @@
app "deriv" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task] import pf.PlatformTasks
provides [main] to pf
# based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs # based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs
IO a : Task.Task a [] IO a : Task a []
main : Task.Task {} [] main : Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
@ -22,14 +26,15 @@ main =
|> Task.map \_ -> {} |> Task.map \_ -> {}
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr
nestHelp = \s, f, m, x -> when m is nestHelp = \s, f, m, x ->
0 -> Task.succeed x when m is
_ -> 0 -> Task.ok x
w <- Task.after (f (s - m) x) _ ->
nestHelp s f (m - 1) w w = 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 -> nestHelp n f n e nest = \f, n, e -> nestHelp n f n e
@ -162,6 +167,5 @@ deriv = \i, f ->
Num.toStr (i + 1) Num.toStr (i + 1)
|> Str.concat " count: " |> Str.concat " count: "
|> Str.concat (Num.toStr (count fprime)) |> Str.concat (Num.toStr (count fprime))
PlatformTasks.putLine! line
Task.putLine line Task.ok fprime
|> Task.after \_ -> Task.succeed fprime

View file

@ -1,7 +1,7 @@
app "issue2279" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [Issue2279Help, pf.Task] import Issue2279Help
provides [main] to pf import pf.PlatformTasks
main = main =
text = text =
@ -10,4 +10,4 @@ main =
else else
Issue2279Help.asText 42 Issue2279Help.asText 42
Task.putLine text PlatformTasks.putLine text

View file

@ -1,20 +1,24 @@
app "nqueens" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task]
provides [main] to pf
main : Task.Task {} [] import pf.PlatformTasks
main : Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
queens n # original koka 13 queens n # original koka 13
|> Num.toStr |> Num.toStr
|> Task.putLine |> PlatformTasks.putLine
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
ConsList a : [Nil, Cons a (ConsList a)] ConsList a : [Nil, Cons a (ConsList a)]

View file

@ -1,10 +0,0 @@
hosted Effect
exposes [Effect, after, map, always, forever, loop, putLine, putInt, getInt]
imports []
generates Effect with [after, map, always, forever, loop]
putLine : Str -> Effect {}
putInt : I64 -> Effect {}
getInt : Effect { value : I64, isError : Bool }

View file

@ -0,0 +1,9 @@
hosted PlatformTasks
exposes [putLine, putInt, getInt]
imports []
putLine : Str -> Task {} *
putInt : I64 -> Task {} *
getInt : Task { value : I64, isError : Bool } *

View file

@ -1,88 +0,0 @@
interface Task
exposes [Task, succeed, fail, after, map, putLine, putInt, getInt, forever, loop, attempt]
imports [pf.Effect]
Task ok err : Effect.Effect (Result ok err)
forever : Task val err -> Task * err
forever = \task ->
looper = \{} ->
task
|> Effect.map
\res ->
when res is
Ok _ -> Step {}
Err e -> Done (Err e)
Effect.loop {} looper
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
loop = \state, step ->
looper = \current ->
step current
|> Effect.map
\res ->
when res is
Ok (Step newState) -> Step newState
Ok (Done result) -> Done (Ok result)
Err e -> Done (Err e)
Effect.loop state looper
succeed : val -> Task val *
succeed = \val ->
Effect.always (Ok val)
fail : err -> Task * err
fail = \val ->
Effect.always (Err val)
after : Task a err, (a -> Task b err) -> Task b err
after = \effect, transform ->
Effect.after
effect
\result ->
when result is
Ok a -> transform a
Err err -> Task.fail err
attempt : Task a b, (Result a b -> Task c d) -> Task c d
attempt = \task, transform ->
Effect.after
task
\result ->
when result is
Ok ok -> transform (Ok ok)
Err err -> transform (Err err)
map : Task a err, (a -> b) -> Task b err
map = \effect, transform ->
Effect.map
effect
\result ->
when result is
Ok a -> Ok (transform a)
Err err -> Err err
putLine : Str -> Task {} *
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
putInt : I64 -> Task {} *
putInt = \line -> Effect.map (Effect.putInt line) (\_ -> Ok {})
getInt : Task I64 [GetIntError]
getInt =
Effect.after
Effect.getInt
\{ isError, value } ->
if
isError
then
# TODO
# when errorCode is
# # A -> Task.fail InvalidCharacter
# # B -> Task.fail IOError
# _ ->
Task.fail GetIntError
else
Task.succeed value

View file

@ -2,7 +2,7 @@ platform "benchmarks"
requires {} { main : Task {} [] } requires {} { main : Task {} [] }
exposes [] exposes []
packages {} packages {}
imports [Task.{ Task }] imports []
provides [mainForHost] provides [mainForHost]
mainForHost : Task {} [] mainForHost : Task {} []

View file

@ -1,11 +1,16 @@
app "quicksortapp" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task, Quicksort] import pf.PlatformTasks
provides [main] to pf import Quicksort
main : Task.Task {} [] main : Task.Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
@ -19,10 +24,10 @@ main =
sort unsortedList sort unsortedList
|> Quicksort.show |> Quicksort.show
|> Task.putLine |> PlatformTasks.putLine
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
sort : List I64 -> List I64 sort : List I64 -> List I64
sort = \list -> sort = \list ->

View file

@ -1,7 +1,6 @@
app "rbtree-ck" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task] import pf.PlatformTasks
provides [main] to pf
Color : [Red, Black] Color : [Red, Black]
@ -38,9 +37,14 @@ fold = \f, tree, b ->
Leaf -> b Leaf -> b
Node _ l k v r -> fold f r (f k v (fold f l b)) Node _ l k v r -> fold f r (f k v (fold f l b))
main : Task.Task {} [] main : Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
@ -54,13 +58,13 @@ main =
val val
|> Num.toStr |> Num.toStr
|> Task.putLine |> PlatformTasks.putLine
Nil -> Nil ->
Task.putLine "fail" PlatformTasks.putLine "fail"
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
insert : Tree (Num k) v, Num k, v -> Tree (Num k) v insert : Tree (Num k) v, Num k, v -> Tree (Num k) v
insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v insert = \t, k, v -> if isRed t then setBlack (ins t k v) else ins t k v

View file

@ -1,7 +1,6 @@
app "rbtree-del" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task] import pf.PlatformTasks
provides [main] to pf
Color : [Red, Black] Color : [Red, Black]
@ -11,9 +10,14 @@ Map : Tree I64 Bool
ConsList a : [Nil, Cons a (ConsList a)] ConsList a : [Nil, Cons a (ConsList a)]
main : Task.Task {} [] main : Task {} []
main = main =
inputResult <- Task.attempt Task.getInt { value, isError } = PlatformTasks.getInt!
inputResult =
if isError then
Err GetIntError
else
Ok value
when inputResult is when inputResult is
Ok n -> Ok n ->
@ -22,10 +26,10 @@ main =
val val
|> Num.toStr |> Num.toStr
|> Task.putLine |> PlatformTasks.putLine
Err GetIntError -> Err GetIntError ->
Task.putLine "Error: Failed to get Integer from stdin." PlatformTasks.putLine "Error: Failed to get Integer from stdin."
boom : Str -> a boom : Str -> a
boom = \_ -> boom "" boom = \_ -> boom ""
@ -248,4 +252,4 @@ del = \t, k ->
rebalanceLeft cx lx ky vy ry rebalanceLeft cx lx ky vy ry
Delmin (Del ry Bool.false) ky vy -> Delmin (Del ry Bool.false) ky vy ->
Del (Node cx lx ky vy ry) Bool.false Del (Node cx lx ky vy ry) Bool.false

View file

@ -1,16 +1,15 @@
app "rbtree-insert" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task]
provides [main] to pf
main : Task.Task {} [] import pf.PlatformTasks
main : Task {} []
main = main =
tree : RedBlackTree I64 {} tree : RedBlackTree I64 {}
tree = insert 0 {} Empty tree = insert 0 {} Empty
tree tree
|> show |> show
|> Task.putLine |> PlatformTasks.putLine
show : RedBlackTree I64 {} -> Str show : RedBlackTree I64 {} -> Str
show = \tree -> showRBTree tree Num.toStr (\{} -> "{}") show = \tree -> showRBTree tree Num.toStr (\{} -> "{}")

View file

@ -1,20 +1,12 @@
app "test-astar" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task, AStar]
provides [main] to pf
main : Task.Task {} [] import pf.PlatformTasks
import AStar
#main : Task {} *
main = main =
Task.putLine (showBool test1) PlatformTasks.putLine! (showBool test1)
# Task.after Task.getInt \n ->
# when n is
# 1 ->
# Task.putLine (showBool test1)
#
# _ ->
# ns = Num.toStr n
# Task.putLine "No test $(ns)"
showBool : Bool -> Str showBool : Bool -> Str
showBool = \b -> showBool = \b ->
if if

View file

@ -1,18 +1,17 @@
app "test-base64" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task, Base64]
provides [main] to pf
IO a : Task.Task a [] import Base64
import pf.PlatformTasks
IO a : Task a []
main : IO {} main : IO {}
main = main =
when Base64.fromBytes (Str.toUtf8 "Hello World") is when Base64.fromBytes (Str.toUtf8 "Hello World") is
Err _ -> Task.putLine "sadness" Err _ -> PlatformTasks.putLine "sadness"
Ok encoded -> Ok encoded ->
Task.after PlatformTasks.putLine! (Str.concat "encoded: " encoded)
(Task.putLine (Str.concat "encoded: " encoded))
\_ -> when Base64.toStr encoded is
when Base64.toStr encoded is Ok decoded -> PlatformTasks.putLine (Str.concat "decoded: " decoded)
Ok decoded -> Task.putLine (Str.concat "decoded: " decoded) Err _ -> PlatformTasks.putLine "sadness"
Err _ -> Task.putLine "sadness"

View file

@ -1,7 +1,6 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import pf.Task exposing [Task]
main = main =
multipleIn = multipleIn =

View file

@ -1,19 +1,18 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin import pf.Stdin
import pf.Stdout import pf.Stdout
import pf.Task exposing [await, loop]
main = main =
_ <- await (Stdout.line "\nLet's count down from 3 together - all you have to do is press <ENTER>.") Stdout.line! "\nLet's count down from 3 together - all you have to do is press <ENTER>."
_ <- await Stdin.line _ = Stdin.line!
loop 3 tick Task.loop 3 tick
tick = \n -> tick = \n ->
if n == 0 then if n == 0 then
_ <- await (Stdout.line "🎉 SURPRISE! Happy Birthday! 🎂") Stdout.line! "🎉 SURPRISE! Happy Birthday! 🎂"
Task.ok (Done {}) Task.ok (Done {})
else else
_ <- await (n |> Num.toStr |> \s -> "$(s)..." |> Stdout.line) Stdout.line! (n |> Num.toStr |> \s -> "$(s)...")
_ <- await Stdin.line _ = Stdin.line!
Task.ok (Step (n - 1)) Task.ok (Step (n - 1))

View file

@ -1,11 +1,10 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin import pf.Stdin
import pf.Stdout import pf.Stdout
import pf.Task exposing [Task]
main = main =
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") Stdout.line! "🗣 Shout into this cave and hear the echo! 👂👂👂"
Task.loop {} tick Task.loop {} tick

View file

@ -1,9 +1,8 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import pf.Stderr import pf.Stderr
import pf.Env import pf.Env
import pf.Task exposing [Task]
main = main =
task = task =

View file

@ -1,7 +1,6 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import pf.Task exposing [Task]
import pf.File import pf.File
import pf.Path import pf.Path
import pf.Env import pf.Env

View file

@ -1,8 +1,7 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdin import pf.Stdin
import pf.Stdout import pf.Stdout
import pf.Task exposing [await, Task]
main = main =
Stdout.line! "What's your first name?" Stdout.line! "What's your first name?"

View file

@ -1,7 +1,6 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Http import pf.Http
import pf.Task exposing [Task]
import pf.Stdout import pf.Stdout
main = main =

View file

@ -1,4 +1,4 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import "test-file.txt" as testFile import "test-file.txt" as testFile

View file

@ -1,4 +1,4 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import "test-file.txt" as testFile : _ # the _ is optional import "test-file.txt" as testFile : _ # the _ is optional

View file

@ -1,4 +1,4 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout import pf.Stdout
import "ingested-file.roc" as ownCode : Str import "ingested-file.roc" as ownCode : Str

View file

@ -1,9 +1,8 @@
app [main] { app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
} }
import pf.Stdout import pf.Stdout
import pf.Task exposing [Task]
main = main =
file = strParam { name: "file" } file = strParam { name: "file" }
@ -12,7 +11,7 @@ main =
file, file,
count: numParam { name: "count" }, count: numParam { name: "count" },
doubled: numParam { name: "doubled" } doubled: numParam { name: "doubled" }
|> cliMap \d -> d * 2, |> cliMap \d -> d * 2,
} }
args = ["parse-args", "file.txt", "5", "7"] args = ["parse-args", "file.txt", "5", "7"]
@ -55,23 +54,23 @@ numParam = \{ name } ->
{ params: [param], parser } { params: [param], parser }
cliMap : ArgParser a, (a -> b) -> ArgParser b cliMap : ArgParser a, (a -> b) -> ArgParser b
cliMap = \{ params, parser }, mapper -> { cliMap = \{ params, parser }, mapper ->
params, mappedParser = \args ->
parser: \args -> (data, afterData) = parser? args
(data, afterData) <- parser args
|> Result.try
Ok (mapper data, afterData), Ok (mapper data, afterData)
}
{
params,
parser: mappedParser,
}
cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c
cliBuild = \firstWeaver, secondWeaver, combine -> cliBuild = \firstWeaver, secondWeaver, combine ->
allParams = List.concat firstWeaver.params secondWeaver.params allParams = List.concat firstWeaver.params secondWeaver.params
combinedParser = \args -> combinedParser = \args ->
(firstValue, afterFirst) <- firstWeaver.parser args (firstValue, afterFirst) = firstWeaver.parser? args
|> Result.try (secondValue, afterSecond) = secondWeaver.parser? afterFirst
(secondValue, afterSecond) <- secondWeaver.parser afterFirst
|> Result.try
Ok (combine firstValue secondValue, afterSecond) Ok (combine firstValue secondValue, afterSecond)

View file

@ -1,5 +1,5 @@
app [main] { app [main] {
cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
} }
@ -26,18 +26,17 @@ Letter : [A, B, C, Other]
letterParser : Parser (List U8) Letter letterParser : Parser (List U8) Letter
letterParser = letterParser =
input <- buildPrimitiveParser buildPrimitiveParser \input ->
valResult =
when input is
[] -> Err (ParsingFailure "Nothing to parse")
['A', ..] -> Ok A
['B', ..] -> Ok B
['C', ..] -> Ok C
_ -> Ok Other
valResult = valResult
when input is |> Result.map \val -> { val, input: List.dropFirst input 1 }
[] -> Err (ParsingFailure "Nothing to parse")
['A', ..] -> Ok A
['B', ..] -> Ok B
['C', ..] -> Ok C
_ -> Ok Other
valResult
|> Result.map \val -> { val, input: List.dropFirst input 1 }
expect expect
input = "B" input = "B"

View file

@ -1,11 +1,10 @@
app [main] { app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
} }
import pf.Stdout import pf.Stdout
import pf.Stderr import pf.Stderr
import pf.Task exposing [Task]
import parser.Core exposing [map, keep] import parser.Core exposing [map, keep]
import parser.String exposing [strFromUtf8] import parser.String exposing [strFromUtf8]
import parser.CSV import parser.CSV

View file

@ -10,8 +10,8 @@ extern crate roc_module;
#[cfg(test)] #[cfg(test)]
mod cli_run { mod cli_run {
use cli_utils::helpers::{ use cli_utils::helpers::{
extract_valgrind_errors, file_path_from_root, fixture_file, fixtures_dir, has_error, cli_testing_dir, extract_valgrind_errors, file_path_from_root, fixture_file, fixtures_dir,
known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError, has_error, known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError,
ValgrindErrorXWhat, ValgrindErrorXWhat,
}; };
use const_format::concatcp; use const_format::concatcp;
@ -23,6 +23,7 @@ mod cli_run {
use serial_test::serial; use serial_test::serial;
use std::iter; use std::iter;
use std::path::Path; use std::path::Path;
use std::process::ExitStatus;
#[cfg(all(unix, not(target_os = "macos")))] #[cfg(all(unix, not(target_os = "macos")))]
const ALLOW_VALGRIND: bool = true; const ALLOW_VALGRIND: bool = true;
@ -85,11 +86,11 @@ mod cli_run {
} }
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) { fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
let compile_out = run_roc( check_compile_error_with(CMD_CHECK, file, flags, expected);
[CMD_CHECK, file.to_str().unwrap()].iter().chain(flags), }
&[],
&[], fn check_compile_error_with(cmd: &str, file: &Path, flags: &[&str], expected: &str) {
); let compile_out = run_roc([cmd, file.to_str().unwrap()].iter().chain(flags), &[], &[]);
let err = compile_out.stdout.trim(); let err = compile_out.stdout.trim();
let err = strip_colors(err); let err = strip_colors(err);
@ -106,6 +107,11 @@ mod cli_run {
assert_multiline_str_eq!(err.as_str(), expected); assert_multiline_str_eq!(err.as_str(), expected);
} }
fn assert_valid_roc_check_status(status: ExitStatus) {
// 0 means no errors or warnings, 2 means just warnings
assert!(status.code().is_some_and(|code| code == 0 || code == 2))
}
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) { fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[], &[]); let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[], &[]);
@ -722,6 +728,200 @@ mod cli_run {
) )
} }
#[test]
#[cfg_attr(windows, ignore)]
fn module_params() {
test_roc_app(
"crates/cli/tests/module_params",
"app.roc",
&[],
&[],
&[],
indoc!(
r#"
App1.baseUrl: https://api.example.com/one
App2.baseUrl: http://api.example.com/two
App3.baseUrl: https://api.example.com/three
App1.getUser 1: https://api.example.com/one/users/1
App2.getUser 2: http://api.example.com/two/users/2
App3.getUser 3: https://api.example.com/three/users/3
App1.getPost 1: https://api.example.com/one/posts/1
App2.getPost 2: http://api.example.com/two/posts/2
App3.getPost 3: https://api.example.com/three/posts/3
App1.getPosts [1, 2]: ["https://api.example.com/one/posts/1", "https://api.example.com/one/posts/2"]
App2.getPosts [3, 4]: ["http://api.example.com/two/posts/3", "http://api.example.com/two/posts/4"]
App2.getPosts [5, 6]: ["http://api.example.com/two/posts/5", "http://api.example.com/two/posts/6"]
App1.getPostComments 1: https://api.example.com/one/posts/1/comments
App2.getPostComments 2: http://api.example.com/two/posts/2/comments
App2.getPostComments 3: http://api.example.com/two/posts/3/comments
App1.getCompanies [1, 2]: ["https://api.example.com/one/companies/1", "https://api.example.com/one/companies/2"]
App2.getCompanies [3, 4]: ["http://api.example.com/two/companies/3", "http://api.example.com/two/companies/4"]
App2.getCompanies [5, 6]: ["http://api.example.com/two/companies/5", "http://api.example.com/two/companies/6"]
App1.getPostAliased 1: https://api.example.com/one/posts/1
App2.getPostAliased 2: http://api.example.com/two/posts/2
App3.getPostAliased 3: https://api.example.com/three/posts/3
App1.baseUrlAliased: https://api.example.com/one
App2.baseUrlAliased: http://api.example.com/two
App3.baseUrlAliased: https://api.example.com/three
App1.getUserSafe 1: https://api.example.com/one/users/1
Prod.getUserSafe 2: http://api.example.com/prod_1/users/2?safe=true
usersApp1: ["https://api.example.com/one/users/1", "https://api.example.com/one/users/2", "https://api.example.com/one/users/3"]
getUserApp3Nested 3: https://api.example.com/three/users/3
usersApp3Passed: ["https://api.example.com/three/users/1", "https://api.example.com/three/users/2", "https://api.example.com/three/users/3"]
"#
),
UseValgrind::No,
TestCliCommands::Run,
);
}
#[test]
#[cfg_attr(windows, ignore)]
fn module_params_arity_mismatch() {
check_compile_error_with(
CMD_DEV,
&cli_testing_dir("/module_params/arity_mismatch.roc"),
&[],
indoc!(
r#"
TOO MANY ARGS in tests/module_params/arity_mismatch.roc
The getUser function expects 1 argument, but it got 2 instead:
12 $(Api.getUser 1 2)
^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
TOO MANY ARGS in tests/module_params/arity_mismatch.roc
This value is not a function, but it was given 1 argument:
13 $(Api.baseUrl 1)
^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
TOO FEW ARGS in tests/module_params/arity_mismatch.roc
The getPostComment function expects 2 arguments, but it got only 1:
16 $(Api.getPostComment 1)
^^^^^^^^^^^^^^^^^^
Roc does not allow functions to be partially applied. Use a closure to
make partial application explicit.
3 errors and 0 warnings found in <ignored for test> ms."#
),
);
}
#[test]
#[cfg_attr(windows, ignore)]
fn module_params_unexpected_fn() {
check_compile_error_with(
CMD_DEV,
&cli_testing_dir("/module_params/unexpected_fn.roc"),
&[],
indoc!(
r#"
TYPE MISMATCH in tests/module_params/unexpected_fn.roc
This argument to this string interpolation has an unexpected type:
11 $(Api.getPost)
^^^^^^^^^^^
The argument is an anonymous function of type:
U32 -> Str
But this string interpolation needs its argument to be:
Str
1 error and 0 warnings found in <ignored for test> ms."#
),
)
}
#[test]
#[cfg_attr(windows, ignore)]
fn module_params_bad_ann() {
check_compile_error_with(
CMD_DEV,
&cli_testing_dir("/module_params/bad_ann.roc"),
&[],
indoc!(
r#"
TYPE MISMATCH in tests/module_params/BadAnn.roc
Something is off with the body of the fnAnnotatedAsValue definition:
3 fnAnnotatedAsValue : Str
4> fnAnnotatedAsValue = /postId, commentId ->
5> "/posts/$(postId)/comments/$(Num.toStr commentId)"
The body is an anonymous function of type:
Str, Num * -> Str
But the type annotation on fnAnnotatedAsValue says it should be:
Str
TYPE MISMATCH in tests/module_params/BadAnn.roc
Something is off with the body of the missingArg definition:
7 missingArg : Str -> Str
8> missingArg = /postId, _ ->
9> "/posts/$(postId)/comments"
The body is an anonymous function of type:
(Str, ? -> Str)
But the type annotation on missingArg says it should be:
(Str -> Str)
Tip: It looks like it takes too many arguments. I'm seeing 1 extra.
2 errors and 1 warning found in <ignored for test> ms."#
),
);
}
#[test]
#[cfg_attr(windows, ignore)]
fn module_params_pass_task() {
test_roc_app(
"crates/cli/tests/module_params",
"pass_task.roc",
&[],
&[],
&[],
indoc!(
r#"
Hi, Agus!
"#
),
UseValgrind::No,
TestCliCommands::Run,
);
}
#[test] #[test]
#[cfg_attr(windows, ignore)] #[cfg_attr(windows, ignore)]
fn transitive_expects() { fn transitive_expects() {
@ -803,7 +1003,7 @@ mod cli_run {
fn check_virtual_dom_server() { fn check_virtual_dom_server() {
let path = file_path_from_root("examples/virtual-dom-wip", "example-server.roc"); let path = file_path_from_root("examples/virtual-dom-wip", "example-server.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
// TODO: write a new test once mono bugs are resolved in investigation // TODO: write a new test once mono bugs are resolved in investigation
@ -812,7 +1012,7 @@ mod cli_run {
fn check_virtual_dom_client() { fn check_virtual_dom_client() {
let path = file_path_from_root("examples/virtual-dom-wip", "example-client.roc"); let path = file_path_from_root("examples/virtual-dom-wip", "example-client.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -821,7 +1021,7 @@ mod cli_run {
fn cli_countdown_check() { fn cli_countdown_check() {
let path = file_path_from_root("crates/cli/tests/cli", "countdown.roc"); let path = file_path_from_root("crates/cli/tests/cli", "countdown.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -830,7 +1030,7 @@ mod cli_run {
fn cli_echo_check() { fn cli_echo_check() {
let path = file_path_from_root("crates/cli/tests/cli", "echo.roc"); let path = file_path_from_root("crates/cli/tests/cli", "echo.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -839,7 +1039,7 @@ mod cli_run {
fn cli_file_check() { fn cli_file_check() {
let path = file_path_from_root("crates/cli/tests/cli", "fileBROKEN.roc"); let path = file_path_from_root("crates/cli/tests/cli", "fileBROKEN.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -848,7 +1048,8 @@ mod cli_run {
fn cli_form_check() { fn cli_form_check() {
let path = file_path_from_root("crates/cli/tests/cli", "form.roc"); let path = file_path_from_root("crates/cli/tests/cli", "form.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); dbg!(out.stdout, out.stderr);
assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -857,7 +1058,7 @@ mod cli_run {
fn cli_http_get_check() { fn cli_http_get_check() {
let path = file_path_from_root("crates/cli/tests/cli", "http-get.roc"); let path = file_path_from_root("crates/cli/tests/cli", "http-get.roc");
let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]);
assert!(out.status.success()); assert_valid_roc_check_status(out.status);
} }
#[test] #[test]
@ -891,8 +1092,8 @@ mod cli_run {
) )
} }
#[ignore = "likely broken because of alias analysis: https://github.com/roc-lang/roc/issues/6544"]
#[test] #[test]
#[cfg_attr(any(target_os = "windows", target_os = "linux"), ignore = "Segfault")]
fn false_interpreter() { fn false_interpreter() {
test_roc_app( test_roc_app(
"examples/cli/false-interpreter", "examples/cli/false-interpreter",
@ -1482,9 +1683,9 @@ mod cli_run {
Something is off with the body of the main definition: Something is off with the body of the main definition:
6 main : Str -> Task {} [] 3 main : Str -> Task {} []
7 main = /_ -> 4 main = /_ ->
8 "this is a string, not a Task {} [] function like the platform expects." 5 "this is a string, not a Task {} [] function like the platform expects."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The body is a string of type: The body is a string of type:
@ -1493,7 +1694,7 @@ mod cli_run {
But the type annotation on main says it should be: But the type annotation on main says it should be:
Effect.Effect (Result {} []) Task {} []
Tip: Add type annotations to functions or values to help you figure Tip: Add type annotations to functions or values to help you figure
this out. this out.
@ -1572,30 +1773,6 @@ mod cli_run {
); );
} }
#[test]
fn unknown_generates_with() {
check_compile_error(
&known_bad_file("UnknownGeneratesWith.roc"),
&[],
indoc!(
r#"
UNKNOWN GENERATES FUNCTION in tests/known_bad/UnknownGeneratesWith.roc
I don't know how to generate the foobar function.
4 generates Effect with [after, map, always, foobar]
^^^^^^
Only specific functions like `after` and `map` can be generated.Learn
more about hosted modules at TODO.
1 error and 0 warnings found in <ignored for test> ms."#
),
);
}
#[test] #[test]
fn format_check_good() { fn format_check_good() {
check_format_check_as_expected(&fixture_file("format", "Formatted.roc"), true); check_format_check_as_expected(&fixture_file("format", "Formatted.roc"), true);

View file

@ -1,8 +1,5 @@
app "type-error" app [main] { pf: platform "../../../../examples/cli/false-interpreter/platform/main.roc" }
packages { pf: "../../../../examples/cli/false-interpreter/platform/main.roc" }
imports [pf.Task.{ Task }]
provides [main] to pf
main : Str -> Task {} [] main : Str -> Task {} []
main = \_ -> main = \_ ->
"this is a string, not a Task {} [] function like the platform expects." "this is a string, not a Task {} [] function like the platform expects."

View file

@ -1,4 +0,0 @@
hosted UnknownGeneratesWith
exposes [Effect, after, map, always]
imports []
generates Effect with [after, map, always, foobar]

View file

@ -0,0 +1,69 @@
module { appId, protocol } -> [
baseUrl,
getUser,
getPost,
getPosts,
getPostComments,
getCompanies,
baseUrlAliased,
getPostAliased,
getUserSafe,
getPostComment,
]
## value def referencing params
baseUrl : Str
baseUrl =
protocol "api.example.com/$(appId)"
## function def referencing params
getUser : U32 -> Str
getUser = \userId ->
# purposefully not using baseUrl to test top-level fn referencing param
protocol "api.example.com/$(appId)/users/$(Num.toStr userId)"
## function def referencing top-level value
getPost : U32 -> Str
getPost = \postId ->
"$(baseUrl)/posts/$(Num.toStr postId)"
## function def passing top-level function
getPosts : List U32 -> List Str
getPosts = \ids ->
List.map ids getPost
## function def calling top-level function
getPostComments : U32 -> Str
getPostComments = \postId ->
"$(getPost postId)/comments"
## function def passing nested function
getCompanies : List U32 -> List Str
getCompanies = \ids ->
getCompany = \id ->
protocol "api.example.com/$(appId)/companies/$(Num.toStr id)"
List.map ids getCompany
## aliasing top-level value
baseUrlAliased : Str
baseUrlAliased =
baseUrl
## aliasing top-level fn
getPostAliased : U32 -> Str
getPostAliased =
getPost
## top-level value returning functions
getUserSafe : U32 -> Str
getUserSafe =
if Str.startsWith appId "prod_" then
\id -> "$(getUser id)?safe=true"
else
getUser
## two-argument function
getPostComment : U32, U32 -> Str
getPostComment = \postId, commentId ->
"$(getPost postId)/comments/$(Num.toStr commentId)"

View file

@ -0,0 +1,9 @@
module { appId } -> [fnAnnotatedAsValue, missingArg]
fnAnnotatedAsValue : Str
fnAnnotatedAsValue = \postId, commentId ->
"/posts/$(postId)/comments/$(Num.toStr commentId)"
missingArg : Str -> Str
missingArg = \postId, _ ->
"/posts/$(postId)/comments"

View file

@ -0,0 +1,7 @@
module { echo } -> [menu]
menu = \name ->
indirect name
indirect = \name ->
echo "Hi, $(name)!"

View file

@ -0,0 +1,59 @@
app [main] {
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
}
import Api { appId: "one", protocol: https } as App1
import Api { appId: "two", protocol: http } as App2
import Api { appId: "prod_1", protocol: http } as Prod
https = \url -> "https://$(url)"
http = \url -> "http://$(url)"
usersApp1 =
# pass top-level fn in a module with params
List.map [1, 2, 3] App1.getUser
main =
app3Id = "three"
import Api { appId: app3Id, protocol: https } as App3
getUserApp3Nested = \userId ->
# use captured params def
App3.getUser userId
usersApp3Passed =
# pass top-level fn in a nested def
List.map [1, 2, 3] App3.getUser
"""
App1.baseUrl: $(App1.baseUrl)
App2.baseUrl: $(App2.baseUrl)
App3.baseUrl: $(App3.baseUrl)
App1.getUser 1: $(App1.getUser 1)
App2.getUser 2: $(App2.getUser 2)
App3.getUser 3: $(App3.getUser 3)
App1.getPost 1: $(App1.getPost 1)
App2.getPost 2: $(App2.getPost 2)
App3.getPost 3: $(App3.getPost 3)
App1.getPosts [1, 2]: $(Inspect.toStr (App1.getPosts [1, 2]))
App2.getPosts [3, 4]: $(Inspect.toStr (App2.getPosts [3, 4]))
App2.getPosts [5, 6]: $(Inspect.toStr (App2.getPosts [5, 6]))
App1.getPostComments 1: $(App1.getPostComments 1)
App2.getPostComments 2: $(App2.getPostComments 2)
App2.getPostComments 3: $(App2.getPostComments 3)
App1.getCompanies [1, 2]: $(Inspect.toStr (App1.getCompanies [1, 2]))
App2.getCompanies [3, 4]: $(Inspect.toStr (App2.getCompanies [3, 4]))
App2.getCompanies [5, 6]: $(Inspect.toStr (App2.getCompanies [5, 6]))
App1.getPostAliased 1: $(App1.getPostAliased 1)
App2.getPostAliased 2: $(App2.getPostAliased 2)
App3.getPostAliased 3: $(App3.getPostAliased 3)
App1.baseUrlAliased: $(App1.baseUrlAliased)
App2.baseUrlAliased: $(App2.baseUrlAliased)
App3.baseUrlAliased: $(App3.baseUrlAliased)
App1.getUserSafe 1: $(App1.getUserSafe 1)
Prod.getUserSafe 2: $(Prod.getUserSafe 2)
usersApp1: $(Inspect.toStr usersApp1)
getUserApp3Nested 3: $(getUserApp3Nested 3)
usersApp3Passed: $(Inspect.toStr usersApp3Passed)
"""

View file

@ -0,0 +1,17 @@
app [main] {
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
}
import Api { appId: "one", protocol: https }
https = \url -> "https://$(url)"
main =
"""
# too many args
$(Api.getUser 1 2)
$(Api.baseUrl 1)
# too few args
$(Api.getPostComment 1)
"""

View file

@ -0,0 +1,8 @@
app [main] {
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
}
import BadAnn { appId: "one" }
main =
""

View file

@ -0,0 +1,7 @@
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" }
import pf.Stdout
import Menu { echo: Stdout.line }
main =
Menu.menu "Agus"

View file

@ -0,0 +1,12 @@
app [main] {
pf: platform "../fixtures/multi-dep-str/platform/main.roc",
}
import Api { appId: "one", protocol: https }
https = \url -> "https://$(url)"
main =
"""
$(Api.getPost)
"""

View file

@ -1129,7 +1129,7 @@ fn link_macos(
Architecture::Aarch64 => { Architecture::Aarch64 => {
ld_child.wait()?; ld_child.wait()?;
let mut codesign_cmd = Command::new("codesign"); let mut codesign_cmd = Command::new("/usr/bin/codesign");
codesign_cmd.args(["-s", "-", output_path.to_str().unwrap()]); codesign_cmd.args(["-s", "-", output_path.to_str().unwrap()]);
debug_print_command(&codesign_cmd); debug_print_command(&codesign_cmd);
let codesign_child = codesign_cmd.spawn()?; let codesign_child = codesign_cmd.spawn()?;

View file

@ -129,8 +129,8 @@ hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
toInspectorDict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter toInspectorDict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter
toInspectorDict = \dict -> toInspectorDict = \dict ->
fmt <- Inspect.custom Inspect.custom \fmt ->
Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt
## Return an empty dictionary. ## Return an empty dictionary.
## ```roc ## ```roc
@ -894,9 +894,9 @@ calcNumBuckets = \shifts ->
maxBucketCount maxBucketCount
fillBucketsFromData = \buckets0, data, shifts -> fillBucketsFromData = \buckets0, data, shifts ->
buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 List.walkWithIndex data buckets0 \buckets1, (key, _), dataIndex ->
(bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts
placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex
nextWhileLess : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq nextWhileLess : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq
nextWhileLess = \buckets, key, shifts -> nextWhileLess = \buckets, key, shifts ->
@ -1213,15 +1213,15 @@ expect
] ]
dict = dict =
acc, k <- List.walk badKeys (Dict.empty {}) List.walk badKeys (Dict.empty {}) \acc, k ->
Dict.update acc k \val -> Dict.update acc k \val ->
when val is when val is
Present p -> Present (p |> Num.addWrap 1) Present p -> Present (p |> Num.addWrap 1)
Missing -> Present 0 Missing -> Present 0
allInsertedCorrectly = allInsertedCorrectly =
acc, k <- List.walk badKeys Bool.true List.walk badKeys Bool.true \acc, k ->
acc && Dict.contains dict k acc && Dict.contains dict k
allInsertedCorrectly allInsertedCorrectly

View file

@ -138,203 +138,203 @@ dbgInit = \{} -> @DbgFormatter { data: "" }
dbgList : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgList : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgList = \content, walkFn, toDbgInspector -> dbgList = \content, walkFn, toDbgInspector ->
f0 <- custom custom \f0 ->
dbgWrite f0 "[" dbgWrite f0 "["
|> \f1 -> |> \f1 ->
(f2, prependSep), elem <- walkFn content (f1, Bool.false) walkFn content (f1, Bool.false) \(f2, prependSep), elem ->
f3 = f3 =
if prependSep then if prependSep then
dbgWrite f2 ", " dbgWrite f2 ", "
else else
f2 f2
elem elem
|> toDbgInspector |> toDbgInspector
|> apply f3 |> apply f3
|> \f4 -> (f4, Bool.true) |> \f4 -> (f4, Bool.true)
|> .0 |> .0
|> dbgWrite "]" |> dbgWrite "]"
dbgSet : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgSet : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgSet = \content, walkFn, toDbgInspector -> dbgSet = \content, walkFn, toDbgInspector ->
f0 <- custom custom \f0 ->
dbgWrite f0 "{" dbgWrite f0 "{"
|> \f1 -> |> \f1 ->
(f2, prependSep), elem <- walkFn content (f1, Bool.false) walkFn content (f1, Bool.false) \(f2, prependSep), elem ->
f3 = f3 =
if prependSep then if prependSep then
dbgWrite f2 ", " dbgWrite f2 ", "
else else
f2 f2
elem elem
|> toDbgInspector |> toDbgInspector
|> apply f3 |> apply f3
|> \f4 -> (f4, Bool.true) |> \f4 -> (f4, Bool.true)
|> .0 |> .0
|> dbgWrite "}" |> dbgWrite "}"
dbgDict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgDict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbgDict = \d, walkFn, keyToInspector, valueToInspector -> dbgDict = \d, walkFn, keyToInspector, valueToInspector ->
f0 <- custom custom \f0 ->
dbgWrite f0 "{" dbgWrite f0 "{"
|> \f1 -> |> \f1 ->
(f2, prependSep), key, value <- walkFn d (f1, Bool.false) walkFn d (f1, Bool.false) \(f2, prependSep), key, value ->
f3 = f3 =
if prependSep then if prependSep then
dbgWrite f2 ", " dbgWrite f2 ", "
else else
f2 f2
apply (keyToInspector key) f3 apply (keyToInspector key) f3
|> dbgWrite ": " |> dbgWrite ": "
|> \x -> apply (valueToInspector value) x |> \x -> apply (valueToInspector value) x
|> \f4 -> (f4, Bool.true) |> \f4 -> (f4, Bool.true)
|> .0 |> .0
|> dbgWrite "}" |> dbgWrite "}"
dbgTag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter dbgTag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbgTag = \name, fields -> dbgTag = \name, fields ->
if List.isEmpty fields then if List.isEmpty fields then
f0 <- custom custom \f0 ->
dbgWrite f0 name dbgWrite f0 name
else else
f0 <- custom custom \f0 ->
dbgWrite f0 "(" dbgWrite f0 "("
|> dbgWrite name |> dbgWrite name
|> \f1 -> |> \f1 ->
f2, inspector <- List.walk fields f1 List.walk fields f1 \f2, inspector ->
dbgWrite f2 " " dbgWrite f2 " "
|> \x -> apply inspector x |> \x -> apply inspector x
|> dbgWrite ")" |> dbgWrite ")"
dbgTuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter dbgTuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbgTuple = \fields -> dbgTuple = \fields ->
f0 <- custom custom \f0 ->
dbgWrite f0 "(" dbgWrite f0 "("
|> \f1 -> |> \f1 ->
(f2, prependSep), inspector <- List.walk fields (f1, Bool.false) List.walk fields (f1, Bool.false) \(f2, prependSep), inspector ->
f3 = f3 =
if prependSep then if prependSep then
dbgWrite f2 ", " dbgWrite f2 ", "
else else
f2 f2
apply inspector f3 apply inspector f3
|> \f4 -> (f4, Bool.true) |> \f4 -> (f4, Bool.true)
|> .0 |> .0
|> dbgWrite ")" |> dbgWrite ")"
dbgRecord : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter dbgRecord : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter
dbgRecord = \fields -> dbgRecord = \fields ->
f0 <- custom custom \f0 ->
dbgWrite f0 "{" dbgWrite f0 "{"
|> \f1 -> |> \f1 ->
(f2, prependSep), { key, value } <- List.walk fields (f1, Bool.false) List.walk fields (f1, Bool.false) \(f2, prependSep), { key, value } ->
f3 = f3 =
if prependSep then if prependSep then
dbgWrite f2 ", " dbgWrite f2 ", "
else else
f2 f2
dbgWrite f3 key dbgWrite f3 key
|> dbgWrite ": " |> dbgWrite ": "
|> \x -> apply value x |> \x -> apply value x
|> \f4 -> (f4, Bool.true) |> \f4 -> (f4, Bool.true)
|> .0 |> .0
|> dbgWrite "}" |> dbgWrite "}"
dbgBool : Bool -> Inspector DbgFormatter dbgBool : Bool -> Inspector DbgFormatter
dbgBool = \b -> dbgBool = \b ->
if b then if b then
f0 <- custom custom \f0 ->
dbgWrite f0 "Bool.true" dbgWrite f0 "Bool.true"
else else
f0 <- custom custom \f0 ->
dbgWrite f0 "Bool.false" dbgWrite f0 "Bool.false"
dbgStr : Str -> Inspector DbgFormatter dbgStr : Str -> Inspector DbgFormatter
dbgStr = \s -> dbgStr = \s ->
f0 <- custom custom \f0 ->
f0 f0
|> dbgWrite "\"" |> dbgWrite "\""
|> dbgWrite s # TODO: Should we be escaping strings for dbg/logging? |> dbgWrite s # TODO: Should we be escaping strings for dbg/logging?
|> dbgWrite "\"" |> dbgWrite "\""
dbgOpaque : * -> Inspector DbgFormatter dbgOpaque : * -> Inspector DbgFormatter
dbgOpaque = \_ -> dbgOpaque = \_ ->
f0 <- custom custom \f0 ->
dbgWrite f0 "<opaque>" dbgWrite f0 "<opaque>"
dbgFunction : * -> Inspector DbgFormatter dbgFunction : * -> Inspector DbgFormatter
dbgFunction = \_ -> dbgFunction = \_ ->
f0 <- custom custom \f0 ->
dbgWrite f0 "<function>" dbgWrite f0 "<function>"
dbgU8 : U8 -> Inspector DbgFormatter dbgU8 : U8 -> Inspector DbgFormatter
dbgU8 = \num -> dbgU8 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgI8 : I8 -> Inspector DbgFormatter dbgI8 : I8 -> Inspector DbgFormatter
dbgI8 = \num -> dbgI8 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgU16 : U16 -> Inspector DbgFormatter dbgU16 : U16 -> Inspector DbgFormatter
dbgU16 = \num -> dbgU16 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgI16 : I16 -> Inspector DbgFormatter dbgI16 : I16 -> Inspector DbgFormatter
dbgI16 = \num -> dbgI16 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgU32 : U32 -> Inspector DbgFormatter dbgU32 : U32 -> Inspector DbgFormatter
dbgU32 = \num -> dbgU32 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgI32 : I32 -> Inspector DbgFormatter dbgI32 : I32 -> Inspector DbgFormatter
dbgI32 = \num -> dbgI32 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgU64 : U64 -> Inspector DbgFormatter dbgU64 : U64 -> Inspector DbgFormatter
dbgU64 = \num -> dbgU64 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgI64 : I64 -> Inspector DbgFormatter dbgI64 : I64 -> Inspector DbgFormatter
dbgI64 = \num -> dbgI64 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgU128 : U128 -> Inspector DbgFormatter dbgU128 : U128 -> Inspector DbgFormatter
dbgU128 = \num -> dbgU128 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgI128 : I128 -> Inspector DbgFormatter dbgI128 : I128 -> Inspector DbgFormatter
dbgI128 = \num -> dbgI128 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgF32 : F32 -> Inspector DbgFormatter dbgF32 : F32 -> Inspector DbgFormatter
dbgF32 = \num -> dbgF32 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgF64 : F64 -> Inspector DbgFormatter dbgF64 : F64 -> Inspector DbgFormatter
dbgF64 = \num -> dbgF64 = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgDec : Dec -> Inspector DbgFormatter dbgDec : Dec -> Inspector DbgFormatter
dbgDec = \num -> dbgDec = \num ->
f0 <- custom custom \f0 ->
dbgWrite f0 (num |> Num.toStr) dbgWrite f0 (num |> Num.toStr)
dbgWrite : DbgFormatter, Str -> DbgFormatter dbgWrite : DbgFormatter, Str -> DbgFormatter
dbgWrite = \@DbgFormatter { data }, added -> dbgWrite = \@DbgFormatter { data }, added ->

View file

@ -62,8 +62,8 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
toInspectorSet = \set -> toInspectorSet = \set ->
fmt <- Inspect.custom Inspect.custom \fmt ->
Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt
## Creates a new empty `Set`. ## Creates a new empty `Set`.
## ```roc ## ```roc

View file

@ -0,0 +1,265 @@
module [
Task,
ok,
err,
await,
map,
mapErr,
onErr,
attempt,
forever,
loop,
fromResult,
batch,
sequence,
forEach,
result,
]
import List
import Result exposing [Result]
## A Task represents an effect; an interaction with state outside your Roc
## program, such as the terminal's standard output, or a file.
Task ok err := {} -> Result ok err
## Run a task repeatedly, until it fails with `err`. Note that this task does not return a success value.
forever : Task a err -> Task * err
forever = \@Task task ->
looper = \{} ->
when task {} is
Err e -> Err e
Ok _ -> looper {}
@Task \{} -> looper {}
## Run a task repeatedly, until it fails with `err` or completes with `done`.
##
## ```
## sum =
## Task.loop! 0 \total ->
## numResult =
## Stdin.line
## |> Task.result!
## |> Result.try Str.toU64
##
## when numResult is
## Ok num -> Task.ok (Step (total + num))
## Err (StdinErr EndOfFile) -> Task.ok (Done total)
## Err InvalidNumStr -> Task.err NonNumberGiven
## ```
loop : state, (state -> Task [Step state, Done done] err) -> Task done err
loop = \state, step ->
looper = \current ->
(@Task next) = step current
when next {} is
Err e -> Err e
Ok (Done newResult) -> Ok newResult
Ok (Step newState) -> looper (newState)
@Task \{} -> looper state
## Create a task that always succeeds with the value provided.
##
## ```
## # Always succeeds with "Louis"
## getName : Task.Task Str *
## getName = Task.ok "Louis"
## ```
##
ok : a -> Task a *
ok = \a -> @Task \{} -> Ok a
## Create a task that always fails with the error provided.
##
## ```
## # Always fails with the tag `CustomError Str`
## customError : Str -> Task.Task {} [CustomError Str]
## customError = \err -> Task.err (CustomError err)
## ```
##
err : a -> Task * a
err = \a -> @Task \{} -> Err a
## Transform a given Task with a function that handles the success or error case
## and returns another task based on that. This is useful for chaining tasks
## together or performing error handling and recovery.
##
## Consider the following task:
##
## `canFail : Task {} [Failure, AnotherFail, YetAnotherFail]`
##
## We can use [attempt] to handle the failure cases using the following:
##
## ```
## Task.attempt canFail \result ->
## when result is
## Ok Success -> Stdout.line "Success!"
## Err Failure -> Stdout.line "Oops, failed!"
## Err AnotherFail -> Stdout.line "Ooooops, another failure!"
## Err YetAnotherFail -> Stdout.line "Really big oooooops, yet again!"
## ```
##
## Here we know that the `canFail` task may fail, and so we use
## `Task.attempt` to convert the task to a `Result` and then use pattern
## matching to handle the success and possible failure cases.
attempt : Task a b, (Result a b -> Task c d) -> Task c d
attempt = \@Task task, transform ->
@Task \{} ->
(@Task transformed) = transform (task {})
transformed {}
## Take the success value from a given [Task] and use that to generate a new [Task].
##
## We can [await] Task results with callbacks:
##
## ```
## Task.await (Stdin.line "What's your name?") \name ->
## Stdout.line "Your name is: $(name)"
## ```
##
## Or we can more succinctly use the `!` bang operator, which desugars to [await]:
##
## ```
## name = Stdin.line! "What's your name?"
## Stdout.line "Your name is: $(name)"
## ```
await : Task a b, (a -> Task c b) -> Task c b
await = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a ->
(@Task transformed) = transform a
transformed {}
Err b ->
Err b
## Take the error value from a given [Task] and use that to generate a new [Task].
##
## ```
## # Prints "Something went wrong!" to standard error if `canFail` fails.
## canFail
## |> Task.onErr \_ -> Stderr.line "Something went wrong!"
## ```
onErr : Task a b, (b -> Task a c) -> Task a c
onErr = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a ->
Ok a
Err b ->
(@Task transformed) = transform b
transformed {}
## Transform the success value of a given [Task] with a given function.
##
## ```
## # Succeeds with a value of "Bonjour Louis!"
## Task.ok "Louis"
## |> Task.map (\name -> "Bonjour $(name)!")
## ```
map : Task a c, (a -> b) -> Task b c
map = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a -> Ok (transform a)
Err b -> Err b
## Transform the error value of a given [Task] with a given function.
##
## ```
## # Ignore the fail value, and map it to the tag `CustomError`
## canFail
## |> Task.mapErr \_ -> CustomError
## ```
mapErr : Task c a, (a -> b) -> Task c b
mapErr = \@Task task, transform ->
@Task \{} ->
when task {} is
Ok a -> Ok a
Err b -> Err (transform b)
## Use a Result among other Tasks by converting it into a [Task].
fromResult : Result a b -> Task a b
fromResult = \res ->
@Task \{} -> res
## Apply a task to another task applicatively. This can be used with
## [ok] to build a [Task] that returns a record.
##
## The following example returns a Record with two fields, `apples` and
## `oranges`, each of which is a `List Str`. If it fails it returns the tag
## `NoFruitAvailable`.
##
## ```
## getFruitBasket : Task { apples : List Str, oranges : List Str } [NoFruitAvailable]
## getFruitBasket = Task.ok {
## apples: <- getFruit Apples |> Task.batch,
## oranges: <- getFruit Oranges |> Task.batch,
## }
## ```
batch : Task a c -> (Task (a -> b) c -> Task b c)
batch = \current ->
\next ->
await next \f ->
map current f
## Apply each task in a list sequentially, and return a list of the resulting values.
## Each task will be awaited before beginning the next task.
##
## ```
## fetchAuthorTasks : List (Task Author [DbError])
##
## getAuthors : Task (List Author) [DbError]
## getAuthors = Task.sequence fetchAuthorTasks
## ```
##
sequence : List (Task ok err) -> Task (List ok) err
sequence = \taskList ->
Task.loop (taskList, List.withCapacity (List.len taskList)) \(tasks, values) ->
when tasks is
[task, .. as rest] ->
value = task!
Task.ok (Step (rest, List.append values value))
[] ->
Task.ok (Done values)
## Apply a task repeatedly for each item in a list
##
## ```
## authors : List Author
## saveAuthor : Author -> Task {} [DbError]
##
## saveAuthors : Task (List Author) [DbError]
## saveAuthors = Task.forEach authors saveAuthor
## ```
##
forEach : List a, (a -> Task {} b) -> Task {} b
forEach = \items, fn ->
List.walk items (ok {}) \state, item ->
state |> await \_ -> fn item
## Transform a task that can either succeed with `ok`, or fail with `err`, into
## a task that succeeds with `Result ok err`.
##
## This is useful when chaining tasks using the `!` suffix. For example:
##
## ```
## # Path.roc
## checkFile : Str -> Task [Good, Bad] [IOError]
##
## # main.roc
## when checkFile "/usr/local/bin/roc" |> Task.result! is
## Ok Good -> "..."
## Ok Bad -> "..."
## Err IOError -> "..."
## ```
##
result : Task ok err -> Task (Result ok err) *
result = \@Task task ->
@Task \{} ->
Ok (task {})

View file

@ -11,4 +11,5 @@ package [
Hash, Hash,
Box, Box,
Inspect, Inspect,
Task,
] {} ] {}

View file

@ -16,6 +16,7 @@ pub fn module_source(module_id: ModuleId) -> &'static str {
ModuleId::DECODE => DECODE, ModuleId::DECODE => DECODE,
ModuleId::HASH => HASH, ModuleId::HASH => HASH,
ModuleId::INSPECT => INSPECT, ModuleId::INSPECT => INSPECT,
ModuleId::TASK => TASK,
_ => internal_error!( _ => internal_error!(
"ModuleId {:?} is not part of the standard library", "ModuleId {:?} is not part of the standard library",
module_id module_id
@ -35,3 +36,4 @@ const ENCODE: &str = include_str!("../roc/Encode.roc");
const DECODE: &str = include_str!("../roc/Decode.roc"); const DECODE: &str = include_str!("../roc/Decode.roc");
const HASH: &str = include_str!("../roc/Hash.roc"); const HASH: &str = include_str!("../roc/Hash.roc");
const INSPECT: &str = include_str!("../roc/Inspect.roc"); const INSPECT: &str = include_str!("../roc/Inspect.roc");
const TASK: &str = include_str!("../roc/Task.roc");

View file

@ -290,12 +290,14 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
Var(sym, var) => Var(*sym, sub!(*var)), Var(sym, var) => Var(*sym, sub!(*var)),
ParamsVar { ParamsVar {
symbol, symbol,
params,
var, var,
params_symbol,
params_var,
} => ParamsVar { } => ParamsVar {
symbol: *symbol, symbol: *symbol,
params: *params,
var: sub!(*var), var: sub!(*var),
params_symbol: *params_symbol,
params_var: sub!(*params_var),
}, },
ImportParams(module_id, region, opt_provided) => ImportParams( ImportParams(module_id, region, opt_provided) => ImportParams(
*module_id, *module_id,

View file

@ -3,12 +3,14 @@
use crate::def::Def; use crate::def::Def;
use crate::expr::Expr::{self, *}; use crate::expr::Expr::{self, *};
use crate::expr::{ use crate::expr::{
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch, ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData,
StructAccessorData, WhenBranch,
}; };
use crate::pattern::{Pattern, RecordDestruct, TupleDestruct}; use crate::pattern::{Pattern, RecordDestruct, TupleDestruct};
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_types::types::IndexOrField;
use ven_pretty::{text, Arena, DocAllocator, DocBuilder}; use ven_pretty::{text, Arena, DocAllocator, DocBuilder};
pub struct Ctx<'a> { pub struct Ctx<'a> {
@ -381,7 +383,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => { OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => {
text!(f, "@{}", opaque_name.as_str(c.interns)) text!(f, "@{}", opaque_name.as_str(c.interns))
} }
RecordAccessor(_) => todo!(), RecordAccessor(StructAccessorData { field, .. }) => match field {
IndexOrField::Index(index) => text!(f, ".{}", index),
IndexOrField::Field(name) => text!(f, ".{}", name),
},
RecordUpdate { RecordUpdate {
symbol, updates, .. symbol, updates, ..
} => f } => f

View file

@ -22,6 +22,7 @@ use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Patte
use crate::procedure::QualifiedReference; use crate::procedure::QualifiedReference;
use crate::procedure::References; use crate::procedure::References;
use crate::scope::create_alias; use crate::scope::create_alias;
use crate::scope::SymbolLookup;
use crate::scope::{PendingAbilitiesInScope, Scope}; use crate::scope::{PendingAbilitiesInScope, Scope};
use roc_collections::ReferenceMatrix; use roc_collections::ReferenceMatrix;
use roc_collections::VecMap; use roc_collections::VecMap;
@ -111,6 +112,23 @@ impl Annotation {
self self
} }
pub fn convert_to_fn(&mut self, argument_count: usize, var_store: &mut VarStore) {
let mut arg_types = Vec::with_capacity(argument_count);
for _ in 0..argument_count {
let var = var_store.fresh();
self.introduced_variables.insert_inferred(Loc::at_zero(var));
arg_types.push(Type::Variable(var));
}
self.signature = Type::Function(
arg_types,
Box::new(Type::Variable(var_store.fresh())),
Box::new(self.signature.clone()),
);
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -165,6 +183,7 @@ enum PendingValueDef<'a> {
/// Module params from an import /// Module params from an import
ImportParams { ImportParams {
symbol: Symbol, symbol: Symbol,
variable: Variable,
loc_pattern: Loc<Pattern>, loc_pattern: Loc<Pattern>,
module_id: ModuleId, module_id: ModuleId,
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>, opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
@ -186,6 +205,7 @@ impl PendingValueDef<'_> {
PendingValueDef::ImportParams { PendingValueDef::ImportParams {
loc_pattern, loc_pattern,
symbol: _, symbol: _,
variable: _,
module_id: _, module_id: _,
opt_provided: _, opt_provided: _,
} => loc_pattern, } => loc_pattern,
@ -1153,6 +1173,7 @@ fn canonicalize_value_defs<'a>(
pending_value_defs.push(PendingValueDef::ImportParams { pending_value_defs.push(PendingValueDef::ImportParams {
symbol: params.symbol, symbol: params.symbol,
variable: params.variable,
loc_pattern: params.loc_pattern, loc_pattern: params.loc_pattern,
opt_provided: params.opt_provided, opt_provided: params.opt_provided,
module_id, module_id,
@ -2400,15 +2421,17 @@ fn canonicalize_pending_value_def<'a>(
} }
ImportParams { ImportParams {
symbol, symbol,
variable,
loc_pattern, loc_pattern,
module_id, module_id,
opt_provided, opt_provided,
} => { } => {
// Insert a reference to the record so that we don't report it as unused // Insert a reference to the record so that we don't report it as unused
// If the whole module is unused, we'll report that separately // If the whole module is unused, we'll report that separately
output output.references.insert_value_lookup(
.references SymbolLookup::no_params(symbol),
.insert_value_lookup(symbol, QualifiedReference::Unqualified); QualifiedReference::Unqualified,
);
let (opt_var_record, references) = match opt_provided { let (opt_var_record, references) = match opt_provided {
Some(params) => { Some(params) => {
@ -2418,7 +2441,7 @@ fn canonicalize_pending_value_def<'a>(
let references = can_output.references.clone(); let references = can_output.references.clone();
output.union(can_output); output.union(can_output);
(Some((var_store.fresh(), Box::new(record))), references) (Some((variable, Box::new(record))), references)
} }
None => (None, References::new()), None => (None, References::new()),
}; };
@ -2725,12 +2748,11 @@ pub fn report_unused_imports(
for (symbol, region) in &import.exposed_symbols { for (symbol, region) in &import.exposed_symbols {
if !references.has_unqualified_type_or_value_lookup(*symbol) if !references.has_unqualified_type_or_value_lookup(*symbol)
&& !scope.abilities_store.is_specialization_name(*symbol) && !scope.abilities_store.is_specialization_name(*symbol)
&& !import.is_task(env)
{ {
env.problem(Problem::UnusedImport(*symbol, *region)); env.problem(Problem::UnusedImport(*symbol, *region));
} }
} }
} else if !import.is_task(env) { } else {
env.problem(Problem::UnusedModuleImport(import.module_id, import.region)); env.problem(Problem::UnusedModuleImport(import.module_id, import.region));
} }
} }
@ -3003,6 +3025,7 @@ struct PendingModuleImport<'a> {
struct PendingModuleImportParams<'a> { struct PendingModuleImportParams<'a> {
symbol: Symbol, symbol: Symbol,
variable: Variable,
loc_pattern: Loc<Pattern>, loc_pattern: Loc<Pattern>,
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>, opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
} }
@ -3013,16 +3036,6 @@ pub struct IntroducedImport {
exposed_symbols: Vec<(Symbol, Region)>, exposed_symbols: Vec<(Symbol, Region)>,
} }
impl IntroducedImport {
pub fn is_task(&self, env: &Env<'_>) -> bool {
// Temporarily needed for `!` convenience. Can be removed when Task becomes a builtin.
match env.qualified_module_ids.get_name(self.module_id) {
Some(name) => name.as_inner().as_str() == "Task",
None => false,
}
}
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn to_pending_value_def<'a>( fn to_pending_value_def<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
@ -3160,15 +3173,17 @@ fn to_pending_value_def<'a>(
// We do this even if params weren't provided so that solve can report if they are missing // We do this even if params weren't provided so that solve can report if they are missing
let params_sym = scope.gen_unique_symbol(); let params_sym = scope.gen_unique_symbol();
let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region); let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region);
let params_var = var_store.fresh();
let params = let params =
PendingModuleImportParams { PendingModuleImportParams {
symbol: params_sym, symbol: params_sym,
variable: params_var,
loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)), loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)),
opt_provided: module_import.params.map(|p| p.params.value), opt_provided: module_import.params.map(|p| p.params.value),
}; };
let provided_params_sym = if module_import.params.is_some() { let provided_params = if module_import.params.is_some() {
// Only add params to scope if they are provided // Only add params to scope if they are provided
Some(params_sym) Some((params_var, params_sym))
} else { } else {
None None
}; };
@ -3176,7 +3191,7 @@ fn to_pending_value_def<'a>(
if let Err(existing_import) = if let Err(existing_import) =
scope scope
.modules .modules
.insert(name_with_alias.clone(), module_id, provided_params_sym, region) .insert(name_with_alias.clone(), module_id, provided_params, region)
{ {
env.problems.push(Problem::ImportNameConflict { env.problems.push(Problem::ImportNameConflict {
name: name_with_alias, name: name_with_alias,

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@ use roc_module::ident::{Ident, ModuleName};
use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol}; use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol};
use roc_problem::can::{Problem, RuntimeError}; use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
/// The canonicalization environment for a particular module. /// The canonicalization environment for a particular module.
pub struct Env<'a> { pub struct Env<'a> {
@ -38,6 +39,8 @@ pub struct Env<'a> {
pub top_level_symbols: VecSet<Symbol>, pub top_level_symbols: VecSet<Symbol>,
pub home_params_record: Option<(Symbol, Variable)>,
pub arena: &'a Bump, pub arena: &'a Bump,
pub opt_shorthand: Option<&'a str>, pub opt_shorthand: Option<&'a str>,
@ -64,6 +67,7 @@ impl<'a> Env<'a> {
qualified_type_lookups: VecSet::default(), qualified_type_lookups: VecSet::default(),
tailcallable_symbol: None, tailcallable_symbol: None,
top_level_symbols: VecSet::default(), top_level_symbols: VecSet::default(),
home_params_record: None,
opt_shorthand, opt_shorthand,
} }
} }

View file

@ -18,7 +18,7 @@ use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia; 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::{ModuleId, Symbol}; use roc_module::symbol::{IdentId, ModuleId, Symbol};
use roc_parse::ast::{self, Defs, PrecedenceConflict, 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::*;
@ -111,8 +111,9 @@ pub enum Expr {
/// Like Var, but from a module with params /// Like Var, but from a module with params
ParamsVar { ParamsVar {
symbol: Symbol, symbol: Symbol,
params: Symbol,
var: Variable, var: Variable,
params_symbol: Symbol,
params_var: Variable,
}, },
AbilityMember( AbilityMember(
/// Actual member name /// Actual member name
@ -320,8 +321,9 @@ impl Expr {
&Self::Var(sym, _) => Category::Lookup(sym), &Self::Var(sym, _) => Category::Lookup(sym),
&Self::ParamsVar { &Self::ParamsVar {
symbol, symbol,
params: _,
var: _, var: _,
params_symbol: _,
params_var: _,
} => Category::Lookup(symbol), } => Category::Lookup(symbol),
&Self::AbilityMember(sym, _, _) => Category::Lookup(sym), &Self::AbilityMember(sym, _, _) => Category::Lookup(sym),
Self::When { .. } => Category::When, Self::When { .. } => Category::When,
@ -648,9 +650,7 @@ pub fn canonicalize_expr<'a>(
(answer, Output::default()) (answer, Output::default())
} }
ast::Expr::Record(fields) => { ast::Expr::Record(fields) => canonicalize_record(env, var_store, scope, region, *fields),
canonicalize_record(env, var_store, scope, region, *fields)
}
ast::Expr::RecordUpdate { ast::Expr::RecordUpdate {
fields, fields,
update: loc_update, update: loc_update,
@ -1022,6 +1022,9 @@ pub fn canonicalize_expr<'a>(
ast::Expr::Backpassing(_, _, _) => { ast::Expr::Backpassing(_, _, _) => {
internal_error!("Backpassing should have been desugared by now") internal_error!("Backpassing should have been desugared by now")
} }
ast::Expr::RecordUpdater(_) => {
internal_error!("Record updater should have been desugared by now")
}
ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => { ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => {
let (closure_data, output) = let (closure_data, output) =
canonicalize_closure(env, var_store, scope, loc_arg_patterns, loc_body_expr, None); canonicalize_closure(env, var_store, scope, loc_arg_patterns, loc_body_expr, None);
@ -1119,7 +1122,9 @@ pub fn canonicalize_expr<'a>(
output, output,
) )
} }
ast::Expr::TaskAwaitBang(..) => internal_error!("a Expr::TaskAwaitBang expression was not completely removed in desugar_value_def_suffixed"), ast::Expr::TrySuffix { .. } => internal_error!(
"a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"
),
ast::Expr::Tag(tag) => { ast::Expr::Tag(tag) => {
let variant_var = var_store.fresh(); let variant_var = var_store.fresh();
let ext_var = var_store.fresh(); let ext_var = var_store.fresh();
@ -1204,7 +1209,7 @@ pub fn canonicalize_expr<'a>(
output, output,
) )
} }
ast::Expr::Dbg(_, _) => { ast::Expr::Dbg | ast::Expr::DbgStmt(_, _) => {
internal_error!("Dbg should have been desugared by now") internal_error!("Dbg should have been desugared by now")
} }
ast::Expr::LowLevelDbg((source_location, source), message, continuation) => { ast::Expr::LowLevelDbg((source_location, source), message, continuation) => {
@ -1371,7 +1376,10 @@ pub fn canonicalize_expr<'a>(
use roc_problem::can::RuntimeError::*; use roc_problem::can::RuntimeError::*;
let sub_region = Region::span_across(&loc_name.region, &loc_value.region); let sub_region = Region::span_across(&loc_name.region, &loc_value.region);
let problem = OptionalFieldInRecordBuilder {record: region, field: sub_region }; let problem = OptionalFieldInRecordBuilder {
record: region,
field: sub_region,
};
env.problem(Problem::RuntimeError(problem.clone())); env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default()) (RuntimeError(problem), Output::default())
@ -1548,6 +1556,8 @@ fn canonicalize_closure_body<'a>(
&loc_body_expr.value, &loc_body_expr.value,
); );
let mut references_top_level = false;
let mut captured_symbols: Vec<_> = new_output let mut captured_symbols: Vec<_> = new_output
.references .references
.value_lookups() .value_lookups()
@ -1558,7 +1568,11 @@ fn canonicalize_closure_body<'a>(
.filter(|s| !new_output.references.bound_symbols().any(|x| x == s)) .filter(|s| !new_output.references.bound_symbols().any(|x| x == s))
.filter(|s| bound_by_argument_patterns.iter().all(|(k, _)| s != k)) .filter(|s| bound_by_argument_patterns.iter().all(|(k, _)| s != k))
// filter out top-level symbols those will be globally available, and don't need to be captured // filter out top-level symbols those will be globally available, and don't need to be captured
.filter(|s| !env.top_level_symbols.contains(s)) .filter(|s| {
let is_top_level = env.top_level_symbols.contains(s);
references_top_level = references_top_level || is_top_level;
!is_top_level
})
// filter out imported symbols those will be globally available, and don't need to be captured // filter out imported symbols those will be globally available, and don't need to be captured
.filter(|s| s.module_id() == env.home) .filter(|s| s.module_id() == env.home)
// filter out functions that don't close over anything // filter out functions that don't close over anything
@ -1567,6 +1581,15 @@ fn canonicalize_closure_body<'a>(
.map(|s| (s, var_store.fresh())) .map(|s| (s, var_store.fresh()))
.collect(); .collect();
if references_top_level {
if let Some(params_record) = env.home_params_record {
// If this module has params and the closure references top-level symbols,
// we need to capture the whole record so we can pass it.
// The lower_params pass will take care of removing the captures for top-level fns.
captured_symbols.push(params_record);
}
}
output.union(new_output); output.union(new_output);
// Now that we've collected all the references, check to see if any of the args we defined // Now that we've collected all the references, check to see if any of the args we defined
@ -1912,7 +1935,7 @@ fn canonicalize_var_lookup(
Ok(lookup) => { Ok(lookup) => {
output output
.references .references
.insert_value_lookup(lookup.symbol, QualifiedReference::Unqualified); .insert_value_lookup(lookup, QualifiedReference::Unqualified);
if scope.abilities_store.is_ability_member_name(lookup.symbol) { if scope.abilities_store.is_ability_member_name(lookup.symbol) {
AbilityMember( AbilityMember(
@ -1921,7 +1944,7 @@ fn canonicalize_var_lookup(
var_store.fresh(), var_store.fresh(),
) )
} else { } else {
lookup_to_expr(lookup, var_store.fresh()) lookup_to_expr(var_store, lookup)
} }
} }
Err(problem) => { Err(problem) => {
@ -1937,7 +1960,7 @@ fn canonicalize_var_lookup(
Ok(lookup) => { Ok(lookup) => {
output output
.references .references
.insert_value_lookup(lookup.symbol, QualifiedReference::Qualified); .insert_value_lookup(lookup, QualifiedReference::Qualified);
if scope.abilities_store.is_ability_member_name(lookup.symbol) { if scope.abilities_store.is_ability_member_name(lookup.symbol) {
AbilityMember( AbilityMember(
@ -1946,7 +1969,7 @@ fn canonicalize_var_lookup(
var_store.fresh(), var_store.fresh(),
) )
} else { } else {
lookup_to_expr(lookup, var_store.fresh()) lookup_to_expr(var_store, lookup)
} }
} }
Err(problem) => { Err(problem) => {
@ -1965,20 +1988,21 @@ fn canonicalize_var_lookup(
} }
fn lookup_to_expr( fn lookup_to_expr(
var_store: &mut VarStore,
SymbolLookup { SymbolLookup {
symbol, symbol,
module_params: params, module_params,
}: SymbolLookup, }: SymbolLookup,
var: Variable,
) -> Expr { ) -> Expr {
if let Some(params) = params { if let Some((params_var, params_symbol)) = module_params {
Expr::ParamsVar { Expr::ParamsVar {
symbol, symbol,
params, var: var_store.fresh(),
var, params_symbol,
params_var,
} }
} else { } else {
Expr::Var(symbol, var) Expr::Var(symbol, var_store.fresh())
} }
} }
@ -2462,22 +2486,30 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::Expr::Num(_) | ast::Expr::Num(_)
| ast::Expr::NonBase10Int { .. } | ast::Expr::NonBase10Int { .. }
| ast::Expr::AccessorFunction(_) | ast::Expr::AccessorFunction(_)
| ast::Expr::RecordUpdater(_)
| ast::Expr::Crash | ast::Expr::Crash
| ast::Expr::Dbg
| ast::Expr::Underscore(_) | ast::Expr::Underscore(_)
| ast::Expr::MalformedIdent(_, _) | ast::Expr::MalformedIdent(_, _)
| ast::Expr::Tag(_) | ast::Expr::Tag(_)
| ast::Expr::OpaqueRef(_) | ast::Expr::OpaqueRef(_)
| ast::Expr::MalformedClosure => true, | ast::Expr::MalformedClosure => true,
// Newlines are disallowed inside interpolation, and these all require newlines // Newlines are disallowed inside interpolation, and these all require newlines
ast::Expr::Dbg(_, _) ast::Expr::DbgStmt(_, _)
| ast::Expr::LowLevelDbg(_, _, _) | ast::Expr::LowLevelDbg(_, _, _)
| ast::Expr::Defs(_, _)
| ast::Expr::Expect(_, _) | ast::Expr::Expect(_, _)
| ast::Expr::When(_, _) | ast::Expr::When(_, _)
| ast::Expr::Backpassing(_, _, _) | ast::Expr::Backpassing(_, _, _)
| ast::Expr::SpaceBefore(_, _) | ast::Expr::SpaceBefore(_, _)
| ast::Expr::Str(StrLiteral::Block(_)) | ast::Expr::Str(StrLiteral::Block(_))
| ast::Expr::SpaceAfter(_, _) => false, | ast::Expr::SpaceAfter(_, _) => false,
// Desugared dbg expression
ast::Expr::Defs(_, loc_ret) => match loc_ret.value {
ast::Expr::LowLevelDbg(_, _, continuation) => {
is_valid_interpolation(&continuation.value)
}
_ => false,
},
// These can contain subexpressions, so we need to recursively check those // These can contain subexpressions, so we need to recursively check those
ast::Expr::Str(StrLiteral::Line(segments)) => { ast::Expr::Str(StrLiteral::Line(segments)) => {
segments.iter().all(|segment| match segment { segments.iter().all(|segment| match segment {
@ -2515,7 +2547,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
ast::Expr::TupleAccess(sub_expr, _) ast::Expr::TupleAccess(sub_expr, _)
| ast::Expr::ParensAround(sub_expr) | ast::Expr::ParensAround(sub_expr)
| ast::Expr::RecordAccess(sub_expr, _) | ast::Expr::RecordAccess(sub_expr, _)
| ast::Expr::TaskAwaitBang(sub_expr) => is_valid_interpolation(sub_expr), | ast::Expr::TrySuffix { expr: sub_expr, .. } => is_valid_interpolation(sub_expr),
ast::Expr::Apply(loc_expr, args, _called_via) => { ast::Expr::Apply(loc_expr, args, _called_via) => {
is_valid_interpolation(&loc_expr.value) is_valid_interpolation(&loc_expr.value)
&& args && args
@ -2767,6 +2799,9 @@ pub struct Declarations {
// used for ability member specializatons. // used for ability member specializatons.
pub specializes: VecMap<usize, Symbol>, pub specializes: VecMap<usize, Symbol>,
// used while lowering params.
arity_by_name: VecMap<IdentId, usize>,
pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>, pub host_exposed_annotations: VecMap<usize, (Variable, crate::def::Annotation)>,
pub function_bodies: Vec<Loc<FunctionDef>>, pub function_bodies: Vec<Loc<FunctionDef>>,
@ -2795,6 +2830,7 @@ impl Declarations {
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
destructs: Vec::new(), // number of destructs is probably low destructs: Vec::new(), // number of destructs is probably low
arity_by_name: VecMap::with_capacity(capacity),
} }
} }
@ -2833,6 +2869,9 @@ impl Declarations {
arguments: loc_closure_data.value.arguments, arguments: loc_closure_data.value.arguments,
}; };
self.arity_by_name
.insert(symbol.value.ident_id(), function_def.arguments.len());
let loc_function_def = Loc::at(loc_closure_data.region, function_def); let loc_function_def = Loc::at(loc_closure_data.region, function_def);
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);
@ -2881,6 +2920,9 @@ impl Declarations {
arguments: loc_closure_data.value.arguments, arguments: loc_closure_data.value.arguments,
}; };
self.arity_by_name
.insert(symbol.value.ident_id(), function_def.arguments.len());
let loc_function_def = Loc::at(loc_closure_data.region, function_def); let loc_function_def = Loc::at(loc_closure_data.region, function_def);
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);
@ -2957,6 +2999,8 @@ impl Declarations {
.insert(self.declarations.len(), annotation); .insert(self.declarations.len(), annotation);
} }
self.arity_by_name.insert(symbol.value.ident_id(), 0);
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);
@ -3073,6 +3117,60 @@ impl Declarations {
} }
} }
/// Convert a value def to a function def with the given arguments
/// Currently used in lower_params
pub fn convert_value_to_function(
&mut self,
index: usize,
new_arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
var_store: &mut VarStore,
) {
match self.declarations[index] {
DeclarationTag::Value => {
let new_args_len = new_arguments.len();
let loc_body = self.expressions[index].clone();
let region = loc_body.region;
let closure_data = ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: self.symbols[index].value,
captured_symbols: vec![],
recursive: Recursive::NotRecursive,
arguments: new_arguments,
loc_body: Box::new(loc_body),
};
let loc_closure_data = Loc::at(region, closure_data);
let function_def = FunctionDef {
closure_type: loc_closure_data.value.closure_type,
return_type: loc_closure_data.value.return_type,
captured_symbols: loc_closure_data.value.captured_symbols,
arguments: loc_closure_data.value.arguments,
};
let loc_function_def = Loc::at(region, function_def);
let function_def_index =
Index::push_new(&mut self.function_bodies, loc_function_def);
if let Some(annotation) = &mut self.annotations[index] {
annotation.convert_to_fn(new_args_len, var_store);
}
if let Some((_var, annotation)) = self.host_exposed_annotations.get_mut(&index) {
annotation.convert_to_fn(new_args_len, var_store);
}
self.declarations[index] = DeclarationTag::Function(function_def_index);
}
_ => internal_error!("Expected value declaration"),
};
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.declarations.len() self.declarations.len()
} }
@ -3142,6 +3240,11 @@ impl Declarations {
collector collector
} }
pub(crate) fn take_arity_by_name(&mut self) -> VecMap<IdentId, usize> {
// `arity_by_name` is only needed for lowering module params
std::mem::take(&mut self.arity_by_name)
}
} }
roc_error_macros::assert_sizeof_default!(DeclarationTag, 8); roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
@ -3198,8 +3301,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
Expr::Var(symbol, var) Expr::Var(symbol, var)
| Expr::ParamsVar { | Expr::ParamsVar {
symbol, symbol,
params: _,
var, var,
params_symbol: _,
params_var: _,
} }
| Expr::RecordUpdate { | Expr::RecordUpdate {
symbol, symbol,

View file

@ -14,7 +14,6 @@ pub mod copy;
pub mod def; pub mod def;
mod derive; mod derive;
pub mod desugar; pub mod desugar;
pub mod effect_module;
pub mod env; pub mod env;
pub mod exhaustive; pub mod exhaustive;
pub mod expected; pub mod expected;
@ -26,6 +25,7 @@ pub mod procedure;
pub mod scope; pub mod scope;
pub mod string; pub mod string;
pub mod suffixed; pub mod suffixed;
pub mod task_module;
pub mod traverse; pub mod traverse;
pub use derive::DERIVED_REGION; pub use derive::DERIVED_REGION;

View file

@ -3,13 +3,12 @@ use std::path::Path;
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl}; use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::{canonicalize_annotation, AnnotationFor}; use crate::annotation::{canonicalize_annotation, AnnotationFor};
use crate::def::{canonicalize_defs, report_unused_imports, Def}; use crate::def::{canonicalize_defs, report_unused_imports, Def};
use crate::effect_module::HostedGeneratedFunctions;
use crate::env::Env; use crate::env::Env;
use crate::expr::{ use crate::expr::{
AnnotatedMark, ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
}; };
use crate::pattern::{ use crate::pattern::{
canonicalize_record_destructure, BindingsFromPattern, Pattern, PermitShadows, canonicalize_record_destructs, BindingsFromPattern, Pattern, PermitShadows, RecordDestruct,
}; };
use crate::procedure::References; use crate::procedure::References;
use crate::scope::Scope; use crate::scope::Scope;
@ -18,14 +17,14 @@ use roc_collections::{MutMap, SendMap, VecMap, VecSet};
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::ident::Ident; use roc_module::ident::Ident;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol}; use roc_module::symbol::{IdentId, IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol};
use roc_parse::ast::{Defs, TypeAnnotation}; use roc_parse::ast::{Defs, TypeAnnotation};
use roc_parse::header::{HeaderType, ModuleParams}; use roc_parse::header::HeaderType;
use roc_parse::pattern::PatternType; use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError}; use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable}; use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable};
use roc_types::types::{AbilitySet, Alias, AliasKind, AliasVar, Type}; use roc_types::types::{AbilitySet, Alias, Type};
/// The types of all exposed values/functions of a collection of modules /// The types of all exposed values/functions of a collection of modules
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
@ -138,7 +137,33 @@ pub struct Module {
pub abilities_store: PendingAbilitiesStore, pub abilities_store: PendingAbilitiesStore,
pub loc_expects: VecMap<Region, Vec<ExpectLookup>>, pub loc_expects: VecMap<Region, Vec<ExpectLookup>>,
pub loc_dbgs: VecMap<Symbol, DbgLookup>, pub loc_dbgs: VecMap<Symbol, DbgLookup>,
pub params_pattern: Option<(Variable, AnnotatedMark, Loc<Pattern>)>, pub module_params: Option<ModuleParams>,
}
#[derive(Debug, Clone)]
pub struct ModuleParams {
pub region: Region,
pub whole_symbol: Symbol,
pub whole_var: Variable,
pub record_var: Variable,
pub record_ext_var: Variable,
pub destructs: Vec<Loc<RecordDestruct>>,
// used while lowering passed functions
pub arity_by_name: VecMap<IdentId, usize>,
}
impl ModuleParams {
pub fn pattern(&self) -> Loc<Pattern> {
let record_pattern = Pattern::RecordDestructure {
whole_var: self.record_var,
ext_var: self.record_ext_var,
destructs: self.destructs.clone(),
};
let loc_record_pattern = Loc::at(self.region, record_pattern);
let as_pattern = Pattern::As(Box::new(loc_record_pattern), self.whole_symbol);
Loc::at(self.region, as_pattern)
}
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -152,7 +177,7 @@ pub struct RigidVariables {
pub struct ModuleOutput { pub struct ModuleOutput {
pub aliases: MutMap<Symbol, Alias>, pub aliases: MutMap<Symbol, Alias>,
pub rigid_variables: RigidVariables, pub rigid_variables: RigidVariables,
pub params_pattern: Option<(Variable, AnnotatedMark, Loc<Pattern>)>, pub module_params: Option<ModuleParams>,
pub declarations: Declarations, pub declarations: Declarations,
pub exposed_imports: MutMap<Symbol, Region>, pub exposed_imports: MutMap<Symbol, Region>,
pub exposed_symbols: VecSet<Symbol>, pub exposed_symbols: VecSet<Symbol>,
@ -165,99 +190,6 @@ pub struct ModuleOutput {
pub loc_dbgs: VecMap<Symbol, DbgLookup>, pub loc_dbgs: VecMap<Symbol, DbgLookup>,
} }
fn validate_generate_with<'a>(
generate_with: &'a [Loc<roc_parse::header::ExposedName<'a>>],
) -> (HostedGeneratedFunctions, Vec<Loc<Ident>>) {
let mut functions = HostedGeneratedFunctions::default();
let mut unknown = Vec::new();
for generated in generate_with {
match generated.value.as_str() {
"after" => functions.after = true,
"map" => functions.map = true,
"always" => functions.always = true,
"loop" => functions.loop_ = true,
"forever" => functions.forever = true,
other => {
// we don't know how to generate this function
let ident = Ident::from(other);
unknown.push(Loc::at(generated.region, ident));
}
}
}
(functions, unknown)
}
#[derive(Debug)]
enum GeneratedInfo {
Hosted {
effect_symbol: Symbol,
generated_functions: HostedGeneratedFunctions,
},
Builtin,
NotSpecial,
}
impl GeneratedInfo {
fn from_header_type(
env: &mut Env,
scope: &mut Scope,
var_store: &mut VarStore,
header_type: &HeaderType,
) -> Self {
match header_type {
HeaderType::Hosted {
generates,
generates_with,
name: _,
exposes: _,
} => {
let name: &str = generates.into();
let (generated_functions, unknown_generated) =
validate_generate_with(generates_with);
for unknown in unknown_generated {
env.problem(Problem::UnknownGeneratesWith(unknown));
}
let effect_symbol = scope.introduce(name.into(), Region::zero()).unwrap();
{
let a_var = var_store.fresh();
let actual =
crate::effect_module::build_effect_actual(Type::Variable(a_var), var_store);
scope.add_alias(
effect_symbol,
Region::zero(),
vec![Loc::at_zero(AliasVar::unbound("a".into(), a_var))],
vec![],
actual,
AliasKind::Opaque,
);
}
GeneratedInfo::Hosted {
effect_symbol,
generated_functions,
}
}
HeaderType::Builtin {
generates_with,
name: _,
exposes: _,
opt_params: _,
} => {
debug_assert!(generates_with.is_empty());
GeneratedInfo::Builtin
}
_ => GeneratedInfo::NotSpecial,
}
}
}
fn has_no_implementation(expr: &Expr) -> bool { fn has_no_implementation(expr: &Expr) -> bool {
match expr { match expr {
Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true, Expr::RuntimeError(RuntimeError::NoImplementationNamed { .. }) => true,
@ -326,9 +258,6 @@ pub fn canonicalize_module_defs<'a>(
); );
} }
let generated_info =
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
// Desugar operators (convert them to Apply calls, taking into account // Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization. // operator precedence and associativity rules), before doing other canonicalization.
// //
@ -337,11 +266,20 @@ pub fn canonicalize_module_defs<'a>(
// operators, and then again on *their* nested operators, ultimately applying the // operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily. // rules multiple times unnecessarily.
crate::desugar::desugar_defs_node_values(arena, loc_defs, src, &mut None, module_path, true); crate::desugar::desugar_defs_node_values(
arena,
var_store,
loc_defs,
src,
&mut None,
module_path,
true,
&mut env.problems,
);
let mut rigid_variables = RigidVariables::default(); let mut rigid_variables = RigidVariables::default();
// Iniital scope values are treated like defs that appear before any others. // Initial scope values are treated like defs that appear before any others.
// They include builtin types that are automatically imported, and for a platform // They include builtin types that are automatically imported, and for a platform
// package, the required values from the app. // package, the required values from the app.
// //
@ -390,13 +328,13 @@ pub fn canonicalize_module_defs<'a>(
let mut output = Output::default(); let mut output = Output::default();
let params_pattern = header_type.get_params().as_ref().map( let module_params = header_type.get_params().as_ref().map(
|ModuleParams { |roc_parse::header::ModuleParams {
pattern, pattern,
before_arrow: _, before_arrow: _,
after_arrow: _, after_arrow: _,
}| { }| {
let can_pattern = canonicalize_record_destructure( let (destructs, _) = canonicalize_record_destructs(
&mut env, &mut env,
var_store, var_store,
&mut scope, &mut scope,
@ -407,17 +345,22 @@ pub fn canonicalize_module_defs<'a>(
PermitShadows(false), PermitShadows(false),
); );
let loc_pattern = Loc::at(pattern.region, can_pattern); let whole_symbol = scope.gen_unique_symbol();
env.top_level_symbols.insert(whole_symbol);
for (symbol, _) in BindingsFromPattern::new(&loc_pattern) { let whole_var = var_store.fresh();
env.top_level_symbols.insert(symbol);
env.home_params_record = Some((whole_symbol, whole_var));
ModuleParams {
region: pattern.region,
whole_var,
whole_symbol,
record_var: var_store.fresh(),
record_ext_var: var_store.fresh(),
destructs,
arity_by_name: Default::default(),
} }
(
var_store.fresh(),
AnnotatedMark::new(var_store),
loc_pattern,
)
}, },
); );
@ -495,6 +438,11 @@ pub fn canonicalize_module_defs<'a>(
&exposed_symbols, &exposed_symbols,
); );
let module_params = module_params.map(|params| ModuleParams {
arity_by_name: declarations.take_arity_by_name(),
..params
});
debug_assert!( debug_assert!(
output.pending_derives.is_empty(), output.pending_derives.is_empty(),
"I thought pending derives are only found during def introduction" "I thought pending derives are only found during def introduction"
@ -534,24 +482,6 @@ pub fn canonicalize_module_defs<'a>(
report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope); report_unused_imports(imports_introduced, &output.references, &mut env, &mut scope);
if let GeneratedInfo::Hosted {
effect_symbol,
generated_functions,
} = generated_info
{
let mut exposed_symbols = VecSet::default();
// NOTE this currently builds all functions, not just the ones that the user requested
crate::effect_module::build_effect_builtins(
&mut scope,
effect_symbol,
var_store,
&mut exposed_symbols,
&mut declarations,
generated_functions,
);
}
for index in 0..declarations.len() { for index in 0..declarations.len() {
use crate::expr::DeclarationTag::*; use crate::expr::DeclarationTag::*;
@ -572,8 +502,8 @@ pub fn canonicalize_module_defs<'a>(
// and which are meant to be normal definitions without a body. So for now // and which are meant to be normal definitions without a body. So for now
// we just assume they are hosted functions (meant to be provided by the platform) // we just assume they are hosted functions (meant to be provided by the platform)
if has_no_implementation(&declarations.expressions[index].value) { if has_no_implementation(&declarations.expressions[index].value) {
match generated_info { match header_type {
GeneratedInfo::Builtin => { HeaderType::Builtin { .. } => {
match crate::builtins::builtin_defs_map(*symbol, var_store) { match crate::builtins::builtin_defs_map(*symbol, var_store) {
None => { None => {
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol) internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
@ -583,7 +513,7 @@ pub fn canonicalize_module_defs<'a>(
} }
} }
} }
GeneratedInfo::Hosted { effect_symbol, .. } => { HeaderType::Hosted { .. } => {
let ident_id = symbol.ident_id(); let ident_id = symbol.ident_id();
let ident = scope let ident = scope
.locals .locals
@ -601,13 +531,8 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(), aliases: Default::default(),
}; };
let hosted_def = crate::effect_module::build_host_exposed_def( let hosted_def = crate::task_module::build_host_exposed_def(
&mut scope, &mut scope, *symbol, &ident, var_store, annotation,
*symbol,
&ident,
effect_symbol,
var_store,
annotation,
); );
declarations.update_builtin_def(index, hosted_def); declarations.update_builtin_def(index, hosted_def);
@ -630,8 +555,8 @@ pub fn canonicalize_module_defs<'a>(
// and which are meant to be normal definitions without a body. So for now // and which are meant to be normal definitions without a body. So for now
// we just assume they are hosted functions (meant to be provided by the platform) // we just assume they are hosted functions (meant to be provided by the platform)
if has_no_implementation(&declarations.expressions[index].value) { if has_no_implementation(&declarations.expressions[index].value) {
match generated_info { match header_type {
GeneratedInfo::Builtin => { HeaderType::Builtin { .. } => {
match crate::builtins::builtin_defs_map(*symbol, var_store) { match crate::builtins::builtin_defs_map(*symbol, var_store) {
None => { None => {
internal_error!("A builtin module contains a signature without implementation for {:?}", symbol) internal_error!("A builtin module contains a signature without implementation for {:?}", symbol)
@ -641,7 +566,7 @@ pub fn canonicalize_module_defs<'a>(
} }
} }
} }
GeneratedInfo::Hosted { effect_symbol, .. } => { HeaderType::Hosted { .. } => {
let ident_id = symbol.ident_id(); let ident_id = symbol.ident_id();
let ident = scope let ident = scope
.locals .locals
@ -659,13 +584,8 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(), aliases: Default::default(),
}; };
let hosted_def = crate::effect_module::build_host_exposed_def( let hosted_def = crate::task_module::build_host_exposed_def(
&mut scope, &mut scope, *symbol, &ident, var_store, annotation,
*symbol,
&ident,
effect_symbol,
var_store,
annotation,
); );
declarations.update_builtin_def(index, hosted_def); declarations.update_builtin_def(index, hosted_def);
@ -691,18 +611,6 @@ pub fn canonicalize_module_defs<'a>(
let mut aliases = MutMap::default(); let mut aliases = MutMap::default();
if let GeneratedInfo::Hosted { effect_symbol, .. } = generated_info {
// Remove this from exposed_symbols,
// so that at the end of the process,
// we can see if there were any
// exposed symbols which did not have
// corresponding defs.
exposed_but_not_defined.remove(&effect_symbol);
let hosted_alias = scope.lookup_alias(effect_symbol).unwrap().clone();
aliases.insert(effect_symbol, hosted_alias);
}
for (symbol, alias) in output.aliases { for (symbol, alias) in output.aliases {
// Remove this from exposed_symbols, // Remove this from exposed_symbols,
// so that at the end of the process, // so that at the end of the process,
@ -851,7 +759,7 @@ pub fn canonicalize_module_defs<'a>(
scope, scope,
aliases, aliases,
rigid_variables, rigid_variables,
params_pattern, module_params,
declarations, declarations,
referenced_values, referenced_values,
exposed_imports: can_exposed_imports, exposed_imports: can_exposed_imports,

View file

@ -623,16 +623,29 @@ pub fn canonicalize_pattern<'a>(
} }
} }
RecordDestructure(patterns) => canonicalize_record_destructure( RecordDestructure(patterns) => {
env, let ext_var = var_store.fresh();
var_store, let whole_var = var_store.fresh();
scope,
output, let (destructs, opt_erroneous) = canonicalize_record_destructs(
pattern_type, env,
patterns, var_store,
region, scope,
permit_shadows, output,
), pattern_type,
patterns,
region,
permit_shadows,
);
// If we encountered an erroneous pattern (e.g. one with shadowing),
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
opt_erroneous.unwrap_or(Pattern::RecordDestructure {
whole_var,
ext_var,
destructs,
})
}
RequiredField(_name, _loc_pattern) => { RequiredField(_name, _loc_pattern) => {
unreachable!("should have been handled in RecordDestructure"); unreachable!("should have been handled in RecordDestructure");
@ -779,7 +792,7 @@ pub fn canonicalize_pattern<'a>(
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn canonicalize_record_destructure<'a>( pub fn canonicalize_record_destructs<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
var_store: &mut VarStore, var_store: &mut VarStore,
scope: &mut Scope, scope: &mut Scope,
@ -788,11 +801,9 @@ pub fn canonicalize_record_destructure<'a>(
patterns: &ast::Collection<Loc<ast::Pattern<'a>>>, patterns: &ast::Collection<Loc<ast::Pattern<'a>>>,
region: Region, region: Region,
permit_shadows: PermitShadows, permit_shadows: PermitShadows,
) -> Pattern { ) -> (Vec<Loc<RecordDestruct>>, Option<Pattern>) {
use ast::Pattern::*; use ast::Pattern::*;
let ext_var = var_store.fresh();
let whole_var = var_store.fresh();
let mut destructs = Vec::with_capacity(patterns.len()); let mut destructs = Vec::with_capacity(patterns.len());
let mut opt_erroneous = None; let mut opt_erroneous = None;
@ -907,13 +918,7 @@ pub fn canonicalize_record_destructure<'a>(
} }
} }
// If we encountered an erroneous pattern (e.g. one with shadowing), (destructs, opt_erroneous)
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
opt_erroneous.unwrap_or(Pattern::RecordDestructure {
whole_var,
ext_var,
destructs,
})
} }
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't /// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't

View file

@ -1,5 +1,5 @@
use crate::expr::Expr;
use crate::pattern::Pattern; use crate::pattern::Pattern;
use crate::{expr::Expr, scope::SymbolLookup};
use roc_module::symbol::{ModuleId, Symbol}; use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::Variable; use roc_types::subs::Variable;
@ -125,8 +125,18 @@ impl References {
} }
} }
pub fn insert_value_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) { pub fn insert_value_lookup(&mut self, lookup: SymbolLookup, qualified: QualifiedReference) {
self.insert(symbol, qualified.flags(ReferencesBitflags::VALUE_LOOKUP)); self.insert(
lookup.symbol,
qualified.flags(ReferencesBitflags::VALUE_LOOKUP),
);
if let Some((_, params_symbol)) = lookup.module_params {
self.insert(
params_symbol,
qualified.flags(ReferencesBitflags::VALUE_LOOKUP),
);
}
} }
pub fn insert_type_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) { pub fn insert_type_lookup(&mut self, symbol: Symbol, qualified: QualifiedReference) {

View file

@ -669,7 +669,7 @@ pub struct ScopeModules {
/// Why is this module in scope? /// Why is this module in scope?
sources: Vec<ScopeModuleSource>, sources: Vec<ScopeModuleSource>,
/// The params of a module if any /// The params of a module if any
params: Vec<Option<Symbol>>, params: Vec<Option<(Variable, Symbol)>>,
} }
impl ScopeModules { impl ScopeModules {
@ -731,7 +731,7 @@ impl ScopeModules {
&mut self, &mut self,
module_name: ModuleName, module_name: ModuleName,
module_id: ModuleId, module_id: ModuleId,
params_symbol: Option<Symbol>, params: Option<(Variable, Symbol)>,
region: Region, region: Region,
) -> Result<(), ScopeModuleSource> { ) -> Result<(), ScopeModuleSource> {
if let Some(index) = self.names.iter().position(|name| name == &module_name) { if let Some(index) = self.names.iter().position(|name| name == &module_name) {
@ -745,7 +745,7 @@ impl ScopeModules {
self.ids.push(module_id); self.ids.push(module_id);
self.names.push(module_name); self.names.push(module_name);
self.sources.push(ScopeModuleSource::Import(region)); self.sources.push(ScopeModuleSource::Import(region));
self.params.push(params_symbol); self.params.push(params);
Ok(()) Ok(())
} }
@ -768,14 +768,14 @@ impl ScopeModules {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub struct SymbolLookup { pub struct SymbolLookup {
pub symbol: Symbol, pub symbol: Symbol,
pub module_params: Option<Symbol>, pub module_params: Option<(Variable, Symbol)>,
} }
impl SymbolLookup { impl SymbolLookup {
pub fn new(symbol: Symbol, params: Option<Symbol>) -> Self { pub fn new(symbol: Symbol, params: Option<(Variable, Symbol)>) -> Self {
Self { Self {
symbol, symbol,
module_params: params, module_params: params,
@ -789,7 +789,7 @@ impl SymbolLookup {
pub struct ModuleLookup { pub struct ModuleLookup {
pub id: ModuleId, pub id: ModuleId,
pub params: Option<Symbol>, pub params: Option<(Variable, Symbol)>,
} }
impl ModuleLookup { impl ModuleLookup {

View file

@ -6,7 +6,7 @@ use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia; use roc_module::called_via::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::{is_expr_suffixed, Pattern, TypeAnnotation, ValueDef, WhenBranch}; use roc_parse::ast::{is_expr_suffixed, Pattern, TryTarget, TypeAnnotation, ValueDef, WhenBranch};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use std::cell::Cell; use std::cell::Cell;
@ -34,14 +34,17 @@ pub enum EUnwrapped<'a> {
/// e.g. x = first! (second! 42) /// e.g. x = first! (second! 42)
/// The first unwrap will produce /// The first unwrap will produce
/// `UnwrappedDefExpr<first (second! 42)>` /// `UnwrappedDefExpr<first (second! 42)>`
UnwrappedDefExpr(&'a Loc<Expr<'a>>), UnwrappedDefExpr {
loc_expr: &'a Loc<Expr<'a>>,
target: TryTarget,
},
/// Suffixed sub expression /// Suffixed sub expression
/// e.g. x = first! (second! 42) /// e.g. x = first! (second! 42)
/// In this example, the second unwrap (after unwrapping the top level `first!`) will produce /// In this example, the second unwrap (after unwrapping the top level `first!`) will produce
/// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>` /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>`
UnwrappedSubExpr { UnwrappedSubExpr {
/// the unwrapped expression argument for Task.await /// the unwrapped expression argument for `try` functions
sub_arg: &'a Loc<Expr<'a>>, sub_arg: &'a Loc<Expr<'a>>,
/// the pattern for the closure /// the pattern for the closure
@ -49,6 +52,9 @@ pub enum EUnwrapped<'a> {
/// the expression to replace the unwrapped /// the expression to replace the unwrapped
sub_new: &'a Loc<Expr<'a>>, sub_new: &'a Loc<Expr<'a>>,
/// The type of the target for the suffix, e.g. a Task or Result
target: TryTarget,
}, },
/// Malformed use of the suffix /// Malformed use of the suffix
@ -59,12 +65,16 @@ fn init_unwrapped_err<'a>(
arena: &'a Bump, arena: &'a Bump,
unwrapped_expr: &'a Loc<Expr<'a>>, unwrapped_expr: &'a Loc<Expr<'a>>,
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>, maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
target: TryTarget,
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> { ) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
match maybe_def_pat { match maybe_def_pat {
Some(..) => { Some(..) => {
// we have a def pattern, so no need to generate a new pattern // we have a def pattern, so no need to generate a new pattern
// as this should only be created in the first call from a def // as this should only be created in the first call from a def
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
})
} }
None => { None => {
// Provide an intermediate answer expression and pattern when unwrapping a // Provide an intermediate answer expression and pattern when unwrapping a
@ -87,13 +97,14 @@ fn init_unwrapped_err<'a>(
sub_arg: unwrapped_expr, sub_arg: unwrapped_expr,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) })
} }
} }
} }
/// Descend through the AST and unwrap each suffixed expression /// Descend through the AST and unwrap each suffixed expression
/// when an expression is unwrapped, we apply a `Task.await` and /// when an expression is unwrapped, we apply the appropriate try function and
/// then descend through the AST again until there are no more suffixed /// then descend through the AST again until there are no more suffixed
/// expressions, or we hit an error /// expressions, or we hit an error
pub fn unwrap_suffixed_expression<'a>( pub fn unwrap_suffixed_expression<'a>(
@ -103,10 +114,13 @@ pub fn unwrap_suffixed_expression<'a>(
) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> { ) -> Result<&'a Loc<Expr<'a>>, EUnwrapped<'a>> {
let unwrapped_expression = { let unwrapped_expression = {
match loc_expr.value { match loc_expr.value {
Expr::TaskAwaitBang(sub_expr) => { Expr::TrySuffix {
expr: sub_expr,
target,
} => {
let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr)); let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr));
init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat) init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat, target)
} }
Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat), Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat),
@ -154,15 +168,22 @@ pub fn unwrap_suffixed_expression<'a>(
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
return Ok(new_expect); return Ok(new_expect);
} }
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
}) => {
let new_expect = arena let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
Err(EUnwrapped::UnwrappedDefExpr(new_expect)) Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: new_expect,
target,
})
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr, sub_arg: unwrapped_expr,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let new_expect = arena let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
@ -170,6 +191,7 @@ pub fn unwrap_suffixed_expression<'a>(
sub_arg: new_expect, sub_arg: new_expect,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) })
} }
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -208,13 +230,14 @@ pub fn unwrap_suffixed_expression_parens_help<'a>(
)); ));
Ok(new_parens) Ok(new_parens)
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead"); internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead");
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let new_parens = arena.alloc(Loc::at( let new_parens = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
@ -224,6 +247,7 @@ pub fn unwrap_suffixed_expression_parens_help<'a>(
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new: new_parens, sub_new: new_parens,
target,
}) })
} }
Err(err) => Err(err), Err(err) => Err(err),
@ -247,13 +271,13 @@ pub fn unwrap_suffixed_expression_closure_help<'a>(
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr))); let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr)));
Ok(new_closure) Ok(new_closure)
} }
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None); let new_closure_loc_ret = apply_try_function(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None, target);
let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret))); let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret)));
Ok(new_closure) Ok(new_closure)
} }
Err(err) => { Err(err) => {
debug_assert!(false,"the closure Defs was malformd, got {:#?}", err); debug_assert!(false,"the closure Defs was malformed, got {:#?}", err);
Err(EUnwrapped::Malformed) Err(EUnwrapped::Malformed)
} }
} }
@ -278,22 +302,22 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
Ok(new_arg) => { Ok(new_arg) => {
*arg = new_arg; *arg = new_arg;
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern"); internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern");
} }
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg, target }) => {
*arg = new_arg; *arg = new_arg;
let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via))); let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via)));
return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply}); return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply, target });
} }
Err(err) => return Err(err), Err(err) => return Err(err),
} }
} }
// special case for when our Apply function is a suffixed Var (but not multiple suffixed) // special case for when our Apply function is a suffixed Var (but not multiple suffixed)
if let Expr::TaskAwaitBang(sub_expr) = function.value { if let Expr::TrySuffix { expr: sub_expr, target } = function.value {
let unwrapped_function = arena.alloc(Loc::at( let unwrapped_function = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
*sub_expr, *sub_expr,
@ -301,7 +325,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
return init_unwrapped_err(arena, new_apply, maybe_def_pat); return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
} }
// function is another expression // function is another expression
@ -310,15 +334,14 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))); let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via)));
Ok(new_apply) Ok(new_apply)
} }
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_function)) => { Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_function, target }) => {
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
Err(EUnwrapped::UnwrappedDefExpr(new_apply)) Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target })
} }
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => {
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via)));
Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply}) Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target })
} }
Err(err) => Err(err) Err(err) => Err(err)
} }
@ -361,21 +384,23 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let unwrapped_expression = apply_task_await( let unwrapped_expression = apply_try_function(
arena, arena,
sub_arg.region, sub_arg.region,
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
None, None,
target,
); );
let mut new_if_thens = Vec::new_in(arena); let mut new_if_thens = Vec::new_in(arena);
@ -422,13 +447,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); return unwrap_suffixed_expression(arena, new_if, maybe_def_pat);
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
if before.is_empty() { if before.is_empty() {
let mut new_if_thens = Vec::new_in(arena); let mut new_if_thens = Vec::new_in(arena);
@ -445,13 +471,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
), ),
)); ));
let unwrapped_if_then = apply_task_await( let unwrapped_if_then = apply_try_function(
arena, arena,
sub_arg.region, sub_arg.region,
sub_arg, sub_arg,
sub_pat, sub_pat,
new_if, new_if,
None, None,
target,
); );
return unwrap_suffixed_expression( return unwrap_suffixed_expression(
@ -473,13 +500,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
), ),
)); ));
let after_if_then = apply_task_await( let after_if_then = apply_try_function(
arena, arena,
sub_arg.region, sub_arg.region,
sub_arg, sub_arg,
sub_pat, sub_pat,
after_if, after_if,
None, None,
target,
); );
let before_if_then = arena.alloc(Loc::at( let before_if_then = arena.alloc(Loc::at(
@ -507,16 +535,24 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>(
Expr::If(if_thens, unwrapped_final_else), Expr::If(if_thens, unwrapped_final_else),
))); )));
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern");
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let unwrapped_final_else = let unwrapped_final_else = apply_try_function(
apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None); arena,
sub_arg.region,
sub_arg,
sub_pat,
sub_new,
None,
target,
);
let new_if = arena.alloc(Loc::at( let new_if = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
@ -551,7 +587,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
if is_expr_suffixed(&branch_loc_expr.value) { if is_expr_suffixed(&branch_loc_expr.value) {
let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) { let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) {
Ok(unwrapped_branch_value) => unwrapped_branch_value, Ok(unwrapped_branch_value) => unwrapped_branch_value,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None), Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => apply_try_function(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None, target),
Err(..) => return Err(EUnwrapped::Malformed), Err(..) => return Err(EUnwrapped::Malformed),
}; };
@ -578,12 +614,12 @@ pub fn unwrap_suffixed_expression_when_help<'a>(
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches))); let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches)));
Ok(new_when) Ok(new_when)
} }
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches))); let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches)));
let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when, None); let applied_task_await = apply_try_function(arena,loc_expr.region,sub_arg,sub_pat,new_when, None, target);
Ok(applied_task_await) Ok(applied_task_await)
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) Err(EUnwrapped::UnwrappedDefExpr { .. })
| Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed) | Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed)
} }
@ -631,7 +667,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
current_value_def.replace_expr(unwrapped_def); current_value_def.replace_expr(unwrapped_def);
local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region); local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region);
} }
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => {
let split_defs = local_defs.split_defs_around(tag_index); let split_defs = local_defs.split_defs_around(tag_index);
let before_empty = split_defs.before.is_empty(); let before_empty = split_defs.before.is_empty();
let after_empty = split_defs.after.is_empty(); let after_empty = split_defs.after.is_empty();
@ -640,48 +676,60 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
// We pass None as a def pattern here because it's desugaring of the ret expression // We pass None as a def pattern here because it's desugaring of the ret expression
let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) { let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) {
Ok(next_expr) => next_expr, Ok(next_expr) => next_expr,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
// We need to apply Task.ok here as the defs final expression was unwrapped // We need to apply Task.ok here as the defs final expression was unwrapped
apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None) apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target)
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up // TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed); return Err(EUnwrapped::Malformed);
}, },
}; };
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat); return unwrap_suffixed_expression(
arena,
apply_try_function(
arena,
def_expr.region,
unwrapped_expr,
def_pattern,
next_expr,
ann_type,
target,
),
maybe_def_pat
);
} else if before_empty { } else if before_empty {
// NIL before, SOME after -> FIRST DEF // NIL before, SOME after -> FIRST DEF
let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret))); let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret)));
let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){ let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){
Ok(next_expr) => next_expr, Ok(next_expr) => next_expr,
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None) apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target)
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up // TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed); return Err(EUnwrapped::Malformed);
}, },
}; };
return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr,ann_type), maybe_def_pat); return unwrap_suffixed_expression(arena, apply_try_function(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type, target), maybe_def_pat);
} else if after_empty { } else if after_empty {
// SOME before, NIL after -> LAST DEF // SOME before, NIL after -> LAST DEF
// We pass None as a def pattern here because it's desugaring of the ret expression // We pass None as a def pattern here because it's desugaring of the ret expression
match unwrap_suffixed_expression(arena,loc_ret,None){ match unwrap_suffixed_expression(arena,loc_ret,None){
Ok(new_loc_ret) => { Ok(new_loc_ret) => {
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
}, },
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None); let new_loc_ret = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target);
let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
// TODO confirm this is correct with test case // TODO confirm this is correct with test case
return Err(EUnwrapped::Malformed); return Err(EUnwrapped::Malformed);
} }
@ -695,28 +743,28 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){ match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){
Ok(new_loc_ret) => { Ok(new_loc_ret) => {
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
}, },
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None); let new_loc_ret = apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target);
let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await)));
return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat);
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => {
// TODO handle case when we have maybe_def_pat so can return an unwrapped up // TODO handle case when we have maybe_def_pat so can return an unwrapped up
return Err(EUnwrapped::Malformed); return Err(EUnwrapped::Malformed);
}, },
}; };
} }
} }
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_body_def = ValueDef::Body(def_pattern, sub_new); let new_body_def = ValueDef::Body(def_pattern, sub_new);
local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region); local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region);
let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret))); let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret)));
let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type); let replaced_def = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type, target);
return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat); return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat);
} }
Err(err) => return Err(err) Err(err) => return Err(err)
@ -730,12 +778,12 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
Ok(new_loc_ret) => { Ok(new_loc_ret) => {
Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))))
}, },
Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => {
let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None); let new_loc_ret = apply_try_function(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None, target);
let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)));
unwrap_suffixed_expression(arena, new_defs, None) unwrap_suffixed_expression(arena, new_defs, None)
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
// TODO confirm this is correct with test case // TODO confirm this is correct with test case
Err(EUnwrapped::Malformed) Err(EUnwrapped::Malformed)
} }
@ -769,6 +817,7 @@ fn unwrap_low_level_dbg<'a>(
sub_arg, sub_arg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let new_dbg = arena.alloc(Loc::at( let new_dbg = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
@ -777,18 +826,19 @@ fn unwrap_low_level_dbg<'a>(
unwrap_suffixed_expression( unwrap_suffixed_expression(
arena, arena,
apply_task_await( apply_try_function(
arena, arena,
new_dbg.region, new_dbg.region,
sub_arg, sub_arg,
sub_pat, sub_pat,
new_dbg, new_dbg,
None, None,
target,
), ),
maybe_def_pat, maybe_def_pat,
) )
} }
Err(EUnwrapped::UnwrappedDefExpr(..)) => { Err(EUnwrapped::UnwrappedDefExpr { .. }) => {
internal_error!( internal_error!(
"unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead" "unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead"
); );
@ -805,17 +855,24 @@ fn unwrap_low_level_dbg<'a>(
)); ));
Ok(&*new_dbg) Ok(&*new_dbg)
} }
Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
}) => {
let new_dbg = arena.alloc(Loc::at( let new_dbg = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
LowLevelDbg(dbg_src, arg, unwrapped_expr), LowLevelDbg(dbg_src, arg, unwrapped_expr),
)); ));
Err(EUnwrapped::UnwrappedDefExpr(new_dbg)) Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: new_dbg,
target,
})
} }
Err(EUnwrapped::UnwrappedSubExpr { Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr, sub_arg: unwrapped_expr,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) => { }) => {
let new_dbg = arena.alloc(Loc::at( let new_dbg = arena.alloc(Loc::at(
loc_expr.region, loc_expr.region,
@ -825,6 +882,7 @@ fn unwrap_low_level_dbg<'a>(
sub_arg: new_dbg, sub_arg: new_dbg,
sub_pat, sub_pat,
sub_new, sub_new,
target,
}) })
} }
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -836,25 +894,26 @@ fn unwrap_low_level_dbg<'a>(
} }
} }
/// Helper for `Task.await loc_expr \loc_pat -> loc_cont` /// Helper for try_function loc_expr \loc_pat -> loc_cont`
pub fn apply_task_await<'a>( pub fn apply_try_function<'a>(
arena: &'a Bump, arena: &'a Bump,
region: Region, region: Region,
loc_expr: &'a Loc<Expr<'a>>, loc_expr: &'a Loc<Expr<'a>>,
loc_pat: &'a Loc<Pattern<'a>>, loc_pat: &'a Loc<Pattern<'a>>,
loc_cont: &'a Loc<Expr<'a>>, loc_cont: &'a Loc<Expr<'a>>,
maybe_loc_ann: Option<(&'a Loc<Pattern>, &'a Loc<TypeAnnotation<'a>>)>, maybe_loc_ann: Option<(&'a Loc<Pattern>, &'a Loc<TypeAnnotation<'a>>)>,
target: TryTarget,
) -> &'a Loc<Expr<'a>> { ) -> &'a Loc<Expr<'a>> {
let task_await_first_arg = match maybe_loc_ann { let try_function_first_arg = match maybe_loc_ann {
Some((loc_ann_pat, loc_type)) => { Some((loc_ann_pat, loc_type)) => {
// loc_ann_pat : loc_type // loc_ann_pat : loc_type
// loc_pat = loc_expr! // loc_pat = loc_expr!
// loc_cont // loc_cont
// desugar to // desugar to
// Task.await // try_function
// ( // (
// #!0_expr : Task loc_type _ // #!0_expr : Target loc_type _
// #!0_expr = loc_expr // #!0_expr = loc_expr
// #!0_expr // #!0_expr
// ) // )
@ -875,8 +934,12 @@ pub fn apply_task_await<'a>(
let new_ident = arena.alloc(new_ident); let new_ident = arena.alloc(new_ident);
// #!0_expr (pattern) // #!0_expr (pattern)
// #!0_expr : Task loc_type _ // #!0_expr : Target loc_type _
// #!0_expr = loc_expr // #!0_expr = loc_expr
let target_type_name = match target {
TryTarget::Task => "Task",
TryTarget::Result => "Result",
};
let value_def = ValueDef::AnnotatedBody { let value_def = ValueDef::AnnotatedBody {
ann_pattern: arena.alloc(Loc::at( ann_pattern: arena.alloc(Loc::at(
loc_ann_pat.region, loc_ann_pat.region,
@ -893,7 +956,7 @@ pub fn apply_task_await<'a>(
loc_type.region, loc_type.region,
TypeAnnotation::Apply( TypeAnnotation::Apply(
arena.alloc(""), arena.alloc(""),
arena.alloc("Task"), arena.alloc(target_type_name),
arena.alloc([ arena.alloc([
*loc_type, *loc_type,
Loc::at(loc_type.region, TypeAnnotation::Inferred), Loc::at(loc_type.region, TypeAnnotation::Inferred),
@ -918,7 +981,7 @@ pub fn apply_task_await<'a>(
)); ));
// ( // (
// #!0_expr : Task loc_type _ // #!0_expr : Target loc_type _
// #!0_expr = loc_expr // #!0_expr = loc_expr
// #!0_expr // #!0_expr
// ) // )
@ -935,7 +998,7 @@ pub fn apply_task_await<'a>(
// loc_cont // loc_cont
// desugar to // desugar to
// Task.await loc_expr \loc_pat -> loc_cont // try_function loc_expr \loc_pat -> loc_cont
loc_expr loc_expr
} }
}; };
@ -945,25 +1008,29 @@ pub fn apply_task_await<'a>(
// \x -> x! // \x -> x!
// \x -> x // \x -> x
if is_matching_intermediate_answer(loc_pat, loc_cont) { if is_matching_intermediate_answer(loc_pat, loc_cont) {
return task_await_first_arg; return try_function_first_arg;
} }
// \loc_pat -> loc_cont // \loc_pat -> loc_cont
let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont)));
// Task.await task_first_arg closure // try_function first_arg closure
let (try_function_module, try_function_ident, called_via) = match target {
TryTarget::Task => (ModuleName::TASK, "await", CalledVia::BangSuffix),
TryTarget::Result => (ModuleName::RESULT, "try", CalledVia::QuestionSuffix),
};
arena.alloc(Loc::at( arena.alloc(Loc::at(
region, region,
Apply( Apply(
arena.alloc(Loc { arena.alloc(Loc {
region, region,
value: Var { value: Var {
module_name: ModuleName::TASK, module_name: try_function_module,
ident: "await", ident: try_function_ident,
}, },
}), }),
arena.alloc([task_await_first_arg, closure]), arena.alloc([try_function_first_arg, closure]),
CalledVia::BangSuffix, called_via,
), ),
)) ))
} }

View file

@ -0,0 +1,207 @@
use crate::def::Def;
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
use crate::pattern::Pattern;
use crate::scope::Scope;
use roc_collections::SendMap;
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{LambdaSet, OptAbleVar, Type};
pub fn build_host_exposed_def(
scope: &mut Scope,
symbol: Symbol,
ident: &str,
var_store: &mut VarStore,
annotation: crate::annotation::Annotation,
) -> Def {
let expr_var = var_store.fresh();
let pattern = Pattern::Identifier(symbol);
let mut pattern_vars = SendMap::default();
pattern_vars.insert(symbol, expr_var);
let mut arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)> = Vec::new();
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
let crate::annotation::Annotation {
introduced_variables,
typ,
aliases,
..
} = annotation;
let def_body = {
match typ.shallow_structural_dealias() {
Type::Function(args, _, _) => {
for i in 0..args.len() {
let name = format!("closure_arg_{ident}_{i}");
let arg_symbol = {
let ident = name.clone().into();
scope.introduce(ident, Region::zero()).unwrap()
};
let arg_var = var_store.fresh();
arguments.push((
arg_var,
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(arg_symbol)),
));
captured_symbols.push((arg_symbol, arg_var));
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
}
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
ret_var: var_store.fresh(),
};
let task_closure_symbol = {
let name = format!("task_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()
};
let task_closure = Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
});
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
let body = Expr::OpaqueRef {
opaque_var: var_store.fresh(),
name: Symbol::TASK_TASK,
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
};
Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: symbol,
captured_symbols: std::vec::Vec::new(),
recursive: Recursive::NotRecursive,
arguments,
loc_body: Box::new(Loc::at_zero(body)),
})
}
_ => {
// not a function
let foreign_symbol_name = format!("roc_fx_{ident}");
let low_level_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
ret_var: var_store.fresh(),
};
let task_closure_symbol = {
let name = format!("task_closure_{ident}");
let ident = name.into();
scope.introduce(ident, Region::zero()).unwrap()
};
let task_closure = Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
arguments: vec![(
var_store.fresh(),
AnnotatedMark::new(var_store),
Loc::at_zero(empty_record_pattern(var_store)),
)],
loc_body: Box::new(Loc::at_zero(low_level_call)),
});
let (specialized_def_type, type_arguments, lambda_set_variables) =
build_fresh_opaque_variables(var_store);
Expr::OpaqueRef {
opaque_var: var_store.fresh(),
name: Symbol::TASK_TASK,
argument: Box::new((var_store.fresh(), Loc::at_zero(task_closure))),
specialized_def_type,
type_arguments,
lambda_set_variables,
}
}
}
};
let def_annotation = crate::def::Annotation {
signature: typ,
introduced_variables,
aliases,
region: Region::zero(),
};
Def {
loc_pattern: Loc::at_zero(pattern),
loc_expr: Loc::at_zero(def_body),
expr_var,
pattern_vars,
annotation: Some(def_annotation),
}
}
fn build_fresh_opaque_variables(
var_store: &mut VarStore,
) -> (Box<Type>, Vec<OptAbleVar>, Vec<LambdaSet>) {
let closure_var = var_store.fresh();
let ok_var = var_store.fresh();
let err_var = var_store.fresh();
let result_var = var_store.fresh();
let actual = Type::Function(
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(Type::Variable(result_var)),
);
let type_arguments = vec![
OptAbleVar {
var: ok_var,
opt_abilities: None,
},
OptAbleVar {
var: err_var,
opt_abilities: None,
},
];
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];
(Box::new(actual), type_arguments, lambda_set_variables)
}
#[inline(always)]
fn empty_record_pattern(var_store: &mut VarStore) -> Pattern {
Pattern::RecordDestructure {
whole_var: var_store.fresh(),
ext_var: var_store.fresh(),
destructs: vec![],
}
}

View file

@ -55,10 +55,12 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
// rules multiple times unnecessarily. // rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr( let loc_expr = desugar::desugar_expr(
arena, arena,
&mut var_store,
&loc_expr, &loc_expr,
expr_str, expr_str,
&mut None, &mut None,
arena.alloc("TestPath"), arena.alloc("TestPath"),
&mut Default::default(),
); );
let mut scope = Scope::new( let mut scope = Scope::new(

View file

@ -0,0 +1,145 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 449
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-26,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 1),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
Body(
@0-4 Identifier {
ident: "main",
},
@11-26 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@15-26,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@15-26 Identifier {
ident: "64",
},
@15-26 ParensAround(
Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@20-25,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@20-25 Identifier {
ident: "63",
},
@20-25 Apply(
@22-23 Var {
module_name: "Num",
ident: "add",
},
[
@20-21 Num(
"1",
),
@24-25 Num(
"1",
),
],
BinOp(
Plus,
),
),
),
],
},
@15-26 LowLevelDbg(
(
"test.roc:3",
" ",
),
@20-25 Apply(
@20-25 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@20-25 Var {
module_name: "",
ident: "63",
},
],
Space,
),
@20-25 Var {
module_name: "",
ident: "63",
},
),
),
),
),
],
},
@11-26 LowLevelDbg(
(
"test.roc:2",
"in =\n ",
),
@15-26 Apply(
@15-26 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@15-26 Var {
module_name: "",
ident: "64",
},
],
Space,
),
@15-26 Var {
module_name: "",
ident: "64",
},
),
),
),
],
}

View file

@ -0,0 +1,81 @@
---
source: crates/compiler/can/tests/test_suffixed.rs
assertion_line: 459
expression: snapshot
---
Defs {
tags: [
Index(2147483648),
],
regions: [
@0-19,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 1),
],
spaces: [
Newline,
],
type_defs: [],
value_defs: [
Body(
@0-4 Identifier {
ident: "main",
},
@11-19 Defs(
Defs {
tags: [
Index(2147483648),
],
regions: [
@11-12,
],
space_before: [
Slice(start = 0, length = 0),
],
space_after: [
Slice(start = 0, length = 0),
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@11-12 Identifier {
ident: "63",
},
@11-12 Num(
"1",
),
),
],
},
@11-19 LowLevelDbg(
(
"test.roc:2",
" ",
),
@11-12 Apply(
@11-12 Var {
module_name: "Inspect",
ident: "toStr",
},
[
@11-12 Var {
module_name: "",
ident: "63",
},
],
Space,
),
@11-12 Var {
module_name: "",
ident: "63",
},
),
),
),
],
}

View file

@ -7,12 +7,23 @@ mod suffixed_tests {
use insta::assert_snapshot; use insta::assert_snapshot;
use roc_can::desugar::desugar_defs_node_values; use roc_can::desugar::desugar_defs_node_values;
use roc_parse::test_helpers::parse_defs_with; use roc_parse::test_helpers::parse_defs_with;
use roc_types::subs::VarStore;
macro_rules! run_test { macro_rules! run_test {
($src:expr) => {{ ($src:expr) => {{
let arena = &Bump::new(); let arena = &Bump::new();
let mut var_store = VarStore::default();
let mut defs = parse_defs_with(arena, indoc!($src)).unwrap(); let mut defs = parse_defs_with(arena, indoc!($src)).unwrap();
desugar_defs_node_values(arena, &mut defs, $src, &mut None, "test.roc", true); desugar_defs_node_values(
arena,
&mut var_store,
&mut defs,
$src,
&mut None,
"test.roc",
true,
&mut Default::default(),
);
let snapshot = format!("{:#?}", &defs); let snapshot = format!("{:#?}", &defs);
println!("{}", snapshot); println!("{}", snapshot);
@ -433,6 +444,16 @@ mod suffixed_tests {
); );
} }
#[test]
fn dbg_expr() {
run_test!(
r#"
main =
dbg (dbg 1 + 1)
"#
);
}
#[test] #[test]
fn apply_argument_single() { fn apply_argument_single() {
run_test!( run_test!(

View file

@ -3424,21 +3424,15 @@
"dev": true "dev": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.18", "version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@jsdevtools/ono": { "node_modules/@jsdevtools/ono": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
@ -4351,20 +4345,10 @@
"@types/json-schema": "*" "@types/json-schema": "*"
} }
}, },
"node_modules/@types/eslint-scope": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"dev": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
}
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.1", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true "dev": true
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
@ -4932,9 +4916,9 @@
} }
}, },
"node_modules/@webassemblyjs/ast": { "node_modules/@webassemblyjs/ast": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-numbers": "1.11.6",
@ -4954,9 +4938,9 @@
"dev": true "dev": true
}, },
"node_modules/@webassemblyjs/helper-buffer": { "node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
"dev": true "dev": true
}, },
"node_modules/@webassemblyjs/helper-numbers": { "node_modules/@webassemblyjs/helper-numbers": {
@ -4977,15 +4961,15 @@
"dev": true "dev": true
}, },
"node_modules/@webassemblyjs/helper-wasm-section": { "node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.11.6", "@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/wasm-gen": "1.11.6" "@webassemblyjs/wasm-gen": "1.12.1"
} }
}, },
"node_modules/@webassemblyjs/ieee754": { "node_modules/@webassemblyjs/ieee754": {
@ -5013,28 +4997,28 @@
"dev": true "dev": true
}, },
"node_modules/@webassemblyjs/wasm-edit": { "node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.11.6", "@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/helper-wasm-section": "1.11.6", "@webassemblyjs/helper-wasm-section": "1.12.1",
"@webassemblyjs/wasm-gen": "1.11.6", "@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-opt": "1.11.6", "@webassemblyjs/wasm-opt": "1.12.1",
"@webassemblyjs/wasm-parser": "1.11.6", "@webassemblyjs/wasm-parser": "1.12.1",
"@webassemblyjs/wast-printer": "1.11.6" "@webassemblyjs/wast-printer": "1.12.1"
} }
}, },
"node_modules/@webassemblyjs/wasm-gen": { "node_modules/@webassemblyjs/wasm-gen": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/ieee754": "1.11.6",
"@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/leb128": "1.11.6",
@ -5042,24 +5026,24 @@
} }
}, },
"node_modules/@webassemblyjs/wasm-opt": { "node_modules/@webassemblyjs/wasm-opt": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-buffer": "1.11.6", "@webassemblyjs/helper-buffer": "1.12.1",
"@webassemblyjs/wasm-gen": "1.11.6", "@webassemblyjs/wasm-gen": "1.12.1",
"@webassemblyjs/wasm-parser": "1.11.6" "@webassemblyjs/wasm-parser": "1.12.1"
} }
}, },
"node_modules/@webassemblyjs/wasm-parser": { "node_modules/@webassemblyjs/wasm-parser": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
"@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/ieee754": "1.11.6",
@ -5068,12 +5052,12 @@
} }
}, },
"node_modules/@webassemblyjs/wast-printer": { "node_modules/@webassemblyjs/wast-printer": {
"version": "1.11.6", "version": "1.12.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@webassemblyjs/ast": "1.11.6", "@webassemblyjs/ast": "1.12.1",
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
@ -5142,10 +5126,10 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/acorn-import-assertions": { "node_modules/acorn-import-attributes": {
"version": "1.9.0", "version": "1.9.5",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"dev": true, "dev": true,
"peerDependencies": { "peerDependencies": {
"acorn": "^8" "acorn": "^8"
@ -6039,9 +6023,9 @@
"dev": true "dev": true
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.21.9", "version": "4.23.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -6058,10 +6042,10 @@
} }
], ],
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001503", "caniuse-lite": "^1.0.30001646",
"electron-to-chromium": "^1.4.431", "electron-to-chromium": "^1.5.4",
"node-releases": "^2.0.12", "node-releases": "^2.0.18",
"update-browserslist-db": "^1.0.11" "update-browserslist-db": "^1.1.0"
}, },
"bin": { "bin": {
"browserslist": "cli.js" "browserslist": "cli.js"
@ -6178,9 +6162,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001516", "version": "1.0.30001655",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
"integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -7575,9 +7559,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.461", "version": "1.5.13",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
"integrity": "sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==", "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==",
"dev": true "dev": true
}, },
"node_modules/elkjs": { "node_modules/elkjs": {
@ -7622,9 +7606,9 @@
} }
}, },
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.15.0", "version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
@ -7815,9 +7799,9 @@
} }
}, },
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -13471,9 +13455,9 @@
"dev": true "dev": true
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.13", "version": "2.0.18",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true "dev": true
}, },
"node_modules/normalize-path": { "node_modules/normalize-path": {
@ -13926,9 +13910,9 @@
"dev": true "dev": true
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
"dev": true "dev": true
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
@ -17473,9 +17457,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.19.0", "version": "5.31.6",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz",
"integrity": "sha512-JpcpGOQLOXm2jsomozdMDpd5f8ZHh1rR48OFgWUH3QsyZcfPgv2qDCYbcDEAYNd4OZRj2bWYKpwdll/udZCk/Q==", "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
@ -17491,16 +17475,16 @@
} }
}, },
"node_modules/terser-webpack-plugin": { "node_modules/terser-webpack-plugin": {
"version": "5.3.9", "version": "5.3.10",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
"integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/trace-mapping": "^0.3.17", "@jridgewell/trace-mapping": "^0.3.20",
"jest-worker": "^27.4.5", "jest-worker": "^27.4.5",
"schema-utils": "^3.1.1", "schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.1", "serialize-javascript": "^6.0.1",
"terser": "^5.16.8" "terser": "^5.26.0"
}, },
"engines": { "engines": {
"node": ">= 10.13.0" "node": ">= 10.13.0"
@ -17984,9 +17968,9 @@
} }
}, },
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.0.11", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
"integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -18003,8 +17987,8 @@
} }
], ],
"dependencies": { "dependencies": {
"escalade": "^3.1.1", "escalade": "^3.1.2",
"picocolors": "^1.0.0" "picocolors": "^1.0.1"
}, },
"bin": { "bin": {
"update-browserslist-db": "cli.js" "update-browserslist-db": "cli.js"
@ -18140,9 +18124,9 @@
} }
}, },
"node_modules/watchpack": { "node_modules/watchpack": {
"version": "2.4.0", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
@ -18171,34 +18155,33 @@
} }
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.88.1", "version": "5.94.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.1.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
"integrity": "sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5",
"@types/estree": "^1.0.0", "@webassemblyjs/ast": "^1.12.1",
"@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.12.1",
"@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.12.1",
"@webassemblyjs/wasm-parser": "^1.11.5",
"acorn": "^8.7.1", "acorn": "^8.7.1",
"acorn-import-assertions": "^1.9.0", "acorn-import-attributes": "^1.9.5",
"browserslist": "^4.14.5", "browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.15.0", "enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1", "es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1", "eslint-scope": "5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9", "graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1", "json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0", "loader-runner": "^4.2.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"schema-utils": "^3.2.0", "schema-utils": "^3.2.0",
"tapable": "^2.1.1", "tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.7", "terser-webpack-plugin": "^5.3.10",
"watchpack": "^2.4.0", "watchpack": "^2.4.1",
"webpack-sources": "^3.2.3" "webpack-sources": "^3.2.3"
}, },
"bin": { "bin": {

View file

@ -17,6 +17,10 @@ impl<T> VecSet<T> {
pub fn into_vec(self) -> Vec<T> { pub fn into_vec(self) -> Vec<T> {
self.elements self.elements
} }
pub fn reserve(&mut self, additional: usize) {
self.elements.reserve(additional)
}
} }
impl<T: PartialEq> VecSet<T> { impl<T: PartialEq> VecSet<T> {

View file

@ -569,8 +569,8 @@ pub fn constrain_expr(
Var(symbol, variable) Var(symbol, variable)
| ParamsVar { | ParamsVar {
symbol, symbol,
params: _,
var: variable, var: variable,
..
} => { } => {
// Save the expectation in the variable, then lookup the symbol's type in the environment // Save the expectation in the variable, then lookup the symbol's type in the environment
let expected_type = *constraints[expected].get_type_ref(); let expected_type = *constraints[expected].get_type_ref();

View file

@ -3,13 +3,12 @@ use crate::pattern::{constrain_pattern, PatternState};
use roc_can::abilities::{PendingAbilitiesStore, PendingMemberType}; use roc_can::abilities::{PendingAbilitiesStore, PendingMemberType};
use roc_can::constraint::{Constraint, Constraints, Generalizable}; use roc_can::constraint::{Constraint, Constraints, Generalizable};
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_can::expr::{AnnotatedMark, Declarations}; use roc_can::expr::Declarations;
use roc_can::module::ModuleParams;
use roc_can::pattern::Pattern; use roc_can::pattern::Pattern;
use roc_collections::MutMap; use roc_collections::MutMap;
use roc_error_macros::internal_error;
use roc_module::symbol::{ModuleId, Symbol}; use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::subs::Variable;
use roc_types::types::{AnnotationSource, Category, Type, Types}; use roc_types::types::{AnnotationSource, Category, Type, Types};
pub fn constrain_module( pub fn constrain_module(
@ -18,12 +17,12 @@ pub fn constrain_module(
symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>, symbols_from_requires: Vec<(Loc<Symbol>, Loc<Type>)>,
abilities_store: &PendingAbilitiesStore, abilities_store: &PendingAbilitiesStore,
declarations: &Declarations, declarations: &Declarations,
params_pattern: &Option<(Variable, AnnotatedMark, Loc<Pattern>)>, opt_module_params: &Option<ModuleParams>,
home: ModuleId, home: ModuleId,
) -> Constraint { ) -> Constraint {
let constraint = crate::expr::constrain_decls(types, constraints, home, declarations); let constraint = crate::expr::constrain_decls(types, constraints, home, declarations);
let constraint = match params_pattern { let constraint = match opt_module_params {
Some(params_pattern) => { Some(params_pattern) => {
constrain_params(types, constraints, home, constraint, params_pattern) constrain_params(types, constraints, home, constraint, params_pattern)
} }
@ -51,7 +50,7 @@ fn constrain_params(
constraints: &mut Constraints, constraints: &mut Constraints,
home: ModuleId, home: ModuleId,
constraint: Constraint, constraint: Constraint,
(pattern_var, _, loc_pattern): &(Variable, AnnotatedMark, Loc<Pattern>), module_params: &ModuleParams,
) -> Constraint { ) -> Constraint {
let mut env = Env { let mut env = Env {
home, home,
@ -59,23 +58,13 @@ fn constrain_params(
resolutions_to_make: vec![], resolutions_to_make: vec![],
}; };
let index = constraints.push_variable(*pattern_var); let index = constraints.push_variable(module_params.whole_var);
let expected_params = constraints.push_pat_expected_type(PExpected::NoExpectation(index)); let expected_params = constraints.push_pat_expected_type(PExpected::NoExpectation(index));
let mut state = PatternState::default(); let mut state = PatternState::default();
let closed_con = match loc_pattern.value { let empty_rec = constraints.push_type(types, Types::EMPTY_RECORD);
Pattern::RecordDestructure { let closed_con = constraints.store(empty_rec, module_params.record_ext_var, file!(), line!());
whole_var: _,
ext_var,
destructs: _,
} => {
// Disallow record extension for module params
let empty_rec = constraints.push_type(types, Types::EMPTY_RECORD);
constraints.store(empty_rec, ext_var, file!(), line!())
}
_ => internal_error!("Only record destructures are allowed in module params. This should've been caught earlier."),
};
state.constraints.push(closed_con); state.constraints.push(closed_con);
@ -83,8 +72,8 @@ fn constrain_params(
types, types,
constraints, constraints,
&mut env, &mut env,
&loc_pattern.value, &module_params.pattern().value,
loc_pattern.region, module_params.region,
expected_params, expected_params,
&mut state, &mut state,
); );
@ -100,7 +89,7 @@ fn constrain_params(
Generalizable(true), Generalizable(true),
); );
constraints.exists([*pattern_var], cons) constraints.exists([module_params.whole_var], cons)
} }
fn constrain_symbols_from_requires( fn constrain_symbols_from_requires(

View file

@ -38,6 +38,7 @@ pub enum Parens {
InFunctionType, InFunctionType,
InApply, InApply,
InOperator, InOperator,
InAsPattern,
} }
/// In an AST node, do we show newlines around it /// In an AST node, do we show newlines around it

View file

@ -10,7 +10,7 @@ use crate::Buf;
use roc_module::called_via::{self, BinOp}; use roc_module::called_via::{self, BinOp};
use roc_parse::ast::{ use roc_parse::ast::{
is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces,
OldRecordBuilderField, Pattern, WhenBranch, OldRecordBuilderField, Pattern, TryTarget, WhenBranch,
}; };
use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ast::{StrLiteral, StrSegment};
use roc_parse::ident::Accessor; use roc_parse::ident::Accessor;
@ -39,15 +39,17 @@ impl<'a> Formattable for Expr<'a> {
| NonBase10Int { .. } | NonBase10Int { .. }
| SingleQuote(_) | SingleQuote(_)
| AccessorFunction(_) | AccessorFunction(_)
| RecordUpdater(_)
| Var { .. } | Var { .. }
| Underscore { .. } | Underscore { .. }
| MalformedIdent(_, _) | MalformedIdent(_, _)
| MalformedClosure | MalformedClosure
| Tag(_) | Tag(_)
| OpaqueRef(_) | OpaqueRef(_)
| Crash => false, | Crash
| Dbg => false,
RecordAccess(inner, _) | TupleAccess(inner, _) | TaskAwaitBang(inner) => { RecordAccess(inner, _) | TupleAccess(inner, _) | TrySuffix { expr: inner, .. } => {
inner.is_multiline() inner.is_multiline()
} }
@ -64,7 +66,7 @@ impl<'a> Formattable for Expr<'a> {
Expect(condition, continuation) => { Expect(condition, continuation) => {
condition.is_multiline() || continuation.is_multiline() condition.is_multiline() || continuation.is_multiline()
} }
Dbg(condition, _) => condition.is_multiline(), DbgStmt(condition, _) => condition.is_multiline(),
LowLevelDbg(_, _, _) => unreachable!( LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting" "LowLevelDbg should only exist after desugaring, not during formatting"
), ),
@ -452,8 +454,12 @@ impl<'a> Formattable for Expr<'a> {
Expect(condition, continuation) => { Expect(condition, continuation) => {
fmt_expect(buf, condition, continuation, self.is_multiline(), indent); fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
} }
Dbg(condition, continuation) => { Dbg => {
fmt_dbg(buf, condition, continuation, self.is_multiline(), indent); buf.indent(indent);
buf.push_str("dbg");
}
DbgStmt(condition, continuation) => {
fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent);
} }
LowLevelDbg(_, _, _) => unreachable!( LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting" "LowLevelDbg should only exist after desugaring, not during formatting"
@ -510,6 +516,11 @@ impl<'a> Formattable for Expr<'a> {
Accessor::TupleIndex(key) => buf.push_str(key), Accessor::TupleIndex(key) => buf.push_str(key),
} }
} }
RecordUpdater(key) => {
buf.indent(indent);
buf.push('&');
buf.push_str(key);
}
RecordAccess(expr, key) => { RecordAccess(expr, key) => {
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('.'); buf.push('.');
@ -520,9 +531,12 @@ impl<'a> Formattable for Expr<'a> {
buf.push('.'); buf.push('.');
buf.push_str(key); buf.push_str(key);
} }
TaskAwaitBang(expr) => { TrySuffix { expr, target } => {
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('!'); match target {
TryTarget::Task => buf.push('!'),
TryTarget::Result => buf.push('?'),
}
} }
MalformedIdent(str, _) => { MalformedIdent(str, _) => {
buf.indent(indent); buf.indent(indent);
@ -615,6 +629,22 @@ fn starts_with_newline(expr: &Expr) -> bool {
} }
} }
fn fmt_str_body(body: &str, buf: &mut Buf) {
for c in body.chars() {
match c {
// Format blank characters as unicode escapes
'\u{200a}' => buf.push_str("\\u(200a)"),
'\u{200b}' => buf.push_str("\\u(200b)"),
'\u{200c}' => buf.push_str("\\u(200c)"),
'\u{feff}' => buf.push_str("\\u(feff)"),
// Don't change anything else in the string
' ' => buf.push_str_allow_spaces(" "),
'\n' => buf.push_str_allow_spaces("\n"),
_ => buf.push(c),
}
}
}
fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) { fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) {
use StrSegment::*; use StrSegment::*;
@ -624,10 +654,10 @@ fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) {
// a line break in the input string // a line break in the input string
match string.strip_suffix('\n') { match string.strip_suffix('\n') {
Some(string_without_newline) => { Some(string_without_newline) => {
buf.push_str_allow_spaces(string_without_newline); fmt_str_body(string_without_newline, buf);
buf.newline(); buf.newline();
} }
None => buf.push_str_allow_spaces(string), None => fmt_str_body(string, buf),
} }
} }
Unicode(loc_str) => { Unicode(loc_str) => {
@ -687,7 +717,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) {
buf.push_newline_literal(); buf.push_newline_literal();
for line in string.split('\n') { for line in string.split('\n') {
buf.indent(indent); buf.indent(indent);
buf.push_str_allow_spaces(line); fmt_str_body(line, buf);
buf.push_newline_literal(); buf.push_newline_literal();
} }
buf.indent(indent); buf.indent(indent);
@ -695,7 +725,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) {
} else { } else {
buf.indent(indent); buf.indent(indent);
buf.push('"'); buf.push('"');
buf.push_str_allow_spaces(string); fmt_str_body(string, buf);
buf.push('"'); buf.push('"');
}; };
} }
@ -993,7 +1023,7 @@ fn fmt_when<'a>(
} }
} }
fn fmt_dbg<'a>( fn fmt_dbg_stmt<'a>(
buf: &mut Buf, buf: &mut Buf,
condition: &'a Loc<Expr<'a>>, condition: &'a Loc<Expr<'a>>,
continuation: &'a Loc<Expr<'a>>, continuation: &'a Loc<Expr<'a>>,
@ -1215,7 +1245,7 @@ fn fmt_closure<'a>(
let mut it = loc_patterns.iter().peekable(); let mut it = loc_patterns.iter().peekable();
while let Some(loc_pattern) = it.next() { while let Some(loc_pattern) = it.next() {
loc_pattern.format(buf, indent); loc_pattern.format_with_options(buf, Parens::InAsPattern, Newlines::No, indent);
if it.peek().is_some() { if it.peek().is_some() {
buf.indent(indent); buf.indent(indent);

View file

@ -7,11 +7,10 @@ use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt
use crate::Buf; use crate::Buf;
use roc_parse::ast::{Collection, CommentOrNewline, Header, Spaced, Spaces, SpacesBefore}; use roc_parse::ast::{Collection, CommentOrNewline, Header, Spaced, Spaces, SpacesBefore};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, AppHeader, ExposedName, ExposesKeyword, HostedHeader, ImportsEntry, ImportsKeyword, Keyword,
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackageKeyword,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword, PlatformRequires,
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
}; };
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc; use roc_region::all::Loc;
@ -63,8 +62,6 @@ macro_rules! keywords {
keywords! { keywords! {
ExposesKeyword, ExposesKeyword,
ImportsKeyword, ImportsKeyword,
WithKeyword,
GeneratesKeyword,
PackageKeyword, PackageKeyword,
PackagesKeyword, PackagesKeyword,
RequiresKeyword, RequiresKeyword,
@ -206,9 +203,6 @@ pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
fmt_exposes(buf, header.exposes.item, indent); fmt_exposes(buf, header.exposes.item, indent);
header.imports.keyword.format(buf, indent); header.imports.keyword.format(buf, indent);
fmt_imports(buf, header.imports.item, indent); fmt_imports(buf, header.imports.item, indent);
header.generates.format(buf, indent);
header.generates_with.keyword.format(buf, indent);
fmt_exposes(buf, header.generates_with.item, indent);
} }
pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) { pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {

View file

@ -244,9 +244,19 @@ impl<'a> Formattable for Pattern<'a> {
} }
As(pattern, pattern_as) => { As(pattern, pattern_as) => {
let needs_parens = parens == Parens::InAsPattern;
if needs_parens {
buf.push('(');
}
fmt_pattern(buf, &pattern.value, indent, parens); fmt_pattern(buf, &pattern.value, indent, parens);
pattern_as.format(buf, indent + INDENT); pattern_as.format(buf, indent + INDENT);
if needs_parens {
buf.push(')');
}
} }
// Space // Space

View file

@ -244,7 +244,7 @@ fn alignment_type(context: &Context, alignment: u32) -> BasicTypeEnum {
2 => context.i16_type().into(), 2 => context.i16_type().into(),
4 => context.i32_type().into(), 4 => context.i32_type().into(),
8 => context.i64_type().into(), 8 => context.i64_type().into(),
16 => context.i128_type().into(), 16 => context.f128_type().into(),
_ => unimplemented!("weird alignment: {alignment}"), _ => unimplemented!("weird alignment: {alignment}"),
} }
} }
@ -385,7 +385,8 @@ impl<'ctx> RocUnion<'ctx> {
let mut width = self.data_width; let mut width = self.data_width;
// add padding between data and the tag id // add padding between data and the tag id
width = round_up_to_alignment(width, tag_id_width); let tag_id_alignment = tag_id_width.max(1);
width = round_up_to_alignment(width, tag_id_alignment);
// add tag id // add tag id
width += tag_id_width; width += tag_id_width;

View file

@ -25,6 +25,7 @@ const MODULES: &[(ModuleId, &str)] = &[
(ModuleId::DECODE, "Decode.roc"), (ModuleId::DECODE, "Decode.roc"),
(ModuleId::HASH, "Hash.roc"), (ModuleId::HASH, "Hash.roc"),
(ModuleId::INSPECT, "Inspect.roc"), (ModuleId::INSPECT, "Inspect.roc"),
(ModuleId::TASK, "Task.roc"),
]; ];
fn main() { fn main() {

View file

@ -256,6 +256,7 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat")); let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat"));
let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat")); let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat"));
let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat")); let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat"));
let mod_task = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Task.dat"));
let mut output = MutMap::default(); let mut output = MutMap::default();
@ -279,6 +280,8 @@ fn read_cached_types() -> MutMap<ModuleId, TypeState> {
output.insert(ModuleId::HASH, deserialize_help(mod_hash)); output.insert(ModuleId::HASH, deserialize_help(mod_hash));
output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect)); output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect));
output.insert(ModuleId::TASK, deserialize_help(mod_task));
} }
output output

View file

@ -51,7 +51,7 @@ pub fn infer_expr(
exposed_by_module: &Default::default(), exposed_by_module: &Default::default(),
derived_module, derived_module,
function_kind: FunctionKind::LambdaSet, function_kind: FunctionKind::LambdaSet,
params_pattern: None, module_params: None,
module_params_vars: Default::default(), module_params_vars: Default::default(),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: None, checkmate: None,
@ -170,10 +170,12 @@ pub fn can_expr_with<'a>(
// rules multiple times unnecessarily. // rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr( let loc_expr = desugar::desugar_expr(
arena, arena,
&mut var_store,
&loc_expr, &loc_expr,
expr_str, expr_str,
&mut None, &mut None,
arena.alloc("TestPath"), arena.alloc("TestPath"),
&mut Default::default(),
); );
let mut scope = Scope::new( let mut scope = Scope::new(

View file

@ -3793,9 +3793,9 @@ mod test_reporting {
of these? of these?
Set Set
Task
List List
Dict Dict
Hash
SYNTAX PROBLEM in /code/proj/Main.roc SYNTAX PROBLEM in /code/proj/Main.roc
@ -3804,7 +3804,7 @@ mod test_reporting {
10 y = { Test.example & age: 3 } 10 y = { Test.example & age: 3 }
^^^^^^^^^^^^ ^^^^^^^^^^^^
Only variables can be updated with record update syntax. Only variables can be updated with record update syntax.
" "
); );
@ -4816,7 +4816,7 @@ mod test_reporting {
expression_indentation_end, expression_indentation_end,
indoc!( indoc!(
r" r"
f <- Foo.foo f = Foo.foo
" "
), ),
@r#" @r#"
@ -4827,8 +4827,8 @@ mod test_reporting {
1 app "test" provides [main] to "./platform" 1 app "test" provides [main] to "./platform"
2 2
3 main = 3 main =
4 f <- Foo.foo 4 f = Foo.foo
^ ^
Looks like the indentation ends prematurely here. Did you mean to have Looks like the indentation ends prematurely here. Did you mean to have
another expression after this line? another expression after this line?
@ -5760,28 +5760,6 @@ mod test_reporting {
"# "#
); );
test_report!(
dbg_without_final_expression,
indoc!(
r"
dbg 42
"
),
@r#"
INDENT ENDS AFTER EXPRESSION in tmp/dbg_without_final_expression/Test.roc
I am partway through parsing a dbg statement, but I got stuck here:
4 dbg 42
^
I was expecting a final expression, like so
dbg 42
"done"
"#
);
test_report!( test_report!(
expect_without_final_expression, expect_without_final_expression,
indoc!( indoc!(
@ -6318,16 +6296,17 @@ All branches in an `if` must have the same type!
) )
} }
// TODO: this test seems out of date (what is the `effects` clause?) and as such should be removed
#[test] #[test]
fn platform_requires_rigids() { fn platform_requires_rigids() {
report_header_problem_as( report_header_problem_as(
indoc!( indoc!(
r#" r#"
platform "folkertdev/foo" platform "folkertdev/foo"
requires { main : Effect {} } requires { main : Task {} [] }
exposes [] exposes []
packages {} packages {}
imports [Task] imports []
provides [mainForHost] provides [mainForHost]
effects fx.Effect effects fx.Effect
{ {
@ -6344,13 +6323,13 @@ All branches in an `if` must have the same type!
I am partway through parsing a header, but I got stuck here: I am partway through parsing a header, but I got stuck here:
1 platform "folkertdev/foo" 1 platform "folkertdev/foo"
2 requires { main : Effect {} } 2 requires { main : Task {} [] }
^ ^
I am expecting a list of type names like `{}` or `{ Model }` next. A full I am expecting a list of type names like `{}` or `{ Model }` next. A full
`requires` definition looks like `requires` definition looks like
requires { Model, Msg } {main : Effect {}} requires { Model, Msg } {main : Task {} []}
"# "#
), ),
) )
@ -6617,34 +6596,6 @@ All branches in an `if` must have the same type!
" "
); );
test_report!(
backpassing_type_error,
indoc!(
r#"
x <- List.map ["a", "b"]
x + 1
"#
),
@r#"
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to `map` has an unexpected type:
4> x <- List.map ["a", "b"]
5>
6> x + 1
The argument is an anonymous function of type:
Num * -> Num *
But `map` needs its 2nd argument to be:
Str -> Num *
"#
);
test_report!( test_report!(
expect_expr_type_error, expect_expr_type_error,
indoc!( indoc!(
@ -8144,7 +8095,7 @@ All branches in an `if` must have the same type!
unimported_modules_reported, unimported_modules_reported,
indoc!( indoc!(
r#" r#"
alt : Task.Task {} [] alt : Unimported.CustomType
alt = "whatever man you don't even know my type" alt = "whatever man you don't even know my type"
alt alt
"# "#
@ -8152,18 +8103,18 @@ All branches in an `if` must have the same type!
@r" @r"
MODULE NOT IMPORTED in /code/proj/Main.roc MODULE NOT IMPORTED in /code/proj/Main.roc
The `Task` module is not imported: The `Unimported` module is not imported:
4 alt : Task.Task {} [] 4 alt : Unimported.CustomType
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
Is there an import missing? Perhaps there is a typo. Did you mean one Is there an import missing? Perhaps there is a typo. Did you mean one
of these? of these?
Hash Encode
Inspect
Dict
List List
Num
Box
" "
); );
@ -10198,9 +10149,9 @@ All branches in an `if` must have the same type!
withOpen : (Handle -> Result {} *) -> Result {} * withOpen : (Handle -> Result {} *) -> Result {} *
withOpen = \callback -> withOpen = \callback ->
handle <- await (open {}) await (open {}) \handle ->
{} <- await (callback handle) await (callback handle) \_ ->
close handle close handle
withOpen withOpen
" "
@ -10212,9 +10163,9 @@ All branches in an `if` must have the same type!
10 withOpen : (Handle -> Result {} *) -> Result {} * 10 withOpen : (Handle -> Result {} *) -> Result {} *
11 withOpen = \callback -> 11 withOpen = \callback ->
12> handle <- await (open {}) 12> await (open {}) \handle ->
13> {} <- await (callback handle) 13> await (callback handle) \_ ->
14> close handle 14> close handle
The type annotation on `withOpen` says this `await` call should have the The type annotation on `withOpen` says this `await` call should have the
type: type:
@ -10227,6 +10178,7 @@ All branches in an `if` must have the same type!
Tip: Any connection between types must use a named type variable, not Tip: Any connection between types must use a named type variable, not
a `*`! Maybe the annotation on `withOpen` should have a named type a `*`! Maybe the annotation on `withOpen` should have a named type
variable in place of the `*`? variable in place of the `*`?
" "
); );
@ -10830,7 +10782,7 @@ All branches in an `if` must have the same type!
7 a: <- "a", 7 a: <- "a",
^^^ ^^^
Tip: Remove `<-` to assign the field directly. Tip: Remove <- to assign the field directly.
"# "#
); );
@ -10962,7 +10914,7 @@ All branches in an `if` must have the same type!
6 { xyz <- 6 { xyz <-
^^^ ^^^
Note: Record builders need a mapper function before the `<-` to combine Note: Record builders need a mapper function before the <- to combine
fields together with. fields together with.
"# "#
); );
@ -11844,6 +11796,32 @@ All branches in an `if` must have the same type!
@r" @r"
" "
); );
test_report!(
deprecated_backpassing,
indoc!(
r#"
foo = \bar ->
baz <- Result.try bar
Ok (baz * 3)
foo (Ok 123)
"#
),
@r###"
BACKPASSING DEPRECATED in /code/proj/Main.roc
Backpassing (<-) like this will soon be deprecated:
5 baz <- Result.try bar
^^^^^^^^^^^^^^^^^^^^^
You should use a ! for awaiting tasks or a ? for trying results, and
functions everywhere else.
"###
);
test_report!( test_report!(
unknown_shorthand_no_deps, unknown_shorthand_no_deps,
indoc!( indoc!(
@ -13881,7 +13859,7 @@ All branches in an `if` must have the same type!
"# "#
), ),
@r#" @r#"
DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc
These 2 definitions are only used in mutual recursion with themselves: These 2 definitions are only used in mutual recursion with themselves:
@ -13890,6 +13868,7 @@ All branches in an `if` must have the same type!
If you don't intend to use or export any of them, they should all be If you don't intend to use or export any of them, they should all be
removed! removed!
"# "#
); );
@ -13953,7 +13932,7 @@ All branches in an `if` must have the same type!
"# "#
), ),
@r#" @r#"
DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc
These 2 definitions are only used in mutual recursion with themselves: These 2 definitions are only used in mutual recursion with themselves:
@ -13962,6 +13941,7 @@ All branches in an `if` must have the same type!
If you don't intend to use or export any of them, they should all be If you don't intend to use or export any of them, they should all be
removed! removed!
"# "#
); );
@ -14515,6 +14495,76 @@ All branches in an `if` must have the same type!
" "
); );
test_report!(
dbg_unapplied,
indoc!(
r"
1 + dbg + 2
"
),
@r"
UNAPPLIED DBG in /code/proj/Main.roc
This `dbg` doesn't have a value given to it:
4 1 + dbg + 2
^^^
`dbg` must be passed a value to print at the exact place it's used. `dbg`
can't be used as a value that's passed around, like functions can be -
it must be applied immediately!
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to + has an unexpected type:
4 1 + dbg + 2
^^^
This `63` value is a:
{}
But + needs its 2nd argument to be:
Num *
"
);
test_report!(
dbg_overapplied,
indoc!(
r#"
1 + dbg "" "" + 2
"#
),
@r#"
OVERAPPLIED DBG in /code/proj/Main.roc
This `dbg` has too many values given to it:
4 1 + dbg "" "" + 2
^^^^^
`dbg` must be given exactly one value to print.
TYPE MISMATCH in /code/proj/Main.roc
This 2nd argument to + has an unexpected type:
4 1 + dbg "" "" + 2
^^^^^^^^^
This `63` value is a:
{}
But + needs its 2nd argument to be:
Num *
"#
);
// TODO: add the following tests after built-in Tasks are added // TODO: add the following tests after built-in Tasks are added
// https://github.com/roc-lang/roc/pull/6836 // https://github.com/roc-lang/roc/pull/6836

View file

@ -33,6 +33,7 @@ roc_tracing = { path = "../../tracing" }
roc_types = { path = "../types" } roc_types = { path = "../types" }
roc_unify = { path = "../unify" } roc_unify = { path = "../unify" }
roc_worker = { path = "../worker" } roc_worker = { path = "../worker" }
roc_lower_params = { path = "../lower_params" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }

View file

@ -18,7 +18,7 @@ use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar};
use roc_can::expr::{DbgLookup, Declarations, ExpectLookup, PendingDerives}; use roc_can::expr::{DbgLookup, Declarations, ExpectLookup, PendingDerives};
use roc_can::module::{ use roc_can::module::{
canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module, canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module,
ResolvedImplementations, TypeState, ModuleParams, ResolvedImplementations, TypeState,
}; };
use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet}; use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet};
use roc_constrain::module::constrain_module; use roc_constrain::module::constrain_module;
@ -251,6 +251,7 @@ fn start_phase<'a>(
let mut aliases = MutMap::default(); let mut aliases = MutMap::default();
let mut abilities_store = PendingAbilitiesStore::default(); let mut abilities_store = PendingAbilitiesStore::default();
let mut imported_module_params = VecMap::default();
for imported in parsed.available_modules.keys() { for imported in parsed.available_modules.keys() {
match state.module_cache.aliases.get(imported) { match state.module_cache.aliases.get(imported) {
@ -293,6 +294,10 @@ fn start_phase<'a>(
.union(import_store.closure_from_imported(exposed_symbols)); .union(import_store.closure_from_imported(exposed_symbols));
} }
} }
if let Some(params) = state.module_cache.module_params.get(imported) {
imported_module_params.insert(*imported, params.clone());
}
} }
let skip_constraint_gen = { let skip_constraint_gen = {
@ -310,6 +315,8 @@ fn start_phase<'a>(
abilities_store, abilities_store,
skip_constraint_gen, skip_constraint_gen,
exposed_module_ids: state.exposed_modules, exposed_module_ids: state.exposed_modules,
exec_mode: state.exec_mode,
imported_module_params,
} }
} }
@ -356,6 +363,7 @@ fn start_phase<'a>(
declarations, declarations,
state.cached_types.clone(), state.cached_types.clone(),
derived_module, derived_module,
state.exec_mode,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -889,6 +897,8 @@ enum BuildTask<'a> {
abilities_store: PendingAbilitiesStore, abilities_store: PendingAbilitiesStore,
exposed_module_ids: &'a [ModuleId], exposed_module_ids: &'a [ModuleId],
skip_constraint_gen: bool, skip_constraint_gen: bool,
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
}, },
Solve { Solve {
module: Module, module: Module,
@ -905,6 +915,7 @@ enum BuildTask<'a> {
dep_idents: IdentIdsByModule, dep_idents: IdentIdsByModule,
cached_subs: CachedTypeState, cached_subs: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: Option<roc_checkmate::Collector>, checkmate: Option<roc_checkmate::Collector>,
@ -2318,6 +2329,7 @@ fn update<'a>(
extend_module_with_builtin_import(parsed, ModuleId::DECODE); extend_module_with_builtin_import(parsed, ModuleId::DECODE);
extend_module_with_builtin_import(parsed, ModuleId::HASH); extend_module_with_builtin_import(parsed, ModuleId::HASH);
extend_module_with_builtin_import(parsed, ModuleId::INSPECT); extend_module_with_builtin_import(parsed, ModuleId::INSPECT);
extend_module_with_builtin_import(parsed, ModuleId::TASK);
} }
state state
@ -2395,11 +2407,11 @@ fn update<'a>(
.pending_abilities .pending_abilities
.insert(module_id, constrained_module.module.abilities_store.clone()); .insert(module_id, constrained_module.module.abilities_store.clone());
if let Some(params_pattern) = constrained_module.module.params_pattern.clone() { if let Some(module_params) = constrained_module.module.module_params.clone() {
state state
.module_cache .module_cache
.param_patterns .module_params
.insert(module_id, params_pattern); .insert(module_id, module_params);
} }
state state
@ -3557,7 +3569,6 @@ fn load_builtin_module_help<'a>(
header_type: HeaderType::Builtin { header_type: HeaderType::Builtin {
name: header::ModuleName::new(name_stem), name: header::ModuleName::new(name_stem),
exposes: unspace(arena, header.exposes.items), exposes: unspace(arena, header.exposes.items),
generates_with: &[],
opt_params: header.params, opt_params: header.params,
}, },
module_comments: comments, module_comments: comments,
@ -3640,6 +3651,7 @@ fn load_module<'a>(
"Decode", ModuleId::DECODE "Decode", ModuleId::DECODE
"Hash", ModuleId::HASH "Hash", ModuleId::HASH
"Inspect", ModuleId::INSPECT "Inspect", ModuleId::INSPECT
"Task", ModuleId::TASK
} }
let (filename, opt_shorthand) = module_name_to_path(src_dir, &module_name, arc_shorthands); let (filename, opt_shorthand) = module_name_to_path(src_dir, &module_name, arc_shorthands);
@ -3875,8 +3887,6 @@ fn parse_header<'a>(
header_type: HeaderType::Hosted { header_type: HeaderType::Hosted {
name: header.name.value, name: header.name.value,
exposes: unspace(arena, header.exposes.item.items), exposes: unspace(arena, header.exposes.item.items),
generates: header.generates.item,
generates_with: unspace(arena, header.generates_with.item.items),
}, },
module_comments: comments, module_comments: comments,
header_imports: Some(header.imports), header_imports: Some(header.imports),
@ -4311,6 +4321,7 @@ impl<'a> BuildTask<'a> {
declarations: Declarations, declarations: Declarations,
cached_subs: CachedTypeState, cached_subs: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>, #[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Self { ) -> Self {
@ -4334,6 +4345,7 @@ impl<'a> BuildTask<'a> {
module_timing, module_timing,
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -4618,6 +4630,7 @@ struct SolveResult {
exposed_vars_by_symbol: Vec<(Symbol, Variable)>, exposed_vars_by_symbol: Vec<(Symbol, Variable)>,
problems: Vec<TypeError>, problems: Vec<TypeError>,
abilities_store: AbilitiesStore, abilities_store: AbilitiesStore,
imported_modules_with_params: Vec<ModuleId>,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: Option<roc_checkmate::Collector>, checkmate: Option<roc_checkmate::Collector>,
@ -4642,7 +4655,7 @@ fn run_solve_solve(
aliases, aliases,
rigid_variables, rigid_variables,
abilities_store: pending_abilities, abilities_store: pending_abilities,
params_pattern, module_params,
.. ..
} = module; } = module;
@ -4663,6 +4676,11 @@ fn run_solve_solve(
&mut imported_flex_vars, &mut imported_flex_vars,
); );
let imported_modules_with_params = imported_param_vars
.keys()
.copied()
.collect::<Vec<ModuleId>>();
let actual_constraint = constraints.let_import_constraint( let actual_constraint = constraints.let_import_constraint(
imported_rigid_vars, imported_rigid_vars,
imported_flex_vars, imported_flex_vars,
@ -4690,7 +4708,7 @@ fn run_solve_solve(
derived_module, derived_module,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
params_pattern: params_pattern.map(|(_, _, pattern)| pattern.value), module_params,
module_params_vars: imported_param_vars, module_params_vars: imported_param_vars,
}; };
@ -4745,6 +4763,7 @@ fn run_solve_solve(
exposed_vars_by_symbol, exposed_vars_by_symbol,
problems: errors, problems: errors,
abilities_store: resolved_abilities_store, abilities_store: resolved_abilities_store,
imported_modules_with_params,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -4766,6 +4785,7 @@ fn run_solve<'a>(
dep_idents: IdentIdsByModule, dep_idents: IdentIdsByModule,
cached_types: CachedTypeState, cached_types: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>, #[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Msg<'a> { ) -> Msg<'a> {
@ -4776,10 +4796,8 @@ fn run_solve<'a>(
// TODO remove when we write builtins in roc // TODO remove when we write builtins in roc
let aliases = module.aliases.clone(); let aliases = module.aliases.clone();
let opt_params_var = module let opt_params_var = module.module_params.as_ref().map(|params| params.whole_var);
.params_pattern let home_has_params = opt_params_var.is_some();
.as_ref()
.map(|(params_var, _, _)| *params_var);
let mut module = module; let mut module = module;
let loc_expects = std::mem::take(&mut module.loc_expects); let loc_expects = std::mem::take(&mut module.loc_expects);
@ -4814,6 +4832,7 @@ fn run_solve<'a>(
exposed_vars_by_symbol, exposed_vars_by_symbol,
problems: vec![], problems: vec![],
abilities_store: abilities, abilities_store: abilities,
imported_modules_with_params: vec![],
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: None, checkmate: None,
@ -4841,8 +4860,9 @@ fn run_solve<'a>(
solved: mut solved_subs, solved: mut solved_subs,
solved_implementations, solved_implementations,
exposed_vars_by_symbol, exposed_vars_by_symbol,
problems, mut problems,
abilities_store, abilities_store,
imported_modules_with_params,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -4857,6 +4877,20 @@ fn run_solve<'a>(
&abilities_store, &abilities_store,
); );
match exec_mode {
ExecutionMode::Check => {
// Params are not lowered in check mode
}
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
roc_lower_params::type_error::remove_module_param_arguments(
&mut problems,
home_has_params,
&decls.symbols,
imported_modules_with_params,
);
}
}
let solved_module = SolvedModule { let solved_module = SolvedModule {
exposed_vars_by_symbol, exposed_vars_by_symbol,
problems, problems,
@ -5006,6 +5040,8 @@ fn canonicalize_and_constrain<'a>(
parsed: ParsedModule<'a>, parsed: ParsedModule<'a>,
skip_constraint_gen: bool, skip_constraint_gen: bool,
exposed_module_ids: &[ModuleId], exposed_module_ids: &[ModuleId],
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
) -> CanAndCon { ) -> CanAndCon {
let canonicalize_start = Instant::now(); let canonicalize_start = Instant::now();
@ -5032,7 +5068,7 @@ fn canonicalize_and_constrain<'a>(
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
let module_output = canonicalize_module_defs( let mut module_output = canonicalize_module_defs(
arena, arena,
parsed_defs, parsed_defs,
&header_type, &header_type,
@ -5091,6 +5127,26 @@ fn canonicalize_and_constrain<'a>(
// _before has an underscore because it's unused in --release builds // _before has an underscore because it's unused in --release builds
let _before = roc_types::types::get_type_clone_count(); let _before = roc_types::types::get_type_clone_count();
match exec_mode {
ExecutionMode::Check => {
// No need to lower params for `roc check` and lang server
// If we did, we'd have to update the language server to exclude the extra arguments
}
ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
// We need to lower params only if the current module has any or imports at least one with params
if module_output.module_params.is_some() || !imported_module_params.is_empty() {
roc_lower_params::lower::lower(
module_id,
&module_output.module_params,
imported_module_params,
&mut module_output.declarations,
&mut module_output.scope.locals.ident_ids,
&mut var_store,
);
}
}
}
let mut constraints = Constraints::new(); let mut constraints = Constraints::new();
let constraint = if skip_constraint_gen { let constraint = if skip_constraint_gen {
@ -5102,7 +5158,7 @@ fn canonicalize_and_constrain<'a>(
module_output.symbols_from_requires, module_output.symbols_from_requires,
&module_output.scope.abilities_store, &module_output.scope.abilities_store,
&module_output.declarations, &module_output.declarations,
&module_output.params_pattern, &module_output.module_params,
module_id, module_id,
) )
}; };
@ -5140,6 +5196,7 @@ fn canonicalize_and_constrain<'a>(
| ModuleId::SET | ModuleId::SET
| ModuleId::HASH | ModuleId::HASH
| ModuleId::INSPECT | ModuleId::INSPECT
| ModuleId::TASK
); );
if !name.is_builtin() || should_include_builtin { if !name.is_builtin() || should_include_builtin {
@ -5159,7 +5216,7 @@ fn canonicalize_and_constrain<'a>(
abilities_store: module_output.scope.abilities_store, abilities_store: module_output.scope.abilities_store,
loc_expects: module_output.loc_expects, loc_expects: module_output.loc_expects,
loc_dbgs: module_output.loc_dbgs, loc_dbgs: module_output.loc_dbgs,
params_pattern: module_output.params_pattern, module_params: module_output.module_params,
}; };
let constrained_module = ConstrainedModule { let constrained_module = ConstrainedModule {
@ -6209,6 +6266,8 @@ fn run_task<'a>(
abilities_store, abilities_store,
skip_constraint_gen, skip_constraint_gen,
exposed_module_ids, exposed_module_ids,
exec_mode,
imported_module_params,
} => { } => {
let can_and_con = canonicalize_and_constrain( let can_and_con = canonicalize_and_constrain(
arena, arena,
@ -6220,6 +6279,8 @@ fn run_task<'a>(
parsed, parsed,
skip_constraint_gen, skip_constraint_gen,
exposed_module_ids, exposed_module_ids,
exec_mode,
imported_module_params,
); );
Ok(Msg::CanonicalizedAndConstrained(can_and_con)) Ok(Msg::CanonicalizedAndConstrained(can_and_con))
@ -6239,6 +6300,7 @@ fn run_task<'a>(
dep_idents, dep_idents,
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -6257,6 +6319,7 @@ fn run_task<'a>(
dep_idents, dep_idents,
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,

View file

@ -25,4 +25,5 @@ pub const BUILTIN_MODULES: &[(ModuleId, &str)] = &[
(ModuleId::DECODE, "Decode"), (ModuleId::DECODE, "Decode"),
(ModuleId::HASH, "Hash"), (ModuleId::HASH, "Hash"),
(ModuleId::INSPECT, "Inspect"), (ModuleId::INSPECT, "Inspect"),
(ModuleId::TASK, "Task"),
]; ];

Some files were not shown because too many files have changed in this diff Show more