mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge remote-tracking branch 'remote/main' into upgrade-llvm-zig
This commit is contained in:
commit
2feb5d3c2e
426 changed files with 8889 additions and 4190 deletions
8
.github/workflows/ci_manager.yml
vendored
8
.github/workflows/ci_manager.yml
vendored
|
|
@ -124,6 +124,11 @@ jobs:
|
|||
if: needs.check-changes.outputs.run_tests == 'full'
|
||||
uses: ./.github/workflows/benchmarks.yml
|
||||
|
||||
start-panic-check:
|
||||
needs: check-changes
|
||||
if: needs.check-changes.outputs.run_tests == 'full'
|
||||
uses: ./.github/workflows/improve_panics.yml
|
||||
|
||||
ran-full:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [
|
||||
|
|
@ -136,7 +141,8 @@ jobs:
|
|||
start-ubuntu-x86-64-nix-tests-debug,
|
||||
start-windows-release-build-test,
|
||||
start-windows-tests,
|
||||
start-roc-benchmarks
|
||||
start-roc-benchmarks,
|
||||
start-panic-check
|
||||
]
|
||||
steps:
|
||||
- run: echo "all workflows succeeded!"
|
||||
|
|
|
|||
39
.github/workflows/docker.yml
vendored
39
.github/workflows/docker.yml
vendored
|
|
@ -1,7 +1,6 @@
|
|||
on:
|
||||
workflow_dispatch:
|
||||
# pull_request:
|
||||
# TODO remove pull_request trigger
|
||||
#pull_request:
|
||||
|
||||
name: Docker images tests
|
||||
|
||||
|
|
@ -19,8 +18,10 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc main.roc
|
||||
|
||||
|
||||
nightly-ubuntu-2204:
|
||||
|
|
@ -36,8 +37,10 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc main.roc
|
||||
|
||||
nightly-ubuntu-2004:
|
||||
name: nightly-ubuntu-2004
|
||||
|
|
@ -52,8 +55,10 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc main.roc
|
||||
|
||||
nightly-debian-latest:
|
||||
name: nightly-debian-latest
|
||||
|
|
@ -68,8 +73,10 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc main.roc
|
||||
|
||||
nightly-debian-bookworm:
|
||||
name: nightly-debian-bookworm
|
||||
|
|
@ -84,8 +91,10 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc main.roc
|
||||
|
||||
nightly-debian-buster:
|
||||
name: nightly-debian-buster
|
||||
|
|
@ -100,5 +109,7 @@ jobs:
|
|||
- name: Build image
|
||||
run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build
|
||||
|
||||
- name: Run hello world test
|
||||
run: docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc
|
||||
- name: Test with hello world
|
||||
run: |
|
||||
curl -OL https://raw.githubusercontent.com/roc-lang/examples/refs/heads/main/examples/HelloWorld/main.roc
|
||||
docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc main.roc
|
||||
|
|
|
|||
56
.github/workflows/improve_panics.yml
vendored
Normal file
56
.github/workflows/improve_panics.yml
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
on:
|
||||
workflow_call:
|
||||
|
||||
# Check that the number of panicking function/macro calls has not increased
|
||||
# https://github.com/roc-lang/roc/issues/2046
|
||||
name: Improve panics/unwrap/expect
|
||||
|
||||
jobs:
|
||||
improve-panics:
|
||||
name: improve panics
|
||||
runs-on: [ubuntu-22.04]
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
RUST_BACKTRACE: 1
|
||||
steps:
|
||||
# install nix
|
||||
- uses: cachix/install-nix-action@v23
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: checkout source branch in separate directory
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: true
|
||||
path: source_branch_dir
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- name: checkout target branch in separate directory
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
clean: false
|
||||
path: target_branch_dir
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
- name: Compare panics, expects, and unwraps between source and target brach
|
||||
run: |
|
||||
for branch_dir in source_branch_dir target_branch_dir; do
|
||||
cd $branch_dir
|
||||
echo "Calculating violations in &branch_dir..."
|
||||
VIOLATIONS=$(nix develop -c cargo clippy --no-deps -- -W clippy::unwrap_used -W clippy::expect_used -W clippy::panic 2> >(grep -e "warning: \`panic\`" -e "warning: used" -e "warning: usage of" | wc -l ))
|
||||
echo $VIOLATIONS > violations
|
||||
cd ..
|
||||
done
|
||||
|
||||
SOURCE_VIOLATIONS=$(cat source_branch_dir/violations)
|
||||
TARGET_VIOLATIONS=$(cat target_branch_dir/violations)
|
||||
if [ "$SOURCE_VIOLATIONS" -gt "$TARGET_VIOLATIONS" ]; then
|
||||
echo "You added panic/unwrap/expect, in this PR their count increased from $TARGET_VIOLATIONS to $SOURCE_VIOLATIONS."
|
||||
echo "These calls can kill the REPL, try alternative error handling."
|
||||
echo ""
|
||||
echo "TIP: Ask AI \"In rust, how can I rewrite code that contains panic or unwrap so it doesn't crash?\""
|
||||
echo ""
|
||||
echo "If you believe your panic/unwrap/expect is justified, ask Anton-4, rtfeldman or lukewilliamboswell to bypass this workflow failure."
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -58,7 +58,7 @@ jobs:
|
|||
|
||||
- name: test with zig platform
|
||||
run: |
|
||||
cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc --build-host --suppress-build-host-warning crates/cli/tests/test-projects/test-platform-simple-zig/app.roc
|
||||
cd ${{ env.RELEASE_FOLDER_NAME }} && ./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc
|
||||
|
||||
- name: print short commit SHA
|
||||
run: git rev-parse --short "$GITHUB_SHA"
|
||||
|
|
|
|||
14
.github/workflows/nix_linux_x86_64.yml
vendored
14
.github/workflows/nix_linux_x86_64.yml
vendored
|
|
@ -18,13 +18,23 @@ jobs:
|
|||
run: nix-build
|
||||
|
||||
- name: execute tests with --release
|
||||
run: nix develop -c cargo test --locked --release
|
||||
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
|
||||
run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests
|
||||
|
||||
- name: glue_cli_tests
|
||||
# single threaded due to difficult bug when multithreading
|
||||
run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1
|
||||
|
||||
- name: roc test all builtins
|
||||
run: nix develop -c ./ci/roc_test_builtins.sh
|
||||
|
||||
- name: test wasm32 cli_tests
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run"
|
||||
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests
|
||||
|
||||
- name: wasm32 glue_cli_tests
|
||||
# single threaded due to difficult bug when multithreading
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" glue_cli_tests -- --test-threads=1
|
||||
|
||||
- name: test the dev backend # these tests require an explicit feature flag
|
||||
run: nix develop -c cargo nextest run --locked --release --package test_gen --no-default-features --features gen-dev --no-fail-fast
|
||||
|
|
|
|||
2
.github/workflows/nix_macos_x86_64.yml
vendored
2
.github/workflows/nix_macos_x86_64.yml
vendored
|
|
@ -9,7 +9,7 @@ env:
|
|||
jobs:
|
||||
nix-macos-x86-64:
|
||||
name: nix-macos-x86-64
|
||||
runs-on: [macos-12]
|
||||
runs-on: [macos-13]
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
|
|
@ -26,14 +26,11 @@ jobs:
|
|||
- name: rename nightly folder
|
||||
run: mv roc_nightly* roc_nightly
|
||||
|
||||
- name: test roc hello world
|
||||
run: cd roc_nightly && ./roc examples/helloWorld.roc
|
||||
|
||||
- name: test platform switching zig
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesZig.roc --build-host --suppress-build-host-warning
|
||||
|
||||
- name: test platform switching c
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc
|
||||
run: cd roc_nightly && ./roc examples/platform-switching/rocLovesC.roc --build-host --suppress-build-host-warning
|
||||
|
||||
- name: test repl
|
||||
run: |
|
||||
|
|
|
|||
4
.github/workflows/test_nightly_many_os.yml
vendored
4
.github/workflows/test_nightly_many_os.yml
vendored
|
|
@ -5,11 +5,11 @@ name: Test latest nightly releases for macOS and Linux x86_64
|
|||
|
||||
jobs:
|
||||
test-nightly:
|
||||
name: test nightly macos 11/12/13, ubuntu 20.04/22.04
|
||||
name: test nightly macos 13 (x64), ubuntu 20.04/22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ macos-12, macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
|
||||
os: [ macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -63,6 +63,7 @@ metadata
|
|||
#editors
|
||||
.idea/
|
||||
.vscode/
|
||||
.helix/
|
||||
.ignore
|
||||
.exrc
|
||||
.vimrc
|
||||
|
|
|
|||
58
Cargo.lock
generated
58
Cargo.lock
generated
|
|
@ -2375,6 +2375,7 @@ dependencies = [
|
|||
"roc_types",
|
||||
"soa",
|
||||
"static_assertions",
|
||||
"test_compile",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
||||
|
|
@ -2652,7 +2653,6 @@ dependencies = [
|
|||
"cli_test_utils",
|
||||
"dircpy",
|
||||
"fnv",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"libc",
|
||||
"libloading",
|
||||
|
|
@ -2976,7 +2976,6 @@ dependencies = [
|
|||
"rustyline-derive",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3029,7 +3028,6 @@ dependencies = [
|
|||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"signal-hook",
|
||||
"strip-ansi-escapes",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
|
|
@ -3163,6 +3161,36 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_specialize_types"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.4",
|
||||
"bitvec",
|
||||
"bumpalo",
|
||||
"hashbrown",
|
||||
"indoc",
|
||||
"parking_lot",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"soa",
|
||||
"static_assertions",
|
||||
"test_compile",
|
||||
"test_solve_helpers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
|
|
@ -3804,6 +3832,30 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_compile"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_problem",
|
||||
"roc_specialize_types",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_derive"
|
||||
version = "0.0.1"
|
||||
|
|
|
|||
25
Cargo.toml
25
Cargo.toml
|
|
@ -17,6 +17,7 @@ members = [
|
|||
"crates/repl_wasm",
|
||||
"crates/repl_expect",
|
||||
"crates/roc_std",
|
||||
"crates/test_compile",
|
||||
"crates/test_utils",
|
||||
"crates/test_utils_dir",
|
||||
"crates/valgrind_tests",
|
||||
|
|
@ -56,8 +57,9 @@ version = "0.0.1"
|
|||
|
||||
[workspace.dependencies]
|
||||
inkwell = { git = "https://github.com/TheDan64/inkwell", rev = "89e06af", features = ["llvm18-0"] }
|
||||
soa = { path = "crates/soa" }
|
||||
roc_specialize_types = { path = "crates/compiler/specialize_types" }
|
||||
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
|
||||
backtrace = "0.3.67"
|
||||
base64-url = "1.4.13"
|
||||
bincode = "1.3.3"
|
||||
bitflags = "1.3.2"
|
||||
|
|
@ -65,9 +67,7 @@ bitvec = "1.0.1"
|
|||
blake3 = "1.3.3"
|
||||
brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||
capstone = { version = "0.11.0", default-features = false }
|
||||
cgmath = "0.18.0"
|
||||
chrono = "0.4.26"
|
||||
clap = { version = "4.2.7", default-features = false, features = [
|
||||
"std",
|
||||
|
|
@ -77,10 +77,8 @@ clap = { version = "4.2.7", default-features = false, features = [
|
|||
"usage",
|
||||
"error-context",
|
||||
] }
|
||||
colored = "2.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
const_format = { version = "0.2.30", features = ["const_generics"] }
|
||||
copypasta = "0.8.2"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [
|
||||
"html_reports",
|
||||
], rev = "30ea0c5" }
|
||||
|
|
@ -94,7 +92,6 @@ flate2 = "1.0.25"
|
|||
fnv = "1.0.7"
|
||||
fs_extra = "1.3.0"
|
||||
futures = "0.3.26"
|
||||
glyph_brush = "0.7.7"
|
||||
hashbrown = { version = "0.14.3" }
|
||||
iced-x86 = { version = "1.18.0", default-features = false, features = [
|
||||
"std",
|
||||
|
|
@ -104,7 +101,6 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [
|
|||
] }
|
||||
im = "15.1.0"
|
||||
im-rc = "15.1.0"
|
||||
indexmap = "2.1.0"
|
||||
indoc = "1.0.9"
|
||||
insta = "1.28.0"
|
||||
js-sys = "0.3.61"
|
||||
|
|
@ -118,18 +114,13 @@ mach_object = "0.1"
|
|||
maplit = "1.0.2"
|
||||
memmap2 = "0.5.10"
|
||||
mimalloc = { version = "0.1.34", default-features = false }
|
||||
nonempty = "0.8.1"
|
||||
object = { version = "0.32.2", default-features = false, features = [
|
||||
"read",
|
||||
"write",
|
||||
] }
|
||||
packed_struct = "0.10.1"
|
||||
page_size = "0.5.0"
|
||||
palette = "0.6.1"
|
||||
parking_lot = "0.12"
|
||||
perfcnt = "0.8.0"
|
||||
pest = "2.5.6"
|
||||
pest_derive = "2.5.6"
|
||||
pretty_assertions = "1.3.0" # update roc_std/Cargo.toml on change
|
||||
proc-macro2 = "1.0.63"
|
||||
proptest = "1.1.0"
|
||||
|
|
@ -163,21 +154,17 @@ syn = { version = "1.0.109", features = ["full", "extra-traits"] }
|
|||
tar = "0.4.38"
|
||||
target-lexicon = "0.12.6"
|
||||
tempfile = "=3.2.0"
|
||||
threadpool = "1.8.1"
|
||||
tracing = { version = "0.1.40", features = ["release_max_level_off"] }
|
||||
tracing-appender = "0.2.2"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
unicode-segmentation = "1.10.1"
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
walkdir = "2.3.2"
|
||||
wasm-bindgen = "0.2.84"
|
||||
wasm-bindgen-futures = "0.4.34"
|
||||
wgpu = "0.12.0"
|
||||
wgpu_glyph = "0.16.0"
|
||||
winapi = { version = "0.3.9", features = ["memoryapi"] }
|
||||
winit = "0.26.1"
|
||||
wyhash = "0.5.0"
|
||||
|
||||
# Testing
|
||||
test_compile = { path = "crates/test_compile" }
|
||||
|
||||
# Optimizations based on https://deterministic.space/high-performance-rust.html
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
|
|
|||
|
|
@ -28,18 +28,16 @@ mv roc_nightly* roc_nightly
|
|||
|
||||
cd roc_nightly
|
||||
|
||||
# test roc hello world
|
||||
./roc examples/helloWorld.roc
|
||||
|
||||
# test rust platform (first prebuild the host)
|
||||
examples/platform-switching/rust-platform/build.sh
|
||||
./roc examples/platform-switching/rocLovesRust.roc
|
||||
# temp disabled
|
||||
# examples/platform-switching/rust-platform/build.sh
|
||||
# ./roc examples/platform-switching/rocLovesRust.roc
|
||||
|
||||
# test zig platform
|
||||
./roc examples/platform-switching/rocLovesZig.roc
|
||||
./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesZig.roc
|
||||
|
||||
# test C platform
|
||||
./roc examples/platform-switching/rocLovesC.roc
|
||||
./roc --build-host --suppress-build-host-warning examples/platform-switching/rocLovesC.roc
|
||||
|
||||
# test repl
|
||||
cd ../ci/repl_basic_test
|
||||
|
|
|
|||
|
|
@ -54,6 +54,6 @@ fi
|
|||
./jump-start.sh
|
||||
|
||||
# build the basic cli platform
|
||||
roc build.roc --prebuilt-platform
|
||||
roc build.roc
|
||||
|
||||
cd ..
|
||||
|
|
|
|||
|
|
@ -47,6 +47,6 @@ cd ..
|
|||
|
||||
cd basic-webserver
|
||||
|
||||
roc build.roc --prebuilt-platform
|
||||
roc build.roc
|
||||
|
||||
cd ..
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ mkdir -p $1 $1/examples $1/crates/compiler/builtins/bitcode
|
|||
mv target/release-with-lto/{roc,roc_language_server,lib} $1
|
||||
mv LICENSE LEGAL_DETAILS $1
|
||||
|
||||
mv examples/{helloWorld.roc,platform-switching,cli} $1/examples
|
||||
mv examples/{platform-switching,cli} $1/examples
|
||||
|
||||
mv crates/roc_std $1/crates
|
||||
mv crates/compiler/builtins/bitcode/src $1/crates/compiler/builtins/bitcode
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ pub fn build_app() -> Command {
|
|||
Arg::new(ROC_FILE)
|
||||
.help("The .roc file to test")
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.num_args(0..)
|
||||
.required(false)
|
||||
.default_value(DEFAULT_ROC_FILENAME)
|
||||
)
|
||||
|
|
@ -516,18 +517,21 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
Some(n) => Threading::AtMost(*n),
|
||||
};
|
||||
|
||||
let path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
||||
let paths: Vec<_> = matches.get_many::<PathBuf>(ROC_FILE).unwrap().collect();
|
||||
|
||||
// Spawn the root task
|
||||
if !path.exists() {
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
let expected_file_path = current_dir.join(path);
|
||||
let paths: Vec<_> = {
|
||||
let mut flatten_paths: Vec<_> = vec![];
|
||||
for path in paths.into_iter() {
|
||||
// Spawn the root task
|
||||
if !path.exists() {
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
let expected_file_path = current_dir.join(path);
|
||||
|
||||
let current_dir_string = current_dir.display();
|
||||
let expected_file_path_string = expected_file_path.display();
|
||||
let current_dir_string = current_dir.display();
|
||||
let expected_file_path_string = expected_file_path.display();
|
||||
|
||||
// TODO these should use roc_reporting to display nicer error messages.
|
||||
match matches.value_source(ROC_FILE) {
|
||||
// TODO these should use roc_reporting to display nicer error messages.
|
||||
match matches.value_source(ROC_FILE) {
|
||||
Some(ValueSource::DefaultValue) => {
|
||||
eprintln!(
|
||||
"\nThe current directory ({current_dir_string}) does not contain a {DEFAULT_ROC_FILENAME} file to use as a default.\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"
|
||||
|
|
@ -535,116 +539,141 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
}
|
||||
_ => eprintln!("\nThis file was not found: {expected_file_path_string}\n\nYou can run `roc help` for more information on how to provide a .roc file.\n"),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let arena = &arena;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target,
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Test,
|
||||
};
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
path.to_path_buf(),
|
||||
opt_main_path.cloned(),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
load_config,
|
||||
);
|
||||
|
||||
let mut loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return handle_loading_problem(problem);
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
|
||||
process::exit(1);
|
||||
} else if path.is_dir() {
|
||||
find_all_roc_files(path, &mut flatten_paths);
|
||||
} else {
|
||||
flatten_paths.push(path.clone());
|
||||
}
|
||||
}
|
||||
flatten_paths
|
||||
};
|
||||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
|
||||
let mut expectations = std::mem::take(&mut loaded.expectations);
|
||||
let mut all_files_total_failed_count = 0;
|
||||
let mut all_files_total_passed_count = 0;
|
||||
|
||||
let interns = loaded.interns.clone();
|
||||
let sources = loaded.sources.clone();
|
||||
for path in paths.iter() {
|
||||
let arena = &arena;
|
||||
let function_kind = FunctionKind::from_env();
|
||||
|
||||
let (dyn_lib, expects_by_module, layout_interner) =
|
||||
roc_repl_expect::run::expect_mono_module_to_dylib(
|
||||
arena,
|
||||
let opt_main_path = matches.get_one::<PathBuf>(FLAG_MAIN);
|
||||
|
||||
// Step 1: compile the app and generate the .o file
|
||||
let load_config = LoadConfig {
|
||||
target,
|
||||
loaded,
|
||||
opt_level,
|
||||
LlvmBackendMode::CliTest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Print warnings before running tests.
|
||||
{
|
||||
debug_assert_eq!(
|
||||
problems.errors, 0,
|
||||
"if there were errors, we would have already exited."
|
||||
function_kind,
|
||||
// TODO: expose this from CLI?
|
||||
render: roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
palette: roc_reporting::report::DEFAULT_PALETTE,
|
||||
threading,
|
||||
exec_mode: ExecutionMode::Test,
|
||||
};
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
path.to_path_buf(),
|
||||
opt_main_path.cloned(),
|
||||
RocCacheDir::Persistent(cache::roc_cache_packages_dir().as_path()),
|
||||
load_config,
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
problems.print_error_warning_count(start_time.elapsed());
|
||||
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80));
|
||||
|
||||
let mut loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return handle_loading_problem(problem);
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return handle_error_module(module, start_time.elapsed(), path.as_os_str(), false);
|
||||
}
|
||||
};
|
||||
let problems = report_problems_monomorphized(&mut loaded);
|
||||
|
||||
let mut expectations = std::mem::take(&mut loaded.expectations);
|
||||
|
||||
let interns = loaded.interns.clone();
|
||||
let sources = loaded.sources.clone();
|
||||
|
||||
let (dyn_lib, expects_by_module, layout_interner) =
|
||||
roc_repl_expect::run::expect_mono_module_to_dylib(
|
||||
arena,
|
||||
target,
|
||||
loaded,
|
||||
opt_level,
|
||||
LlvmBackendMode::CliTest,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Print warnings before running tests.
|
||||
{
|
||||
debug_assert_eq!(
|
||||
problems.errors, 0,
|
||||
"if there were errors, we would have already exited."
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
problems.print_error_warning_count(start_time.elapsed());
|
||||
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80));
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
let arena = &bumpalo::Bump::new();
|
||||
let interns = arena.alloc(interns);
|
||||
|
||||
let mut writer = std::io::stdout();
|
||||
|
||||
let mut total_failed_count = 0;
|
||||
let mut total_passed_count = 0;
|
||||
|
||||
let mut results_by_module = Vec::new();
|
||||
let global_layout_interner = layout_interner.into_global();
|
||||
|
||||
let compilation_duration = start_time.elapsed();
|
||||
|
||||
for (module_id, expects) in expects_by_module.into_iter() {
|
||||
let test_start_time = Instant::now();
|
||||
|
||||
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
|
||||
&mut writer,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
arena,
|
||||
interns,
|
||||
&global_layout_interner,
|
||||
&dyn_lib,
|
||||
&mut expectations,
|
||||
expects,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tests_duration = test_start_time.elapsed();
|
||||
|
||||
results_by_module.push(ModuleTestResults {
|
||||
module_id,
|
||||
failed_count,
|
||||
passed_count,
|
||||
tests_duration,
|
||||
});
|
||||
|
||||
total_failed_count += failed_count;
|
||||
total_passed_count += passed_count;
|
||||
}
|
||||
|
||||
let total_duration = start_time.elapsed();
|
||||
all_files_total_failed_count += total_failed_count;
|
||||
all_files_total_passed_count += total_passed_count;
|
||||
if total_failed_count == 0 && total_passed_count == 0 {
|
||||
// Only report no expectations found once.
|
||||
continue;
|
||||
} else if matches.get_flag(FLAG_VERBOSE) {
|
||||
println!("Compiled in {} ms.", compilation_duration.as_millis());
|
||||
for module_test_results in results_by_module {
|
||||
print_test_results(module_test_results, &sources);
|
||||
}
|
||||
} else {
|
||||
let test_summary_str =
|
||||
test_summary(total_failed_count, total_passed_count, total_duration);
|
||||
println!("{test_summary_str}");
|
||||
}
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
let arena = &bumpalo::Bump::new();
|
||||
let interns = arena.alloc(interns);
|
||||
|
||||
let mut writer = std::io::stdout();
|
||||
|
||||
let mut total_failed_count = 0;
|
||||
let mut total_passed_count = 0;
|
||||
|
||||
let mut results_by_module = Vec::new();
|
||||
let global_layout_interner = layout_interner.into_global();
|
||||
|
||||
let compilation_duration = start_time.elapsed();
|
||||
|
||||
for (module_id, expects) in expects_by_module.into_iter() {
|
||||
let test_start_time = Instant::now();
|
||||
|
||||
let (failed_count, passed_count) = roc_repl_expect::run::run_toplevel_expects(
|
||||
&mut writer,
|
||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||
arena,
|
||||
interns,
|
||||
&global_layout_interner,
|
||||
&dyn_lib,
|
||||
&mut expectations,
|
||||
expects,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tests_duration = test_start_time.elapsed();
|
||||
|
||||
results_by_module.push(ModuleTestResults {
|
||||
module_id,
|
||||
failed_count,
|
||||
passed_count,
|
||||
tests_duration,
|
||||
});
|
||||
|
||||
total_failed_count += failed_count;
|
||||
total_passed_count += passed_count;
|
||||
}
|
||||
|
||||
let total_duration = start_time.elapsed();
|
||||
|
||||
if total_failed_count == 0 && total_passed_count == 0 {
|
||||
if all_files_total_failed_count == 0 && all_files_total_passed_count == 0 {
|
||||
// TODO print this in a more nicely formatted way!
|
||||
println!("No expectations were found.");
|
||||
|
||||
|
|
@ -655,18 +684,32 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result<i32> {
|
|||
// running tests altogether!
|
||||
Ok(2)
|
||||
} else {
|
||||
if matches.get_flag(FLAG_VERBOSE) {
|
||||
println!("Compiled in {} ms.", compilation_duration.as_millis());
|
||||
for module_test_results in results_by_module {
|
||||
print_test_results(module_test_results, &sources);
|
||||
}
|
||||
} else {
|
||||
let test_summary_str =
|
||||
test_summary(total_failed_count, total_passed_count, total_duration);
|
||||
println!("{test_summary_str}");
|
||||
}
|
||||
Ok((all_files_total_failed_count > 0) as i32)
|
||||
}
|
||||
}
|
||||
|
||||
Ok((total_failed_count > 0) as i32)
|
||||
fn find_all_roc_files(path: &PathBuf, flatten_paths: &mut Vec<PathBuf>) {
|
||||
if path.is_dir() {
|
||||
if let Ok(entries) = std::fs::read_dir(path) {
|
||||
entries.for_each(|entry| {
|
||||
if let Ok(entry) = entry {
|
||||
let entry_path = entry.path();
|
||||
find_all_roc_files(&entry_path, flatten_paths);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
eprintln!(
|
||||
"\nSomething went wrong opening the directory {}\n",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
} else if path.is_file() {
|
||||
match path.extension() {
|
||||
Some(extension) if extension == "roc" => {
|
||||
flatten_paths.push(path.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ mod cli_tests {
|
|||
.unwrap();
|
||||
|
||||
let cli_build = ExecCli::new(
|
||||
roc_cli::CMD_RUN,
|
||||
roc_cli::CMD_DEV,
|
||||
file_from_root("examples/platform-switching", "rocLovesRust.roc"),
|
||||
);
|
||||
|
||||
|
|
@ -371,7 +371,7 @@ mod cli_tests {
|
|||
mod test_platform_basic_cli {
|
||||
|
||||
use super::*;
|
||||
use roc_cli::CMD_RUN;
|
||||
use roc_cli::CMD_DEV;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
|
|
@ -430,7 +430,7 @@ mod cli_tests {
|
|||
)]
|
||||
fn module_params_issue_7116() {
|
||||
let cli_build = ExecCli::new(
|
||||
CMD_RUN,
|
||||
CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/module_params",
|
||||
"issue_7116.roc",
|
||||
|
|
@ -451,7 +451,7 @@ mod cli_tests {
|
|||
)]
|
||||
fn module_params_pass_task() {
|
||||
let cli_build = ExecCli::new(
|
||||
CMD_RUN,
|
||||
CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/module_params",
|
||||
"pass_task.roc",
|
||||
|
|
@ -971,6 +971,62 @@ mod cli_tests {
|
|||
|
||||
cli_build.run().assert_clean_stdout(expected_out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn effectful_suffixed_record_field() {
|
||||
build_platform_host();
|
||||
|
||||
let cli_build = ExecCli::new(
|
||||
roc_cli::CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/effectful",
|
||||
"suffixed_record_field.roc",
|
||||
),
|
||||
);
|
||||
|
||||
let expected_output = "notEffectful: hardcoded\neffectful: from stdin\n";
|
||||
|
||||
cli_build.check_build_and_run(
|
||||
expected_output,
|
||||
ALLOW_VALGRIND,
|
||||
Some("from stdin"),
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn effectful_on_err() {
|
||||
build_platform_host();
|
||||
|
||||
let cli_build = ExecCli::new(
|
||||
roc_cli::CMD_DEV,
|
||||
file_from_root("crates/cli/tests/test-projects/effectful", "on_err.roc"),
|
||||
);
|
||||
|
||||
let expected_output = "Enter your password:\nLOG: Failed login attempt\n";
|
||||
|
||||
cli_build.check_build_and_run(expected_output, ALLOW_VALGRIND, Some("42"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn effectful_for_each_try() {
|
||||
build_platform_host();
|
||||
|
||||
let cli_build = ExecCli::new(
|
||||
roc_cli::CMD_DEV,
|
||||
file_from_root(
|
||||
"crates/cli/tests/test-projects/effectful",
|
||||
"for_each_try.roc",
|
||||
),
|
||||
);
|
||||
|
||||
let expected_output = "✅ 0\n✅ 2\n✅ 4\n✅ 6\n✅ 8\n9 is not even! ABORT!\n";
|
||||
|
||||
cli_build.check_build_and_run(expected_output, ALLOW_VALGRIND, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
// this is for testing the benchmarks (on small inputs), to perform proper benchmarks see crates/cli/benches/README.md
|
||||
|
|
|
|||
35
crates/cli/tests/test-projects/effectful/echo.roc
Normal file
35
crates/cli/tests/test-projects/effectful/echo.roc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} -> tick! {}
|
||||
|
||||
tick! = \{} ->
|
||||
line = Effect.getLine! {}
|
||||
|
||||
if !(Str.isEmpty line) then
|
||||
Effect.putLine! (echo line)
|
||||
else
|
||||
Effect.putLine! "Received no input."
|
||||
|
||||
echo : Str -> Str
|
||||
echo = \shout ->
|
||||
silence = \length -> List.repeat ' ' length
|
||||
|
||||
shout
|
||||
|> Str.toUtf8
|
||||
|> List.mapWithIndex \_, i ->
|
||||
length = (List.len (Str.toUtf8 shout) - i)
|
||||
phrase = (List.splitAt (Str.toUtf8 shout) length).before
|
||||
|
||||
List.concat (silence (if i == 0 then 2 * length else length)) phrase
|
||||
|> List.join
|
||||
|> Str.fromUtf8
|
||||
|> Result.withDefault ""
|
||||
|
||||
expect
|
||||
message = "hello!"
|
||||
echoedMessage = echo message
|
||||
|
||||
echoedMessage == " hello! hello hell hel he h"
|
||||
23
crates/cli/tests/test-projects/effectful/for_each_try.roc
Normal file
23
crates/cli/tests/test-projects/effectful/for_each_try.roc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
good = [0, 2, 4] |> List.forEachTry! validate!
|
||||
expect good == Ok {}
|
||||
|
||||
bad = [6, 8, 9, 10] |> List.forEachTry! validate!
|
||||
expect bad == Err 9
|
||||
|
||||
{}
|
||||
|
||||
validate! : U32 => Result {} U32
|
||||
validate! = \x ->
|
||||
if Num.isEven x then
|
||||
Effect.putLine! "✅ $(Num.toStr x)"
|
||||
Ok {}
|
||||
|
||||
else
|
||||
Effect.putLine! "$(Num.toStr x) is not even! ABORT!"
|
||||
Err x
|
||||
24
crates/cli/tests/test-projects/effectful/on_err.roc
Normal file
24
crates/cli/tests/test-projects/effectful/on_err.roc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
_ =
|
||||
authenticate! {}
|
||||
|> Result.onErr! \BadPass ->
|
||||
Effect.putLine! "LOG: Failed login attempt"
|
||||
Ok "Bad password"
|
||||
|
||||
{}
|
||||
|
||||
authenticate! : {} => Result Str [BadPass]
|
||||
authenticate! = \{} ->
|
||||
Effect.putLine! "Enter your password:"
|
||||
|
||||
password = Effect.getLine! {}
|
||||
|
||||
if password == "password" then
|
||||
Ok "You are in"
|
||||
else
|
||||
Err BadPass
|
||||
|
|
@ -5,7 +5,7 @@ import pf.Effect
|
|||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
["Welcome!", "What's your name?"]
|
||||
|> forEach! Effect.putLine!
|
||||
|> List.forEach! Effect.putLine!
|
||||
|
||||
line = Effect.getLine! {}
|
||||
|
||||
|
|
@ -17,11 +17,3 @@ main! = \{} ->
|
|||
|
||||
Effect.putLine! "You entered: $(line)"
|
||||
Effect.putLine! "It is known"
|
||||
|
||||
forEach! : List a, (a => {}) => {}
|
||||
forEach! = \l, f! ->
|
||||
when l is
|
||||
[] -> {}
|
||||
[x, .. as xs] ->
|
||||
f! x
|
||||
forEach! xs f!
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
app [main!] { pf: platform "../test-platform-effects-zig/main.roc" }
|
||||
|
||||
import pf.Effect
|
||||
|
||||
Fx : {
|
||||
getLine!: {} => Str,
|
||||
}
|
||||
|
||||
main! : {} => {}
|
||||
main! = \{} ->
|
||||
notEffectful : Fx
|
||||
notEffectful = {
|
||||
getLine!: \{} -> "hardcoded"
|
||||
}
|
||||
|
||||
effectful : Fx
|
||||
effectful = {
|
||||
getLine!: Effect.getLine!
|
||||
}
|
||||
|
||||
Effect.putLine! "notEffectful: $(notEffectful.getLine! {})"
|
||||
Effect.putLine! "effectful: $(effectful.getLine! {})"
|
||||
|
|
@ -21,7 +21,7 @@ polyDbg = \x ->
|
|||
|
||||
main =
|
||||
str = "this will for sure be a large string so when we split it it will use seamless slices which affect printing"
|
||||
words = Str.split str " "
|
||||
words = Str.splitOn str " "
|
||||
expect words == []
|
||||
|
||||
x = 42
|
||||
|
|
|
|||
|
|
@ -616,7 +616,6 @@ fn stmt_spec<'a>(
|
|||
}
|
||||
Dbg { remainder, .. } => stmt_spec(builder, interner, env, block, layout, remainder),
|
||||
Expect { remainder, .. } => stmt_spec(builder, interner, env, block, layout, remainder),
|
||||
ExpectFx { remainder, .. } => stmt_spec(builder, interner, env, block, layout, remainder),
|
||||
Ret(symbol) => Ok(env.symbols[symbol]),
|
||||
Refcounting(modify_rc, continuation) => {
|
||||
apply_refcount_operation(builder, env, block, modify_rc)?;
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ fn gen_from_mono_module_dev_wasm32<'a>(
|
|||
|
||||
let host_bytes = std::fs::read(built_host_path).unwrap_or_else(|_| {
|
||||
internal_error!(
|
||||
"Failed to read host object file {}! Try omitting --prebuilt-platform",
|
||||
"Failed to read host object file {}!",
|
||||
built_host_path.display()
|
||||
)
|
||||
});
|
||||
|
|
@ -883,7 +883,7 @@ fn build_loaded_file<'a>(
|
|||
|
||||
let built_host_opt =
|
||||
// Not sure if this is correct for all calls with LinkType::Dylib...
|
||||
if link_type == LinkType::Dylib || target == Target::Wasm32 {
|
||||
if link_type == LinkType::None || link_type == LinkType::Dylib || target == Target::Wasm32 {
|
||||
BuiltHostOpt::None
|
||||
} else {
|
||||
let prebuilt_host = determine_built_host_path(&platform_main_roc_path, target, build_host_requested, link_type, linking_strategy, suppress_build_host_warning);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Next, look towards the bottom of the `compiler/module/src/symbol.rs` file. Insid
|
|||
|
||||
For each of the builtin modules, there is a file in `compiler/test_gen/src/` like `gen_num.rs`, `gen_str.rs` etc. Add new tests for the module you are changing to the appropriate file here. You can look at the existing test cases for examples and inspiration.
|
||||
|
||||
You can run your new tests locally using `cargo test-gen-llvm`. You can add a filter like `cargo test-gen-llvm gen_str` (to only run tests defined in `gen_str.rs`) or `cargo test-gen-llvm gen_str::str_split` (to only run tests defined in `gen_str` whose names start with `str_split`). More details can be found in the README in the `compiler/test_gen` directory.
|
||||
You can run your new tests locally using `cargo test-gen-llvm`. You can add a filter like `cargo test-gen-llvm gen_str` (to only run tests defined in `gen_str.rs`) or `cargo test-gen-llvm gen_str::str_split_on` (to only run tests defined in `gen_str` whose names start with `str_split`). More details can be found in the README in the `compiler/test_gen` directory.
|
||||
|
||||
## A builtin implemented directly as LLVM
|
||||
|
||||
|
|
@ -22,8 +22,8 @@ Some of these have `#` inside their name (`first#list`, `#lt` ..). This is a tri
|
|||
|
||||
But we can use these values and some of these are necessary for implementing builtins. For example, `List.get` returns tags, and it is not easy for us to create tags when composing LLVM. What is easier however, is:
|
||||
|
||||
- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, U64 -> elem` in LLVM
|
||||
- ..writing `List elem, U64 -> Result elem [OutOfBounds]*` in a type safe way that uses `getUnsafe` internally, only after it checks if the `elem` at `U64` index exists.
|
||||
- ..writing `List.#getUnsafe` that has the dangerous signature of `List elem, U64 -> elem` in LLVM
|
||||
- ..writing `List elem, U64 -> Result elem [OutOfBounds]*` in a type safe way that uses `getUnsafe` internally, only after it checks if the `elem` at `U64` index exists.
|
||||
|
||||
### can/src/builtins.rs
|
||||
|
||||
|
|
@ -123,5 +123,5 @@ But replace `Num.atan`, the return value, and the return type with your new buil
|
|||
|
||||
When implementing a new builtin, it is often easy to copy and paste the implementation for an existing builtin. This can take you quite far since many builtins are very similar, but it also risks forgetting to change one small part of what you copy and pasted and losing a lot of time later on when you cant figure out why things don't work. So, speaking from experience, even if you are copying an existing builtin, try and implement it manually without copying and pasting. Two recent instances of this (as of September 7th, 2020):
|
||||
|
||||
- `List.keepIf` did not work for a long time because in builtins its `LowLevel` was `ListMap`. This was because I copy and pasted the `List.map` implementation in `builtins.rs
|
||||
- `List.walkBackwards` had mysterious memory bugs for a little while because in `unique.rs` its return type was `list_type(flex(b))` instead of `flex(b)` since it was copy and pasted from `List.keepIf`.
|
||||
- `List.keepIf` did not work for a long time because in builtins its `LowLevel` was `ListMap`. This was because I copy and pasted the `List.map` implementation in `builtins.rs
|
||||
- `List.walkBackwards` had mysterious memory bugs for a little while because in `unique.rs` its return type was `list_type(flex(b))` instead of `flex(b)` since it was copy and pasted from `List.keepIf`.
|
||||
|
|
|
|||
|
|
@ -442,16 +442,16 @@ pub const RocDec = extern struct {
|
|||
const numerator_i128 = self.num;
|
||||
const denominator_i128 = other.num;
|
||||
|
||||
// (0 / n) is always 0
|
||||
if (numerator_i128 == 0) {
|
||||
return RocDec{ .num = 0 };
|
||||
}
|
||||
|
||||
// (n / 0) is an error
|
||||
if (denominator_i128 == 0) {
|
||||
roc_panic("Decimal division by 0!", 0);
|
||||
}
|
||||
|
||||
// (0 / n) is always 0
|
||||
if (numerator_i128 == 0) {
|
||||
return RocDec{ .num = 0 };
|
||||
}
|
||||
|
||||
// If they're both negative, or if neither is negative, the final answer
|
||||
// is positive or zero. If one is negative and the denominator isn't, the
|
||||
// final answer is negative (or zero, in which case final sign won't matter).
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ comptime {
|
|||
const str = @import("str.zig");
|
||||
comptime {
|
||||
exportStrFn(str.init, "init");
|
||||
exportStrFn(str.strSplit, "str_split");
|
||||
exportStrFn(str.strSplitOn, "str_split_on");
|
||||
exportStrFn(str.countSegments, "count_segments");
|
||||
exportStrFn(str.countUtf8Bytes, "count_utf8_bytes");
|
||||
exportStrFn(str.isEmpty, "is_empty");
|
||||
|
|
|
|||
|
|
@ -598,14 +598,14 @@ fn strFromFloatHelp(comptime T: type, float: T) RocStr {
|
|||
return RocStr.init(&buf, result.len);
|
||||
}
|
||||
|
||||
// Str.split
|
||||
pub fn strSplit(string: RocStr, delimiter: RocStr) callconv(.C) RocList {
|
||||
// Str.splitOn
|
||||
pub fn strSplitOn(string: RocStr, delimiter: RocStr) callconv(.C) RocList {
|
||||
const segment_count = countSegments(string, delimiter);
|
||||
const list = RocList.allocate(@alignOf(RocStr), segment_count, @sizeOf(RocStr), true);
|
||||
|
||||
if (list.bytes) |bytes| {
|
||||
const strings = @as([*]RocStr, @ptrCast(@alignCast(bytes)));
|
||||
strSplitHelp(strings, string, delimiter);
|
||||
strSplitOnHelp(strings, string, delimiter);
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
@ -625,7 +625,7 @@ fn initFromBigStr(slice_bytes: [*]u8, len: usize, alloc_ptr: usize) RocStr {
|
|||
};
|
||||
}
|
||||
|
||||
fn strSplitHelp(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
||||
fn strSplitOnHelp(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
||||
if (delimiter.len() == 0) {
|
||||
string.incref(1);
|
||||
array[0] = string;
|
||||
|
|
@ -650,7 +650,7 @@ fn strSplitHelp(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
|
|||
}
|
||||
|
||||
test "strSplitHelp: empty delimiter" {
|
||||
// Str.split "abc" "" == ["abc"]
|
||||
// Str.splitOn "abc" "" == ["abc"]
|
||||
const str_arr = "abc";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
|
|
@ -660,7 +660,7 @@ test "strSplitHelp: empty delimiter" {
|
|||
var array: [1]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const expected = [1]RocStr{
|
||||
str,
|
||||
|
|
@ -684,7 +684,7 @@ test "strSplitHelp: empty delimiter" {
|
|||
}
|
||||
|
||||
test "strSplitHelp: no delimiter" {
|
||||
// Str.split "abc" "!" == ["abc"]
|
||||
// Str.splitOn "abc" "!" == ["abc"]
|
||||
const str_arr = "abc";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
|
|
@ -694,7 +694,7 @@ test "strSplitHelp: no delimiter" {
|
|||
var array: [1]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const expected = [1]RocStr{
|
||||
str,
|
||||
|
|
@ -731,7 +731,7 @@ test "strSplitHelp: empty start" {
|
|||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const one = RocStr.init("a", 1);
|
||||
|
||||
|
|
@ -772,7 +772,7 @@ test "strSplitHelp: empty end" {
|
|||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const one = RocStr.init("1", 1);
|
||||
const two = RocStr.init("2", 1);
|
||||
|
|
@ -811,7 +811,7 @@ test "strSplitHelp: string equals delimiter" {
|
|||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str_delimiter, str_delimiter);
|
||||
strSplitOnHelp(array_ptr, str_delimiter, str_delimiter);
|
||||
|
||||
const expected = [2]RocStr{ RocStr.empty(), RocStr.empty() };
|
||||
|
||||
|
|
@ -846,7 +846,7 @@ test "strSplitHelp: delimiter on sides" {
|
|||
undefined,
|
||||
};
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const ghi_arr = "ghi";
|
||||
const ghi = RocStr.init(ghi_arr, ghi_arr.len);
|
||||
|
|
@ -875,7 +875,7 @@ test "strSplitHelp: delimiter on sides" {
|
|||
}
|
||||
|
||||
test "strSplitHelp: three pieces" {
|
||||
// Str.split "a!b!c" "!" == ["a", "b", "c"]
|
||||
// Str.splitOn "a!b!c" "!" == ["a", "b", "c"]
|
||||
const str_arr = "a!b!c";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
|
|
@ -886,7 +886,7 @@ test "strSplitHelp: three pieces" {
|
|||
var array: [array_len]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const a = RocStr.init("a", 1);
|
||||
const b = RocStr.init("b", 1);
|
||||
|
|
@ -916,7 +916,7 @@ test "strSplitHelp: three pieces" {
|
|||
}
|
||||
|
||||
test "strSplitHelp: overlapping delimiter 1" {
|
||||
// Str.split "aaa" "aa" == ["", "a"]
|
||||
// Str.splitOn "aaa" "aa" == ["", "a"]
|
||||
const str_arr = "aaa";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
|
|
@ -926,7 +926,7 @@ test "strSplitHelp: overlapping delimiter 1" {
|
|||
var array: [2]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const expected = [2]RocStr{
|
||||
RocStr.empty(),
|
||||
|
|
@ -941,7 +941,7 @@ test "strSplitHelp: overlapping delimiter 1" {
|
|||
}
|
||||
|
||||
test "strSplitHelp: overlapping delimiter 2" {
|
||||
// Str.split "aaa" "aa" == ["", "a"]
|
||||
// Str.splitOn "aaa" "aa" == ["", "a"]
|
||||
const str_arr = "aaaa";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
||||
|
|
@ -951,7 +951,7 @@ test "strSplitHelp: overlapping delimiter 2" {
|
|||
var array: [3]RocStr = undefined;
|
||||
const array_ptr: [*]RocStr = &array;
|
||||
|
||||
strSplitHelp(array_ptr, str, delimiter);
|
||||
strSplitOnHelp(array_ptr, str, delimiter);
|
||||
|
||||
const expected = [3]RocStr{
|
||||
RocStr.empty(),
|
||||
|
|
@ -967,7 +967,7 @@ test "strSplitHelp: overlapping delimiter 2" {
|
|||
try expect(array[2].eq(expected[2]));
|
||||
}
|
||||
|
||||
// This is used for `Str.split : Str, Str -> Array Str
|
||||
// This is used for `Str.splitOn : Str, Str -> List Str
|
||||
// It is used to count how many segments the input `_str`
|
||||
// needs to be broken into, so that we can allocate a array
|
||||
// of that size. It always returns at least 1.
|
||||
|
|
@ -985,7 +985,7 @@ pub fn countSegments(string: RocStr, delimiter: RocStr) callconv(.C) usize {
|
|||
}
|
||||
|
||||
test "countSegments: long delimiter" {
|
||||
// Str.split "str" "delimiter" == ["str"]
|
||||
// Str.splitOn "str" "delimiter" == ["str"]
|
||||
// 1 segment
|
||||
const str_arr = "str";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
|
@ -1003,7 +1003,7 @@ test "countSegments: long delimiter" {
|
|||
}
|
||||
|
||||
test "countSegments: delimiter at start" {
|
||||
// Str.split "hello there" "hello" == ["", " there"]
|
||||
// Str.splitOn "hello there" "hello" == ["", " there"]
|
||||
// 2 segments
|
||||
const str_arr = "hello there";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
|
@ -1022,7 +1022,7 @@ test "countSegments: delimiter at start" {
|
|||
}
|
||||
|
||||
test "countSegments: delimiter interspered" {
|
||||
// Str.split "a!b!c" "!" == ["a", "b", "c"]
|
||||
// Str.splitOn "a!b!c" "!" == ["a", "b", "c"]
|
||||
// 3 segments
|
||||
const str_arr = "a!b!c";
|
||||
const str = RocStr.init(str_arr, str_arr.len);
|
||||
|
|
@ -1041,7 +1041,7 @@ test "countSegments: delimiter interspered" {
|
|||
}
|
||||
|
||||
test "countSegments: string equals delimiter" {
|
||||
// Str.split "/" "/" == ["", ""]
|
||||
// Str.splitOn "/" "/" == ["", ""]
|
||||
// 2 segments
|
||||
const str_delimiter_arr = "/";
|
||||
const str_delimiter = RocStr.init(str_delimiter_arr, str_delimiter_arr.len);
|
||||
|
|
@ -1056,14 +1056,14 @@ test "countSegments: string equals delimiter" {
|
|||
}
|
||||
|
||||
test "countSegments: overlapping delimiter 1" {
|
||||
// Str.split "aaa" "aa" == ["", "a"]
|
||||
// Str.splitOn "aaa" "aa" == ["", "a"]
|
||||
const segments_count = countSegments(RocStr.init("aaa", 3), RocStr.init("aa", 2));
|
||||
|
||||
try expectEqual(segments_count, 2);
|
||||
}
|
||||
|
||||
test "countSegments: overlapping delimiter 2" {
|
||||
// Str.split "aaa" "aa" == ["", "a"]
|
||||
// Str.splitOn "aaa" "aa" == ["", "a"]
|
||||
const segments_count = countSegments(RocStr.init("aaaa", 4), RocStr.init("aa", 2));
|
||||
|
||||
try expectEqual(segments_count, 3);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ module [
|
|||
findLastIndex,
|
||||
sublist,
|
||||
intersperse,
|
||||
split,
|
||||
splitAt,
|
||||
splitOn,
|
||||
splitOnList,
|
||||
splitFirst,
|
||||
splitLast,
|
||||
startsWith,
|
||||
|
|
@ -70,6 +72,8 @@ module [
|
|||
countIf,
|
||||
chunksOf,
|
||||
concatUtf8,
|
||||
forEach!,
|
||||
forEachTry!,
|
||||
]
|
||||
|
||||
import Bool exposing [Bool, Eq]
|
||||
|
|
@ -1026,7 +1030,7 @@ first = \list ->
|
|||
## To remove elements from both the beginning and end of the list,
|
||||
## use `List.sublist`.
|
||||
##
|
||||
## To split the list into two lists, use `List.split`.
|
||||
## To split the list into two lists, use `List.splitAt`.
|
||||
##
|
||||
takeFirst : List elem, U64 -> List elem
|
||||
takeFirst = \list, outputLength ->
|
||||
|
|
@ -1046,7 +1050,7 @@ takeFirst = \list, outputLength ->
|
|||
## To remove elements from both the beginning and end of the list,
|
||||
## use `List.sublist`.
|
||||
##
|
||||
## To split the list into two lists, use `List.split`.
|
||||
## To split the list into two lists, use `List.splitAt`.
|
||||
##
|
||||
takeLast : List elem, U64 -> List elem
|
||||
takeLast = \list, outputLength ->
|
||||
|
|
@ -1247,8 +1251,8 @@ endsWith = \list, suffix ->
|
|||
## than the given index, # and the `others` list will be all the others. (This
|
||||
## means if you give an index of 0, the `before` list will be empty and the
|
||||
## `others` list will have the same elements as the original list.)
|
||||
split : List elem, U64 -> { before : List elem, others : List elem }
|
||||
split = \elements, userSplitIndex ->
|
||||
splitAt : List elem, U64 -> { before : List elem, others : List elem }
|
||||
splitAt = \elements, userSplitIndex ->
|
||||
length = List.len elements
|
||||
splitIndex = if length > userSplitIndex then userSplitIndex else length
|
||||
before = List.sublist elements { start: 0, len: splitIndex }
|
||||
|
|
@ -1256,6 +1260,44 @@ split = \elements, userSplitIndex ->
|
|||
|
||||
{ before, others }
|
||||
|
||||
## Splits the input list on the delimiter element.
|
||||
##
|
||||
## ```roc
|
||||
## List.splitOn [1, 2, 3] 2 == [[1], [3]]
|
||||
## ```
|
||||
splitOn : List a, a -> List (List a) where a implements Eq
|
||||
splitOn = \elements, delimiter ->
|
||||
help = \remaining, chunks, currentChunk ->
|
||||
when remaining is
|
||||
[] -> List.append chunks currentChunk
|
||||
[x, .. as rest] if x == delimiter ->
|
||||
help rest (List.append chunks currentChunk) []
|
||||
|
||||
[x, .. as rest] ->
|
||||
help rest chunks (List.append currentChunk x)
|
||||
help elements [] []
|
||||
|
||||
## Splits the input list on the delimiter list.
|
||||
##
|
||||
## ```roc
|
||||
## List.splitOnList [1, 2, 3] [1, 2] == [[], [3]]
|
||||
## ```
|
||||
splitOnList : List a, List a -> List (List a) where a implements Eq
|
||||
splitOnList = \elements, delimiter ->
|
||||
help = \remaining, chunks, currentChunk ->
|
||||
when remaining is
|
||||
[] -> List.append chunks currentChunk
|
||||
[x, .. as rest] ->
|
||||
if List.startsWith remaining delimiter then
|
||||
help (List.dropFirst remaining (List.len delimiter)) (List.append chunks currentChunk) []
|
||||
else
|
||||
help rest chunks (List.append currentChunk x)
|
||||
|
||||
if delimiter == [] then
|
||||
[elements]
|
||||
else
|
||||
help elements [] []
|
||||
|
||||
## Returns the elements before the first occurrence of a delimiter, as well as the
|
||||
## remaining elements after that occurrence. If the delimiter is not found, returns `Err`.
|
||||
## ```roc
|
||||
|
|
@ -1305,7 +1347,7 @@ chunksOfHelp = \listRest, chunkSize, chunks ->
|
|||
if List.isEmpty listRest then
|
||||
chunks
|
||||
else
|
||||
{ before, others } = List.split listRest chunkSize
|
||||
{ before, others } = List.splitAt listRest chunkSize
|
||||
chunksOfHelp others chunkSize (List.append chunks before)
|
||||
|
||||
## Like [List.map], except the transformation function returns a [Result].
|
||||
|
|
@ -1383,3 +1425,44 @@ concatUtf8 : List U8, Str -> List U8
|
|||
|
||||
expect (List.concatUtf8 [1, 2, 3, 4] "🐦") == [1, 2, 3, 4, 240, 159, 144, 166]
|
||||
|
||||
## Run an effectful function for each element on the list.
|
||||
##
|
||||
## ```roc
|
||||
## List.forEach! ["Alice", "Bob", "Charlie"] \name ->
|
||||
## createAccount! name
|
||||
## log! "Account created"
|
||||
## ```
|
||||
##
|
||||
## If the function might fail or you need to return early, use [forEachTry!].
|
||||
forEach! : List a, (a => {}) => {}
|
||||
forEach! = \list, func! ->
|
||||
when list is
|
||||
[] ->
|
||||
{}
|
||||
|
||||
[elem, .. as rest] ->
|
||||
func! elem
|
||||
forEach! rest func!
|
||||
|
||||
## Run an effectful function that might fail for each element on the list.
|
||||
##
|
||||
## If the function returns `Err`, the iteration stops and the error is returned.
|
||||
##
|
||||
## ```roc
|
||||
## List.forEachTry! filesToDelete \path ->
|
||||
## try File.delete! path
|
||||
## Stdout.line! "$(path) deleted"
|
||||
## ```
|
||||
forEachTry! : List a, (a => Result {} err) => Result {} err
|
||||
forEachTry! = \list, func! ->
|
||||
when list is
|
||||
[] ->
|
||||
Ok {}
|
||||
|
||||
[elem, .. as rest] ->
|
||||
when func! elem is
|
||||
Ok {} ->
|
||||
forEachTry! rest func!
|
||||
|
||||
Err err ->
|
||||
Err err
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ module [
|
|||
map2,
|
||||
try,
|
||||
onErr,
|
||||
onErr!,
|
||||
withDefault,
|
||||
]
|
||||
|
||||
|
|
@ -119,3 +120,16 @@ onErr = \result, transform ->
|
|||
when result is
|
||||
Ok v -> Ok v
|
||||
Err e -> transform e
|
||||
|
||||
## Like [onErr], but it allows the transformation function to produce effects.
|
||||
##
|
||||
## ```roc
|
||||
## Result.onErr (Err "missing user") \msg ->
|
||||
## try Stdout.line! "ERROR: $(msg)"
|
||||
## Err msg
|
||||
## ```
|
||||
onErr! : Result a err, (err => Result a otherErr) => Result a otherErr
|
||||
onErr! = \result, transform! ->
|
||||
when result is
|
||||
Ok v -> Ok v
|
||||
Err e -> transform! e
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@
|
|||
##
|
||||
## The way Roc organizes the `Str` module and supporting packages is designed to help answer this question. Every situation is different, but the following rules of thumb are typical:
|
||||
##
|
||||
## * Most often, using `Str` values along with helper functions like [`split`](https://www.roc-lang.org/builtins/Str#split), [`joinWith`](https://www.roc-lang.org/builtins/Str#joinWith), and so on, is the best option.
|
||||
## * Most often, using `Str` values along with helper functions like [`splitOn`](https://www.roc-lang.org/builtins/Str#splitOn), [`joinWith`](https://www.roc-lang.org/builtins/Str#joinWith), and so on, is the best option.
|
||||
## * If you are specifically implementing a parser, working in UTF-8 bytes is usually the best option. So functions like [`walkUtf8`](https://www.roc-lang.org/builtins/Str#walkUtf8), [toUtf8](https://www.roc-lang.org/builtins/Str#toUtf8), and so on. (Note that single-quote literals produce number literals, so ASCII-range literals like `'a'` gives an integer literal that works with a UTF-8 `U8`.)
|
||||
## * If you are implementing a Unicode library like [roc-lang/unicode](https://github.com/roc-lang/unicode), working in terms of code points will be unavoidable. Aside from basic readability considerations like `\u(...)` in string literals, if you have the option to avoid working in terms of code points, it is almost always correct to avoid them.
|
||||
## * If it seems like a good idea to split a string into "characters" (graphemes), you should definitely stop and reconsider whether this is really the best design. Almost always, doing this is some combination of more error-prone or slower (usually both) than doing something else that does not require taking graphemes into consideration.
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
## Try putting this into `roc repl`:
|
||||
##
|
||||
## ```
|
||||
## » "foo/bar/baz" |> Str.split "/"
|
||||
## » "foo/bar/baz" |> Str.splitOn "/"
|
||||
##
|
||||
## ["foo", "bar", "baz"] : List Str
|
||||
## ```
|
||||
|
|
@ -304,7 +304,7 @@
|
|||
## Now let's suppose they were long enough that this optimization no longer applied:
|
||||
##
|
||||
## ```
|
||||
## » "a much, much, much, much/longer/string compared to the last one!" |> Str.split "/"
|
||||
## » "a much, much, much, much/longer/string compared to the last one!" |> Str.splitOn "/"
|
||||
##
|
||||
## ["a much, much, much, much", "longer", "string compared to the last one!"] : List Str
|
||||
## ```
|
||||
|
|
@ -332,7 +332,7 @@ module [
|
|||
concat,
|
||||
isEmpty,
|
||||
joinWith,
|
||||
split,
|
||||
splitOn,
|
||||
repeat,
|
||||
countUtf8Bytes,
|
||||
toUtf8,
|
||||
|
|
@ -499,10 +499,10 @@ joinWith : List Str, Str -> Str
|
|||
## Passing `""` for the separator is not useful;
|
||||
## it returns the original string wrapped in a [List].
|
||||
## ```roc
|
||||
## expect Str.split "1,2,3" "," == ["1","2","3"]
|
||||
## expect Str.split "1,2,3" "" == ["1,2,3"]
|
||||
## expect Str.splitOn "1,2,3" "," == ["1","2","3"]
|
||||
## expect Str.splitOn "1,2,3" "" == ["1,2,3"]
|
||||
## ```
|
||||
split : Str, Str -> List Str
|
||||
splitOn : Str, Str -> List Str
|
||||
|
||||
## Repeats a string the given number of times.
|
||||
## ```roc
|
||||
|
|
@ -518,7 +518,7 @@ repeat : Str, U64 -> Str
|
|||
|
||||
## Returns a [List] of the string's [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit).
|
||||
## (To split the string into a [List] of smaller [Str] values instead of [U8] values,
|
||||
## see [Str.split].)
|
||||
## see [Str.splitOn].)
|
||||
## ```roc
|
||||
## expect Str.toUtf8 "Roc" == [82, 111, 99]
|
||||
## expect Str.toUtf8 "鹏" == [233, 185, 143]
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ pub const STR_INIT: &str = "roc_builtins.str.init";
|
|||
pub const STR_COUNT_SEGMENTS: &str = "roc_builtins.str.count_segments";
|
||||
pub const STR_CONCAT: &str = "roc_builtins.str.concat";
|
||||
pub const STR_JOIN_WITH: &str = "roc_builtins.str.joinWith";
|
||||
pub const STR_SPLIT: &str = "roc_builtins.str.str_split";
|
||||
pub const STR_SPLIT_ON: &str = "roc_builtins.str.str_split_on";
|
||||
pub const STR_COUNT_UTF8_BYTES: &str = "roc_builtins.str.count_utf8_bytes";
|
||||
pub const STR_IS_EMPTY: &str = "roc_builtins.str.is_empty";
|
||||
pub const STR_CAPACITY: &str = "roc_builtins.str.capacity";
|
||||
|
|
|
|||
|
|
@ -30,3 +30,4 @@ soa = { path = "../../soa" }
|
|||
indoc.workspace = true
|
||||
insta.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
test_compile.workspace = true
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use crate::env::Env;
|
|||
use crate::procedure::{QualifiedReference, References};
|
||||
use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup};
|
||||
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::ident::{Ident, IdentSuffix, Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{
|
||||
AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use roc_problem::can::ShadowKind;
|
||||
use roc_problem::can::{Problem, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::{
|
||||
|
|
@ -132,7 +132,7 @@ pub struct AbleVariable {
|
|||
pub first_seen: Region,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct IntroducedVariables {
|
||||
pub wildcards: Vec<Loc<Variable>>,
|
||||
pub lambda_sets: Vec<Variable>,
|
||||
|
|
@ -482,7 +482,6 @@ pub fn find_type_def_symbols(
|
|||
AssignedField::LabelOnly(_) => {}
|
||||
AssignedField::SpaceBefore(inner, _)
|
||||
| AssignedField::SpaceAfter(inner, _) => inner_stack.push(inner),
|
||||
AssignedField::Malformed(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -507,7 +506,6 @@ pub fn find_type_def_symbols(
|
|||
Tag::SpaceBefore(inner, _) | Tag::SpaceAfter(inner, _) => {
|
||||
inner_stack.push(inner)
|
||||
}
|
||||
Tag::Malformed(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1355,7 +1353,7 @@ fn can_assigned_fields<'a>(
|
|||
// field names we've seen so far in this record
|
||||
let mut seen = std::collections::HashMap::with_capacity(fields.len());
|
||||
|
||||
'outer: for loc_field in fields.iter() {
|
||||
for loc_field in fields.iter() {
|
||||
let mut field = &loc_field.value;
|
||||
|
||||
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
|
||||
|
|
@ -1378,6 +1376,8 @@ fn can_assigned_fields<'a>(
|
|||
);
|
||||
|
||||
let label = Lowercase::from(field_name.value);
|
||||
check_record_field_suffix(env, label.suffix(), &field_type, &loc_field.region);
|
||||
|
||||
field_types.insert(label.clone(), RigidRequired(field_type));
|
||||
|
||||
break 'inner label;
|
||||
|
|
@ -1396,6 +1396,8 @@ fn can_assigned_fields<'a>(
|
|||
);
|
||||
|
||||
let label = Lowercase::from(field_name.value);
|
||||
check_record_field_suffix(env, label.suffix(), &field_type, &loc_field.region);
|
||||
|
||||
field_types.insert(label.clone(), RigidOptional(field_type));
|
||||
|
||||
break 'inner label;
|
||||
|
|
@ -1426,12 +1428,6 @@ fn can_assigned_fields<'a>(
|
|||
field = nested;
|
||||
continue 'inner;
|
||||
}
|
||||
Malformed(string) => {
|
||||
malformed(env, region, string);
|
||||
|
||||
// completely skip this element, advance to the next tag
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1450,6 +1446,23 @@ fn can_assigned_fields<'a>(
|
|||
field_types
|
||||
}
|
||||
|
||||
fn check_record_field_suffix(
|
||||
env: &mut Env,
|
||||
suffix: IdentSuffix,
|
||||
field_type: &Type,
|
||||
region: &Region,
|
||||
) {
|
||||
match (suffix, field_type) {
|
||||
(IdentSuffix::None, Type::Function(_, _, _, fx)) if **fx == Type::Effectful => env
|
||||
.problems
|
||||
.push(Problem::UnsuffixedEffectfulRecordField(*region)),
|
||||
(IdentSuffix::Bang, Type::Function(_, _, _, fx)) if **fx == Type::Pure => {
|
||||
env.problems.push(Problem::SuffixedPureRecordField(*region))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO trim down these arguments!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn can_assigned_tuple_elems(
|
||||
|
|
@ -1501,7 +1514,7 @@ fn can_tags<'a>(
|
|||
// tag names we've seen so far in this tag union
|
||||
let mut seen = std::collections::HashMap::with_capacity(tags.len());
|
||||
|
||||
'outer: for loc_tag in tags.iter() {
|
||||
for loc_tag in tags.iter() {
|
||||
let mut tag = &loc_tag.value;
|
||||
|
||||
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
|
||||
|
|
@ -1540,12 +1553,6 @@ fn can_tags<'a>(
|
|||
tag = nested;
|
||||
continue 'inner;
|
||||
}
|
||||
Tag::Malformed(string) => {
|
||||
malformed(env, region, string);
|
||||
|
||||
// completely skip this element, advance to the next tag
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
StrIsEmpty; STR_IS_EMPTY; 1,
|
||||
StrStartsWith; STR_STARTS_WITH; 2,
|
||||
StrEndsWith; STR_ENDS_WITH; 2,
|
||||
StrSplit; STR_SPLIT; 2,
|
||||
StrSplitOn; STR_SPLIT_ON; 2,
|
||||
StrCountUtf8Bytes; STR_COUNT_UTF8_BYTES; 1,
|
||||
StrFromUtf8; STR_FROM_UTF8_LOWLEVEL; 1,
|
||||
StrToUtf8; STR_TO_UTF8; 1,
|
||||
|
|
|
|||
|
|
@ -618,15 +618,10 @@ impl Constraints {
|
|||
Constraint::FxSuffix(constraint_index)
|
||||
}
|
||||
|
||||
pub fn fx_record_field_suffix(
|
||||
&mut self,
|
||||
suffix: IdentSuffix,
|
||||
variable: Variable,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
pub fn fx_record_field_unsuffixed(&mut self, variable: Variable, region: Region) -> Constraint {
|
||||
let type_index = Self::push_type_variable(variable);
|
||||
let constraint = FxSuffixConstraint {
|
||||
kind: FxSuffixKind::RecordField(suffix),
|
||||
kind: FxSuffixKind::UnsuffixedRecordField,
|
||||
type_index,
|
||||
region,
|
||||
};
|
||||
|
|
@ -934,7 +929,7 @@ pub struct FxExpectation {
|
|||
pub ann_region: Option<Region>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum FxCallKind {
|
||||
Call(Option<Symbol>),
|
||||
Stmt,
|
||||
|
|
@ -948,23 +943,30 @@ pub struct FxSuffixConstraint {
|
|||
pub region: Region,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum FxSuffixKind {
|
||||
Let(Symbol),
|
||||
Pattern(Symbol),
|
||||
RecordField(IdentSuffix),
|
||||
UnsuffixedRecordField,
|
||||
}
|
||||
|
||||
impl FxSuffixKind {
|
||||
pub fn suffix(&self) -> IdentSuffix {
|
||||
match self {
|
||||
Self::Let(symbol) | Self::Pattern(symbol) => symbol.suffix(),
|
||||
Self::RecordField(suffix) => *suffix,
|
||||
Self::UnsuffixedRecordField => IdentSuffix::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> Option<&Symbol> {
|
||||
match self {
|
||||
Self::Let(symbol) | Self::Pattern(symbol) => Some(symbol),
|
||||
Self::UnsuffixedRecordField => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ExpectEffectfulReason {
|
||||
Stmt,
|
||||
Ignored,
|
||||
|
|
|
|||
|
|
@ -694,16 +694,6 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
lookups_in_cond: lookups_in_cond.to_vec(),
|
||||
},
|
||||
|
||||
ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
lookups_in_cond,
|
||||
} => ExpectFx {
|
||||
loc_condition: Box::new(loc_condition.map(|e| go_help!(e))),
|
||||
loc_continuation: Box::new(loc_continuation.map(|e| go_help!(e))),
|
||||
lookups_in_cond: lookups_in_cond.to_vec(),
|
||||
},
|
||||
|
||||
Return {
|
||||
return_value,
|
||||
return_var,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ fn print_declarations_help<'a>(
|
|||
toplevel_function(c, f, symbol, function_def, &body.value)
|
||||
}
|
||||
DeclarationTag::Expectation => todo!(),
|
||||
DeclarationTag::ExpectationFx => todo!(),
|
||||
DeclarationTag::Destructure(_) => todo!(),
|
||||
DeclarationTag::MutualRecursion { .. } => {
|
||||
// the defs will be printed next
|
||||
|
|
@ -453,7 +452,6 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
),
|
||||
Dbg { .. } => todo!(),
|
||||
Expect { .. } => todo!(),
|
||||
ExpectFx { .. } => todo!(),
|
||||
Return { .. } => todo!(),
|
||||
TypedHole(_) => todo!(),
|
||||
RuntimeError(_) => todo!(),
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ use std::io::Read;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Def {
|
||||
pub loc_pattern: Loc<Pattern>,
|
||||
pub loc_expr: Loc<Expr>,
|
||||
|
|
@ -91,7 +91,7 @@ impl Def {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DefKind {
|
||||
/// A def that introduces identifiers
|
||||
Let,
|
||||
|
|
@ -123,7 +123,7 @@ impl DefKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Annotation {
|
||||
pub signature: Type,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
|
|
@ -171,7 +171,6 @@ pub(crate) struct CanDefs {
|
|||
defs: Vec<Option<Def>>,
|
||||
dbgs: ExpectsOrDbgs,
|
||||
expects: ExpectsOrDbgs,
|
||||
expects_fx: ExpectsOrDbgs,
|
||||
def_ordering: DefOrdering,
|
||||
aliases: VecMap<Symbol, Alias>,
|
||||
}
|
||||
|
|
@ -343,7 +342,6 @@ pub enum Declaration {
|
|||
DeclareRec(Vec<Def>, IllegalCycleMark),
|
||||
Builtin(Def),
|
||||
Expects(ExpectsOrDbgs),
|
||||
ExpectsFx(ExpectsOrDbgs),
|
||||
/// If we know a cycle is illegal during canonicalization.
|
||||
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
|
||||
InvalidCycle(Vec<CycleEntry>),
|
||||
|
|
@ -358,7 +356,6 @@ impl Declaration {
|
|||
InvalidCycle { .. } => 0,
|
||||
Builtin(_) => 0,
|
||||
Expects(_) => 0,
|
||||
ExpectsFx(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +371,7 @@ impl Declaration {
|
|||
&cycles.first().unwrap().expr_region,
|
||||
&cycles.last().unwrap().expr_region,
|
||||
),
|
||||
Declaration::Expects(expects) | Declaration::ExpectsFx(expects) => Region::span_across(
|
||||
Declaration::Expects(expects) => Region::span_across(
|
||||
expects.regions.first().unwrap(),
|
||||
expects.regions.last().unwrap(),
|
||||
),
|
||||
|
|
@ -715,10 +712,6 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
});
|
||||
Err(())
|
||||
}
|
||||
AssignedField::Malformed(_) => {
|
||||
// An error will already have been reported
|
||||
Err(())
|
||||
}
|
||||
AssignedField::SpaceBefore(_, _)
|
||||
| AssignedField::SpaceAfter(_, _)
|
||||
| AssignedField::IgnoredValue(_, _, _) => {
|
||||
|
|
@ -1174,7 +1167,6 @@ fn canonicalize_value_defs<'a>(
|
|||
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
||||
let mut pending_dbgs = Vec::with_capacity(value_defs.len());
|
||||
let mut pending_expects = Vec::with_capacity(value_defs.len());
|
||||
let mut pending_expect_fx = Vec::with_capacity(value_defs.len());
|
||||
|
||||
let mut imports_introduced = Vec::with_capacity(value_defs.len());
|
||||
|
||||
|
|
@ -1194,9 +1186,6 @@ fn canonicalize_value_defs<'a>(
|
|||
PendingValue::Expect(pending_expect) => {
|
||||
pending_expects.push(pending_expect);
|
||||
}
|
||||
PendingValue::ExpectFx(pending_expect) => {
|
||||
pending_expect_fx.push(pending_expect);
|
||||
}
|
||||
PendingValue::ModuleImport(PendingModuleImport {
|
||||
module_id,
|
||||
region,
|
||||
|
|
@ -1282,7 +1271,6 @@ fn canonicalize_value_defs<'a>(
|
|||
|
||||
let mut dbgs = ExpectsOrDbgs::with_capacity(pending_dbgs.len());
|
||||
let mut expects = ExpectsOrDbgs::with_capacity(pending_expects.len());
|
||||
let mut expects_fx = ExpectsOrDbgs::with_capacity(pending_expects.len());
|
||||
|
||||
for pending in pending_dbgs {
|
||||
let (loc_can_condition, can_output) = canonicalize_expr(
|
||||
|
|
@ -1312,25 +1300,10 @@ fn canonicalize_value_defs<'a>(
|
|||
output.union(can_output);
|
||||
}
|
||||
|
||||
for pending in pending_expect_fx {
|
||||
let (loc_can_condition, can_output) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
pending.condition.region,
|
||||
&pending.condition.value,
|
||||
);
|
||||
|
||||
expects_fx.push(loc_can_condition, pending.preceding_comment);
|
||||
|
||||
output.union(can_output);
|
||||
}
|
||||
|
||||
let can_defs = CanDefs {
|
||||
defs,
|
||||
dbgs,
|
||||
expects,
|
||||
expects_fx,
|
||||
def_ordering,
|
||||
aliases,
|
||||
};
|
||||
|
|
@ -1738,7 +1711,6 @@ pub(crate) fn sort_top_level_can_defs(
|
|||
defs,
|
||||
dbgs: _,
|
||||
expects,
|
||||
expects_fx,
|
||||
def_ordering,
|
||||
aliases,
|
||||
} = defs;
|
||||
|
|
@ -1769,19 +1741,6 @@ pub(crate) fn sort_top_level_can_defs(
|
|||
declarations.push_expect(preceding_comment, name, Loc::at(region, condition));
|
||||
}
|
||||
|
||||
let it = expects_fx
|
||||
.conditions
|
||||
.into_iter()
|
||||
.zip(expects_fx.regions)
|
||||
.zip(expects_fx.preceding_comment);
|
||||
|
||||
for ((condition, region), preceding_comment) in it {
|
||||
// an `expect` does not have a user-defined name, but we'll need a name to call the expectation
|
||||
let name = scope.gen_unique_symbol();
|
||||
|
||||
declarations.push_expect_fx(preceding_comment, name, Loc::at(region, condition));
|
||||
}
|
||||
|
||||
for (symbol, alias) in aliases.into_iter() {
|
||||
output.aliases.insert(symbol, alias);
|
||||
}
|
||||
|
|
@ -2019,7 +1978,6 @@ pub(crate) fn sort_can_defs(
|
|||
mut defs,
|
||||
dbgs,
|
||||
expects,
|
||||
expects_fx,
|
||||
def_ordering,
|
||||
aliases,
|
||||
} = defs;
|
||||
|
|
@ -2153,10 +2111,6 @@ pub(crate) fn sort_can_defs(
|
|||
declarations.push(Declaration::Expects(expects));
|
||||
}
|
||||
|
||||
if !expects_fx.conditions.is_empty() {
|
||||
declarations.push(Declaration::ExpectsFx(expects_fx));
|
||||
}
|
||||
|
||||
(declarations, output)
|
||||
}
|
||||
|
||||
|
|
@ -2879,10 +2833,6 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
|||
|
||||
loc_ret
|
||||
}
|
||||
Declaration::ExpectsFx(expects) => {
|
||||
// Expects should only be added to top-level decls, not to let-exprs!
|
||||
unreachable!("{:?}", &expects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3082,7 +3032,6 @@ enum PendingValue<'a> {
|
|||
Def(PendingValueDef<'a>),
|
||||
Dbg(PendingExpectOrDbg<'a>),
|
||||
Expect(PendingExpectOrDbg<'a>),
|
||||
ExpectFx(PendingExpectOrDbg<'a>),
|
||||
ModuleImport(PendingModuleImport<'a>),
|
||||
SignatureDefMismatch,
|
||||
InvalidIngestedFile,
|
||||
|
|
@ -3222,14 +3171,6 @@ fn to_pending_value_def<'a>(
|
|||
preceding_comment: *preceding_comment,
|
||||
}),
|
||||
|
||||
ExpectFx {
|
||||
condition,
|
||||
preceding_comment,
|
||||
} => PendingValue::ExpectFx(PendingExpectOrDbg {
|
||||
condition,
|
||||
preceding_comment: *preceding_comment,
|
||||
}),
|
||||
|
||||
ModuleImport(module_import) => {
|
||||
let qualified_module_name: QualifiedModuleName = module_import.name.value.into();
|
||||
let module_name = qualified_module_name.module.clone();
|
||||
|
|
|
|||
|
|
@ -167,16 +167,6 @@ fn desugar_value_def<'a>(
|
|||
preceding_comment: *preceding_comment,
|
||||
}
|
||||
}
|
||||
ExpectFx {
|
||||
condition,
|
||||
preceding_comment,
|
||||
} => {
|
||||
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
|
||||
ExpectFx {
|
||||
condition: desugared_condition,
|
||||
preceding_comment: *preceding_comment,
|
||||
}
|
||||
}
|
||||
ModuleImport(roc_parse::ast::ModuleImport {
|
||||
before_name,
|
||||
name,
|
||||
|
|
@ -380,8 +370,8 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
|
|||
}
|
||||
},
|
||||
|
||||
// TODO support desugaring of Dbg and ExpectFx
|
||||
Dbg { .. } | ExpectFx { .. } => value_def,
|
||||
// TODO support desugaring of Dbg
|
||||
Dbg { .. } => value_def,
|
||||
ModuleImport { .. } | IngestedFileImport(_) | StmtAfterExpr => value_def,
|
||||
|
||||
Stmt(..) => {
|
||||
|
|
@ -407,7 +397,6 @@ pub fn desugar_expr<'a>(
|
|||
| AccessorFunction(_)
|
||||
| Underscore { .. }
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| MalformedSuffixed(..)
|
||||
| PrecedenceConflict { .. }
|
||||
| EmptyRecordBuilder(_)
|
||||
|
|
@ -712,7 +701,6 @@ pub fn desugar_expr<'a>(
|
|||
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => {
|
||||
unreachable!("Should have been desugared in `desugar_field`")
|
||||
}
|
||||
AssignedField::Malformed(_name) => continue,
|
||||
};
|
||||
|
||||
field_data.push(FieldData {
|
||||
|
|
@ -1112,15 +1100,6 @@ pub fn desugar_expr<'a>(
|
|||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
Expect(condition, continuation) => {
|
||||
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
|
||||
let desugared_continuation = &*env.arena.alloc(desugar_expr(env, scope, continuation));
|
||||
|
||||
env.arena.alloc(Loc {
|
||||
value: Expect(desugared_condition, desugared_continuation),
|
||||
region: loc_expr.region,
|
||||
})
|
||||
}
|
||||
Dbg => {
|
||||
// Allow naked dbg, necessary for piping values into dbg with the `Pizza` binop
|
||||
loc_expr
|
||||
|
|
@ -1316,8 +1295,6 @@ fn desugar_field<'a>(
|
|||
}
|
||||
SpaceBefore(field, _spaces) => desugar_field(env, scope, field),
|
||||
SpaceAfter(field, _spaces) => desugar_field(env, scope, field),
|
||||
|
||||
Malformed(string) => Malformed(string),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::pattern::Pattern;
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::types::{AnnotationSource, PReason, Reason};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expected<T> {
|
||||
NoExpectation(T),
|
||||
FromAnnotation(Loc<Pattern>, usize, AnnotationSource, T),
|
||||
|
|
@ -10,7 +10,7 @@ pub enum Expected<T> {
|
|||
}
|
||||
|
||||
/// Like Expected, but for Patterns.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PExpected<T> {
|
||||
NoExpectation(T),
|
||||
ForReason(PReason, T, Region),
|
||||
|
|
|
|||
|
|
@ -85,7 +85,55 @@ impl Display for IntValue {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
impl IntValue {
|
||||
pub fn as_u8(self) -> u8 {
|
||||
self.as_u128() as u8
|
||||
}
|
||||
|
||||
pub fn as_i8(self) -> i8 {
|
||||
self.as_i128() as i8
|
||||
}
|
||||
|
||||
pub fn as_u16(self) -> u16 {
|
||||
self.as_u128() as u16
|
||||
}
|
||||
|
||||
pub fn as_i16(self) -> i16 {
|
||||
self.as_i128() as i16
|
||||
}
|
||||
|
||||
pub fn as_u32(self) -> u32 {
|
||||
self.as_u128() as u32
|
||||
}
|
||||
|
||||
pub fn as_i32(self) -> i32 {
|
||||
self.as_i128() as i32
|
||||
}
|
||||
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.as_u128() as u64
|
||||
}
|
||||
|
||||
pub fn as_i64(self) -> i64 {
|
||||
self.as_i128() as i64
|
||||
}
|
||||
|
||||
pub fn as_u128(self) -> u128 {
|
||||
match self {
|
||||
IntValue::I128(i128) => i128::from_ne_bytes(i128) as u128,
|
||||
IntValue::U128(u128) => u128::from_ne_bytes(u128),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_i128(self) -> i128 {
|
||||
match self {
|
||||
IntValue::I128(i128) => i128::from_ne_bytes(i128),
|
||||
IntValue::U128(u128) => u128::from_ne_bytes(u128) as i128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr {
|
||||
// Literals
|
||||
|
||||
|
|
@ -104,7 +152,7 @@ pub enum Expr {
|
|||
loc_elems: Vec<Loc<Expr>>,
|
||||
},
|
||||
|
||||
// An ingested files, it's bytes, and the type variable.
|
||||
// An ingested files, its bytes, and the type variable.
|
||||
IngestedFile(Box<PathBuf>, Arc<Vec<u8>>, Variable),
|
||||
|
||||
// Lookups
|
||||
|
|
@ -131,7 +179,7 @@ pub enum Expr {
|
|||
/// The actual condition of the when expression.
|
||||
loc_cond: Box<Loc<Expr>>,
|
||||
cond_var: Variable,
|
||||
/// Result type produced by the branches.
|
||||
/// Type of each branch (and therefore the type of the entire `when` expression)
|
||||
expr_var: Variable,
|
||||
region: Region,
|
||||
/// The branches of the when, and the type of the condition that they expect to be matched
|
||||
|
|
@ -271,13 +319,6 @@ pub enum Expr {
|
|||
lookups_in_cond: Vec<ExpectLookup>,
|
||||
},
|
||||
|
||||
// not parsed, but is generated when lowering toplevel effectful expects
|
||||
ExpectFx {
|
||||
loc_condition: Box<Loc<Expr>>,
|
||||
loc_continuation: Box<Loc<Expr>>,
|
||||
lookups_in_cond: Vec<ExpectLookup>,
|
||||
},
|
||||
|
||||
Dbg {
|
||||
source_location: Box<str>,
|
||||
source: Box<str>,
|
||||
|
|
@ -299,7 +340,7 @@ pub enum Expr {
|
|||
RuntimeError(RuntimeError),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ExpectLookup {
|
||||
pub symbol: Symbol,
|
||||
pub var: Variable,
|
||||
|
|
@ -364,7 +405,6 @@ impl Expr {
|
|||
Category::OpaqueWrap(opaque_name)
|
||||
}
|
||||
Self::Expect { .. } => Category::Expect,
|
||||
Self::ExpectFx { .. } => Category::Expect,
|
||||
Self::Crash { .. } => Category::Crash,
|
||||
Self::Return { .. } => Category::Return,
|
||||
|
||||
|
|
@ -378,7 +418,7 @@ impl Expr {
|
|||
|
||||
/// Stores exhaustiveness-checking metadata for a closure argument that may
|
||||
/// have an annotated type.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct AnnotatedMark {
|
||||
pub annotation_var: Variable,
|
||||
pub exhaustive: ExhaustiveMark,
|
||||
|
|
@ -402,7 +442,7 @@ impl AnnotatedMark {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ClosureData {
|
||||
pub function_type: Variable,
|
||||
pub closure_type: Variable,
|
||||
|
|
@ -499,7 +539,7 @@ impl StructAccessorData {
|
|||
/// An opaque wrapper like `@Foo`, which is equivalent to `\p -> @Foo p`
|
||||
/// These are desugared to closures, but we distinguish them so we can have
|
||||
/// better error messages during constraint generation.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct OpaqueWrapFunctionData {
|
||||
pub opaque_name: Symbol,
|
||||
pub opaque_var: Variable,
|
||||
|
|
@ -571,7 +611,7 @@ impl OpaqueWrapFunctionData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Field {
|
||||
pub var: Variable,
|
||||
// The region of the full `foo: f bar`, rather than just `f bar`
|
||||
|
|
@ -595,7 +635,7 @@ impl Recursive {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranchPattern {
|
||||
pub pattern: Loc<Pattern>,
|
||||
/// Degenerate branch patterns are those that don't fully bind symbols that the branch body
|
||||
|
|
@ -604,7 +644,7 @@ pub struct WhenBranchPattern {
|
|||
pub degenerate: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranch {
|
||||
pub patterns: Vec<WhenBranchPattern>,
|
||||
pub value: Loc<Expr>,
|
||||
|
|
@ -1196,36 +1236,6 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::Expect(condition, continuation) => {
|
||||
let mut output = Output::default();
|
||||
|
||||
let (loc_condition, output1) =
|
||||
canonicalize_expr(env, var_store, scope, condition.region, &condition.value);
|
||||
|
||||
// Get all the lookups that were referenced in the condition,
|
||||
// so we can print their values later.
|
||||
let lookups_in_cond = get_lookup_symbols(&loc_condition.value);
|
||||
|
||||
let (loc_continuation, output2) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
continuation.region,
|
||||
&continuation.value,
|
||||
);
|
||||
|
||||
output.union(output1);
|
||||
output.union(output2);
|
||||
|
||||
(
|
||||
Expect {
|
||||
loc_condition: Box::new(loc_condition),
|
||||
loc_continuation: Box::new(loc_continuation),
|
||||
lookups_in_cond,
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::Dbg => {
|
||||
// Dbg was not desugared as either part of an `Apply` or a `Pizza` binop, so it's
|
||||
// invalid.
|
||||
|
|
@ -1391,10 +1401,6 @@ pub fn canonicalize_expr<'a>(
|
|||
Output::default(),
|
||||
)
|
||||
}
|
||||
ast::Expr::MalformedClosure => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
(RuntimeError(MalformedClosure(region)), Output::default())
|
||||
}
|
||||
ast::Expr::MalformedIdent(name, bad_ident) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
|
|
@ -1976,10 +1982,6 @@ fn canonicalize_field<'a>(
|
|||
SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => {
|
||||
canonicalize_field(env, var_store, scope, sub_field)
|
||||
}
|
||||
|
||||
Malformed(_string) => {
|
||||
internal_error!("TODO canonicalize malformed record field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2225,28 +2227,6 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
}
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
lookups_in_cond,
|
||||
} => {
|
||||
let loc_condition = Loc {
|
||||
region: loc_condition.region,
|
||||
value: inline_calls(var_store, loc_condition.value),
|
||||
};
|
||||
|
||||
let loc_continuation = Loc {
|
||||
region: loc_continuation.region,
|
||||
value: inline_calls(var_store, loc_continuation.value),
|
||||
};
|
||||
|
||||
ExpectFx {
|
||||
loc_condition: Box::new(loc_condition),
|
||||
loc_continuation: Box::new(loc_continuation),
|
||||
lookups_in_cond,
|
||||
}
|
||||
}
|
||||
|
||||
Dbg {
|
||||
source_location,
|
||||
source,
|
||||
|
|
@ -2568,12 +2548,10 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::Expr::Underscore(_)
|
||||
| ast::Expr::MalformedIdent(_, _)
|
||||
| ast::Expr::Tag(_)
|
||||
| ast::Expr::OpaqueRef(_)
|
||||
| ast::Expr::MalformedClosure => true,
|
||||
| ast::Expr::OpaqueRef(_) => true,
|
||||
// Newlines are disallowed inside interpolation, and these all require newlines
|
||||
ast::Expr::DbgStmt(_, _)
|
||||
| ast::Expr::LowLevelDbg(_, _, _)
|
||||
| ast::Expr::Expect(_, _)
|
||||
| ast::Expr::Return(_, _)
|
||||
| ast::Expr::When(_, _)
|
||||
| ast::Expr::Backpassing(_, _, _)
|
||||
|
|
@ -2604,7 +2582,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
ast::Expr::Tuple(fields) => fields
|
||||
|
|
@ -2655,7 +2633,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
|
|
@ -2668,7 +2646,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
|
|
@ -3028,24 +3006,6 @@ impl Declarations {
|
|||
index
|
||||
}
|
||||
|
||||
pub fn push_expect_fx(
|
||||
&mut self,
|
||||
preceding_comment: Region,
|
||||
name: Symbol,
|
||||
loc_expr: Loc<Expr>,
|
||||
) -> usize {
|
||||
let index = self.declarations.len();
|
||||
|
||||
self.declarations.push(DeclarationTag::ExpectationFx);
|
||||
self.variables.push(Variable::BOOL);
|
||||
self.symbols.push(Loc::at(preceding_comment, name));
|
||||
self.annotations.push(None);
|
||||
|
||||
self.expressions.push(loc_expr);
|
||||
|
||||
index
|
||||
}
|
||||
|
||||
pub fn push_value_def(
|
||||
&mut self,
|
||||
symbol: Loc<Symbol>,
|
||||
|
|
@ -3298,12 +3258,6 @@ impl Declarations {
|
|||
|
||||
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr =
|
||||
toplevel_expect_to_inline_expect_fx(self.expressions[index].clone());
|
||||
|
||||
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3322,7 +3276,6 @@ roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
|
|||
pub enum DeclarationTag {
|
||||
Value,
|
||||
Expectation,
|
||||
ExpectationFx,
|
||||
Function(Index<Loc<FunctionDef>>),
|
||||
Recursive(Index<Loc<FunctionDef>>),
|
||||
TailRecursive(Index<Loc<FunctionDef>>),
|
||||
|
|
@ -3340,7 +3293,7 @@ impl DeclarationTag {
|
|||
match self {
|
||||
Function(_) | Recursive(_) | TailRecursive(_) => 1,
|
||||
Value => 1,
|
||||
Expectation | ExpectationFx => 1,
|
||||
Expectation => 1,
|
||||
Destructure(_) => 1,
|
||||
MutualRecursion { length, .. } => length as usize + 1,
|
||||
}
|
||||
|
|
@ -3486,9 +3439,6 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
Expr::Expect {
|
||||
loc_continuation, ..
|
||||
}
|
||||
| Expr::ExpectFx {
|
||||
loc_continuation, ..
|
||||
}
|
||||
| Expr::Dbg {
|
||||
loc_continuation, ..
|
||||
} => {
|
||||
|
|
@ -3544,15 +3494,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
/// This is supposed to happen just before monomorphization:
|
||||
/// all type errors and such are generated from the user source,
|
||||
/// but this transformation means that we don't need special codegen for toplevel expects
|
||||
pub fn toplevel_expect_to_inline_expect_pure(loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
toplevel_expect_to_inline_expect_help(loc_expr, false)
|
||||
}
|
||||
|
||||
pub fn toplevel_expect_to_inline_expect_fx(loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
toplevel_expect_to_inline_expect_help(loc_expr, true)
|
||||
}
|
||||
|
||||
fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: bool) -> Loc<Expr> {
|
||||
pub fn toplevel_expect_to_inline_expect_pure(mut loc_expr: Loc<Expr>) -> Loc<Expr> {
|
||||
enum StoredDef {
|
||||
NonRecursive(Region, Box<Def>),
|
||||
Recursive(Region, Vec<Def>, IllegalCycleMark),
|
||||
|
|
@ -3590,18 +3532,10 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: b
|
|||
}
|
||||
|
||||
let expect_region = loc_expr.region;
|
||||
let expect = if has_effects {
|
||||
Expr::ExpectFx {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
}
|
||||
} else {
|
||||
Expr::Expect {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
}
|
||||
let expect = Expr::Expect {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
};
|
||||
|
||||
let mut loc_expr = Loc::at(expect_region, expect);
|
||||
|
|
@ -3633,11 +3567,6 @@ impl crate::traverse::Visitor for ExpectCollector {
|
|||
lookups_in_cond,
|
||||
loc_condition,
|
||||
..
|
||||
}
|
||||
| Expr::ExpectFx {
|
||||
lookups_in_cond,
|
||||
loc_condition,
|
||||
..
|
||||
} => {
|
||||
self.expects
|
||||
.insert(loc_condition.region, lookups_in_cond.to_vec());
|
||||
|
|
|
|||
|
|
@ -609,7 +609,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
// the declarations of this group will be treaded individually by later iterations
|
||||
}
|
||||
Expectation => { /* ignore */ }
|
||||
ExpectationFx => { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -747,14 +746,6 @@ pub fn canonicalize_module_defs<'a>(
|
|||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr = &mut declarations.expressions[index];
|
||||
fix_values_captured_in_closure_expr(
|
||||
&mut loc_expr.value,
|
||||
&mut fix_closures_no_capture_symbols,
|
||||
&mut fix_closures_closure_captures,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -956,11 +947,6 @@ fn fix_values_captured_in_closure_expr(
|
|||
loc_continuation,
|
||||
..
|
||||
}
|
||||
| ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
..
|
||||
}
|
||||
| Dbg {
|
||||
loc_message: loc_condition,
|
||||
loc_continuation,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
|
|||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// codegen can generate a runtime error if this pattern is reached.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
Identifier(Symbol),
|
||||
As(Box<Loc<Pattern>>, Symbol),
|
||||
|
|
@ -198,7 +198,7 @@ impl Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ListPatterns {
|
||||
pub patterns: Vec<Loc<Pattern>>,
|
||||
/// Where a rest pattern splits patterns before and after it, if it does at all.
|
||||
|
|
@ -207,6 +207,7 @@ pub struct ListPatterns {
|
|||
/// [ .., A, B ] -> patterns = [A, B], rest = 0
|
||||
/// [ A, .., B ] -> patterns = [A, B], rest = 1
|
||||
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
|
||||
/// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]`
|
||||
pub opt_rest: Option<(usize, Option<Loc<Symbol>>)>,
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +229,7 @@ impl ListPatterns {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RecordDestruct {
|
||||
pub var: Variable,
|
||||
pub label: Lowercase,
|
||||
|
|
@ -236,14 +237,14 @@ pub struct RecordDestruct {
|
|||
pub typ: DestructType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TupleDestruct {
|
||||
pub var: Variable,
|
||||
pub destruct_index: usize,
|
||||
pub typ: (Variable, Loc<Pattern>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DestructType {
|
||||
Required,
|
||||
Optional(Variable, Loc<Expr>),
|
||||
|
|
|
|||
|
|
@ -155,49 +155,6 @@ pub fn unwrap_suffixed_expression<'a>(
|
|||
|
||||
Expr::LowLevelDbg(..) => unwrap_low_level_dbg(arena, loc_expr, maybe_def_pat),
|
||||
|
||||
Expr::Expect(condition, continuation) => {
|
||||
if is_expr_suffixed(&condition.value) {
|
||||
// we cannot unwrap a suffixed expression within expect
|
||||
// e.g. expect (foo! "bar")
|
||||
return Err(EUnwrapped::Malformed);
|
||||
}
|
||||
|
||||
match unwrap_suffixed_expression(arena, continuation, maybe_def_pat) {
|
||||
Ok(unwrapped_expr) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
return Ok(new_expect);
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedDefExpr {
|
||||
loc_expr: unwrapped_expr,
|
||||
target,
|
||||
}) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
Err(EUnwrapped::UnwrappedDefExpr {
|
||||
loc_expr: new_expect,
|
||||
target,
|
||||
})
|
||||
}
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: unwrapped_expr,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
target,
|
||||
}) => {
|
||||
let new_expect = arena
|
||||
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
|
||||
Err(EUnwrapped::UnwrappedSubExpr {
|
||||
sub_arg: new_expect,
|
||||
sub_pat,
|
||||
sub_new,
|
||||
target,
|
||||
})
|
||||
}
|
||||
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
|
||||
}
|
||||
}
|
||||
|
||||
// we only need to unwrap some expressions, leave the rest as is
|
||||
_ => Ok(loc_expr),
|
||||
}
|
||||
|
|
@ -680,7 +637,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
|
|||
};
|
||||
|
||||
let maybe_suffixed_value_def = match current_value_def {
|
||||
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
|
||||
Annotation(..) | Dbg{..} | Expect{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
|
||||
AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))),
|
||||
Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)),
|
||||
StmtAfterExpr => None,
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
|||
annotation: decls.annotations[index].as_ref(),
|
||||
}
|
||||
}
|
||||
Expectation | ExpectationFx => {
|
||||
Expectation => {
|
||||
let loc_condition = &decls.expressions[index];
|
||||
|
||||
DeclarationInfo::Expectation { loc_condition }
|
||||
|
|
@ -385,18 +385,6 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
|||
Variable::NULL,
|
||||
);
|
||||
}
|
||||
Expr::ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
lookups_in_cond: _,
|
||||
} => {
|
||||
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||
visitor.visit_expr(
|
||||
&loc_continuation.value,
|
||||
loc_continuation.region,
|
||||
Variable::NULL,
|
||||
);
|
||||
}
|
||||
Expr::Dbg {
|
||||
variable,
|
||||
source: _,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -65,10 +66,10 @@ Defs {
|
|||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-25,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -49,10 +50,10 @@ Defs {
|
|||
@15-19,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -59,10 +60,10 @@ Defs {
|
|||
@15-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -59,10 +60,10 @@ Defs {
|
|||
@15-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -49,10 +50,10 @@ Defs {
|
|||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-14,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -49,10 +50,10 @@ Defs {
|
|||
@16-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-69,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -34,10 +34,10 @@ Defs {
|
|||
@15-57,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -67,10 +67,10 @@ Defs {
|
|||
@31-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-114,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@39-101,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -84,10 +85,10 @@ Defs {
|
|||
@82-91,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-143,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@56-119,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -112,10 +113,10 @@ Defs {
|
|||
@76-83,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -181,10 +182,10 @@ Defs {
|
|||
@92-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-26,
|
||||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -24,88 +25,90 @@ Defs {
|
|||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-26 Defs(
|
||||
@11-28 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-26,
|
||||
@16-27,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@15-26 Identifier {
|
||||
@16-27 Identifier {
|
||||
ident: "1",
|
||||
},
|
||||
@15-26 ParensAround(
|
||||
@16-27 ParensAround(
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@20-25,
|
||||
@21-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@20-25 Identifier {
|
||||
@21-26 Identifier {
|
||||
ident: "0",
|
||||
},
|
||||
@20-25 Apply(
|
||||
@22-23 Var {
|
||||
module_name: "Num",
|
||||
ident: "add",
|
||||
},
|
||||
[
|
||||
@20-21 Num(
|
||||
"1",
|
||||
@21-26 ParensAround(
|
||||
Apply(
|
||||
@23-24 Var {
|
||||
module_name: "Num",
|
||||
ident: "add",
|
||||
},
|
||||
[
|
||||
@21-22 Num(
|
||||
"1",
|
||||
),
|
||||
@25-26 Num(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Plus,
|
||||
),
|
||||
@24-25 Num(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Plus,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@15-26 LowLevelDbg(
|
||||
@16-27 LowLevelDbg(
|
||||
(
|
||||
"test.roc:3",
|
||||
" ",
|
||||
),
|
||||
@20-25 Apply(
|
||||
@20-25 Var {
|
||||
@21-26 Apply(
|
||||
@21-26 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@20-25 Var {
|
||||
@21-26 Var {
|
||||
module_name: "",
|
||||
ident: "0",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@20-25 Var {
|
||||
@21-26 Var {
|
||||
module_name: "",
|
||||
ident: "0",
|
||||
},
|
||||
|
|
@ -115,25 +118,25 @@ Defs {
|
|||
),
|
||||
],
|
||||
},
|
||||
@11-26 LowLevelDbg(
|
||||
@11-28 LowLevelDbg(
|
||||
(
|
||||
"test.roc:2",
|
||||
"in =\n ",
|
||||
"n =\n ",
|
||||
),
|
||||
@15-26 Apply(
|
||||
@15-26 Var {
|
||||
@16-27 Apply(
|
||||
@16-27 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@15-26 Var {
|
||||
@16-27 Var {
|
||||
module_name: "",
|
||||
ident: "1",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-26 Var {
|
||||
@16-27 Var {
|
||||
module_name: "",
|
||||
ident: "1",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -12,12 +13,12 @@ Defs {
|
|||
@56-98,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -39,10 +40,10 @@ Defs {
|
|||
@15-20,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -74,10 +75,10 @@ Defs {
|
|||
@25-39,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -151,10 +152,10 @@ Defs {
|
|||
@75-80,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-158,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -45,10 +46,10 @@ Defs {
|
|||
@50-52,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-31,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@11-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-307,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -37,14 +38,14 @@ Defs {
|
|||
@109-298,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice { start: 1, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice { start: 2, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -171,10 +172,10 @@ Defs {
|
|||
@140-152,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -293,10 +294,10 @@ Defs {
|
|||
@227-239,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-189,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -35,12 +36,12 @@ Defs {
|
|||
@52-70,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -14,14 +15,14 @@ Defs {
|
|||
@229-266,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice { start: 2, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 0 },
|
||||
Slice { start: 4, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 4, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -120,12 +121,12 @@ Defs {
|
|||
@203-208,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -12,12 +13,12 @@ Defs {
|
|||
@35-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@15-17,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-14,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -99,10 +100,10 @@ Defs {
|
|||
@20-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-72,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -40,10 +40,10 @@ Defs {
|
|||
@11-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-51,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@17-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-16,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-61,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@23-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-51,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -33,10 +34,10 @@ Defs {
|
|||
@11-40,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
@ -60,10 +61,10 @@ Defs {
|
|||
@11-12,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-73,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -40,10 +40,10 @@ Defs {
|
|||
@11-57,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-67,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -48,10 +49,10 @@ Defs {
|
|||
@19-30,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-154,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-44,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -45,10 +46,10 @@ Defs {
|
|||
@28-29,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-120,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
@ -67,10 +68,10 @@ Defs {
|
|||
@54-65,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-74,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
|||
13
crates/compiler/can/tests/test_can_expr.rs
Normal file
13
crates/compiler/can/tests/test_can_expr.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#[cfg(test)]
|
||||
mod test_can_expr {
|
||||
use roc_can::expr::Expr;
|
||||
use test_compile::can_expr;
|
||||
|
||||
#[test]
|
||||
fn test_can_unit() {
|
||||
let output = can_expr("{}");
|
||||
|
||||
assert_eq!(output.problems, Vec::new());
|
||||
assert!(matches!(output.expr, Expr::EmptyRecord));
|
||||
}
|
||||
}
|
||||
|
|
@ -464,7 +464,7 @@ mod suffixed_tests {
|
|||
run_test!(
|
||||
r#"
|
||||
main =
|
||||
dbg (dbg 1 + 1)
|
||||
dbg (dbg (1 + 1))
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod all;
|
||||
mod push;
|
||||
mod reference_matrix;
|
||||
mod small_string_interner;
|
||||
mod small_vec;
|
||||
|
|
@ -12,6 +13,7 @@ mod vec_map;
|
|||
mod vec_set;
|
||||
|
||||
pub use all::{default_hasher, BumpMap, ImEntry, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
pub use push::Push;
|
||||
pub use reference_matrix::{ReferenceMatrix, Sccs, TopologicalSort};
|
||||
pub use small_string_interner::SmallStringInterner;
|
||||
pub use small_vec::SmallVec;
|
||||
|
|
|
|||
15
crates/compiler/collections/src/push.rs
Normal file
15
crates/compiler/collections/src/push.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
pub trait Push<T> {
|
||||
fn push(&mut self, entry: T);
|
||||
}
|
||||
|
||||
impl<T> Push<T> for Vec<T> {
|
||||
fn push(&mut self, entry: T) {
|
||||
self.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Push<T> for &mut Vec<T> {
|
||||
fn push(&mut self, entry: T) {
|
||||
(*self).push(entry);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ use roc_can::pattern::Pattern;
|
|||
use roc_can::traverse::symbols_introduced_from_pattern;
|
||||
use roc_collections::all::{HumanIndex, MutMap, SendMap};
|
||||
use roc_collections::VecMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::ident::{IdentSuffix, Lowercase};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::{IllegalCycleMark, Variable};
|
||||
|
|
@ -64,6 +64,15 @@ pub struct Env {
|
|||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new(home: ModuleId) -> Self {
|
||||
Self {
|
||||
rigids: MutMap::default(),
|
||||
resolutions_to_make: Vec::new(),
|
||||
home,
|
||||
fx_expectation: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_fx_expectation<F, T>(
|
||||
&mut self,
|
||||
fx_var: Variable,
|
||||
|
|
@ -168,37 +177,38 @@ fn constrain_untyped_closure(
|
|||
vars.push(closure_var);
|
||||
vars.push(fn_var);
|
||||
|
||||
let body_type = constraints.push_expected_type(ForReason(
|
||||
let return_type_index = constraints.push_expected_type(ForReason(
|
||||
Reason::FunctionOutput,
|
||||
return_type_index,
|
||||
loc_body_expr.region,
|
||||
));
|
||||
|
||||
let ret_constraint = env.with_fx_expectation(fx_var, None, |env| {
|
||||
constrain_expr(
|
||||
let returns_constraint = env.with_fx_expectation(fx_var, None, |env| {
|
||||
let return_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
)
|
||||
});
|
||||
|
||||
let mut early_return_constraints = Vec::with_capacity(early_returns.len());
|
||||
for (early_return_variable, early_return_region) in early_returns {
|
||||
let early_return_var = constraints.push_variable(*early_return_variable);
|
||||
let early_return_con = constraints.equal_types(
|
||||
early_return_var,
|
||||
body_type,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
return_type_index,
|
||||
);
|
||||
|
||||
early_return_constraints.push(early_return_con);
|
||||
}
|
||||
let mut return_constraints = Vec::with_capacity(early_returns.len() + 1);
|
||||
return_constraints.push(return_con);
|
||||
|
||||
let early_returns_constraint = constraints.and_constraint(early_return_constraints);
|
||||
for (early_return_variable, early_return_region) in early_returns {
|
||||
let early_return_con = constraints.equal_types_var(
|
||||
*early_return_variable,
|
||||
return_type_index,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
);
|
||||
|
||||
return_constraints.push(early_return_con);
|
||||
}
|
||||
|
||||
constraints.and_constraint(return_constraints)
|
||||
});
|
||||
|
||||
// make sure the captured symbols are sorted!
|
||||
debug_assert_eq!(captured_symbols.to_vec(), {
|
||||
|
|
@ -231,7 +241,7 @@ fn constrain_untyped_closure(
|
|||
pattern_state.vars,
|
||||
pattern_state.headers,
|
||||
pattern_state_constraints,
|
||||
ret_constraint,
|
||||
returns_constraint,
|
||||
Generalizable(true),
|
||||
),
|
||||
constraints.and_constraint(pattern_state.delayed_fx_suffix_constraints),
|
||||
|
|
@ -242,7 +252,6 @@ fn constrain_untyped_closure(
|
|||
region,
|
||||
fn_var,
|
||||
),
|
||||
early_returns_constraint,
|
||||
closure_constraint,
|
||||
constraints.flex_to_pure(fx_var),
|
||||
];
|
||||
|
|
@ -284,9 +293,14 @@ pub fn constrain_expr(
|
|||
let (field_type, field_con) =
|
||||
constrain_field(types, constraints, env, field_var, loc_field_expr);
|
||||
|
||||
let check_field_con =
|
||||
constraints.fx_record_field_suffix(label.suffix(), field_var, field.region);
|
||||
let field_con = constraints.and_constraint([field_con, check_field_con]);
|
||||
let field_con = match label.suffix() {
|
||||
IdentSuffix::None => {
|
||||
let check_field_con =
|
||||
constraints.fx_record_field_unsuffixed(field_var, field.region);
|
||||
constraints.and_constraint([field_con, check_field_con])
|
||||
}
|
||||
IdentSuffix::Bang => field_con,
|
||||
};
|
||||
|
||||
field_vars.push(field_var);
|
||||
field_types.insert(label.clone(), RecordField::Required(field_type));
|
||||
|
|
@ -787,63 +801,6 @@ pub fn constrain_expr(
|
|||
constraints.exists_many(vars, all_constraints)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
lookups_in_cond,
|
||||
} => {
|
||||
let expected_bool = {
|
||||
let bool_type = constraints.push_variable(Variable::BOOL);
|
||||
constraints.push_expected_type(Expected::ForReason(
|
||||
Reason::ExpectCondition,
|
||||
bool_type,
|
||||
loc_condition.region,
|
||||
))
|
||||
};
|
||||
|
||||
let cond_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_condition.region,
|
||||
&loc_condition.value,
|
||||
expected_bool,
|
||||
);
|
||||
|
||||
let continuation_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_continuation.region,
|
||||
&loc_continuation.value,
|
||||
expected,
|
||||
);
|
||||
|
||||
// + 2 for cond_con and continuation_con
|
||||
let mut all_constraints = Vec::with_capacity(lookups_in_cond.len() + 2);
|
||||
|
||||
all_constraints.push(cond_con);
|
||||
all_constraints.push(continuation_con);
|
||||
|
||||
let mut vars = Vec::with_capacity(lookups_in_cond.len());
|
||||
|
||||
for ExpectLookup {
|
||||
symbol,
|
||||
var,
|
||||
ability_info: _,
|
||||
} in lookups_in_cond.iter()
|
||||
{
|
||||
vars.push(*var);
|
||||
|
||||
let var_index = constraints.push_variable(*var);
|
||||
let store_into = constraints.push_expected_type(NoExpectation(var_index));
|
||||
|
||||
all_constraints.push(constraints.lookup(*symbol, store_into, Region::zero()));
|
||||
}
|
||||
|
||||
constraints.exists_many(vars, all_constraints)
|
||||
}
|
||||
|
||||
Dbg {
|
||||
source_location: _,
|
||||
source: _,
|
||||
|
|
@ -1475,23 +1432,20 @@ pub fn constrain_expr(
|
|||
return_var,
|
||||
} => {
|
||||
let return_type_index = constraints.push_variable(*return_var);
|
||||
|
||||
let expected_return_value = constraints.push_expected_type(ForReason(
|
||||
Reason::FunctionOutput,
|
||||
return_type_index,
|
||||
return_value.region,
|
||||
));
|
||||
|
||||
let return_con = constrain_expr(
|
||||
constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
return_value.region,
|
||||
&return_value.value,
|
||||
expected_return_value,
|
||||
);
|
||||
|
||||
constraints.exists([*return_var], return_con)
|
||||
)
|
||||
}
|
||||
Tag {
|
||||
tag_union_var: variant_var,
|
||||
|
|
@ -2127,8 +2081,8 @@ fn constrain_function_def(
|
|||
constraints.push_type(types, fn_type)
|
||||
};
|
||||
|
||||
let ret_constraint = {
|
||||
let con = constrain_expr(
|
||||
let returns_constraint = {
|
||||
let return_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
|
|
@ -2136,7 +2090,33 @@ fn constrain_function_def(
|
|||
&loc_body_expr.value,
|
||||
return_type_annotation_expected,
|
||||
);
|
||||
attach_resolution_constraints(constraints, env, con)
|
||||
|
||||
let mut return_constraints =
|
||||
Vec::with_capacity(function_def.early_returns.len() + 1);
|
||||
return_constraints.push(return_con);
|
||||
|
||||
for (early_return_variable, early_return_region) in &function_def.early_returns {
|
||||
let early_return_type_expected =
|
||||
constraints.push_expected_type(Expected::ForReason(
|
||||
Reason::FunctionOutput,
|
||||
ret_type_index,
|
||||
*early_return_region,
|
||||
));
|
||||
|
||||
vars.push(*early_return_variable);
|
||||
let early_return_con = constraints.equal_types_var(
|
||||
*early_return_variable,
|
||||
early_return_type_expected,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
);
|
||||
|
||||
return_constraints.push(early_return_con);
|
||||
}
|
||||
|
||||
let returns_constraint = constraints.and_constraint(return_constraints);
|
||||
|
||||
attach_resolution_constraints(constraints, env, returns_constraint)
|
||||
};
|
||||
|
||||
vars.push(expr_var);
|
||||
|
|
@ -2156,7 +2136,7 @@ fn constrain_function_def(
|
|||
argument_pattern_state.vars,
|
||||
argument_pattern_state.headers,
|
||||
defs_constraint,
|
||||
ret_constraint,
|
||||
returns_constraint,
|
||||
// This is a syntactic function, it can be generalized
|
||||
Generalizable(true),
|
||||
),
|
||||
|
|
@ -2790,34 +2770,6 @@ pub fn constrain_decls(
|
|||
expected,
|
||||
);
|
||||
|
||||
constraint = constraints.let_constraint(
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
expect_constraint,
|
||||
constraint,
|
||||
Generalizable(false),
|
||||
)
|
||||
}
|
||||
ExpectationFx => {
|
||||
let loc_expr = &declarations.expressions[index];
|
||||
|
||||
let bool_type = constraints.push_variable(Variable::BOOL);
|
||||
let expected = constraints.push_expected_type(Expected::ForReason(
|
||||
Reason::ExpectCondition,
|
||||
bool_type,
|
||||
loc_expr.region,
|
||||
));
|
||||
|
||||
let expect_constraint = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
&mut env,
|
||||
loc_expr.region,
|
||||
&loc_expr.value,
|
||||
expected,
|
||||
);
|
||||
|
||||
constraint = constraints.let_constraint(
|
||||
[],
|
||||
[],
|
||||
|
|
@ -2940,6 +2892,7 @@ fn constrain_typed_def(
|
|||
function_type: fn_var,
|
||||
closure_type: closure_var,
|
||||
return_type: ret_var,
|
||||
early_returns,
|
||||
fx_type: fx_var,
|
||||
captured_symbols,
|
||||
arguments,
|
||||
|
|
@ -3009,7 +2962,7 @@ fn constrain_typed_def(
|
|||
constraints.push_type(types, fn_type)
|
||||
};
|
||||
|
||||
let body_type = constraints.push_expected_type(FromAnnotation(
|
||||
let return_type = constraints.push_expected_type(FromAnnotation(
|
||||
def.loc_pattern.clone(),
|
||||
arguments.len(),
|
||||
AnnotationSource::TypedBody {
|
||||
|
|
@ -3018,18 +2971,35 @@ fn constrain_typed_def(
|
|||
ret_type_index,
|
||||
));
|
||||
|
||||
let ret_constraint = env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
|
||||
constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
)
|
||||
});
|
||||
let returns_constraint =
|
||||
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
|
||||
let return_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
return_type,
|
||||
);
|
||||
|
||||
let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint);
|
||||
let mut return_constraints = Vec::with_capacity(early_returns.len() + 1);
|
||||
return_constraints.push(return_con);
|
||||
|
||||
for (early_return_variable, early_return_region) in early_returns {
|
||||
let early_return_con = constraints.equal_types_var(
|
||||
*early_return_variable,
|
||||
return_type,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
);
|
||||
|
||||
return_constraints.push(early_return_con);
|
||||
}
|
||||
|
||||
let returns_constraint = constraints.and_constraint(return_constraints);
|
||||
|
||||
attach_resolution_constraints(constraints, env, returns_constraint)
|
||||
});
|
||||
|
||||
vars.push(*fn_var);
|
||||
let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints);
|
||||
|
|
@ -3042,7 +3012,7 @@ fn constrain_typed_def(
|
|||
argument_pattern_state.vars,
|
||||
argument_pattern_state.headers,
|
||||
defs_constraint,
|
||||
ret_constraint,
|
||||
returns_constraint,
|
||||
// This is a syntactic function, it can be generalized
|
||||
Generalizable(true),
|
||||
),
|
||||
|
|
@ -4049,18 +4019,38 @@ fn constraint_recursive_function(
|
|||
constraints.push_type(types, typ)
|
||||
};
|
||||
|
||||
let expr_con = env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
|
||||
let expected = constraints.push_expected_type(NoExpectation(ret_type_index));
|
||||
constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
expected,
|
||||
)
|
||||
});
|
||||
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
|
||||
let returns_constraint =
|
||||
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
|
||||
let expected = constraints.push_expected_type(NoExpectation(ret_type_index));
|
||||
let return_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
expected,
|
||||
);
|
||||
|
||||
let mut return_constraints =
|
||||
Vec::with_capacity(function_def.early_returns.len() + 1);
|
||||
return_constraints.push(return_con);
|
||||
|
||||
for (early_return_variable, early_return_region) in &function_def.early_returns
|
||||
{
|
||||
let early_return_con = constraints.equal_types_var(
|
||||
*early_return_variable,
|
||||
expected,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
);
|
||||
|
||||
return_constraints.push(early_return_con);
|
||||
}
|
||||
|
||||
let returns_constraint = constraints.and_constraint(return_constraints);
|
||||
|
||||
attach_resolution_constraints(constraints, env, returns_constraint)
|
||||
});
|
||||
|
||||
vars.push(expr_var);
|
||||
|
||||
|
|
@ -4072,7 +4062,7 @@ fn constraint_recursive_function(
|
|||
argument_pattern_state.vars,
|
||||
argument_pattern_state.headers,
|
||||
state_constraints,
|
||||
expr_con,
|
||||
returns_constraint,
|
||||
// Syntactic function can be generalized
|
||||
Generalizable(true),
|
||||
),
|
||||
|
|
@ -4411,7 +4401,6 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool {
|
|||
| TupleAccess { .. }
|
||||
| RecordUpdate { .. }
|
||||
| Expect { .. }
|
||||
| ExpectFx { .. }
|
||||
| Dbg { .. }
|
||||
| Return { .. }
|
||||
| TypedHole(_)
|
||||
|
|
@ -4535,6 +4524,7 @@ fn rec_defs_help(
|
|||
function_type: fn_var,
|
||||
closure_type: closure_var,
|
||||
return_type: ret_var,
|
||||
early_returns,
|
||||
fx_type: fx_var,
|
||||
captured_symbols,
|
||||
arguments,
|
||||
|
|
@ -4602,22 +4592,40 @@ fn rec_defs_help(
|
|||
let typ = types.function(pattern_types, lambda_set, ret_type, fx_type);
|
||||
constraints.push_type(types, typ)
|
||||
};
|
||||
let expr_con =
|
||||
let returns_constraint =
|
||||
env.with_fx_expectation(fx_var, Some(annotation.region), |env| {
|
||||
let body_type =
|
||||
let return_type_expected =
|
||||
constraints.push_expected_type(NoExpectation(ret_type_index));
|
||||
|
||||
constrain_expr(
|
||||
let return_con = constrain_expr(
|
||||
types,
|
||||
constraints,
|
||||
env,
|
||||
loc_body_expr.region,
|
||||
&loc_body_expr.value,
|
||||
body_type,
|
||||
)
|
||||
});
|
||||
return_type_expected,
|
||||
);
|
||||
|
||||
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
|
||||
let mut return_constraints =
|
||||
Vec::with_capacity(early_returns.len() + 1);
|
||||
return_constraints.push(return_con);
|
||||
|
||||
for (early_return_variable, early_return_region) in early_returns {
|
||||
let early_return_con = constraints.equal_types_var(
|
||||
*early_return_variable,
|
||||
return_type_expected,
|
||||
Category::Return,
|
||||
*early_return_region,
|
||||
);
|
||||
|
||||
return_constraints.push(early_return_con);
|
||||
}
|
||||
|
||||
let returns_constraint =
|
||||
constraints.and_constraint(return_constraints);
|
||||
|
||||
attach_resolution_constraints(constraints, env, returns_constraint)
|
||||
});
|
||||
|
||||
vars.push(*fn_var);
|
||||
|
||||
|
|
@ -4632,7 +4640,7 @@ fn rec_defs_help(
|
|||
argument_pattern_state.vars,
|
||||
argument_pattern_state.headers,
|
||||
state_constraints,
|
||||
expr_con,
|
||||
returns_constraint,
|
||||
generalizable,
|
||||
),
|
||||
// Check argument suffixes against usage
|
||||
|
|
|
|||
|
|
@ -438,7 +438,6 @@ fn is_multiline_assigned_field_help<T: Formattable>(afield: &AssignedField<'_, T
|
|||
| IgnoredValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(),
|
||||
LabelOnly(_) => false,
|
||||
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true,
|
||||
Malformed(text) => text.chars().any(|c| c == '\n'),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,9 +521,6 @@ fn format_assigned_field_help<T>(
|
|||
format_assigned_field_help(sub_field, buf, indent, separator_spaces, is_multiline);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
}
|
||||
Malformed(raw) => {
|
||||
buf.push_str(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,7 +531,6 @@ impl<'a> Formattable for Tag<'a> {
|
|||
match self {
|
||||
Apply { args, .. } => args.iter().any(|arg| arg.value.is_multiline()),
|
||||
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true,
|
||||
Malformed(text) => text.chars().any(|c| c == '\n'),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,10 +567,6 @@ impl<'a> Formattable for Tag<'a> {
|
|||
}
|
||||
}
|
||||
Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => unreachable!(),
|
||||
Tag::Malformed(raw) => {
|
||||
buf.indent(indent);
|
||||
buf.push_str(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
|||
buf.indent(item_indent);
|
||||
item.item.format(buf, item_indent);
|
||||
|
||||
buf.indent(item_indent);
|
||||
buf.push(',');
|
||||
|
||||
if !item.after.is_empty() {
|
||||
|
|
|
|||
|
|
@ -419,7 +419,6 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
||||
AnnotatedBody { .. } => true,
|
||||
Expect { condition, .. } => condition.is_multiline(),
|
||||
ExpectFx { condition, .. } => condition.is_multiline(),
|
||||
Dbg { condition, .. } => condition.is_multiline(),
|
||||
ModuleImport(module_import) => module_import.is_multiline(),
|
||||
IngestedFileImport(ingested_file_import) => ingested_file_import.is_multiline(),
|
||||
|
|
@ -446,9 +445,6 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
}
|
||||
Dbg { condition, .. } => fmt_dbg_in_def(buf, condition, self.is_multiline(), indent),
|
||||
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
|
||||
ExpectFx { condition, .. } => {
|
||||
fmt_expect_fx(buf, condition, self.is_multiline(), indent)
|
||||
}
|
||||
AnnotatedBody {
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
|
|
@ -557,22 +553,6 @@ fn fmt_expect<'a>(buf: &mut Buf, condition: &'a Loc<Expr<'a>>, is_multiline: boo
|
|||
condition.format(buf, return_indent);
|
||||
}
|
||||
|
||||
fn fmt_expect_fx<'a>(buf: &mut Buf, condition: &'a Loc<Expr<'a>>, is_multiline: bool, indent: u16) {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.push_str("expect-fx");
|
||||
|
||||
let return_indent = if is_multiline {
|
||||
buf.newline();
|
||||
indent + INDENT
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
indent
|
||||
};
|
||||
|
||||
condition.format(buf, return_indent);
|
||||
}
|
||||
|
||||
pub fn fmt_value_def(buf: &mut Buf, def: &roc_parse::ast::ValueDef, indent: u16) {
|
||||
def.format(buf, indent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
| Var { .. }
|
||||
| Underscore { .. }
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| Tag(_)
|
||||
| OpaqueRef(_)
|
||||
| Crash
|
||||
|
|
@ -65,16 +64,11 @@ impl<'a> Formattable for Expr<'a> {
|
|||
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
||||
}
|
||||
|
||||
Expect(condition, continuation) => {
|
||||
condition.is_multiline() || continuation.is_multiline()
|
||||
}
|
||||
DbgStmt(condition, _) => condition.is_multiline(),
|
||||
LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
),
|
||||
Return(return_value, after_return) => {
|
||||
return_value.is_multiline() || after_return.is_some()
|
||||
}
|
||||
Return(_return_value, _after_return) => true,
|
||||
|
||||
If {
|
||||
if_thens: branches,
|
||||
|
|
@ -448,15 +442,12 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Expect(condition, continuation) => {
|
||||
fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
|
||||
}
|
||||
Dbg => {
|
||||
buf.indent(indent);
|
||||
buf.push_str("dbg");
|
||||
}
|
||||
DbgStmt(condition, continuation) => {
|
||||
fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent);
|
||||
fmt_dbg_stmt(buf, condition, continuation, parens, indent);
|
||||
}
|
||||
LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
|
|
@ -557,7 +548,6 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.indent(indent);
|
||||
loc_expr.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
MalformedClosure => {}
|
||||
PrecedenceConflict { .. } => {}
|
||||
EmptyRecordBuilder { .. } => {}
|
||||
SingleFieldRecordBuilder { .. } => {}
|
||||
|
|
@ -1032,16 +1022,15 @@ fn fmt_dbg_stmt<'a>(
|
|||
buf: &mut Buf,
|
||||
condition: &'a Loc<Expr<'a>>,
|
||||
continuation: &'a Loc<Expr<'a>>,
|
||||
_: bool,
|
||||
parens: Parens,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.push_str("dbg");
|
||||
|
||||
buf.spaces(1);
|
||||
|
||||
condition.format(buf, indent);
|
||||
Expr::Apply(
|
||||
&Loc::at_zero(Expr::Dbg),
|
||||
&[condition],
|
||||
called_via::CalledVia::Space,
|
||||
)
|
||||
.format_with_options(buf, parens, Newlines::Yes, indent);
|
||||
|
||||
// Always put a blank line after the `dbg` line(s)
|
||||
buf.ensure_ends_with_blank_line();
|
||||
|
|
@ -1049,33 +1038,6 @@ fn fmt_dbg_stmt<'a>(
|
|||
continuation.format(buf, indent);
|
||||
}
|
||||
|
||||
fn fmt_expect<'a>(
|
||||
buf: &mut Buf,
|
||||
condition: &'a Loc<Expr<'a>>,
|
||||
continuation: &'a Loc<Expr<'a>>,
|
||||
is_multiline: bool,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.push_str("expect");
|
||||
|
||||
let return_indent = if is_multiline {
|
||||
buf.newline();
|
||||
indent + INDENT
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
indent
|
||||
};
|
||||
|
||||
condition.format(buf, return_indent);
|
||||
|
||||
// Always put a blank line after the `expect` line(s)
|
||||
buf.ensure_ends_with_blank_line();
|
||||
|
||||
continuation.format(buf, indent);
|
||||
}
|
||||
|
||||
fn fmt_return<'a>(
|
||||
buf: &mut Buf,
|
||||
return_value: &'a Loc<Expr<'a>>,
|
||||
|
|
@ -1099,6 +1061,9 @@ fn fmt_return<'a>(
|
|||
return_value.format(buf, return_indent);
|
||||
|
||||
if let Some(after_return) = after_return {
|
||||
if after_return.value.extract_spaces().before.is_empty() {
|
||||
buf.ensure_ends_with_newline();
|
||||
}
|
||||
after_return.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
}
|
||||
|
|
@ -1646,9 +1611,6 @@ fn format_assigned_field_multiline<T>(
|
|||
format_assigned_field_multiline(buf, sub_field, indent, separator_prefix);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Top, indent);
|
||||
}
|
||||
Malformed(raw) => {
|
||||
buf.push_str(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1686,6 +1648,7 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
|
|||
})
|
||||
}
|
||||
Expr::If { .. } => true,
|
||||
Expr::Defs(_, _) => true,
|
||||
Expr::SpaceBefore(e, _) => sub_expr_requests_parens(e),
|
||||
Expr::SpaceAfter(e, _) => sub_expr_requests_parens(e),
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -1846,6 +1846,26 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
neg_reg64_reg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F64, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F32, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
|
@ -3953,6 +3973,24 @@ fn fsub_freg_freg_freg(
|
|||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FNEG Sd/Dd, Sn/Dn`
|
||||
#[inline(always)]
|
||||
fn fneg_freg_freg(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
ftype: FloatWidth,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
let inst =
|
||||
FloatingPointDataProcessingOneSource::new(FloatingPointDataProcessingOneSourceParams {
|
||||
ptype: ftype,
|
||||
opcode: 0b00010,
|
||||
rn: src,
|
||||
rd: dst,
|
||||
});
|
||||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags.
|
||||
#[inline(always)]
|
||||
fn fcmp_freg_freg(
|
||||
|
|
|
|||
|
|
@ -557,6 +557,18 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
||||
|
||||
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn mul_freg32_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: FloatReg,
|
||||
|
|
@ -1791,7 +1803,24 @@ impl<
|
|||
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src);
|
||||
ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
||||
}
|
||||
x => todo!("NumNeg: layout, {:?}", x),
|
||||
LayoutRepr::F32 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg32_freg32(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::F64 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::DEC => self.build_fn_call(
|
||||
dst,
|
||||
bitcode::DEC_NEGATE.to_string(),
|
||||
&[*src],
|
||||
&[Layout::DEC],
|
||||
&Layout::DEC,
|
||||
),
|
||||
other => internal_error!("unreachable: NumNeg for layout, {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2602,6 +2602,28 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
neg_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg64_imm64(buf, relocs, dst, f64::from_bits(0x8000_0000_0000_0000));
|
||||
xorpd_freg64_freg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg32_imm32(buf, relocs, dst, f32::from_bits(0x8000_0000));
|
||||
xorps_freg32_freg32(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
|
@ -3352,6 +3374,49 @@ fn sqrtss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64F
|
|||
}
|
||||
}
|
||||
|
||||
/// `XORPD xmm1, xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
|
||||
if dst_high || src_high {
|
||||
buf.extend([
|
||||
0x66,
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
])
|
||||
} else {
|
||||
buf.extend([0x66, 0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `XORPS xmm1,xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorps_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
|
||||
if dst_high || src_high {
|
||||
buf.extend([
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
]);
|
||||
} else {
|
||||
buf.extend([0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result.
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
|
|
|
|||
|
|
@ -279,7 +279,6 @@ impl<'a> LastSeenMap<'a> {
|
|||
|
||||
Stmt::Dbg { .. } => todo!("dbg not implemented in the dev backend"),
|
||||
Stmt::Expect { .. } => todo!("expect is not implemented in the dev backend"),
|
||||
Stmt::ExpectFx { .. } => todo!("expect-fx is not implemented in the dev backend"),
|
||||
|
||||
Stmt::Crash(msg, _crash_tag) => {
|
||||
self.set_last_seen(*msg, stmt);
|
||||
|
|
@ -664,7 +663,9 @@ trait Backend<'a> {
|
|||
cond_layout,
|
||||
branches,
|
||||
default_branch,
|
||||
ret_layout,
|
||||
// always use the proc's ret_layout, as early returns can make
|
||||
// this ret_layout inaccurate
|
||||
ret_layout: _,
|
||||
} => {
|
||||
self.load_literal_symbols(&[*cond_symbol]);
|
||||
self.build_switch(
|
||||
|
|
@ -1641,9 +1642,9 @@ trait Backend<'a> {
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::StrSplit => self.build_fn_call(
|
||||
LowLevel::StrSplitOn => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::STR_SPLIT.to_string(),
|
||||
bitcode::STR_SPLIT_ON.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
|
|
|
|||
|
|
@ -3561,74 +3561,6 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>(
|
|||
)
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
condition: cond_symbol,
|
||||
region,
|
||||
lookups,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let bd = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
let (cond, _cond_layout) = scope.load_symbol_and_layout(cond_symbol);
|
||||
|
||||
let condition = bd.new_build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
cond.into_int_value(),
|
||||
context.bool_type().const_int(1, false),
|
||||
"is_true",
|
||||
);
|
||||
|
||||
let then_block = context.append_basic_block(parent, "then_block");
|
||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||
|
||||
bd.new_build_conditional_branch(condition, then_block, throw_block);
|
||||
|
||||
if env.mode.runs_expects() {
|
||||
bd.position_at_end(throw_block);
|
||||
|
||||
match env.target.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes8 => {
|
||||
let shared_memory = SharedMemoryPointer::get(env);
|
||||
|
||||
clone_to_shared_memory(
|
||||
env,
|
||||
layout_interner,
|
||||
scope,
|
||||
layout_ids,
|
||||
&shared_memory,
|
||||
*cond_symbol,
|
||||
*region,
|
||||
lookups,
|
||||
variables,
|
||||
);
|
||||
|
||||
bd.new_build_unconditional_branch(then_block);
|
||||
}
|
||||
roc_target::PtrWidth::Bytes4 => {
|
||||
// temporary WASM implementation
|
||||
throw_internal_exception(env, "An expectation failed!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bd.position_at_end(throw_block);
|
||||
bd.new_build_unconditional_branch(then_block);
|
||||
}
|
||||
|
||||
bd.position_at_end(then_block);
|
||||
|
||||
build_exp_stmt(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_ids,
|
||||
func_spec_solutions,
|
||||
scope,
|
||||
parent,
|
||||
remainder,
|
||||
)
|
||||
}
|
||||
|
||||
Crash(sym, tag) => {
|
||||
throw_exception(env, scope, sym, *tag);
|
||||
|
||||
|
|
|
|||
|
|
@ -461,8 +461,8 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
bitcode::STR_REPEAT,
|
||||
)
|
||||
}
|
||||
StrSplit => {
|
||||
// Str.split : Str, Str -> List Str
|
||||
StrSplitOn => {
|
||||
// Str.splitOn : Str, Str -> List Str
|
||||
arguments!(string, delimiter);
|
||||
|
||||
call_str_bitcode_fn(
|
||||
|
|
@ -470,7 +470,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
&[string, delimiter],
|
||||
&[],
|
||||
BitcodeReturns::List,
|
||||
bitcode::STR_SPLIT,
|
||||
bitcode::STR_SPLIT_ON,
|
||||
)
|
||||
}
|
||||
StrIsEmpty => {
|
||||
|
|
@ -2205,6 +2205,7 @@ fn build_dec_unary_op<'a, 'ctx>(
|
|||
|
||||
match op {
|
||||
NumAbs => dec_unary_op(env, bitcode::DEC_ABS, arg),
|
||||
NumNeg => dec_unary_op(env, bitcode::DEC_NEGATE, arg),
|
||||
NumAcos => dec_unary_op(env, bitcode::DEC_ACOS, arg),
|
||||
NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg),
|
||||
NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue