Merge remote-tracking branch 'origin/main' into rust-1-62-1

This commit is contained in:
Folkert 2022-11-09 13:51:51 +01:00
commit 14cd48fce7
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
685 changed files with 44531 additions and 20189 deletions

View file

@ -1,4 +0,0 @@
AUTHORS
nix
.envrc
.gitignore

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: roc-lang

View file

@ -7,3 +7,5 @@ updates:
day: "monday"
time: "07:00"
timezone: "Europe/Brussels"
# Disable all version updates, only critical security updates will be submitted
open-pull-requests-limit: 0

View file

@ -23,25 +23,18 @@ jobs:
ref: "main"
clean: "true"
- name: Earthly version
run: earthly --version
- name: on main; prepare a self-contained benchmark folder
run: ./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=main +prep-bench-folder
run: nix develop -c ./ci/benchmarks/prep_folder.sh main
- uses: actions/checkout@v3
with:
clean: "false" # we want to keep the benchmark folder
- name: on current branch; prepare a self-contained benchmark folder
run: ./ci/safe-earthly.sh +prep-bench-folder
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
run: nix develop -c ./ci/benchmarks/prep_folder.sh branch
- name: build benchmark runner
run: cd ci/bench-runner && cargo build --release && cd ../..
run: nix develop -c bash -c "cd ci/benchmarks/bench-runner && cargo build --release && cd ../../.."
- name: run benchmarks with regression check
run: ./ci/bench-runner/target/release/bench-runner --check-executables-changed
run: nix develop -c ./ci/benchmarks/bench-runner/target/release/bench-runner --check-executables-changed

View file

@ -0,0 +1,30 @@
on:
pull_request:
schedule:
- cron: '0 9 * * *' # 9=9am utc+0
name: Check Markdown links
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
markdown-link-check:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: 'yes'
use-verbose-mode: 'yes'
base-branch: 'main'
check-modified-files-only: 'yes'
if: ${{ github.event_name == 'pull_request' }}
- uses: gaurav-nelson/github-action-markdown-link-check@v1
with:
use-quiet-mode: 'yes'
use-verbose-mode: 'yes'
base-branch: 'main'
check-modified-files-only: 'no'
if: ${{ github.event_name == 'schedule' }}

View file

@ -29,7 +29,7 @@ jobs:
env:
DATE: ${{ env.DATE }}
SHA: ${{ env.SHA }}
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_apple_silicon-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_12_apple_silicon-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
- name: write version to file
run: ./ci/write_version.sh

View file

@ -6,15 +6,21 @@ name: Nightly Release macOS x86_64
env:
ZIG_VERSION: 0.9.1
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm
LLVM_SYS_130_PREFIX: /usr/local/opt/llvm@13
jobs:
test-build-upload:
name: build, test, package and upload nightly release
runs-on: [macos-12]
timeout-minutes: 90
strategy:
matrix:
os: [ macos-11, macos-12 ]
runs-on: ${{ matrix.os }}
timeout-minutes: 120
steps:
- uses: actions/checkout@v3
- name: write version to file
run: ./ci/write_version.sh
- name: Install zig
run: |
@ -32,26 +38,41 @@ jobs:
command: build
args: --release --locked
- name: execute rust tests
- name: execute rust tests if macos 12
if: endsWith(matrix.os, '12')
uses: actions-rs/cargo@v1
with:
command: test
args: --release --locked -- --skip opaque_wrap_function --skip bool_list_literal
- name: execute rust tests if macos 11
if: endsWith(matrix.os, '11')
uses: actions-rs/cargo@v1
with:
command: test
args: --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
# this issue may be caused by using older versions of XCode
- name: get commit SHA
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
- name: get date
run: echo "DATE=$(date "+%Y-%m-%d")" >> $GITHUB_ENV
- name: get macos version if 11
if: endsWith(matrix.os, '11')
run: echo "MACOSVERSION=11" >> $GITHUB_ENV
- name: get macos version if 12
if: endsWith(matrix.os, '12')
run: echo "MACOSVERSION=12" >> $GITHUB_ENV
- name: build file name
env:
DATE: ${{ env.DATE }}
SHA: ${{ env.SHA }}
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_x86_64-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
- name: write version to file
run: ./ci/write_version.sh
run: echo "RELEASE_TAR_FILENAME=roc_nightly-macos_${MACOSVERSION}_x86_64-$DATE-$SHA.tar.gz" >> $GITHUB_ENV
- name: package release
run: ./ci/package_release.sh ${{ env.RELEASE_TAR_FILENAME }}

View file

@ -0,0 +1,16 @@
on:
schedule:
- cron: '0 9 * * *'
name: Nightly netlify build and deploy
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: trigger netlify build and deploy
env:
HOOK: ${{ secrets.NETLIFY_BUILD_HOOK }}
run: |
curl -X POST -d {} https://api.netlify.com/build_hooks/${HOOK}

View file

@ -20,4 +20,7 @@ jobs:
clean: "true"
- name: execute tests with --release
run: /home/big-ci-user/.nix-profile/bin/nix develop -c cargo test --locked --release
run: nix develop -c cargo test --locked --release
- name: test wasm32 cli_run
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run"

View file

@ -31,6 +31,9 @@ jobs:
- name: execute tests with --release
run: nix develop -c cargo test --locked --release
# we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-platform
- name: test launching the editor
run: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs
# we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-target
- name: execute llvm wasm tests with --release
run: nix develop -c cargo test-gen-llvm-wasm --locked --release

View file

@ -28,7 +28,7 @@ jobs:
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
- name: test roc hello world
run: ./roc examples/hello-world/main.roc
run: ./roc examples/helloWorld.roc

View file

@ -2,14 +2,14 @@ on:
schedule:
- cron: '0 13 * * *'
name: Test latest nightly release for macOS x86_64
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
jobs:
test-nightly:
name: test nightly macos x86_64
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
strategy:
matrix:
os: [ macos-12, ubuntu-20.04, ubuntu-22.04 ]
os: [ macos-11, macos-12, ubuntu-20.04, ubuntu-22.04 ]
runs-on: ${{ matrix.os }}
timeout-minutes: 90
steps:
@ -23,13 +23,17 @@ jobs:
--header 'content-type: application/json' \
--output roc_releases.json
- name: get the url of today`s release for linux x86_64
- name: get the url of today`s release for linux (x86_64)
if: startsWith(matrix.os, 'ubuntu')
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh linux_x86_64)" >> $GITHUB_ENV
- name: get the url of today`s release for macos x86_64
if: startsWith(matrix.os, 'macos')
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_x86_64)" >> $GITHUB_ENV
- name: get the url of today`s release for macos 11 (x86_64)
if: startsWith(matrix.os, 'macos-11')
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_11_x86_64)" >> $GITHUB_ENV
- name: get the url of today`s release for macos 12 (x86_64)
if: startsWith(matrix.os, 'macos-12')
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_12_x86_64)" >> $GITHUB_ENV
- name: get the archive from the url
run: curl -OL ${{ env.RELEASE_URL }}
@ -41,7 +45,7 @@ jobs:
run: ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf
- name: test roc hello world
run: ./roc examples/hello-world/main.roc
run: ./roc examples/helloWorld.roc

View file

@ -26,6 +26,9 @@ jobs:
- name: zig fmt check, zig tests
run: cd crates/compiler/builtins/bitcode && ./run-tests.sh
- name: roc format check on builtins
run: cargo run --locked --release format --check crates/compiler/builtins/roc
- name: zig wasm tests
run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh
@ -33,6 +36,9 @@ jobs:
- name: regular rust tests
run: cargo test --locked --release --features with_sound serde --workspace && sccache --show-stats
- name: test launching the editor
run: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs
- name: test the dev backend # these tests require an explicit feature flag
run: cargo test --locked --release --package test_gen --no-default-features --features gen-dev && sccache --show-stats
@ -42,6 +48,9 @@ jobs:
- name: run `roc test` on Str builtins
run: cargo run --locked --release -- test crates/compiler/builtins/roc/Str.roc && sccache --show-stats
- name: run `roc test` on Dict builtins
run: cargo run --locked --release -- test crates/compiler/builtins/roc/Dict.roc && sccache --show-stats
#TODO pass --locked into the script here as well, this avoids rebuilding dependencies unnecessarily
- name: wasm repl test
run: crates/repl_test/test_wasm.sh && sccache --show-stats

52
.github/workflows/windows.yml vendored Normal file
View file

@ -0,0 +1,52 @@
on: [pull_request]
name: Test windows build
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
RUST_BACKTRACE: 1
jobs:
windows-cargo-build:
name: windows-cargo-build
runs-on: windows-2022
env:
LLVM_SYS_130_PREFIX: C:\LLVM-13.0.1-win64
timeout-minutes: 150
steps:
- uses: actions/checkout@v2
- run: Add-Content -Path "$env:GITHUB_ENV" -Value "GITHUB_RUNNER_CPU=$((Get-CimInstance Win32_Processor).Name)"
- uses: Swatinem/rust-cache@v2
with:
shared-key: "rust-cache-windows-${{env.GITHUB_RUNNER_CPU}}"
- name: download and install zig
run: |
curl.exe --output "C:\zig-windows-x86_64-0.9.1.zip" --url https://ziglang.org/download/0.9.1/zig-windows-x86_64-0.9.1.zip
cd C:\
7z x zig-windows-x86_64-0.9.1.zip
Add-Content $env:GITHUB_PATH "C:\zig-windows-x86_64-0.9.1\"
- name: zig version
run: zig version
- name: set up llvm 13
run: |
curl.exe -L -O https://github.com/roc-lang/llvm-package-windows/releases/download/v13.0.1/LLVM-13.0.1-win64.7z
7z x LLVM-13.0.1-win64.7z -oC:\LLVM-13.0.1-win64
- name: Build tests --release without running. Twice for zig lld-link error.
run: cargo test --locked --release --no-run || cargo test --locked --release --no-run
# Why are these tests not build with previous command? => fingerprint error. Use `CARGO_LOG=cargo::core::compiler::fingerprint=info` to investigate
- name: Build specific tests without running. Twice for zig lld-link error.
run: cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker || cargo test --locked --release --no-run -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker
- name: Actually run the tests.
run: cargo test --locked --release -p roc_ident -p roc_region -p roc_collections -p roc_can -p roc_types -p roc_solve -p roc_mono -p roc_gen_dev -p roc_gen_wasm -p roc_serialize -p roc_editor -p roc_linker

5
.gitignore vendored
View file

@ -6,6 +6,9 @@ zig-cache
*.rs.bk
*.o
*.obj
*.dll
*.lib
*.def
*.tmp
*.wasm
*.exe
@ -36,7 +39,7 @@ editor/benches/resources/50000_lines.roc
editor/benches/resources/500_lines.roc
# file editor creates when no arg is passed
new-roc-project
roc-projects
# rust cache (sccache folder)
sccache_dir

30
AUTHORS
View file

@ -65,6 +65,7 @@ Mats Sigge <<mats.sigge@gmail.com>>
Drew Lazzeri <dlazzeri1@gmail.com>
Tom Dohrmann <erbse.13@gmx.de>
Elijah Schow <elijah.schow@gmail.com>
Emi Simpson <emi@alchemi.dev>
Derek Gustafson <degustaf@gmail.com>
Philippe Vinchon <p.vinchon@gmail.com>
Pierre-Henri Trivier <phtrivier@yahoo.fr>
@ -74,17 +75,21 @@ Ananda Umamil <zweimach@zweimach.org>
SylvanSign <jake.d.bray@gmail.com>
Nikita Mounier <36044205+nikitamounier@users.noreply.github.com>
Cai Bingjun <62678643+C-BJ@users.noreply.github.com>
Kevin Gillette <kgillette628@gmail.com>
Jared Cone <jared.cone@gmail.com>
Sean Hagstrom <sean@seanhagstrom.com>
Kas Buunk <kasbuunk@icloud.com>
Kas Buunk <kasbuunk@icloud.com>
Tommy Graves <tommy@rwx.com>
Oskar Hahn <mail@oshahn.de>
Nuno Ferreira <nunogcferreira@gmail.com>
Jonas Schell <jonasschell@ocupe.org>
Mfon Eti-mfon <mfonetimfon@gmail.com>
Drake Bennion <drake.bennion@gmail.com>
Hashi364 <49736221+Kiyoshi364@users.noreply.github.com>
Jared Forsyth <jared@jaredforsyth.com>
Patrick Kilgore <git@pck.email>
Marten/Qqwy <w-m@wmcode.nl>
Tobias Steckenborn <tobias.steckenborn@consolvis.de>
Christoph Rüßler <christoph.ruessler@mailbox.org>
Ralf Engbers <raleng@users.noreply.github.com>
Mostly Void <7rat13@gmail.com>
@ -94,4 +99,25 @@ David A. Kunz <david.kunz@sap.com>
Paul Young <84700+paulyoung@users.noreply.github.com>
Rod <randomer@users.noreply.github.com>
Marko Vujanic <crashxx@gmail.com>
kilianv <r0754877>
KilianVounckx <kilianvounckx@hotmail.be>
David Dunn <26876072+doubledup@users.noreply.github.com>
Jelle Besseling <jelle@pingiun.com>
isaacthefallenapple <isaacthefallenapple@gmail.com>
Bryce Miller <sandprickle@users.noreply.github.com>
Bjørn Madsen <bm@aeons.dk>
Vilem <17603372+buggymcbugfix@users.noreply.github.com>
J Teeuwissen <jelleteeuwissen@hotmail.nl>
Matthieu Pizenberg <matthieu.pizenberg@gmail.com>
rezzaghi <lbrezzaghi@gmail.com>
João Mota <jackthemotorcycle@gmail.com>
Marcos Prieto <marcospri@gmail.com>
Prajwal S N <prajwalnadig21@gmail.com>
Christopher Duncan <chris.duncan.arauz+git@protonmail.com>
Luke Boswell <lukewilliamboswell@gmail.com>
Luca Cervello <luca.cervello@gmail.com>
Josh Mak <joshmak@berkeley.edu>
Travis Staloch <twostepted@gmail.com>
Nick Gravgaard <nick@nickgravgaard.com>
Keerthana Kasthuril <76804118+keerthanak-tw@users.noreply.github.com>
Salman Shaik <salmansiddiq.shaik@gmail.com>
Austin Clements <austinclementsbass@gmail.com>

View file

@ -1,6 +1,6 @@
# Building the Roc compiler from source
Installation should be a smooth process, let us now if anything does not work perfectly on [Roc Zulip](https://roc.zulipchat.com) or by creating an issue.
If you run into any problems getting Roc built from source, please ask for help in the `#beginners` channel on [Roc Zulip](https://roc.zulipchat.com) (the fastest way), or create an issue in this repo!
## Using Nix
@ -28,13 +28,7 @@ sh <(curl -L https://nixos.org/nix/install) --no-daemon
sh <(curl -L https://nixos.org/nix/install) --daemon
```
Open a new terminal and install nixFlakes in your environment:
```sh
nix-env -iA nixpkgs.nixFlakes
```
Edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
Open a new terminal and edit either `~/.config/nix/nix.conf` or `/etc/nix/nix.conf` and add:
```text
experimental-features = nix-command flakes
@ -135,7 +129,7 @@ If you want to install it manually, you can also download Zig directly [here](ht
For macOS, you can install LLVM 13 using `brew install llvm@13` and then adding
`$(brew --prefix llvm@13)/bin` to your `PATH`. You can confirm this worked by
running `llc --version` - it should mention "LLVM version 13.0.0" at the top.
running `llc --version` - it should mention "LLVM version 13.0.1" at the top.
You may also need to manually specify a prefix env var like so:
```sh
@ -202,26 +196,14 @@ export CPPFLAGS="-I/usr/local/opt/llvm/include"
**Warning** While `cargo build` works on windows, linking roc programs does not yet, see issue #2608. This also means the repl, the editor and many tests will not work on windows.
The official LLVM pre-built binaries for Windows lack features that roc needs. Instead:
1. Download and install [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work too; the Build Tools are just the CLI tools, which is all we need)
1. Download the custom LLVM 7z archive [here](https://github.com/PLC-lang/llvm-package-windows/releases/tag/v13.0.0).
1. Download the custom LLVM 7z archive [here](https://github.com/roc-lang/llvm-package-windows/releases/download/v13.0.1/LLVM-13.0.1-win64.7z).
1. [Download 7-zip](https://www.7-zip.org/) to be able to extract this archive.
1. Extract the 7z file to where you want to permanently keep the folder. We recommend you pick a path without any spaces in it.
1. In powershell, set the `LLVM_SYS_130_PREFIX` environment variable (check [here](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.2#saving-environment-variables-with-the-system-control-panel) to make this a permanent environment variable):
```text
<# ! Replace YOUR_USERNAME ! #>
$env:LLVM_SYS_130_PREFIX = 'C:\Users\YOUR_USERNAME\Downloads\LLVM-13.0.0-win64'
```
1. add the LLVM bin to the path to prevent issue #3952:
```text
<# ! Replace YOUR_USERNAME ! #>
[Environment]::SetEnvironmentVariable(
"Path",
[Environment]::GetEnvironmentVariable("Path", "User") + ";C:\Users\YOUR_USERNAME\Downloads\LLVM-13.0.0-win64\bin",
"User"
)
$env:LLVM_SYS_130_PREFIX = 'C:\Users\YOUR_USERNAME\Downloads\LLVM-13.0.1-win64'
```
Once all that was done, `cargo build` ran successfully for Roc!

View file

@ -2,7 +2,20 @@
## Code of Conduct
We are committed to providing a friendly, safe and welcoming environment for all. Make sure to take a look at the [Code of Conduct](CodeOfConduct.md)!
We are committed to providing a friendly, safe and welcoming environment for all. Make sure to take a look at the [Code of Conduct](CODE_OF_CONDUCT.md)!
## How to contribute
All contributions are appreciated! Typo fixes, bug fixes, feature requests,
bug reports are all helpful for the project.
If you are looking for a good place to start, consider reaching out on the `#contributing` channel on [Roc Zulip][roc-zulip].
Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip][roc-zulip] first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
If you are interested in larger, implementation- or research-heavy projects
related to Roc, check out [Roc Project Ideas][project-ideas] and reach out to us
on Zulip! These projects may be suitable for academic theses, independent
research, or even just valuable projects to learn from and improve Roc with.
## Building from Source
@ -10,7 +23,7 @@ Check [Building from source](BUILDING_FROM_SOURCE.md) for instructions.
## Running Tests
Most contributors execute the following commands befor pushing their code:
Most contributors execute the following commands before pushing their code:
```sh
cargo test
@ -20,24 +33,60 @@ cargo clippy --workspace --tests -- --deny warnings
Execute `cargo fmt --all` to fix the formatting.
## Generating Docs
If you make changes to [Roc's Standard Library](https://www.roc-lang.org/builtins/Str), you can add comments to the code following [the CommonMark Spec](https://spec.commonmark.org/current/) to further explain your intentions. You can view these changes locally with:
```sh
cargo run docs crates/compiler/builtins/roc
```
This command will generate the documentation in the [`generated-docs`](generated-docs) directory.
## Contribution Tips
- If you've never made a pull request on github before, [this](https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github-3/) will be a good place to start.
- Create an issue if the purpose of a struct/field/type/function/... is not immediately clear from its name or nearby comments.
- You can find good first issues [here](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
- Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
- You can find good first issues [here][good-first-issues]. Once you have gained some experience you can take a look at the [intermediate issues](https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22intermediate+issue%22).
- [Fork](https://github.com/roc-lang/roc/fork) the repo so that you can apply your changes first on your own copy of the roc repo.
- It's a good idea to open a draft pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Click the button "ready for review" when it's ready.
- All your commits need to be signed to prevent impersonation:
1. If you have a Yubikey, follow [guide 1](https://dev.to/paulmicheli/using-your-yubikey-to-get-started-with-gpg-3h4k), [guide 2](https://dev.to/paulmicheli/using-your-yubikey-for-signed-git-commits-4l73) and skip the steps below.
2. [Make a key to sign your commits.](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key).
3. [Configure git to use your key.](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key)
4. Make git sign your commits automatically:
- All your commits need to be signed [to prevent impersonation](https://dev.to/martiliones/how-i-got-linus-torvalds-in-my-contributors-on-github-3k4g):
- If you don't have signing set up on your device and you only want to change a single file, it will be easier to use [github's edit button](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files). This will sign your commit automatically.
- For multi-file or complex changes you will want to set up signing on your device:
1. If you have a Yubikey, follow [guide 1](https://dev.to/paulmicheli/using-your-yubikey-to-get-started-with-gpg-3h4k), [guide 2](https://dev.to/paulmicheli/using-your-yubikey-for-signed-git-commits-4l73) and skip the steps below.
2. [Make a key to sign your commits.](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key)
3. [Configure git to use your key.](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key)
4. Make git sign your commits automatically:
```sh
git config --global commit.gpgsign true
```
```sh
git config --global commit.gpgsign true
```
### Forgot to sign commits?
You can find which commits need to be signed by running `git log --show-signature`.
If you have only one commit, running `git commit --amend --no-edit -S` would sign the latest commit 🚀.
In case you have multiple commits, you can sign them in two ways:
1. Switching to interactive rebase mode and editing the file:
- Enter into interactive mode, by running `git rebase -i HEAD~n` where `n` is the number of commits up to the most current commit you would like to see.
- This would display a set of commits in a text file like below:
```
pick hash2 commit message 2
pick hash1 commit message 1
```
- After every commit you want to sign, add `exec git commit --amend --no-edit -S`.
2. Or run git rebase recursively:
- Find the oldest commit you want to sign, using the `git log --show-signature` command.
- Run the command `git rebase --exec 'git commit --amend --no-edit -n -S' -i HASH` which would sign all commits up to commit `HASH`.
If you already pushed unsigned commits, you mmay have to do a force push with `git push origin -f <branch_name>`.
## Can we do better?
Feel free to open an issue if you think this document can be improved or is unclear in any way.
[roc-zulip]: https://roc.zulipchat.com
[good-first-issues]: https://github.com/roc-lang/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22
[project-ideas]: https://docs.google.com/document/d/1mMaxIi7vxyUyNAUCs98d68jYj6C9Fpq4JIZRU735Kwg/edit?usp=sharing

353
Cargo.lock generated
View file

@ -75,15 +75,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "approx"
version = "0.4.0"
@ -105,6 +96,9 @@ dependencies = [
[[package]]
name = "arena-pool"
version = "0.0.1"
dependencies = [
"roc_error_macros",
]
[[package]]
name = "arrayvec"
@ -253,34 +247,13 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array 0.14.5",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
"generic-array",
]
[[package]]
@ -297,15 +270,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.10.0"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "bytecheck"
@ -475,9 +442,9 @@ dependencies = [
[[package]]
name = "clap"
version = "3.2.18"
version = "3.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15f2ea93df33549dbe2e8eecd1ca55269d63ae0b3ba1f55db030817d1c2867f"
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
dependencies = [
"atty",
"bitflags",
@ -520,7 +487,7 @@ checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
[[package]]
name = "cli_utils"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"bumpalo",
"criterion",
@ -529,6 +496,7 @@ dependencies = [
"roc_load",
"roc_module",
"roc_reporting",
"roc_utils",
"serde",
"serde-xml-rs",
"strip-ansi-escapes",
@ -620,10 +588,10 @@ dependencies = [
[[package]]
name = "confy"
version = "0.4.0"
source = "git+https://github.com/rust-cli/confy#c6b62039281b8643539b436440bcea1b0d634bc7"
version = "0.5.0"
source = "git+https://github.com/rust-cli/confy#fd069f062aa3373c846f0d8c6e3b5e2a5cd0096b"
dependencies = [
"directories-next",
"directories",
"serde",
"serde_yaml",
"thiserror",
@ -924,7 +892,7 @@ dependencies = [
"clap 2.34.0",
"criterion-plot",
"csv",
"itertools 0.10.3",
"itertools 0.10.5",
"lazy_static",
"num-traits",
"oorandom",
@ -1023,7 +991,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f"
dependencies = [
"generic-array 0.14.5",
"generic-array",
"typenum",
]
@ -1129,22 +1097,13 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer 0.10.2",
"block-buffer",
"crypto-common",
]
@ -1160,13 +1119,13 @@ dependencies = [
]
[[package]]
name = "directories-next"
version = "2.0.0"
name = "directories"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
dependencies = [
"cfg-if 1.0.0",
"dirs-sys-next",
"cfg-if 0.1.10",
"dirs-sys",
]
[[package]]
@ -1179,6 +1138,17 @@ dependencies = [
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
@ -1231,9 +1201,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "dunce"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c"
[[package]]
name = "dynasm"
@ -1336,19 +1306,6 @@ dependencies = [
"regex",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "errno"
version = "0.2.8"
@ -1380,12 +1337,6 @@ dependencies = [
"str-buf",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
@ -1577,15 +1528,6 @@ dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.5"
@ -1766,12 +1708,6 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iced-x86"
version = "1.17.0"
@ -1871,9 +1807,9 @@ checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca"
[[package]]
name = "insta"
version = "1.19.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f37dc501f12ec0c339b385787fa89ffda3d5d2caa62e558da731134c24d6e0c4"
checksum = "58a931b01c76064c5be919faa2ef0dc570e9a889dcd1e5fef08a8ca6eb4d6c0b"
dependencies = [
"console",
"linked-hash-map",
@ -1911,9 +1847,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.3"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
@ -1961,9 +1897,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.59"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
@ -2025,9 +1961,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.132"
version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "libloading"
@ -2168,12 +2104,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memexec"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc62ccb14881da5d1862cda3a9648fb4a4897b2aff0b2557b89da44a5e550b7c"
[[package]]
name = "memmap2"
version = "0.3.1"
@ -2457,6 +2387,16 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f1f8e5676e1a1f2ee8b21f38238e1243c827531c9435624c7bfb305102cee4"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num-derive"
version = "0.3.3"
@ -2615,9 +2555,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.13.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "oorandom"
@ -2625,12 +2565,6 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "ordered-float"
version = "3.0.0"
@ -2655,6 +2589,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owned_ttf_parser"
version = "0.15.0"
@ -2776,9 +2716,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "peg"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af728fe826811af3b38c37e93de6d104485953ea373d656eebae53d6987fcd2c"
checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554"
dependencies = [
"peg-macros",
"peg-runtime",
@ -2786,9 +2726,9 @@ dependencies = [
[[package]]
name = "peg-macros"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4536be147b770b824895cbad934fccce8e49f14b4c4946eaa46a6e4a12fcdc16"
checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b"
dependencies = [
"peg-runtime",
"proc-macro2",
@ -2797,9 +2737,9 @@ dependencies = [
[[package]]
name = "peg-runtime"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
[[package]]
name = "percent-encoding"
@ -2809,18 +2749,19 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.1.3"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1"
dependencies = [
"pest",
"pest_generator",
@ -2828,9 +2769,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.1.3"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c"
dependencies = [
"pest",
"pest_meta",
@ -2841,13 +2782,13 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.1.3"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6"
dependencies = [
"maplit",
"once_cell",
"pest",
"sha-1",
"sha1 0.10.4",
]
[[package]]
@ -2945,14 +2886,14 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "pretty_assertions"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563"
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
dependencies = [
"ansi_term",
"ctor",
"diff",
"output_vt100",
"yansi",
]
[[package]]
@ -3056,7 +2997,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
"env_logger 0.8.4",
"env_logger",
"log",
"rand",
]
@ -3293,6 +3234,7 @@ version = "0.0.1"
dependencies = [
"indoc",
"lazy_static",
"roc_build",
"roc_cli",
"roc_repl_cli",
"roc_test_utils",
@ -3342,13 +3284,14 @@ dependencies = [
"morphic_lib",
"roc_collections",
"roc_debug_flags",
"roc_error_macros",
"roc_module",
"roc_mono",
]
[[package]]
name = "roc_ast"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"arrayvec 0.7.2",
"bumpalo",
@ -3437,16 +3380,17 @@ dependencies = [
"roc_parse",
"roc_problem",
"roc_region",
"roc_serialize",
"roc_types",
"static_assertions",
]
[[package]]
name = "roc_cli"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"bumpalo",
"clap 3.2.18",
"clap 3.2.20",
"cli_utils",
"const_format",
"criterion",
@ -3455,8 +3399,9 @@ dependencies = [
"inkwell 0.1.0",
"libc",
"libloading",
"memexec",
"mimalloc",
"once_cell",
"parking_lot 0.12.1",
"pretty_assertions",
"roc_build",
"roc_builtins",
@ -3468,6 +3413,7 @@ dependencies = [
"roc_fmt",
"roc_gen_llvm",
"roc_glue",
"roc_intern",
"roc_linker",
"roc_load",
"roc_module",
@ -3480,10 +3426,9 @@ dependencies = [
"roc_target",
"roc_test_utils",
"roc_tracing",
"roc_utils",
"serial_test",
"signal-hook",
"strum",
"strum_macros",
"target-lexicon",
"tempfile",
"ven_pretty",
@ -3493,10 +3438,9 @@ dependencies = [
[[package]]
name = "roc_code_markup"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"bumpalo",
"itertools 0.10.3",
"palette",
"roc_ast",
"roc_module",
@ -3591,7 +3535,7 @@ dependencies = [
name = "roc_docs_cli"
version = "0.0.1"
dependencies = [
"clap 3.2.18",
"clap 3.2.20",
"libc",
"roc_docs",
]
@ -3607,7 +3551,6 @@ dependencies = [
"colored",
"confy",
"copypasta",
"env_logger 0.9.0",
"fs_extra",
"futures",
"glyph_brush",
@ -3633,6 +3576,7 @@ dependencies = [
"roc_solve",
"roc_types",
"roc_unify",
"roc_utils",
"rodio",
"serde",
"snafu",
@ -3654,6 +3598,7 @@ name = "roc_exhaustive"
version = "0.0.1"
dependencies = [
"roc_collections",
"roc_error_macros",
"roc_module",
"roc_region",
]
@ -3740,7 +3685,7 @@ name = "roc_glue"
version = "0.0.1"
dependencies = [
"bumpalo",
"clap 3.2.18",
"clap 3.2.20",
"cli_utils",
"dircpy",
"fnv",
@ -3759,6 +3704,7 @@ dependencies = [
"roc_std",
"roc_target",
"roc_test_utils",
"roc_tracing",
"roc_types",
"strum",
"strum_macros",
@ -3807,15 +3753,18 @@ version = "0.0.1"
dependencies = [
"bincode",
"bumpalo",
"clap 3.2.18",
"iced-x86",
"indoc",
"libc",
"mach_object",
"memmap2 0.5.7",
"object 0.29.0",
"roc_build",
"roc_collections",
"roc_error_macros",
"roc_load",
"roc_mono",
"roc_reporting",
"serde",
"target-lexicon",
"tempfile",
@ -3890,6 +3839,7 @@ dependencies = [
name = "roc_mono"
version = "0.0.1"
dependencies = [
"bitvec 1.0.1",
"bumpalo",
"hashbrown 0.12.3",
"roc_builtins",
@ -3974,6 +3924,7 @@ dependencies = [
"rustyline",
"rustyline-derive",
"target-lexicon",
"unicode-segmentation",
]
[[package]]
@ -4009,7 +3960,9 @@ dependencies = [
"pretty_assertions",
"roc_build",
"roc_builtins",
"roc_can",
"roc_collections",
"roc_error_macros",
"roc_gen_llvm",
"roc_intern",
"roc_load",
@ -4046,6 +3999,7 @@ dependencies = [
"roc_reporting",
"roc_target",
"roc_types",
"roc_utils",
"wasi_libc_sys",
"wasm-bindgen",
"wasm-bindgen-futures",
@ -4082,6 +4036,13 @@ dependencies = [
"ven_pretty",
]
[[package]]
name = "roc_serialize"
version = "0.0.1"
dependencies = [
"roc_collections",
]
[[package]]
name = "roc_solve"
version = "0.0.1"
@ -4172,6 +4133,7 @@ dependencies = [
"roc_error_macros",
"roc_module",
"roc_region",
"roc_serialize",
"static_assertions",
]
@ -4365,9 +4327,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.139"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
@ -4405,9 +4367,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.139"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
@ -4463,18 +4425,6 @@ dependencies = [
"syn",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug",
]
[[package]]
name = "sha1"
version = "0.6.1"
@ -4484,6 +4434,17 @@ dependencies = [
"sha1_smol",
]
[[package]]
name = "sha1"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
@ -4498,7 +4459,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.3",
"digest",
]
[[package]]
@ -4741,7 +4702,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"sha1",
"sha1 0.6.1",
"syn",
]
@ -4780,9 +4741,9 @@ checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]]
name = "strum_macros"
version = "0.24.2"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
@ -4906,6 +4867,7 @@ dependencies = [
"roc_target",
"roc_types",
"roc_unify",
"roc_utils",
"target-lexicon",
"tempfile",
"wasi_libc_sys",
@ -5119,9 +5081,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.29"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
"valuable",
@ -5140,12 +5102,12 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.15"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"ansi_term",
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
@ -5208,9 +5170,9 @@ checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-width"
@ -5308,12 +5270,15 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi_libc_sys"
version = "0.0.1"
dependencies = [
"roc_utils",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@ -5321,9 +5286,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
@ -5336,9 +5301,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.32"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@ -5348,9 +5313,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -5358,9 +5323,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
@ -5371,9 +5336,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.82"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "wasm-encoder"
@ -6156,3 +6121,9 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

View file

@ -32,6 +32,7 @@ members = [
"crates/compiler/test_gen",
"crates/compiler/roc_target",
"crates/compiler/debug_flags",
"crates/compiler/serialize",
"crates/vendor/inkwell",
"crates/vendor/pathfinding",
"crates/vendor/pretty",
@ -57,9 +58,10 @@ members = [
"crates/wasi-libc-sys",
]
exclude = [
"ci/benchmarks/bench-runner",
# Examples sometimes have Rust hosts in their platforms. The compiler should ignore those.
"crates/cli_testing_examples",
"examples",
"ci/bench-runner",
# Ignore building these normally. They are only imported by tests.
# The tests will still correctly build them.
"crates/cli_utils",

126
FAQ.md
View file

@ -1,3 +1,5 @@
Click the ☰ button in the top left to see and search the table of contents.
# Frequently Asked Questions
## Where did the name Roc come from?
@ -45,6 +47,16 @@ This is an unusual approach, but there are more details in [this 2021 interview]
In the meantime, using CoffeeScript syntax highlighting for .roc files turns out to work surprisingly well!
## Why won't the editor be able to edit non-roc files like .md, .gitignore, .yml, ... ?
The downside of having the Roc editor support files other than .roc is that it seems extremely difficult to avoid scope creep if we allow it. For example, it starts with just editing json as plaintext but then it's annoying that there's no syntax highlighting, so maybe we add the capability to do syntax highlighting for json but of course then some people want it for toml, .md, etc, so we need to add a way to specify custom syntax highlighting rules for all of those.
Then of course people don't want to be copy/pasting syntax highlighting rules from online, so maybe someone develops a third party "plugin manager" for the editor to distribute these syntax highlighting definitions.
So maybe we add sharing syntax highlighting as a first-class thing, so people don't have to download a separate tool to use their editor normally but then some people who are using it for .json and .yaml start using it for .css too. Syntax highlighting is okay but it's annoying that they don't get error reporting when they mess up syntax or type an invalid selector or import and pretty soon there's demand for the Roc editor to do all the hardest parts of VS code.
We have to draw the line somewhere in there...but where to draw it?
It seems like drawing a bright line at .roc files is the most straightforward. It means the roc editor is the absolute best at editing .roc files and it isn't a weak editor for anything else because it doesn't try to be an editor for anything else and it means the scope is very clear.
## Why is there no way to specify "import everything this module exposes" in `imports`?
In [Elm](https://elm-lang.org), it's possible to import a module in a way that brings everything that module
@ -95,20 +107,20 @@ the function might give different answers.
Both of these would make revising code riskier across the entire language, which is very undesirable.
Another option would be to define that function equality always returns `False`. So both of these would evaluate
to `False`:
Another option would be to define that function equality always returns `false`. So both of these would evaluate
to `false`:
- `(\x -> x + 1) == (\x -> 1 + x)`
- `(\x -> x + 1) == (\x -> x + 1)`
This makes function equality effectively useless, while still technically allowing it. It has some other downsides:
- Now if you put a function inside a record, using `==` on that record will still type-check, but it will then return `False`. This could lead to bugs if you didn't realize you had accidentally put a function in there - for example, because you were actually storing a different type (e.g. an opaque type) and didn't realize it had a function inside it.
- Now if you put a function inside a record, using `==` on that record will still type-check, but it will then return `false`. This could lead to bugs if you didn't realize you had accidentally put a function in there - for example, because you were actually storing a different type (e.g. an opaque type) and didn't realize it had a function inside it.
- If you put a function (or a value containing a function) into a `Dict` or `Set`, you'll never be able to get it out again. This is a common problem with [NaN](https://en.wikipedia.org/wiki/NaN), which is also defined not to be equal to itself.
The first of these problems could be addressed by having function equality always return `True` instead of `False` (since that way it would not affect other fields' equality checks in a record), but that design has its own problems:
The first of these problems could be addressed by having function equality always return true instead of false (since that way it would not affect other fields' equality checks in a record), but that design has its own problems:
- Although function equality is still useless, `(\x -> x + 1) == (\x -> x)` returns `True`. Even if it didn't lead to bugs in practice, this would certainly be surprising and confusing to beginners.
- Although function equality is still useless, `(\x -> x + 1) == (\x -> x)` returns `Bool.true`. Even if it didn't lead to bugs in practice, this would certainly be surprising and confusing to beginners.
- Now if you put several different functions into a `Dict` or `Set`, only one of them will be kept; the others will be discarded or overwritten. This could cause bugs if a value stored a function internally, and then other functions relied on that internal function for correctness.
Each of these designs makes Roc a language that's some combination of more error-prone, more confusing, and more
@ -309,56 +321,83 @@ Here are some more details about the downsides as I see them.
### Currying and the `|>` operator
In Roc, this code produces `"Hello, World!"`
In Roc, both of these expressions evaluate to `"Hello, World!"`
```elm
"Hello, World"
|> Str.concat "!"
```elixir
Str.concat "Hello, " "World!"
```
This is because Roc's `|>` operator uses the expression before the `|>` as the _first_ argument to the function
after it. For functions where both arguments have the same type, but it's obvious which argument goes where (e.g.
`Str.concat "Hello, " "World!"`, `List.concat [1, 2] [3, 4]`), this works out well. Another example would
be `|> Num.sub 1`, which subtracts 1 from whatever came before the `|>`.
```elixir
"Hello, "
|> Str.concat "World!"
```
For this reason, "pipeline-friendliness" in Roc means that the first argument to each function is typically
the one that's most likely to be built up using a pipeline. For example, `List.map`:
In curried languages with a `|>` operator, the first expression still returns `"Hello, World!"` but the second one returns `"World!Hello, "`. This is because Roc's `|>` operator uses the expression before the `|>` as the _first_ argument, whereas in curried languages, `|>` uses it as the _last_ argument.
```elm
(For example, this is how `|>` works in both [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/#function-symbols-and-operators) and in [Elm](https://package.elm-lang.org/packages/elm/core/1.0.5/Basics#|%3E), both of which are curried languages. In contrast, Roc's `|>` design uses the same argument ordering as [Elixir](https://hexdocs.pm/elixir/1.14.0/Kernel.html#%7C%3E/2) and [Gleam](https://gleam.run/book/tour/functions.html#pipe-operator), neither of which is a curried language.)
This comes up in other situations as well. For example, consider subtraction and division:
```elixir
someNumber
|> Num.div 2
```
```elixir
someNumber
|> Num.sub 1
```
What do you expect these expressions to do?
In Roc, the first divides `someNumber` by 2 and the second one subtracts 1 from `someNumber`. In languages where `|>` uses the other argument ordering, the first example instead takes 2 and divides it by `someNumber`, while the second takes 1 and subtracts `someNumber` from it. This was a pain point I ran into with curried languages, and I was pleasantly surprised that changing the argument ordering in `|>` addressed the pain point.
This style has a second benefit when it comes to higher-order functions. Consider these two examples:
```elixir
answer = List.map numbers \num ->
someFunction
"some argument"
anotherArg
someOtherArg
```
```elixir
numbers
|> List.map Num.abs
|> List.map Num.abs
```
This argument ordering convention also often makes it possible to pass anonymous functions to higher-order
functions without needing parentheses, like so:
In Roc, `List.map` takes a list and then a function. Because of the way `|>` works in Roc, `numbers |> List.map Num.abs` passes `numbers` as the first argument to `List.map`, and `Num.abs` as the second argument. So both of these examples work fine.
```elm
List.map numbers \num -> Num.abs (num - 1)
In a curried language, these two examples couldn't both be valid. In order for `|> List.map Num.abs` to work in a curried language (where `|>` works the other way), `List.map` would have to take its arguments in the opposite order: the function first and the list second.
This means the first example would have to change from this...
```elixir
answer = List.map numbers \num ->
someFunction
"some argument"
anotherArg
someOtherArg
```
(If the arguments were reversed, this would be `List.map (\num -> Num.abs (num - 1)) numbers` and the
extra parentheses would be required.)
...to this:
Neither of these benefits is compatible with the argument ordering currying encourages. Currying encourages
`List.map` to take the `List` as its second argument instead of the first, so that you can partially apply it
like `(List.map Num.abs)`; if Roc introduced currying but kept the order of `List.map` the same way it is today,
then partially applying `List.map` (e.g. `(List.map numbers)`) would be much less useful than if the arguments
were swapped - but that in turn would make it less useful with `|>` and would require parentheses when passing
it an anonymous function.
```elixir
answer =
List.map
(\num ->
someFunction
"some argument"
anotherArg
someOtherArg
)
numbers
```
This is a fundamental design tension. One argument order works well with `|>` (at least the way it works in Roc
today) and with passing anonymous functions to higher-order functions, and the other works well with currying.
It's impossible to have both.
This was also a pain point I'd encountered in curried languages. I prefer the way the former example reads, but that style doesn't work with the argument order that currying encourages for higher-order functions like `List.map`. (Prior to using curried languages, I'd used [CoffeeScript](https://coffeescript.org/) in a functional style with [`_.map`](https://underscorejs.org/#map), and was disappointed to realize that I could no longer use the enjoyable style of `answer = _.map numbers (num) -> …` as I had before. In Roc, this style works.)
Of note, one possible design is to have currying while also having `|>` pass the _last_ argument instead of the first.
This is what Elm does, and it makes pipeline-friendliness and curry-friendliness the same thing. However, it also
means that either `|> Str.concat "!"` would add the `"!"` to the front of the string, or else `Str.concat`'s
arguments would have to be flipped - meaning that `Str.concat "Hello, World" "!"` would evaluate to `"!Hello, World"`.
The only way to have `Str.concat` work the way it does in Roc today (where both pipelines and non-pipeline calling
do what you'd want them to) is to order function arguments in a way that is not conducive to currying. This design
tension only exists if there's currying in the language; without it, you can order arguments for pipeline-friendliness
without concern.
As a historical note, these stylistic benefits (of `|> Num.sub 1` working as expected, and being able to write `List.map numbers \num ->`) were not among the original reasons Roc did not have currying. These benefits were discovered after the decision had already been made that Roc would not be a curried language, and they served to reinforce after the fact that the decision was the right one for Roc given the language's goals.
### Currying and learning curve
@ -406,9 +445,10 @@ reverseSort = \list -> List.reverse (List.sort list)
I've consistently found that I can more quickly and accurately understand function definitions that use
named arguments, even though the code is longer. I suspect this is because I'm faster at reading than I am at
desugaring, and whenever I read the top version I end up needing to mentally desugar it into the bottom version.
eta-expanding ( e.g. converting `List.sort` into `\l -> List.sort l` ). Whenever I read
the top version I end up needing to mentally eta-expand it into the bottom version.
In more complex examples (this is among the tamest pointfree function composition examples I've seen), I make
a mistake in my mental desugaring, and misunderstand what the function is doing - which can cause bugs.
a mistake in my mental eta-expansion, and misunderstand what the function is doing - which can cause bugs.
I assumed I would get faster and more accurate at this over time. However, by now it's been about a decade
since I first learned about the technique, and I'm still slower and less accurate at reading code that uses

View file

@ -4,6 +4,7 @@ Roc is not ready for a 0.1 release yet, but we do have:
- [**installation** guide](https://github.com/roc-lang/roc/tree/main/getting_started)
- [**tutorial**](https://github.com/roc-lang/roc/blob/main/TUTORIAL.md)
- [**docs** for the standard library](https://www.roc-lang.org/builtins/Str)
- [frequently asked questions](https://github.com/roc-lang/roc/blob/main/FAQ.md)
- [Zulip chat](https://roc.zulipchat.com) for help, questions and discussions

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bench-runner"
version = "0.1.0"
version = "0.0.1"
dependencies = [
"clap",
"data-encoding",
@ -240,9 +240,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.5.4"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [
"aho-corasick",
"memchr",

View file

@ -1,13 +1,13 @@
[package]
name = "bench-runner"
version = "0.1.0"
version = "0.0.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "3.1.15", features = ["derive"] }
regex = "1.5.4"
regex = "1.5.5"
is_executable = "1.0.1"
ring = "0.16.20"
data-encoding = "2.3.2"

View file

@ -33,7 +33,7 @@ fn main() {
if check_if_bench_executables_changed() {
println!(
"Comparison of sha256 of executables reveals changes, doing full benchmarks..."
"\n\nComparison of sha256 of executables reveals changes, doing full benchmarks...\n\n"
);
let all_regressed_benches = do_all_benches(optional_args.nr_repeat_benchmarks);
@ -51,8 +51,8 @@ fn main() {
eprintln!(
r#"I can't find bench-folder-main and bench-folder-branch from the current directory.
I should be executed from the repo root.
Use `./ci/safe-earthly.sh --build-arg BENCH_SUFFIX=main +prep-bench-folder` to generate bench-folder-main.
Use `./ci/safe-earthly.sh +prep-bench-folder` to generate bench-folder-branch."#
Use `./ci/benchmarks/prep_folder.sh main` to generate bench-folder-main.
Use `./ci/benchmarks/prep_folder.sh branch` to generate bench-folder-branch."#
);
process::exit(1)
@ -85,6 +85,8 @@ fn do_all_benches(nr_repeat_benchmarks: usize) -> HashSet<String> {
return HashSet::new();
}
println!("\n\nDoing benchmarks {:?} times to reduce flukes.\n\n", nr_repeat_benchmarks);
for _ in 1..nr_repeat_benchmarks {
delete_old_bench_results();
do_benchmark("main");
@ -112,7 +114,7 @@ fn do_benchmark(branch_name: &'static str) -> HashSet<String> {
))
.args(&["--bench", "--noplot"])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
.unwrap_or_else(|_| panic!("Failed to benchmark {}.", branch_name));
@ -133,14 +135,14 @@ fn do_benchmark(branch_name: &'static str) -> HashSet<String> {
"Failed to get line that contains benchmark name from last_three_lines_queue.",
);
let regex_match = bench_name_regex.find(regressed_bench_name_line).expect("This line should hoave the benchmark name between double quotes but I could not match it");
let regex_match = bench_name_regex.find(regressed_bench_name_line).expect("This line should have the benchmark name between double quotes but I could not match it");
regressed_benches.insert(regex_match.as_str().to_string().replace("\"", ""));
}
last_three_lines_queue.push_front(line_str.clone());
println!("bench {:?}: {:?}", branch_name, line_str);
println!(">>bench {:?}: {:?}", branch_name, line_str);
}
regressed_benches
@ -186,8 +188,20 @@ fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest, io::Error> {
}
fn sha_file(file_path: &Path) -> Result<String, io::Error> {
let input = File::open(file_path)?;
let reader = BufReader::new(input);
// Debug info is dependent on the dir in which executable was created,
// so we need to strip that to be able to compare binaries.
let no_debug_info_file_path = file_path.to_str().unwrap().to_string() + ("_no_debug_info");
std::fs::copy(file_path, &no_debug_info_file_path)?;
let strip_output = Command::new("strip")
.args(["--strip-debug", &no_debug_info_file_path])
.output()
.expect("failed to execute process");
assert!(strip_output.status.success());
let no_debug_info_file = File::open(no_debug_info_file_path)?;
let reader = BufReader::new(no_debug_info_file);
let digest = sha256_digest(reader)?;
Ok(HEXUPPER.encode(digest.as_ref()))
@ -227,9 +241,10 @@ fn calc_hashes_for_folder(benches_path_str: &str) -> HashMap<String, String> {
}
fn check_if_bench_executables_changed() -> bool {
let bench_folder_str = "/examples/benchmarks/";
let bench_folder_str = "/crates/cli_testing_examples/benchmarks/";
let main_benches_path_str = [BENCH_FOLDER_MAIN, bench_folder_str].join("");
let main_bench_hashes = calc_hashes_for_folder(&main_benches_path_str);
let branch_benches_path_str = [BENCH_FOLDER_BRANCH, bench_folder_str].join("");

26
ci/benchmarks/prep_folder.sh Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run.
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
# to make use of avx, avx2, sse2, sse4.2... instructions
RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native"
BENCH_SUFFIX=$1
cargo criterion -V
cd crates/cli && cargo criterion --no-run && cd ../..
mkdir -p bench-folder/crates/cli_testing_examples/benchmarks
mkdir -p bench-folder/crates/compiler/builtins/bitcode/src
mkdir -p bench-folder/target/release/deps
mkdir -p bench-folder/target/release/lib
cp "crates/cli_testing_examples/benchmarks/"*".roc" bench-folder/crates/cli_testing_examples/benchmarks/
cp -r crates/cli_testing_examples/benchmarks/platform bench-folder/crates/cli_testing_examples/benchmarks/
cp crates/compiler/builtins/bitcode/src/str.zig bench-folder/crates/compiler/builtins/bitcode/src
cp target/release/roc bench-folder/target/release
cp -r target/release/lib bench-folder/target/release
# copy the most recent time bench to bench-folder
cp target/release/deps/`ls -t target/release/deps/ | grep time_bench | head -n 1` bench-folder/target/release/deps/time_bench
mv bench-folder bench-folder-$BENCH_SUFFIX

View file

@ -1,5 +1,8 @@
#!/usr/bin/env bash
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
mkdir -p $HOME/.cargo
echo -e "[build]\nrustflags = [\"-C\", \"link-arg=-fuse-ld=lld\", \"-C\", \"target-cpu=native\"]" > $HOME/.cargo/config

View file

@ -2,6 +2,9 @@
# assumes roc_releases.json is present
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
LATEST_RELEASE_URL=`cat roc_releases.json | jq --arg arch $1 --arg today $(date +'%Y-%m-%d') '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("\($arch)-\($today)"))) | .[0]'`
if [[ "$LATEST_RELEASE_URL" == "null" ]]

View file

@ -1,4 +1,8 @@
#!/usr/bin/env bash
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
cp target/release/roc ./roc # to be able to exclude "target" later in the tar command
cp -r target/release/lib ./lib
tar -czvf $1 --exclude="target" --exclude="zig-cache" roc lib LICENSE LEGAL_DETAILS examples/hello-world crates/roc_std
tar -czvf $1 --exclude="target" --exclude="zig-cache" roc lib LICENSE LEGAL_DETAILS examples/helloWorld.roc examples/platform-switching examples/cli crates/roc_std

View file

@ -1,23 +0,0 @@
#!/usr/bin/env bash
LOG_FILE="earthly_log.txt"
touch $LOG_FILE
# first arg + everything after
ARGS=${@:1}
FULL_CMD="earthly --config ci/earthly-conf.yml $ARGS"
echo $FULL_CMD
script -efq $LOG_FILE -c "$FULL_CMD"
EXIT_CODE=$?
if grep -q "failed to mount" "$LOG_FILE"; then
echo ""
echo ""
echo "------<<<<<<!!!!!!>>>>>>------"
echo "DETECTED FAILURE TO MOUNT ERROR: running without cache"
echo "------<<<<<<!!!!!!>>>>>>------"
echo ""
echo ""
earthly --config ci/earthly-conf.yml --no-cache $ARGS
else
exit $EXIT_CODE
fi

View file

@ -1,3 +1,7 @@
#!/usr/bin/env bash
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
# version.txt is used by the CLI: roc --version
printf 'nightly pre-release, built from commit ' > version.txt && git log --pretty=format:'%h' -n 1 >> version.txt && printf ' on ' >> version.txt && date -u >> version.txt

View file

@ -1,3 +1,6 @@
#!/usr/bin/env bash
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail
crates/repl_wasm/build-www.sh `pwd`/roc_repl_wasm.tar.gz

158
crates/README.md Normal file
View file

@ -0,0 +1,158 @@
# Roc Internals
Roc has different rust crates for various binaries and libraries. Their roles are briefly described below. If you'd like to learn more, have any questions, or suspect something is out of date, please start a discussion on the [Roc Zulip](https://roc.zulipchat.com/)!
You can use `cargo doc` to generate docs for a specific package; e.g.
```
cargo doc --package roc_ast --open
```
## `ast/` - `roc_ast`
Code to represent the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) as used by the editor. In contrast to the compiler, the types in this AST do not keep track of the location of the matching code in the source file.
## `cli/` - `roc_cli`
The `roc` binary that brings together all functionality in the Roc toolset.
## `cli_utils/` - `cli_utils`
Provides shared code for cli tests and benchmarks.
## `code_markup/` - `roc_code_markup`
A [markup language](https://en.wikipedia.org/wiki/Markup_language) to display Roc code in the editor.
## `compiler/`
Compiles `.roc` files and combines them with their platform into an executable binary. See [compiler/README.md](./compiler/README.md) for more information.
TODO explain what "compiler frontend" is
TODO explain what "compiler backend" is
The compiler includes the following sub-crates;
- `roc_alias_analysis` Performs analysis and optimizations to remove unneeded [reference counts](https://en.wikipedia.org/wiki/Reference_counting) at runtime, and supports in-place mutation.
- `arena_pool` An implementation of an [arena allocator](https://mgravell.github.io/Pipelines.Sockets.Unofficial/docs/arenas.html) designed for the compiler's workloads.
- `roc_build` Responsible for coordinating building and linking of a Roc app with its host.
- `roc_builtins` provides the Roc functions and modules that are implicitly imported into every module. See [README.md](./compiler/builtins/README.md) for more information.
- `roc_can` [Canonicalize](https://en.wikipedia.org/wiki/Canonicalization) a roc [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree), [resolving symbols](https://stackoverflow.com/a/1175493/4200103), [re-ordering definitions](https://www.oreilly.com/library/view/c-high-performance/9781787120952/546b5677-9157-4333-bc90-16db696436ac.xhtml), and preparing a module for [type inference](https://en.wikipedia.org/wiki/Type_inference).
- `roc_collections` Domain-specific collections created for the needs of the compiler.
- `roc_constrain` Responsible for building the set of constraints that are used during [type inference](https://en.wikipedia.org/wiki/Type_inference) of a program, and for gathering context needed for pleasant error messages when a type error occurs.
- `roc_debug_flags` Environment variables that can be toggled to aid debugging of the compiler itself.
- `roc_derive` provides auto-derivers for builtin abilities like `Hash` and `Decode`.
- `roc_exhaustive` provides [exhaustiveness](https://dev.to/babak/exhaustive-type-checking-with-typescript-4l3f) checking for Roc.
- `roc_fmt` The roc code formatter.
- `roc_gen_dev` provides the compiler backend to generate Roc binaries fast, for a nice developer experience. See [README.md](./compiler/gen_dev/README.md) for more information.
- `roc_gen_llvm` provides the LLVM backend to generate Roc binaries. Used to generate a binary with the fastest possible execution speed.
- `roc_gen_wasm` provides the WASM backend to generate Roc binaries. See [README.md](./compiler/gen_wasm/README.md) for more information.
- `roc_ident` Implements data structures used for efficiently representing small strings, like identifiers.
- `roc_intern` provides generic interners for concurrent and single-thread use cases.
- `roc_late_solve` provides type unification and solving primitives from the perspective of the compiler backend.
- `roc_load` Used to load a .roc file and coordinate the compiler pipeline, including parsing, type checking, and [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)).
- `roc_load_internal` The internal implementation of roc_load, separate from roc_load to support caching.
- `roc_module` Implements data structures used for efficiently representing unique modules and identifiers in Roc programs.
- `roc_mono` Roc's main intermediate representation (IR), which is responsible for [monomorphization](https://en.wikipedia.org/wiki/Monomorphization), defunctionalization, inserting [ref-count](https://en.wikipedia.org/wiki/Reference_counting) instructions, and transforming a Roc program into a form that is easy to consume by a backend.
- `roc_parse` Implements the Roc parser, which transforms a textual representation of a Roc program to an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
- `roc_problem` provides types to describe problems that can occur when compiling `.roc` code.
- `roc_region` Data structures for storing source-code-location information, used heavily for contextual error messages.
- `roc_target` provides types and helpers for compiler targets such as `default_x86_64`.
- `roc_serialize` provides helpers for serializing and deserializing to/from bytes.
- `roc_solve` The entry point of Roc's [type inference](https://en.wikipedia.org/wiki/Type_inference) system. Implements type inference and specialization of abilities.
- `roc_solve_problem` provides types to describe problems that can occur during solving.
- `roc_str` provides `Roc` styled collection [reference counting](https://en.wikipedia.org/wiki/Reference_counting). See [README.md](./compiler/str/README.md) for more information.
- `test_derive` Tests Roc's auto-derivers.
- `test_gen` contains all of Roc's [code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)) tests. See [README.md](./compiler/test_gen/README.md) for more information.
- `test_mono` Tests Roc's generation of the mono intermediate representation.
- `test_mono_macros` Macros for use in `test_mono`.
- `roc_types` Various representations and utilities for dealing with types in the Roc compiler.
- `roc_unify` Implements Roc's unification algorithm, the heartstone of Roc's [type inference](https://en.wikipedia.org/wiki/Type_inference).
## `docs/` - `roc_docs`
Generates html documentation from Roc files.
Used for [roc-lang.org/builtins/Num](https://www.roc-lang.org/builtins/Num).
## `docs_cli/` - `roc_docs_cli` library and `roc-docs` binary
Provides a binary that is only used for static build servers.
## `editor/` - `roc_editor`
Roc's editor. See [README.md](./editor/README.md) for more information.
## `error_macros/` - `roc_error_macros`
Provides macros for consistent reporting of errors in Roc's rust code.
## `glue/` - `roc_glue`
The `roc_glue` crate generates code needed for platform hosts to communicate with Roc apps. This tool is not necessary for writing a platform in another language, however, it's a great convenience! Currently supports Rust platforms, and the plan is to support any language via a plugin model.
## `highlight/` - `roc_highlight`
Provides syntax highlighting for the editor by transforming a string to markup nodes.
## `linker/` - `roc_linker`
Surgical linker that links platforms to Roc applications. We created our own linker for performance, since regular linkers add complexity that is not needed for linking Roc apps. Because we want `roc` to manage the build system and final linking of the executable, it is significantly less practical to use a regular linker. See [README.md](./linker/README.md) for more information.
## `repl_cli/` - `roc_repl_cli`
Command Line Interface(CLI) functionality for the Read-Evaluate-Print-Loop (REPL).
## `repl_eval/` - `roc_repl_eval`
Provides the functionality for the REPL to evaluate Roc expressions.
## `repl_expect/` - `roc_repl_expect`
Supports evaluating `expect` and printing contextual information when they fail.
## `repl_test/` - `repl_test`
Tests the roc REPL.
## `repl_wasm/` - `roc_repl_wasm`
Provides a build of the REPL for the Roc website using WebAssembly. See [README.md](./repl_wasm/README.md) for more information.
## `reporting/` - `roc_reporting`
Responsible for generating warning and error messages.
## `roc_std/` - `roc_std`
Provides Rust representations of Roc data structures.
## `test_utils/` - `roc_test_utils`
Provides testing utility functions for use throughout the Rust code base.
## `tracing/` - `roc_tracing`
Provides tracing utility functions for various executable entry points.
## `utils/` - `roc_utils`
Provides utility functions used all over the code base.
## `vendor/`
These are files that were originally obtained somewhere else (e.g. crates.io) but which we needed to fork for some Roc-specific reason. See [README.md](./vendor/README.md) for more information.
## `wasi-libc-sys/` - `wasi_libc_sys`
Provides a Rust wrapper for the WebAssembly test platform built on libc and is primarily used for testing purposes.
# Building a Roc Application
Below is a simplified diagram to illustrate how a Roc application and host are combined to build an executable file.
![Building a Roc Application using Rust](./building_a_roc_application.svg)
# Roc Compiler Stages
Below is a simplified diagram to illustrate the different stages of the Roc Compiler.
![Roc Compiler Stages](./roc_compiler_stages.svg)

View file

@ -1,6 +1,6 @@
[package]
name = "roc_ast"
version = "0.1.0"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
edition = "2021"
@ -22,11 +22,11 @@ roc_target = { path = "../compiler/roc_target" }
roc_error_macros = { path = "../error_macros" }
roc_reporting = { path = "../reporting" }
arrayvec = "0.7.2"
bumpalo = { version = "3.8.0", features = ["collections"] }
bumpalo = { version = "3.11.0", features = ["collections"] }
page_size = "0.4.2"
snafu = { version = "0.7.1", features = ["backtraces"] }
ven_graph = { path = "../vendor/pathfinding" }
libc = "0.2.132"
libc = "0.2.135"
[dev-dependencies]
indoc = "1.0.7"

View file

@ -3,7 +3,7 @@ use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarId, Variable};
use roc_types::types::{AliasKind, Problem, RecordField};
use roc_types::types::{AliasKind, RecordField};
use std::collections::HashMap;
#[derive(Debug, Clone)]
@ -44,7 +44,7 @@ pub enum SolvedType {
EmptyTagUnion,
/// A type from an Invalid module
#[allow(unused)]
Erroneous(Problem),
Erroneous,
Alias(
Symbol,

View file

@ -2199,7 +2199,7 @@ pub mod test_constrain {
Foo
"#
),
"[Foo]*",
"[Foo]",
)
}
@ -2235,7 +2235,7 @@ pub mod test_constrain {
if True then Green else Red
"#
),
"[Green, Red]*",
"[Green, Red]",
)
}
@ -2249,7 +2249,7 @@ pub mod test_constrain {
Red -> Purple
"#
),
"[Blue, Purple]*",
"[Blue, Purple]",
)
}
@ -2302,7 +2302,7 @@ pub mod test_constrain {
\a, b -> Pair a b
"#
),
"a, b -> [Pair a b]*",
"a, b -> [Pair a b]",
);
}
@ -2445,7 +2445,7 @@ pub mod test_constrain {
curryPair
"#
),
"a -> (b -> [Pair a b]*)",
"a -> (b -> [Pair a b])",
);
}
@ -2658,7 +2658,7 @@ pub mod test_constrain {
B -> Y
"#
),
"[A, B] -> [X, Y]*",
"[A, B] -> [X, Y]",
)
}
@ -2674,7 +2674,7 @@ pub mod test_constrain {
_ -> Z
"#
),
"[A, B]* -> [X, Y, Z]*",
"[A, B]* -> [X, Y, Z]",
)
}
@ -2689,7 +2689,7 @@ pub mod test_constrain {
A N -> Y
"#
),
"[A [M, N]] -> [X, Y]*",
"[A [M, N]] -> [X, Y]",
)
}
@ -2705,7 +2705,7 @@ pub mod test_constrain {
A _ -> Z
"#
),
"[A [M, N]] -> [X, Y, Z]*",
"[A [M, N]] -> [X, Y, Z]",
)
}
@ -2737,7 +2737,7 @@ pub mod test_constrain {
A N -> X
"#
),
"[A [M, N], B] -> [X]*",
"[A [M, N], B] -> [X]",
)
}

View file

@ -323,7 +323,7 @@ fn from_pending_alias<'a>(
let symbol = name.value;
match to_annotation2(env, scope, &ann.value, ann.region) {
Annotation2::Erroneous(_) => todo!(),
Annotation2::Erroneous => todo!(),
Annotation2::Annotation {
named_rigids,
unnamed_rigids,
@ -419,7 +419,7 @@ fn canonicalize_pending_def<'a>(
// but the rigids can show up in type error messages, so still register them
match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) {
Annotation2::Erroneous(_) => todo!(),
Annotation2::Erroneous => todo!(),
Annotation2::Annotation {
named_rigids,
unnamed_rigids,
@ -468,7 +468,7 @@ fn canonicalize_pending_def<'a>(
TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
match to_annotation2(env, scope, &loc_ann.value, loc_ann.region) {
Annotation2::Erroneous(_) => todo!(),
Annotation2::Erroneous => todo!(),
Annotation2::Annotation {
named_rigids,
unnamed_rigids,

View file

@ -352,7 +352,7 @@ pub fn expr_to_expr2<'a>(
for (node_id, branch) in can_branches.iter_node_ids().zip(branches.iter()) {
let (can_when_branch, branch_references) =
canonicalize_when_branch(env, scope, *branch, &mut output);
canonicalize_when_branch(env, scope, branch, &mut output);
output.references.union_mut(branch_references);

View file

@ -447,6 +447,9 @@ pub fn to_pattern2<'a>(
unreachable!("should have been handled in RecordDestructure");
}
List(..) => todo!(),
ListRest => todo!(),
Malformed(_str) => {
let problem = MalformedPatternProblem::Unknown;
malformed_pattern(env, problem, region)

View file

@ -7,7 +7,7 @@ use roc_error_macros::todo_abilities;
use roc_module::ident::{Ident, Lowercase, TagName, Uppercase};
use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
use roc_types::types::{AliasKind, Problem, RecordField};
use roc_types::types::{AliasKind, RecordField};
use roc_types::{subs::Variable, types::ErrorType};
use crate::lang::env::Env;
@ -185,7 +185,7 @@ pub enum Annotation2 {
symbols: MutSet<Symbol>,
signature: Signature,
},
Erroneous(roc_types::types::Problem),
Erroneous,
}
pub fn to_annotation2<'a>(
@ -346,8 +346,8 @@ pub fn to_type2<'a>(
references.symbols.insert(symbol);
Type2::Alias(symbol, args, actual)
}
TypeApply::Erroneous(_problem) => {
// Type2::Erroneous(problem)
TypeApply::Erroneous => {
// Type2::Erroneous
todo!()
}
}
@ -414,24 +414,7 @@ pub fn to_type2<'a>(
for (node_id, (label, field)) in field_types.iter_node_ids().zip(field_types_map) {
let poolstr = PoolStr::new(label.as_str(), env.pool);
let rec_field = match field {
RecordField::Optional(_) => {
let field_id = env.pool.add(field.into_inner());
RecordField::Optional(field_id)
}
RecordField::RigidOptional(_) => {
let field_id = env.pool.add(field.into_inner());
RecordField::RigidOptional(field_id)
}
RecordField::Demanded(_) => {
let field_id = env.pool.add(field.into_inner());
RecordField::Demanded(field_id)
}
RecordField::Required(_) => {
let field_id = env.pool.add(field.into_inner());
RecordField::Required(field_id)
}
};
let rec_field = field.map_owned(|field| env.pool.add(field));
env.pool[node_id] = (poolstr, rec_field);
}
@ -738,7 +721,7 @@ fn can_tags<'a>(
enum TypeApply {
Apply(Symbol, PoolVec<Type2>),
Alias(Symbol, PoolVec<TypeId>, TypeId),
Erroneous(roc_types::types::Problem),
Erroneous,
}
#[inline(always)]
@ -761,7 +744,7 @@ fn to_type_apply<'a>(
Err(problem) => {
env.problem(roc_problem::can::Problem::RuntimeError(problem));
return TypeApply::Erroneous(Problem::UnrecognizedIdent(ident.into()));
return TypeApply::Erroneous;
}
}
} else {
@ -772,7 +755,7 @@ fn to_type_apply<'a>(
// it was imported but it doesn't expose this ident.
env.problem(roc_problem::can::Problem::RuntimeError(problem));
return TypeApply::Erroneous(Problem::UnrecognizedIdent((*ident).into()));
return TypeApply::Erroneous;
}
}
};
@ -792,14 +775,7 @@ fn to_type_apply<'a>(
let mut substitutions: MutMap<Variable, TypeId> = MutMap::default();
if alias.targs.len() != args.len() {
let error = TypeApply::Erroneous(Problem::BadTypeArguments {
symbol,
region,
alias_needs: alias.targs.len() as u8,
type_got: args.len() as u8,
alias_kind: AliasKind::Structural,
});
return error;
return TypeApply::Erroneous;
}
let arguments = PoolVec::with_capacity(type_arguments.len() as u32, env.pool);

View file

@ -169,7 +169,7 @@ impl Scope {
aliases.insert(symbol, alias);
}
let idents = Symbol::default_in_scope();
let idents = Symbol::apply_types_in_scope();
let idents: MutMap<_, _> = idents.into_iter().collect();
Scope {

View file

@ -1,3 +1,8 @@
//! Library for the Roc AST
//!
//! Code to represent the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)
//! as used by the editor. In contrast to the compiler, the types in this AST do
//! not keep track of the location of the matching code in the source file.
pub mod ast_error;
mod builtin_aliases;
mod canonicalization;

View file

@ -4,7 +4,7 @@ use crate::lang::core::{expr::expr2::ExprId, header::AppHeader};
pub fn parse_from_string(_header_str: &str, ast_node_id: ExprId) -> AppHeader {
AppHeader {
app_name: "\"untitled-app\"".to_owned(),
packages_base: "\"platform/main.roc\"".to_owned(),
packages_base: "\"rust-platform/main.roc\"".to_owned(),
imports: vec![],
provides: vec!["main".to_owned()],
ast_node_id,

View file

@ -13,7 +13,7 @@ use roc_types::subs::{
Subs, SubsSlice, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
};
use roc_types::types::{
gather_fields_unsorted_iter, Alias, AliasKind, Category, ErrorType, PatternCategory,
gather_fields_unsorted_iter, Alias, AliasKind, Category, ErrorType, PatternCategory, Polarity,
RecordField,
};
use roc_unify::unify::unify;
@ -82,7 +82,6 @@ pub enum TypeError {
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
CircularType(Region, Symbol, ErrorType),
BadType(roc_types::types::Problem),
UnexposedLookup(Symbol),
}
@ -228,7 +227,13 @@ fn solve<'a>(
expectation.get_type_ref(),
);
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
match unify(
&mut UEnv::new(subs),
actual,
expected,
Mode::EQ,
Polarity::OF_VALUE,
) {
Success {
vars,
must_implement_ability: _,
@ -252,13 +257,6 @@ fn solve<'a>(
problems.push(problem);
state
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
state
}
}
@ -327,7 +325,13 @@ fn solve<'a>(
expectation.get_type_ref(),
);
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
match unify(
&mut UEnv::new(subs),
actual,
expected,
Mode::EQ,
Polarity::OF_VALUE,
) {
Success {
vars,
must_implement_ability: _,
@ -352,13 +356,6 @@ fn solve<'a>(
problems.push(problem);
state
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
state
}
}
@ -404,7 +401,13 @@ fn solve<'a>(
);
// TODO(ayazhafiz): presence constraints for Expr2/Type2
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
match unify(
&mut UEnv::new(subs),
actual,
expected,
Mode::EQ,
Polarity::OF_PATTERN,
) {
Success {
vars,
must_implement_ability: _,
@ -428,13 +431,6 @@ fn solve<'a>(
problems.push(problem);
state
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
state
}
}
@ -718,7 +714,13 @@ fn solve<'a>(
);
let includes = type_to_var(arena, mempool, subs, rank, pools, cached_aliases, &tag_ty);
match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) {
match unify(
&mut UEnv::new(subs),
actual,
includes,
Mode::PRESENT,
Polarity::OF_PATTERN,
) {
Success {
vars,
must_implement_ability: _,
@ -743,13 +745,6 @@ fn solve<'a>(
problems.push(problem);
state
}
BadType(vars, problem) => {
introduce(subs, rank, pools, &vars);
problems.push(TypeError::BadType(problem));
state
}
}
@ -834,6 +829,15 @@ fn type_to_variable<'a>(
cached,
mempool.get(*type_id),
)),
RigidRequired(type_id) => RigidRequired(type_to_variable(
arena,
mempool,
subs,
rank,
pools,
cached,
mempool.get(*type_id),
)),
Optional(type_id) => Optional(type_to_variable(
arena,
mempool,
@ -925,7 +929,7 @@ fn type_to_variable<'a>(
arg_vars.push(arg_var);
}
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, []);
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, [], []);
let alias_var = type_to_variable(arena, mempool, subs, rank, pools, cached, alias_type);
@ -1182,7 +1186,7 @@ fn circular_error(
loc_var: &Loc<Variable>,
) {
let var = loc_var.value;
let (error_type, _) = subs.var_to_error_type(var);
let error_type = subs.var_to_error_type(var, Polarity::Pos);
let problem = TypeError::CircularType(loc_var.region, symbol, error_type);
subs.set_content(var, Content::Error);
@ -1422,8 +1426,6 @@ fn adjust_rank_content(
rank
}
Erroneous(_) => group_rank,
}
}
@ -1562,7 +1564,7 @@ fn instantiate_rigids_help(
}
}
EmptyRecord | EmptyTagUnion | Erroneous(_) => {}
EmptyRecord | EmptyTagUnion => {}
Record(fields, ext_var) => {
for index in fields.iter_variables() {
@ -1742,7 +1744,7 @@ fn deep_copy_var_help(
Func(arg_vars, new_closure_var, new_ret_var)
}
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
same @ EmptyRecord | same @ EmptyTagUnion => same,
Record(fields, ext_var) => {
let record_fields = {

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 120 KiB

View file

@ -1,11 +1,11 @@
[package]
name = "roc_cli"
version = "0.1.0"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
repository = "https://github.com/roc-lang/roc"
edition = "2021"
description = "A CLI for Roc"
description = "The Roc binary that brings together all functionality in the Roc toolset."
default-run = "roc"
[[bin]]
@ -24,7 +24,7 @@ editor = ["roc_editor"]
run-wasm32 = ["wasmer", "wasmer-wasi"]
# Compiling for a different platform than the host can cause linker errors.
# Compiling for a different target than the current machine can cause linker errors.
target-arm = ["roc_build/target-arm", "roc_repl_cli/target-arm"]
target-aarch64 = ["roc_build/target-aarch64", "roc_repl_cli/target-aarch64"]
target-x86 = ["roc_build/target-x86", "roc_repl_cli/target-x86"]
@ -60,11 +60,12 @@ roc_editor = { path = "../editor", optional = true }
roc_linker = { path = "../linker" }
roc_repl_cli = { path = "../repl_cli", optional = true }
roc_tracing = { path = "../tracing" }
clap = { version = "3.2.18", default-features = false, features = ["std", "color", "suggestions"] }
roc_intern = { path = "../compiler/intern" }
clap = { version = "3.2.20", default-features = false, features = ["std", "color", "suggestions"] }
const_format = { version = "0.2.23", features = ["const_generics"] }
bumpalo = { version = "3.8.0", features = ["collections"] }
bumpalo = { version = "3.11.0", features = ["collections"] }
mimalloc = { version = "0.1.26", default-features = false }
libc = "0.2.132"
libc = "0.2.135"
errno = "0.2.8"
ven_pretty = { path = "../vendor/pretty" }
@ -77,9 +78,6 @@ roc_gen_llvm = {path = "../compiler/gen_llvm"}
inkwell = {path = "../vendor/inkwell"}
signal-hook = "0.3.14"
[target.'cfg(windows)'.dependencies]
memexec = "0.2.0"
# for now, uses unix/libc functions that windows does not support
[target.'cfg(not(windows))'.dependencies]
roc_repl_expect = { path = "../repl_expect" }
@ -93,14 +91,15 @@ wasmer = { version = "2.2.1", optional = true, default-features = false, feature
[dev-dependencies]
wasmer-wasi = "2.2.1"
pretty_assertions = "1.0.0"
pretty_assertions = "1.3.0"
roc_test_utils = { path = "../test_utils" }
roc_utils = { path = "../utils" }
indoc = "1.0.7"
serial_test = "0.9.0"
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
cli_utils = { path = "../cli_utils" }
strum = "0.24.0"
strum_macros = "0.24"
once_cell = "1.15.0"
parking_lot = "0.12"
# Wasmer singlepass compiler only works on x86_64.
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]

View file

@ -1,15 +1,13 @@
use bumpalo::Bump;
use roc_build::{
link::{link, preprocess_host_wasm32, rebuild_host, LinkType, LinkingStrategy},
program::{self, Problems},
program::{self, CodeGenOptions, Problems},
};
use roc_builtins::bitcode;
use roc_collections::VecMap;
use roc_load::{
EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule,
EntryPoint, ExecutionMode, ExpectMetadata, LoadConfig, LoadMonomorphizedError, LoadedModule,
LoadingProblem, Threading,
};
use roc_module::symbol::{Interns, ModuleId};
use roc_mono::ir::OptLevel;
use roc_reporting::report::RenderTarget;
use roc_target::TargetInfo;
@ -30,12 +28,11 @@ fn report_timing(buf: &mut String, label: &str, duration: Duration) {
.unwrap()
}
pub struct BuiltFile {
pub struct BuiltFile<'a> {
pub binary_path: PathBuf,
pub problems: Problems,
pub total_time: Duration,
pub expectations: VecMap<ModuleId, Expectations>,
pub interns: Interns,
pub expect_metadata: ExpectMetadata<'a>,
}
pub enum BuildOrdering {
@ -60,16 +57,15 @@ pub fn build_file<'a>(
arena: &'a Bump,
target: &Triple,
app_module_path: PathBuf,
opt_level: OptLevel,
emit_debug_info: bool,
code_gen_options: CodeGenOptions,
emit_timings: bool,
link_type: LinkType,
linking_strategy: LinkingStrategy,
precompiled: bool,
prebuilt: bool,
threading: Threading,
wasm_dev_stack_bytes: Option<u32>,
order: BuildOrdering,
) -> Result<BuiltFile, BuildFileError<'a>> {
) -> Result<BuiltFile<'a>, BuildFileError<'a>> {
let compilation_start = Instant::now();
let target_info = TargetInfo::from(target);
@ -121,7 +117,7 @@ pub fn build_file<'a>(
match roc_target::OperatingSystem::from(target.operating_system) {
Wasi => {
if matches!(opt_level, OptLevel::Development) {
if matches!(code_gen_options.opt_level, OptLevel::Development) {
("wasm", "wasm", Some("wasm"))
} else {
("zig", "bc", Some("wasm"))
@ -151,7 +147,7 @@ pub fn build_file<'a>(
// TODO this should probably be moved before load_and_monomorphize.
// To do this we will need to preprocess files just for their exported symbols.
// Also, we should no longer need to do this once we have platforms on
// a package repository, as we can then get precompiled hosts from there.
// a package repository, as we can then get prebuilt platforms from there.
let exposed_values = loaded
.exposed_to_host
@ -180,9 +176,9 @@ pub fn build_file<'a>(
};
let rebuild_thread = spawn_rebuild_thread(
opt_level,
code_gen_options.opt_level,
linking_strategy,
precompiled,
prebuilt,
host_input_path.clone(),
preprocessed_host_path.clone(),
binary_path.clone(),
@ -191,14 +187,6 @@ pub fn build_file<'a>(
exposed_closure_types,
);
// TODO try to move as much of this linking as possible to the precompiled
// host, to minimize the amount of host-application linking required.
let app_o_file = Builder::new()
.prefix("roc_app")
.suffix(&format!(".{}", app_extension))
.tempfile()
.map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?;
let app_o_file = app_o_file.path();
let buf = &mut String::with_capacity(1024);
let mut it = loaded.timings.iter().peekable();
@ -249,21 +237,20 @@ pub fn build_file<'a>(
// inside a nested scope without causing a borrow error!
let mut loaded = loaded;
let problems = program::report_problems_monomorphized(&mut loaded);
let expectations = std::mem::take(&mut loaded.expectations);
let loaded = loaded;
let interns = loaded.interns.clone();
enum HostRebuildTiming {
BeforeApp(u128),
ConcurrentWithApp(JoinHandle<u128>),
}
let rebuild_timing = if linking_strategy == LinkingStrategy::Additive {
let rebuild_duration = rebuild_thread.join().unwrap();
if emit_timings && !precompiled {
let rebuild_duration = rebuild_thread
.join()
.expect("Failed to (re)build platform.");
if emit_timings && !prebuilt {
println!(
"Finished rebuilding and preprocessing the host in {} ms\n",
"Finished rebuilding the platform in {} ms\n",
rebuild_duration
);
}
@ -272,14 +259,12 @@ pub fn build_file<'a>(
HostRebuildTiming::ConcurrentWithApp(rebuild_thread)
};
let code_gen_timing = program::gen_from_mono_module(
let (roc_app_bytes, code_gen_timing, expect_metadata) = program::gen_from_mono_module(
arena,
loaded,
&app_module_path,
target,
app_o_file,
opt_level,
emit_debug_info,
code_gen_options,
&preprocessed_host_path,
wasm_dev_stack_bytes,
);
@ -294,18 +279,10 @@ pub fn build_file<'a>(
"Generate Assembly from Mono IR",
code_gen_timing.code_gen,
);
report_timing(buf, "Emit .o file", code_gen_timing.emit_o_file);
let compilation_end = compilation_start.elapsed();
let size = std::fs::metadata(&app_o_file)
.unwrap_or_else(|err| {
panic!(
"Could not open {:?} - which was supposed to have been generated. Error: {:?}",
app_o_file, err
);
})
.len();
let size = roc_app_bytes.len();
if emit_timings {
println!(
@ -321,29 +298,44 @@ pub fn build_file<'a>(
}
if let HostRebuildTiming::ConcurrentWithApp(thread) = rebuild_timing {
let rebuild_duration = thread.join().unwrap();
if emit_timings && !precompiled {
let rebuild_duration = thread.join().expect("Failed to (re)build platform.");
if emit_timings && !prebuilt {
println!(
"Finished rebuilding and preprocessing the host in {} ms\n",
"Finished rebuilding the platform in {} ms\n",
rebuild_duration
);
}
}
// Step 2: link the precompiled host and compiled app
// Step 2: link the prebuilt platform and compiled app
let link_start = Instant::now();
let problems = match (linking_strategy, link_type) {
(LinkingStrategy::Surgical, _) => {
roc_linker::link_preprocessed_host(target, &host_input_path, app_o_file, &binary_path);
roc_linker::link_preprocessed_host(
target,
&host_input_path,
&roc_app_bytes,
&binary_path,
);
problems
}
(LinkingStrategy::Additive, _) | (LinkingStrategy::Legacy, LinkType::None) => {
// Just copy the object file to the output folder.
binary_path.set_extension(app_extension);
std::fs::copy(app_o_file, &binary_path).unwrap();
std::fs::write(&binary_path, &*roc_app_bytes).unwrap();
problems
}
(LinkingStrategy::Legacy, _) => {
let app_o_file = Builder::new()
.prefix("roc_app")
.suffix(&format!(".{}", app_extension))
.tempfile()
.map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?;
let app_o_file = app_o_file.path();
std::fs::write(app_o_file, &*roc_app_bytes).unwrap();
let mut inputs = vec![
host_input_path.as_path().to_str().unwrap(),
app_o_file.to_str().unwrap(),
@ -351,7 +343,7 @@ pub fn build_file<'a>(
let str_host_obj_path = bitcode::get_builtins_host_obj_path();
if matches!(opt_level, OptLevel::Development) {
if matches!(code_gen_options.backend, program::CodeGenBackend::Assembly) {
inputs.push(&str_host_obj_path);
}
@ -388,8 +380,7 @@ pub fn build_file<'a>(
binary_path,
problems,
total_time,
interns,
expectations,
expect_metadata,
})
}
@ -397,7 +388,7 @@ pub fn build_file<'a>(
fn spawn_rebuild_thread(
opt_level: OptLevel,
linking_strategy: LinkingStrategy,
precompiled: bool,
prebuilt: bool,
host_input_path: PathBuf,
preprocessed_host_path: PathBuf,
binary_path: PathBuf,
@ -407,13 +398,16 @@ fn spawn_rebuild_thread(
) -> std::thread::JoinHandle<u128> {
let thread_local_target = target.clone();
std::thread::spawn(move || {
if !precompiled {
println!("🔨 Rebuilding host...");
if !prebuilt {
// Printing to stderr because we want stdout to contain only the output of the roc program.
// We are aware of the trade-offs.
// `cargo run` follows the same approach
eprintln!("🔨 Rebuilding platform...");
}
let rebuild_host_start = Instant::now();
if !precompiled {
if !prebuilt {
match linking_strategy {
LinkingStrategy::Additive => {
let host_dest = rebuild_host(

View file

@ -100,11 +100,11 @@ pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), Str
let mut before_file = file.clone();
before_file.set_extension("roc-format-failed-ast-before");
std::fs::write(&before_file, &format!("{:#?}\n", ast)).unwrap();
std::fs::write(&before_file, &format!("{:#?}\n", ast_normalized)).unwrap();
let mut after_file = file.clone();
after_file.set_extension("roc-format-failed-ast-after");
std::fs::write(&after_file, &format!("{:#?}\n", reparsed_ast)).unwrap();
std::fs::write(&after_file, &format!("{:#?}\n", reparsed_ast_normalized)).unwrap();
internal_error!(
"Formatting bug; formatting didn't reparse as the same tree\n\n\
@ -157,7 +157,9 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result<Ast<'a>, SyntaxError<'
let (module, state) = module::parse_header(arena, State::new(src.as_bytes()))
.map_err(|e| SyntaxError::Header(e.problem))?;
let (_, defs, _) = module_defs().parse(arena, state).map_err(|(_, e, _)| e)?;
let (_, defs, _) = module_defs()
.parse(arena, state, 0)
.map_err(|(_, e, _)| e)?;
Ok(Ast { module, defs })
}

View file

@ -1,3 +1,5 @@
//! Provides the core CLI functionality for the Roc binary.
#[macro_use]
extern crate const_format;
@ -5,10 +7,9 @@ use build::BuiltFile;
use bumpalo::Bump;
use clap::{Arg, ArgMatches, Command, ValueSource};
use roc_build::link::{LinkType, LinkingStrategy};
use roc_collections::VecMap;
use roc_build::program::{CodeGenBackend, CodeGenOptions, Problems};
use roc_error_macros::{internal_error, user_error};
use roc_load::{Expectations, LoadingProblem, Threading};
use roc_module::symbol::{Interns, ModuleId};
use roc_load::{ExpectMetadata, LoadingProblem, Threading};
use roc_mono::ir::OptLevel;
use std::env;
use std::ffi::{CString, OsStr};
@ -43,6 +44,7 @@ pub const CMD_VERSION: &str = "version";
pub const CMD_FORMAT: &str = "format";
pub const CMD_TEST: &str = "test";
pub const CMD_GLUE: &str = "glue";
pub const CMD_GEN_STUB_LIB: &str = "gen-stub-lib";
pub const FLAG_DEBUG: &str = "debug";
pub const FLAG_DEV: &str = "dev";
@ -54,7 +56,7 @@ pub const FLAG_NO_LINK: &str = "no-link";
pub const FLAG_TARGET: &str = "target";
pub const FLAG_TIME: &str = "time";
pub const FLAG_LINKER: &str = "linker";
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
pub const FLAG_PREBUILT: &str = "prebuilt-platform";
pub const FLAG_CHECK: &str = "check";
pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb";
pub const ROC_FILE: &str = "ROC_FILE";
@ -104,9 +106,9 @@ pub fn build_app<'a>() -> Command<'a> {
.possible_values(["surgical", "legacy"])
.required(false);
let flag_precompiled = Arg::new(FLAG_PRECOMPILED)
.long(FLAG_PRECOMPILED)
.help("Assume the host has been precompiled and skip recompiling the host\n(This is enabled by default when using `roc build` with a --target other than `--target host`.)")
let flag_prebuilt = Arg::new(FLAG_PREBUILT)
.long(FLAG_PREBUILT)
.help("Assume the platform has been prebuilt and skip rebuilding the platform\n(This is enabled by default when using `roc build` with a --target other than `--target <current machine>`.)")
.possible_values(["true", "false"])
.required(false);
@ -143,7 +145,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_debug.clone())
.arg(flag_time.clone())
.arg(flag_linker.clone())
.arg(flag_precompiled.clone())
.arg(flag_prebuilt.clone())
.arg(flag_wasm_stack_size_kb.clone())
.arg(
Arg::new(FLAG_TARGET)
@ -182,7 +184,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_debug.clone())
.arg(flag_time.clone())
.arg(flag_linker.clone())
.arg(flag_precompiled.clone())
.arg(flag_prebuilt.clone())
.arg(
Arg::new(ROC_FILE)
.help("The .roc file for the main module")
@ -204,7 +206,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_debug.clone())
.arg(flag_time.clone())
.arg(flag_linker.clone())
.arg(flag_precompiled.clone())
.arg(flag_prebuilt.clone())
.arg(roc_file_to_run.clone())
.arg(args_for_app.clone())
)
@ -217,7 +219,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_debug.clone())
.arg(flag_time.clone())
.arg(flag_linker.clone())
.arg(flag_precompiled.clone())
.arg(flag_prebuilt.clone())
.arg(roc_file_to_run.clone())
.arg(args_for_app.clone())
)
@ -275,6 +277,23 @@ pub fn build_app<'a>() -> Command<'a> {
.required(true)
)
)
.subcommand(Command::new(CMD_GEN_STUB_LIB)
.about("Generate a stubbed shared library that can be used for linking a platform binary.\nThe stubbed library has prototypes, but no function bodies.\n\nNote: This command will be removed in favor of just using `roc build` once all platforms support the surgical linker")
.arg(
Arg::new(ROC_FILE)
.help("The .roc file for an app using the platform")
.allow_invalid_utf8(true)
.required(true)
)
.arg(
Arg::new(FLAG_TARGET)
.long(FLAG_TARGET)
.help("Choose a different target")
.default_value(Target::default().as_str())
.possible_values(Target::OPTIONS)
.required(false),
)
)
.trailing_var_arg(true)
.arg(flag_optimize)
.arg(flag_max_threads.clone())
@ -283,7 +302,7 @@ pub fn build_app<'a>() -> Command<'a> {
.arg(flag_debug)
.arg(flag_time)
.arg(flag_linker)
.arg(flag_precompiled)
.arg(flag_prebuilt)
.arg(roc_file_to_run.required(false))
.arg(args_for_app);
@ -411,7 +430,7 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
let mut writer = std::io::stdout();
let (failed, passed) = roc_repl_expect::run::run_expects(
let (failed, passed) = roc_repl_expect::run::run_toplevel_expects(
&mut writer,
roc_reporting::report::RenderTarget::ColorTerminal,
arena,
@ -460,18 +479,33 @@ pub fn build(
use build::build_file;
use BuildConfig::*;
let arena = Bump::new();
let filename = matches.value_of_os(ROC_FILE).unwrap();
let opt_level = match (
matches.is_present(FLAG_OPTIMIZE),
matches.is_present(FLAG_OPT_SIZE),
matches.is_present(FLAG_DEV),
) {
(true, false, false) => OptLevel::Optimize,
(false, true, false) => OptLevel::Size,
(false, false, true) => OptLevel::Development,
(false, false, false) => OptLevel::Normal,
_ => user_error!("build can be only one of `--dev`, `--optimize`, or `--opt-size`"),
// the process will end after this function,
// so we don't want to spend time freeing these values
let arena = ManuallyDrop::new(Bump::new());
let code_gen_backend = if matches!(triple.architecture, Architecture::Wasm32) {
CodeGenBackend::Wasm
} else {
match matches.is_present(FLAG_DEV) {
true => CodeGenBackend::Assembly,
false => CodeGenBackend::Llvm,
}
};
let opt_level = if let BuildConfig::BuildAndRunIfNoErrors = config {
OptLevel::Development
} else {
match (
matches.is_present(FLAG_OPTIMIZE),
matches.is_present(FLAG_OPT_SIZE),
) {
(true, false) => OptLevel::Optimize,
(false, true) => OptLevel::Size,
(false, false) => OptLevel::Normal,
(true, true) => {
user_error!("build can be only one of `--optimize` and `--opt-size`")
}
}
};
let emit_debug_info = matches.is_present(FLAG_DEBUG);
let emit_timings = matches.is_present(FLAG_TIME);
@ -487,7 +521,7 @@ pub fn build(
};
let wasm_dev_backend = matches!(opt_level, OptLevel::Development)
&& matches!(triple.architecture, Architecture::Wasm32);
&& matches!(code_gen_backend, CodeGenBackend::Wasm);
let linking_strategy = if wasm_dev_backend {
LinkingStrategy::Additive
@ -499,14 +533,16 @@ pub fn build(
LinkingStrategy::Surgical
};
let precompiled = if matches.is_present(FLAG_PRECOMPILED) {
matches.value_of(FLAG_PRECOMPILED) == Some("true")
let prebuilt = if matches.is_present(FLAG_PREBUILT) {
matches.value_of(FLAG_PREBUILT) == Some("true")
} else {
// When compiling for a different target, default to assuming a precompiled host.
// Otherwise compilation would most likely fail because many toolchains assume you're compiling for the host
// When compiling for a different target, default to assuming a prebuilt platform.
// Otherwise compilation would most likely fail because many toolchains assume you're compiling for the current machine.
// We make an exception for Wasm, because cross-compiling is the norm in that case.
triple != Triple::host() && !matches!(triple.architecture, Architecture::Wasm32)
};
let filename = matches.value_of_os(ROC_FILE).unwrap();
let path = Path::new(filename);
// Spawn the root task
@ -538,16 +574,22 @@ pub fn build(
BuildAndRunIfNoErrors => BuildOrdering::BuildIfChecks,
_ => BuildOrdering::AlwaysBuild,
};
let code_gen_options = CodeGenOptions {
backend: code_gen_backend,
opt_level,
emit_debug_info,
};
let res_binary_path = build_file(
&arena,
&triple,
path.to_path_buf(),
opt_level,
emit_debug_info,
code_gen_options,
emit_timings,
link_type,
linking_strategy,
precompiled,
prebuilt,
threading,
wasm_dev_stack_bytes,
build_ordering,
@ -558,97 +600,43 @@ pub fn build(
binary_path,
problems,
total_time,
expectations,
interns,
expect_metadata,
}) => {
match config {
BuildOnly => {
// If possible, report the generated executable name relative to the current dir.
let generated_filename = binary_path
.strip_prefix(env::current_dir().unwrap())
.unwrap_or(&binary_path);
.unwrap_or(&binary_path)
.to_str()
.unwrap();
// No need to waste time freeing this memory,
// since the process is about to exit anyway.
std::mem::forget(arena);
// std::mem::forget(arena);
println!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms while successfully building:\n\n {}",
if problems.errors == 0 {
32 // green
} else {
33 // yellow
},
problems.errors,
if problems.errors == 1 {
"error"
} else {
"errors"
},
if problems.warnings == 0 {
32 // green
} else {
33 // yellow
},
problems.warnings,
if problems.warnings == 1 {
"warning"
} else {
"warnings"
},
total_time.as_millis(),
generated_filename.to_str().unwrap()
);
print_problems(problems, total_time);
println!(" while successfully building:\n\n {generated_filename}");
// Return a nonzero exit code if there were problems
Ok(problems.exit_code())
}
BuildAndRun => {
if problems.errors > 0 || problems.warnings > 0 {
print_problems(problems, total_time);
println!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
if problems.errors == 0 {
32 // green
} else {
33 // yellow
},
problems.errors,
if problems.errors == 1 {
"error"
} else {
"errors"
},
if problems.warnings == 0 {
32 // green
} else {
33 // yellow
},
problems.warnings,
if problems.warnings == 1 {
"warning"
} else {
"warnings"
},
total_time.as_millis(),
".\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
"".repeat(80)
);
}
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
let bytes = std::fs::read(&binary_path).unwrap();
// don't waste time deallocating; the process ends anyway
// ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
let x = roc_run(
arena,
opt_level,
triple,
args,
&bytes,
expectations,
interns,
);
std::mem::forget(bytes);
x
roc_run(&arena, opt_level, triple, args, bytes, expect_metadata)
}
BuildAndRunIfNoErrors => {
debug_assert!(
@ -656,25 +644,20 @@ pub fn build(
"if there are errors, they should have been returned as an error variant"
);
if problems.warnings > 0 {
print_problems(problems, total_time);
println!(
"\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
problems.warnings,
if problems.warnings == 1 {
"warning"
} else {
"warnings"
},
total_time.as_millis(),
".\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
"".repeat(80)
);
}
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
// don't waste time deallocating; the process ends anyway
// ManuallyDrop will leak the bytes because we don't drop manually
let bytes = &ManuallyDrop::new(std::fs::read(&binary_path).unwrap());
roc_run(arena, opt_level, triple, args, bytes, expectations, interns)
roc_run(&arena, opt_level, triple, args, bytes, expect_metadata)
}
}
}
@ -686,40 +669,17 @@ pub fn build(
let problems = roc_build::program::report_problems_typechecked(&mut module);
let mut output = format!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run",
if problems.errors == 0 {
32 // green
} else {
33 // yellow
},
problems.errors,
if problems.errors == 1 {
"error"
} else {
"errors"
},
if problems.warnings == 0 {
32 // green
} else {
33 // yellow
},
problems.warnings,
if problems.warnings == 1 {
"warning"
} else {
"warnings"
},
total_time.as_millis(),
);
print_problems(problems, total_time);
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
// If you're running "main.roc" then you can just do `roc run`
// to re-run the program.
if filename != DEFAULT_ROC_FILENAME {
output.push(' ');
output.push_str(&filename.to_string_lossy());
print!(" {}", &filename.to_string_lossy());
}
println!("{}\x1B[39m", output);
println!("\x1B[39m");
Ok(problems.exit_code())
}
@ -734,14 +694,41 @@ pub fn build(
}
}
fn print_problems(problems: Problems, total_time: std::time::Duration) {
const GREEN: usize = 32;
const YELLOW: usize = 33;
print!(
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms",
match problems.errors {
0 => GREEN,
_ => YELLOW,
},
problems.errors,
match problems.errors {
1 => "error",
_ => "errors",
},
match problems.warnings {
0 => GREEN,
_ => YELLOW,
},
problems.warnings,
match problems.warnings {
1 => "warning",
_ => "warnings",
},
total_time.as_millis(),
);
}
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
arena: &Bump,
opt_level: OptLevel,
triple: Triple,
args: I,
binary_bytes: &[u8],
expectations: VecMap<ModuleId, Expectations>,
interns: Interns,
expect_metadata: ExpectMetadata,
) -> io::Result<i32> {
match triple.architecture {
Architecture::Wasm32 => {
@ -752,10 +739,6 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
.strip_prefix(env::current_dir().unwrap())
.unwrap_or(path);
// No need to waste time freeing this memory,
// since the process is about to exit anyway.
std::mem::forget(arena);
#[cfg(target_family = "unix")]
{
use std::os::unix::ffi::OsStrExt;
@ -780,11 +763,21 @@ fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
Ok(0)
}
_ => roc_run_native(arena, opt_level, args, binary_bytes, expectations, interns),
_ => roc_run_native(arena, opt_level, args, binary_bytes, expect_metadata),
}
}
#[cfg(target_family = "unix")]
fn os_str_as_utf8_bytes(os_str: &OsStr) -> &[u8] {
use std::os::unix::ffi::OsStrExt;
os_str.as_bytes()
}
#[cfg(not(target_family = "unix"))]
fn os_str_as_utf8_bytes(os_str: &OsStr) -> &[u8] {
os_str.to_str().unwrap().as_bytes()
}
fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: &'a Bump,
executable: &ExecutableFile,
@ -794,10 +787,9 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
bumpalo::collections::Vec<'a, CString>,
) {
use bumpalo::collections::CollectIn;
use std::os::unix::ffi::OsStrExt;
let path = executable.as_path();
let path_cstring = CString::new(path.as_os_str().as_bytes()).unwrap();
let path_cstring = CString::new(os_str_as_utf8_bytes(path.as_os_str())).unwrap();
// argv is an array of pointers to strings passed to the new program
// as its command-line arguments. By convention, the first of these
@ -806,7 +798,7 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
// by a NULL pointer. (Thus, in the new program, argv[argc] will be NULL.)
let it = args
.into_iter()
.map(|x| CString::new(x.as_ref().as_bytes()).unwrap());
.map(|x| CString::new(os_str_as_utf8_bytes(x.as_ref())).unwrap());
let argv_cstrings: bumpalo::collections::Vec<CString> =
std::iter::once(path_cstring).chain(it).collect_in(arena);
@ -814,12 +806,17 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
// envp is an array of pointers to strings, conventionally of the
// form key=value, which are passed as the environment of the new
// program. The envp array must be terminated by a NULL pointer.
let mut buffer = Vec::with_capacity(100);
let envp_cstrings: bumpalo::collections::Vec<CString> = std::env::vars_os()
.flat_map(|(k, v)| {
[
CString::new(k.as_bytes()).unwrap(),
CString::new(v.as_bytes()).unwrap(),
]
.map(|(k, v)| {
buffer.clear();
use std::io::Write;
buffer.write_all(os_str_as_utf8_bytes(&k)).unwrap();
buffer.write_all(b"=").unwrap();
buffer.write_all(os_str_as_utf8_bytes(&v)).unwrap();
CString::new(buffer.as_slice()).unwrap()
})
.collect_in(arena);
@ -829,35 +826,32 @@ fn make_argv_envp<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
/// Run on the native OS (not on wasm)
#[cfg(target_family = "unix")]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: Bump,
arena: &Bump,
opt_level: OptLevel,
args: I,
binary_bytes: &[u8],
expectations: VecMap<ModuleId, Expectations>,
interns: Interns,
expect_metadata: ExpectMetadata,
) -> std::io::Result<i32> {
use bumpalo::collections::CollectIn;
unsafe {
let executable = roc_run_executable_file_path(binary_bytes)?;
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
let (argv_cstrings, envp_cstrings) = make_argv_envp(arena, &executable, args);
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
match opt_level {
OptLevel::Development => {
roc_run_native_debug(executable, &argv, &envp, expectations, interns)
}
OptLevel::Development => roc_dev_native(arena, executable, argv, envp, expect_metadata),
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
roc_run_native_fast(executable, &argv, &envp);
}
@ -921,12 +915,9 @@ impl ExecutableFile {
#[cfg(target_family = "windows")]
ExecutableFile::OnDisk(_, path) => {
let _ = argv;
let _ = envp;
use memexec::memexec_exe;
let bytes = std::fs::read(path).unwrap();
memexec_exe(&bytes).unwrap();
std::process::exit(0);
let path_cstring = CString::new(path.to_str().unwrap()).unwrap();
libc::execve(path_cstring.as_ptr().cast(), argv.as_ptr(), envp.as_ptr())
}
}
}
@ -934,14 +925,76 @@ impl ExecutableFile {
// with Expect
#[cfg(target_family = "unix")]
unsafe fn roc_run_native_debug(
_executable: ExecutableFile,
_argv: &[*const c_char],
_envp: &[*const c_char],
_expectations: VecMap<ModuleId, Expectations>,
_interns: Interns,
) {
todo!()
fn roc_dev_native(
arena: &Bump,
executable: ExecutableFile,
argv: bumpalo::collections::Vec<*const c_char>,
envp: bumpalo::collections::Vec<*const c_char>,
expect_metadata: ExpectMetadata,
) -> ! {
use roc_repl_expect::run::ExpectMemory;
use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals};
let ExpectMetadata {
mut expectations,
interns,
layout_interner,
} = expect_metadata;
let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap();
// let shm_name =
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
let memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
let layout_interner = layout_interner.into_global();
let mut writer = std::io::stdout();
match unsafe { libc::fork() } {
0 => unsafe {
// we are the child
executable.execve(&argv, &envp);
// Display a human-friendly error message
println!("Error {:?}", std::io::Error::last_os_error());
std::process::exit(1);
},
-1 => {
// something failed
// Display a human-friendly error message
println!("Error {:?}", std::io::Error::last_os_error());
std::process::exit(1)
}
1.. => {
for sig in &mut signals {
match sig {
SIGCHLD => break,
SIGUSR1 => {
// this is the signal we use for an expect failure. Let's see what the child told us
roc_repl_expect::run::render_expects_in_memory(
&mut writer,
arena,
&mut expectations,
&interns,
&layout_interner,
&memory,
)
.unwrap();
}
_ => println!("received signal {}", sig),
}
}
std::process::exit(0)
}
_ => unreachable!(),
}
}
#[cfg(target_os = "linux")]
@ -1020,12 +1073,11 @@ fn roc_run_executable_file_path(binary_bytes: &[u8]) -> std::io::Result<Executab
/// Run on the native OS (not on wasm)
#[cfg(not(target_family = "unix"))]
fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
arena: &Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
opt_level: OptLevel,
_args: I,
args: I,
binary_bytes: &[u8],
_expectations: VecMap<ModuleId, Expectations>,
_interns: Interns,
_expect_metadata: ExpectMetadata,
) -> io::Result<i32> {
use bumpalo::collections::CollectIn;
@ -1033,26 +1085,24 @@ fn roc_run_native<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
let executable = roc_run_executable_file_path(binary_bytes)?;
// TODO forward the arguments
// let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
let argv_cstrings = bumpalo::vec![ in &arena; CString::default()];
let envp_cstrings = bumpalo::vec![ in &arena; CString::default()];
let (argv_cstrings, envp_cstrings) = make_argv_envp(&arena, &executable, args);
let argv: bumpalo::collections::Vec<*const c_char> = argv_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
let envp: bumpalo::collections::Vec<*const c_char> = envp_cstrings
.iter()
.map(|s| s.as_ptr())
.chain([std::ptr::null()])
.collect_in(&arena);
.collect_in(arena);
match opt_level {
OptLevel::Development => {
// roc_run_native_debug(executable, &argv, &envp, expectations, interns)
todo!()
internal_error!("running `expect`s does not currently work on windows")
}
OptLevel::Normal | OptLevel::Size | OptLevel::Optimize => {
roc_run_native_fast(executable, &argv, &envp);
@ -1165,7 +1215,7 @@ impl Target {
Wasm32 => Triple {
architecture: Architecture::Wasm32,
vendor: Vendor::Unknown,
operating_system: OperatingSystem::Unknown,
operating_system: OperatingSystem::Wasi,
environment: Environment::Unknown,
binary_format: BinaryFormat::Wasm,
},

View file

@ -1,10 +1,11 @@
//! The `roc` binary that brings together all functionality in the Roc toolset.
use roc_build::link::LinkType;
use roc_cli::build::check_file;
use roc_cli::{
build_app, format, test, BuildConfig, FormatMode, Target, CMD_BUILD, CMD_CHECK, CMD_DEV,
CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST, CMD_VERSION,
DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, GLUE_FILE,
ROC_FILE,
CMD_DOCS, CMD_EDIT, CMD_FORMAT, CMD_GEN_STUB_LIB, CMD_GLUE, CMD_REPL, CMD_RUN, CMD_TEST,
CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_LIB, FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME,
GLUE_FILE, ROC_FILE,
};
use roc_docs::generate_docs_html;
use roc_error_macros::user_error;
@ -93,6 +94,12 @@ fn main() -> io::Result<()> {
Ok(1)
}
}
Some((CMD_GEN_STUB_LIB, matches)) => {
let input_path = Path::new(matches.value_of_os(ROC_FILE).unwrap());
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default();
roc_linker::generate_stub_lib(input_path, &target.to_triple())
}
Some((CMD_BUILD, matches)) => {
let target: Target = matches.value_of_t(FLAG_TARGET).unwrap_or_default();
@ -171,14 +178,7 @@ fn main() -> io::Result<()> {
}
}
}
Some((CMD_REPL, _)) => {
{
roc_repl_cli::main()?;
// Exit 0 if the repl exited normally
Ok(0)
}
}
Some((CMD_REPL, _)) => Ok(roc_repl_cli::main()),
Some((CMD_EDIT, matches)) => {
match matches
.values_of_os(DIRECTORY_OR_FILES)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
#[cfg(test)]
mod editor_launch_test {
use core::time;
use std::{
env,
process::{Command, Stdio},
thread,
};
use cli_utils::helpers::build_roc_bin_cached;
use roc_cli::CMD_EDIT;
use roc_utils::root_dir;
use std::io::Read;
// ignored because we don't want to bring up the editor window during regular tests, only on specific CI machines
#[ignore]
#[test]
fn launch() {
let root_dir = root_dir();
// The editor expects to be run from the root of the repo, so it can find the cli-platform to init a new project folder.
env::set_current_dir(&root_dir)
.unwrap_or_else(|_| panic!("Failed to set current dir to {:?}", root_dir));
let roc_binary_path = build_roc_bin_cached();
let mut roc_process = Command::new(roc_binary_path)
.arg(CMD_EDIT)
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start editor from cli.");
// wait for editor to show
thread::sleep(time::Duration::from_millis(2000));
// We extract 12 bytes from the logs for verification
let mut stdout_buffer = [0; 12];
let mut stdout = roc_process.stdout.take().unwrap();
stdout.read_exact(&mut stdout_buffer).unwrap();
match roc_process.try_wait() {
Ok(Some(status)) => panic!(
"The editor exited with status \"{status}\" but I expected it to still be running."
),
Ok(None) => {
// The editor is still running as desired, we check if logs are as expected:
assert_eq!("Loading file", std::str::from_utf8(&stdout_buffer).unwrap());
// Kill the editor, we don't want it to stay open forever.
roc_process.kill().unwrap();
}
Err(e) => panic!("Failed to wait launch editor cli command: {e}"),
}
}
}

View file

@ -65,32 +65,55 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}
const Unit = extern struct {};
pub export fn main() i32 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
// start time
var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts1) catch unreachable;
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
// end time
var ts2: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts2) catch unreachable;
const nanos = timer.read();
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
// stdout the result
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
callresult.deinit();
const delta = to_seconds(ts2) - to_seconds(ts1);
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}

View file

@ -64,32 +64,55 @@ export fn roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void {
std.process.exit(0);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}
const Unit = extern struct {};
pub export fn main() i32 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
// start time
var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts1) catch unreachable;
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult
var callresult = RocStr.empty();
roc__mainForHost_1_exposed_generic(&callresult);
// end time
var ts2: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts2) catch unreachable;
const nanos = timer.read();
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
// stdout the result
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
callresult.deinit();
const delta = to_seconds(ts2) - to_seconds(ts1);
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}

View file

@ -1,3 +1,3 @@
interface Foo
interface ExposedNotDefined
exposes [bar]
imports []

View file

@ -1,11 +1,13 @@
app "type-error"
packages { pf: "../../../../examples/interactive/cli-platform/main.roc" }
imports [pf.Stdout.{ line }, pf.Task.{ await }]
packages { pf: "../../../../examples/cli/cli-platform/main.roc" }
imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Program]
provides [main] to pf
main =
_ <- await (line "a")
_ <- await (line "b")
_ <- await (line "c")
_ <- await (line d)
_ <- await (line "d")
line "e"
# Type mismatch because this line is missing:
# |> Program.quick

View file

@ -0,0 +1,6 @@
*.dSYM
libhost.a
libapp.so
dynhost
preprocessedhost
metadata

View file

@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const testing = std.testing;
const expectEqual = testing.expectEqual;
const expect = testing.expect;
@ -13,7 +14,6 @@ comptime {
// -fcompiler-rt in link.rs instead of doing this. Note that this
// workaround is present in many host.zig files, so make sure to undo
// it everywhere!
const builtin = @import("builtin");
if (builtin.os.tag == .macos) {
_ = @import("compiler_rt");
}
@ -81,25 +81,48 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}
pub export fn main() u8 {
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
// start time
var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts1) catch unreachable;
var timer = std.time.Timer.start() catch unreachable;
const result = roc__mainForHost_1_exposed(10);
// end time
var ts2: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts2) catch unreachable;
const nanos = timer.read();
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
stdout.print("{d}\n", .{result}) catch unreachable;
const delta = to_seconds(ts2) - to_seconds(ts1);
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}

View file

@ -80,6 +80,34 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}
// warning! the array is currently stack-allocated so don't make this too big
const NUM_NUMS = 100;
@ -103,9 +131,7 @@ pub export fn main() u8 {
var roc_list = RocList{ .elements = numbers, .length = NUM_NUMS, .capacity = NUM_NUMS };
// start time
var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts1) catch unreachable;
var timer = std.time.Timer.start() catch unreachable;
// actually call roc to populate the callresult
const callresult: RocList = roc__mainForHost_1_exposed(roc_list);
@ -114,9 +140,8 @@ pub export fn main() u8 {
const length = std.math.min(20, callresult.length);
var result = callresult.elements[0..length];
// end time
var ts2: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts2) catch unreachable;
const nanos = timer.read();
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
for (result) |x, i| {
if (i == 0) {
@ -128,9 +153,8 @@ pub export fn main() u8 {
}
}
// TODO apparently the typestamps are still (partially) undefined?
// const delta = to_seconds(ts2) - to_seconds(ts1);
// stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
}

View file

@ -20,7 +20,7 @@ initialModel = \start -> {
cameFrom: Dict.empty,
}
cheapestOpen : (position -> F64), Model position -> Result position {}
cheapestOpen : (position -> F64), Model position -> Result position {} | position has Eq
cheapestOpen = \costFn, model ->
model.openSet
|> Set.toList
@ -35,13 +35,13 @@ cheapestOpen = \costFn, model ->
|> Result.map .position
|> Result.mapErr (\_ -> {})
reconstructPath : Dict position position, position -> List position
reconstructPath : Dict position position, position -> List position | position has Eq
reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is
Err _ -> []
Ok next -> List.append (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position
updateCost : position, position, Model position -> Model position | position has Eq
updateCost = \current, neighbor, model ->
newCameFrom =
Dict.insert model.cameFrom neighbor current
@ -70,7 +70,7 @@ updateCost = \current, neighbor, model ->
else
model
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {}
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} | position has Eq
astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\source -> costFn source goal) model is
Err {} -> Err {}

View file

@ -1,7 +1,7 @@
interface Base64 exposes [fromBytes, fromStr, toBytes, toStr] imports [Base64.Decode, Base64.Encode]
# base 64 encoding from a sequence of bytes
fromBytes : List U8 -> Result Str [InvalidInput]*
fromBytes : List U8 -> Result Str [InvalidInput]
fromBytes = \bytes ->
when Base64.Decode.fromBytes bytes is
Ok v ->
@ -11,16 +11,16 @@ fromBytes = \bytes ->
Err InvalidInput
# base 64 encoding from a string
fromStr : Str -> Result Str [InvalidInput]*
fromStr : Str -> Result Str [InvalidInput]
fromStr = \str ->
fromBytes (Str.toUtf8 str)
# base64-encode bytes to the original
toBytes : Str -> Result (List U8) [InvalidInput]*
toBytes : Str -> Result (List U8) [InvalidInput]
toBytes = \str ->
Ok (Base64.Encode.toBytes str)
toStr : Str -> Result Str [InvalidInput]*
toStr : Str -> Result Str [InvalidInput]
toStr = \str ->
when toBytes str is
Ok bytes ->

View file

@ -1,13 +1,13 @@
interface Base64.Decode exposes [fromBytes] imports [Bytes.Decode.{ Decoder, DecodeProblem }]
interface Base64.Decode exposes [fromBytes] imports [Bytes.Decode.{ ByteDecoder, DecodeProblem }]
fromBytes : List U8 -> Result Str DecodeProblem
fromBytes = \bytes ->
Bytes.Decode.decode bytes (decodeBase64 (List.len bytes))
decodeBase64 : Nat -> Decoder Str
decodeBase64 : Nat -> ByteDecoder Str
decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string: "" }
loopHelp : { remaining : Nat, string : Str } -> Decoder (Bytes.Decode.Step { remaining : Nat, string : Str } Str)
loopHelp : { remaining : Nat, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : Nat, string : Str } Str)
loopHelp = \{ remaining, string } ->
if remaining >= 3 then
x, y, z <- Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8

View file

@ -1,6 +1,6 @@
interface Base64.Encode
exposes [toBytes]
imports [Bytes.Encode.{ Encoder }]
imports [Bytes.Encode.{ ByteEncoder }]
InvalidChar : U8
@ -13,7 +13,7 @@ toBytes = \str ->
|> Bytes.Encode.sequence
|> Bytes.Encode.encode
encodeChunks : List U8 -> List Encoder
encodeChunks : List U8 -> List ByteEncoder
encodeChunks = \bytes ->
List.walk bytes { output: [], accum: None } folder
|> encodeResidual
@ -21,7 +21,7 @@ encodeChunks = \bytes ->
coerce : Nat, a -> a
coerce = \_, x -> x
# folder : { output : List Encoder, accum : State }, U8 -> { output : List Encoder, accum : State }
# folder : { output : List ByteEncoder, accum : State }, U8 -> { output : List ByteEncoder, accum : State }
folder = \{ output, accum }, char ->
when accum is
Unreachable n -> coerce n { output, accum: Unreachable n }
@ -40,7 +40,7 @@ folder = \{ output, accum }, char ->
{ output, accum: None }
# SGVs bG8g V29y bGQ=
# encodeResidual : { output : List Encoder, accum : State } -> List Encoder
# encodeResidual : { output : List ByteEncoder, accum : State } -> List ByteEncoder
encodeResidual = \{ output, accum } ->
when accum is
Unreachable _ -> output
@ -59,8 +59,8 @@ encodeResidual = \{ output, accum } ->
equals : U8
equals = 61
# Convert 4 characters to 24 bits (as an Encoder)
encodeCharacters : U8, U8, U8, U8 -> Result Encoder InvalidChar
# Convert 4 characters to 24 bits (as an ByteEncoder)
encodeCharacters : U8, U8, U8, U8 -> Result ByteEncoder InvalidChar
encodeCharacters = \a, b, c, d ->
if !(isValidChar a) then
Err a
@ -131,19 +131,19 @@ encodeCharacters = \a, b, c, d ->
isValidChar : U8 -> Bool
isValidChar = \c ->
if isAlphaNum c then
True
Bool.true
else
when c is
43 ->
# '+'
True
Bool.true
47 ->
# '/'
True
Bool.true
_ ->
False
Bool.false
isAlphaNum : U8 -> Bool
isAlphaNum = \key ->

View file

@ -1,13 +1,13 @@
interface Bytes.Decode exposes [Decoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3] imports []
interface Bytes.Decode exposes [ByteDecoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3] imports []
State : { bytes : List U8, cursor : Nat }
DecodeProblem : [OutOfBytes]
Decoder a := State -> [Good State a, Bad DecodeProblem]
ByteDecoder a := State -> [Good State a, Bad DecodeProblem]
decode : List U8, Decoder a -> Result a DecodeProblem
decode = \bytes, @Decoder decoder ->
decode : List U8, ByteDecoder a -> Result a DecodeProblem
decode = \bytes, @ByteDecoder decoder ->
when decoder { bytes, cursor: 0 } is
Good _ value ->
Ok value
@ -15,12 +15,12 @@ decode = \bytes, @Decoder decoder ->
Bad e ->
Err e
succeed : a -> Decoder a
succeed = \value -> @Decoder \state -> Good state value
succeed : a -> ByteDecoder a
succeed = \value -> @ByteDecoder \state -> Good state value
map : Decoder a, (a -> b) -> Decoder b
map = \@Decoder decoder, transform ->
@Decoder
map : ByteDecoder a, (a -> b) -> ByteDecoder b
map = \@ByteDecoder decoder, transform ->
@ByteDecoder
\state ->
when decoder state is
Good state1 value ->
@ -29,9 +29,9 @@ map = \@Decoder decoder, transform ->
Bad e ->
Bad e
map2 : Decoder a, Decoder b, (a, b -> c) -> Decoder c
map2 = \@Decoder decoder1, @Decoder decoder2, transform ->
@Decoder
map2 : ByteDecoder a, ByteDecoder b, (a, b -> c) -> ByteDecoder c
map2 = \@ByteDecoder decoder1, @ByteDecoder decoder2, transform ->
@ByteDecoder
\state1 ->
when decoder1 state1 is
Good state2 a ->
@ -45,9 +45,9 @@ map2 = \@Decoder decoder1, @Decoder decoder2, transform ->
Bad e ->
Bad e
map3 : Decoder a, Decoder b, Decoder c, (a, b, c -> d) -> Decoder d
map3 = \@Decoder decoder1, @Decoder decoder2, @Decoder decoder3, transform ->
@Decoder
map3 : ByteDecoder a, ByteDecoder b, ByteDecoder c, (a, b, c -> d) -> ByteDecoder d
map3 = \@ByteDecoder decoder1, @ByteDecoder decoder2, @ByteDecoder decoder3, transform ->
@ByteDecoder
\state1 ->
when decoder1 state1 is
Good state2 a ->
@ -66,21 +66,21 @@ map3 = \@Decoder decoder1, @Decoder decoder2, @Decoder decoder3, transform ->
Bad e ->
Bad e
after : Decoder a, (a -> Decoder b) -> Decoder b
after = \@Decoder decoder, transform ->
@Decoder
after : ByteDecoder a, (a -> ByteDecoder b) -> ByteDecoder b
after = \@ByteDecoder decoder, transform ->
@ByteDecoder
\state ->
when decoder state is
Good state1 value ->
(@Decoder decoder1) = transform value
(@ByteDecoder decoder1) = transform value
decoder1 state1
Bad e ->
Bad e
u8 : Decoder U8
u8 = @Decoder
u8 : ByteDecoder U8
u8 = @ByteDecoder
\state ->
when List.get state.bytes state.cursor is
Ok b ->
@ -91,14 +91,14 @@ u8 = @Decoder
Step state b : [Loop state, Done b]
loop : (state -> Decoder (Step state a)), state -> Decoder a
loop : (state -> ByteDecoder (Step state a)), state -> ByteDecoder a
loop = \stepper, initial ->
@Decoder
@ByteDecoder
\state ->
loopHelp stepper initial state
loopHelp = \stepper, accum, state ->
(@Decoder stepper1) = stepper accum
(@ByteDecoder stepper1) = stepper accum
when stepper1 state is
Good newState (Done value) ->

View file

@ -1,30 +1,30 @@
interface Bytes.Encode exposes [Encoder, sequence, u8, u16, bytes, empty, encode] imports []
interface Bytes.Encode exposes [ByteEncoder, sequence, u8, u16, bytes, empty, encode] imports []
Endianness : [BE, LE]
Encoder : [Signed8 I8, Unsigned8 U8, Signed16 Endianness I16, Unsigned16 Endianness U16, Sequence Nat (List Encoder), Bytes (List U8)]
ByteEncoder : [Signed8 I8, Unsigned8 U8, Signed16 Endianness I16, Unsigned16 Endianness U16, Sequence Nat (List ByteEncoder), Bytes (List U8)]
u8 : U8 -> Encoder
u8 : U8 -> ByteEncoder
u8 = \value -> Unsigned8 value
empty : Encoder
empty : ByteEncoder
empty =
foo : List Encoder
foo : List ByteEncoder
foo = []
Sequence 0 foo
u16 : Endianness, U16 -> Encoder
u16 : Endianness, U16 -> ByteEncoder
u16 = \endianness, value -> Unsigned16 endianness value
bytes : List U8 -> Encoder
bytes : List U8 -> ByteEncoder
bytes = \bs -> Bytes bs
sequence : List Encoder -> Encoder
sequence : List ByteEncoder -> ByteEncoder
sequence = \encoders ->
Sequence (getWidths encoders 0) encoders
getWidth : Encoder -> Nat
getWidth : ByteEncoder -> Nat
getWidth = \encoder ->
when encoder is
Signed8 _ -> 1
@ -40,18 +40,18 @@ getWidth = \encoder ->
Sequence w _ -> w
Bytes bs -> List.len bs
getWidths : List Encoder, Nat -> Nat
getWidths : List ByteEncoder, Nat -> Nat
getWidths = \encoders, initial ->
List.walk encoders initial \accum, encoder -> accum + getWidth encoder
encode : Encoder -> List U8
encode : ByteEncoder -> List U8
encode = \encoder ->
output = List.repeat 0 (getWidth encoder)
encodeHelp encoder 0 output
|> .output
encodeHelp : Encoder, Nat, List U8 -> { output : List U8, offset : Nat }
encodeHelp : ByteEncoder, Nat, List U8 -> { output : List U8, offset : Nat }
encodeHelp = \encoder, offset, output ->
when encoder is
Unsigned8 value ->

View file

@ -36,7 +36,7 @@ nestHelp = \{ s, f, m, x } ->
Expr : [Val I64, Var Str, Add Expr Expr, Mul Expr Expr, Pow Expr Expr, Ln Expr]
divmod : I64, I64 -> Result { div : I64, mod : I64 } [DivByZero]*
divmod : I64, I64 -> Result { div : I64, mod : I64 } [DivByZero]
divmod = \l, r ->
when Pair (Num.divTruncChecked l r) (Num.remChecked l r) is
Pair (Ok div) (Ok mod) -> Ok { div, mod }

View file

@ -5,7 +5,7 @@ app "issue2279"
main =
text =
if True then
if Bool.true then
Issue2279Help.text
else
Issue2279Help.asText 42

View file

@ -28,7 +28,7 @@ lengthHelp = \foobar, acc ->
safe : I64, I64, ConsList I64 -> Bool
safe = \queen, diagonal, xs ->
when xs is
Nil -> True
Nil -> Bool.true
Cons q t ->
queen != q && queen != q + diagonal && queen != q - diagonal && safe queen (diagonal + 1) t

View file

@ -70,8 +70,8 @@ setBlack = \tree ->
isRed : Tree a b -> Bool
isRed = \tree ->
when tree is
Node Red _ _ _ _ -> True
_ -> False
Node Red _ _ _ _ -> Bool.true
_ -> Bool.false
lt = \x, y -> x < y

View file

@ -74,8 +74,8 @@ setBlack = \tree ->
isRed : Tree a b -> Bool
isRed = \tree ->
when tree is
Node Red _ _ _ _ -> True
_ -> False
Node Red _ _ _ _ -> Bool.true
_ -> Bool.false
ins : Tree I64 Bool, I64, Bool -> Tree I64 Bool
ins = \tree, kx, vx ->
@ -93,13 +93,13 @@ ins = \tree, kx, vx ->
when Num.compare kx ky is
LT ->
when isRed a is
True -> balanceLeft (ins a kx vx) ky vy b
False -> Node Black (ins a kx vx) ky vy b
Bool.true -> balanceLeft (ins a kx vx) ky vy b
Bool.false -> Node Black (ins a kx vx) ky vy b
GT ->
when isRed b is
True -> balanceRight a ky vy (ins b kx vx)
False -> Node Black a ky vy (ins b kx vx)
Bool.true -> balanceRight a ky vy (ins b kx vx)
Bool.false -> Node Black a ky vy (ins b kx vx)
EQ ->
Node Black a kx vx b
@ -137,8 +137,8 @@ balanceRight = \l, k, v, r ->
isBlack : Color -> Bool
isBlack = \c ->
when c is
Black -> True
Red -> False
Black -> Bool.true
Red -> Bool.false
Del a b : [Del (Tree a b) Bool]
@ -155,10 +155,10 @@ makeBlack : Map -> Del I64 Bool
makeBlack = \t ->
when t is
Node Red l k v r ->
Del (Node Black l k v r) False
Del (Node Black l k v r) Bool.false
_ ->
Del t True
Del t Bool.true
rebalanceLeft = \c, l, k, v, r ->
when l is
@ -166,7 +166,7 @@ rebalanceLeft = \c, l, k, v, r ->
Del (balanceLeft (setRed l) k v r) (isBlack c)
Node Red lx kx vx rx ->
Del (Node Black lx kx vx (balanceLeft (setRed rx) k v r)) False
Del (Node Black lx kx vx (balanceLeft (setRed rx) k v r)) Bool.false
_ ->
boom "unreachable"
@ -177,7 +177,7 @@ rebalanceRight = \c, l, k, v, r ->
Del (balanceRight l k v (setRed r)) (isBlack c)
Node Red lx kx vx rx ->
Del (Node Black (balanceRight l k v (setRed lx)) kx vx rx) False
Del (Node Black (balanceRight l k v (setRed lx)) kx vx rx) Bool.false
_ ->
boom "unreachable"
@ -187,24 +187,24 @@ delMin = \t ->
Node Black Leaf k v r ->
when r is
Leaf ->
Delmin (Del Leaf True) k v
Delmin (Del Leaf Bool.true) k v
_ ->
Delmin (Del (setBlack r) False) k v
Delmin (Del (setBlack r) Bool.false) k v
Node Red Leaf k v r ->
Delmin (Del r False) k v
Delmin (Del r Bool.false) k v
Node c l k v r ->
when delMin l is
Delmin (Del lx True) kx vx ->
Delmin (Del lx Bool.true) kx vx ->
Delmin (rebalanceRight c lx k v r) kx vx
Delmin (Del lx False) kx vx ->
Delmin (Del (Node c lx k v r) False) kx vx
Delmin (Del lx Bool.false) kx vx ->
Delmin (Del (Node c lx k v r) Bool.false) kx vx
Leaf ->
Delmin (Del t False) 0 False
Delmin (Del t Bool.false) 0 Bool.false
delete : Tree I64 Bool, I64 -> Tree I64 Bool
delete = \t, k ->
@ -216,32 +216,32 @@ del : Tree I64 Bool, I64 -> Del I64 Bool
del = \t, k ->
when t is
Leaf ->
Del Leaf False
Del Leaf Bool.false
Node cx lx kx vx rx ->
if (k < kx) then
when del lx k is
Del ly True ->
Del ly Bool.true ->
rebalanceRight cx ly kx vx rx
Del ly False ->
Del (Node cx ly kx vx rx) False
Del ly Bool.false ->
Del (Node cx ly kx vx rx) Bool.false
else if (k > kx) then
when del rx k is
Del ry True ->
Del ry Bool.true ->
rebalanceLeft cx lx kx vx ry
Del ry False ->
Del (Node cx lx kx vx ry) False
Del ry Bool.false ->
Del (Node cx lx kx vx ry) Bool.false
else
when rx is
Leaf ->
if isBlack cx then makeBlack lx else Del lx False
if isBlack cx then makeBlack lx else Del lx Bool.false
Node _ _ _ _ _ ->
when delMin rx is
Delmin (Del ry True) ky vy ->
Delmin (Del ry Bool.true) ky vy ->
rebalanceLeft cx lx ky vy ry
Delmin (Del ry False) ky vy ->
Del (Node cx lx ky vy ry) False
Delmin (Del ry Bool.false) ky vy ->
Del (Node cx lx ky vy ry) Bool.false

View file

@ -17,9 +17,12 @@ main =
# Task.putLine "No test \(ns)"
showBool : Bool -> Str
showBool = \b ->
when b is
True -> "True"
False -> "False"
if
b
then
"True"
else
"False"
test1 : Bool
test1 =

View file

@ -66,13 +66,13 @@ getInt =
Effect.after
Effect.getInt
\{ isError, value } ->
when isError is
True ->
# when errorCode is
# # A -> Task.fail InvalidCharacter
# # B -> Task.fail IOError
# _ ->
Task.succeed -1
False ->
Task.succeed value
if
isError
then
# when errorCode is
# # A -> Task.fail InvalidCharacter
# # B -> Task.fail IOError
# _ ->
Task.succeed -1
else
Task.succeed value

View file

@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const str = @import("str");
const RocStr = str.RocStr;
const testing = std.testing;
@ -15,7 +16,6 @@ comptime {
// -fcompiler-rt in link.rs instead of doing this. Note that this
// workaround is present in many host.zig files, so make sure to undo
// it everywhere!
const builtin = @import("builtin");
if (builtin.os.tag == .macos) {
_ = @import("compiler_rt");
}
@ -85,10 +85,41 @@ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size);
}
extern fn kill(pid: c_int, sig: c_int) c_int;
extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn getppid() c_int;
fn roc_getppid() callconv(.C) c_int {
return getppid();
}
fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque {
return mmap(addr, length, prot, flags, fd, offset);
}
comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}
const Unit = extern struct {};
pub export fn main() callconv(.C) u8 {
const size = @intCast(usize, roc__mainForHost_size());
pub fn main() !u8 {
const stderr = std.io.getStdErr().writer();
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr
const size = std.math.max(@intCast(usize, roc__mainForHost_size()), 8);
const raw_output = roc_alloc(@intCast(usize, size), @alignOf(u64)).?;
var output = @ptrCast([*]u8, raw_output);
@ -107,7 +138,6 @@ pub export fn main() callconv(.C) u8 {
const nanos = timer.read();
const seconds = (@intToFloat(f64, nanos) / 1_000_000_000.0);
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable;
return 0;
@ -120,7 +150,8 @@ fn to_seconds(tms: std.os.timespec) f64 {
fn call_the_closure(closure_data_pointer: [*]u8) void {
const allocator = std.heap.page_allocator;
const size = roc__mainForHost_1__Fx_result_size();
// The size might be zero; if so, make it at least 8 so that we don't have a nullptr
const size = std.math.max(roc__mainForHost_1__Fx_result_size(), 8);
const raw_output = allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output);
@ -187,14 +218,14 @@ fn roc_fx_getInt_64bit() callconv(.C) GetInt {
fn roc_fx_getInt_32bit(output: *GetInt) callconv(.C) void {
if (roc_fx_getInt_help()) |value| {
const get_int = GetInt{ .is_error = false, .value = value, .error_code = false };
const get_int = GetInt{ .is_error = false, .value = value };
output.* = get_int;
} else |err| switch (err) {
error.InvalidCharacter => {
output.* = GetInt{ .is_error = true, .value = 0, .error_code = false };
output.* = GetInt{ .is_error = true, .value = 0 };
},
else => {
output.* = GetInt{ .is_error = true, .value = 0, .error_code = true };
output.* = GetInt{ .is_error = true, .value = 0 };
},
}
@ -208,7 +239,9 @@ fn roc_fx_getInt_help() !i64 {
const stdin = std.io.getStdIn().reader();
var buf: [40]u8 = undefined;
const line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse "";
// make sure to strip `\r` on windows
const raw_line: []u8 = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse "";
const line = std.mem.trimRight(u8, raw_line, &std.ascii.spaces);
return std.fmt.parseInt(i64, line, 10);
}

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,11 @@
[package]
name = "cli_utils"
version = "0.1.0"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
repository = "https://github.com/roc-lang/roc"
edition = "2021"
description = "Shared code for cli tests and benchmarks"
description = "Provides shared code for cli tests and benchmarks."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,6 +14,7 @@ roc_collections = { path = "../compiler/collections" }
roc_reporting = { path = "../reporting" }
roc_load = { path = "../compiler/load" }
roc_module = { path = "../compiler/module" }
roc_utils = { path = "../utils" }
bumpalo = { version = "3.8.0", features = ["collections"] }
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
serde = { version = "1.0.130", features = ["derive"] }

File diff suppressed because one or more lines are too long

View file

@ -4,6 +4,8 @@ extern crate roc_load;
extern crate roc_module;
extern crate tempfile;
use roc_utils::cargo;
use roc_utils::root_dir;
use serde::Deserialize;
use serde_xml_rs::from_str;
use std::env;
@ -22,14 +24,20 @@ pub struct Out {
pub status: ExitStatus,
}
pub fn run_roc<I, S>(args: I, stdin_vals: &[&str]) -> Out
pub fn run_roc<I, S>(args: I, stdin_vals: &[&str], extra_env: &[(&str, &str)]) -> Out
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let roc_binary_path = build_roc_bin_cached();
run_roc_with_stdin_and_env(&roc_binary_path, args, stdin_vals, extra_env)
}
// If we don't already have a /target/release/roc, build it!
pub fn build_roc_bin_cached() -> PathBuf {
let roc_binary_path = path_to_roc_binary();
// If we don't have a /target/release/roc, rebuild it!
if !roc_binary_path.exists() {
// Remove the /target/release/roc part
let root_project_dir = roc_binary_path
@ -48,21 +56,25 @@ where
vec!["build", "--release", "--bin", "roc"]
};
let output = Command::new("cargo")
.current_dir(root_project_dir)
.args(args)
.output()
.unwrap();
let mut cargo_cmd = cargo();
if !output.status.success() {
panic!("cargo build --release --bin roc failed. stdout was:\n\n{:?}\n\nstderr was:\n\n{:?}\n",
output.stdout,
output.stderr
cargo_cmd.current_dir(root_project_dir).args(&args);
let cargo_cmd_str = format!("{:?}", cargo_cmd);
let cargo_output = cargo_cmd.output().unwrap();
if !cargo_output.status.success() {
panic!(
"The following cargo command failed:\n\n {}\n\n stdout was:\n\n {}\n\n stderr was:\n\n {}\n",
cargo_cmd_str,
String::from_utf8(cargo_output.stdout).unwrap(),
String::from_utf8(cargo_output.stderr).unwrap()
);
}
}
run_with_stdin(&roc_binary_path, args, stdin_vals)
roc_binary_path
}
pub fn run_glue<I, S>(args: I) -> Out
@ -70,11 +82,11 @@ where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
run_with_stdin(&path_to_roc_binary(), args, &[])
run_roc_with_stdin(&path_to_roc_binary(), args, &[])
}
pub fn path_to_roc_binary() -> PathBuf {
path_to_binary("roc")
path_to_binary(if cfg!(windows) { "roc.exe" } else { "roc" })
}
pub fn path_to_binary(binary_name: &str) -> PathBuf {
@ -117,54 +129,75 @@ pub fn strip_colors(str: &str) -> String {
.replace(ANSI_STYLE_CODES.color_reset, "")
}
pub fn run_with_stdin<I, S>(path: &Path, args: I, stdin_vals: &[&str]) -> Out
pub fn run_roc_with_stdin<I, S>(path: &Path, args: I, stdin_vals: &[&str]) -> Out
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut cmd = Command::new(path);
run_roc_with_stdin_and_env(path, args, stdin_vals, &[])
}
pub fn run_roc_with_stdin_and_env<I, S>(
roc_path: &Path,
args: I,
stdin_vals: &[&str],
extra_env: &[(&str, &str)],
) -> Out
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut roc_cmd = Command::new(roc_path);
for arg in args {
cmd.arg(arg);
roc_cmd.arg(arg);
}
let mut child = cmd
for (k, v) in extra_env {
roc_cmd.env(k, v);
}
let roc_cmd_str = format!("{:?}", roc_cmd);
let mut roc_cmd_child = roc_cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap_or_else(|err| {
panic!(
"failed to execute compiled binary {} in CLI test: {err}",
path.to_string_lossy()
)
panic!("Failed to execute command\n\n {roc_cmd_str}\n\nwith error:\n\n {err}",)
});
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
let stdin = roc_cmd_child.stdin.as_mut().expect("Failed to open stdin");
for stdin_str in stdin_vals.iter() {
stdin
.write_all(stdin_str.as_bytes())
.expect("Failed to write to stdin");
.unwrap_or_else(|err| {
panic!(
"Failed to write to stdin for command\n\n {roc_cmd_str}\n\nwith error:\n\n {err}",
)
});
}
}
let output = child
.wait_with_output()
.expect("failed to get output for compiled binary in CLI test");
let roc_cmd_output = roc_cmd_child.wait_with_output().unwrap_or_else(|err| {
panic!("Failed to get output for command\n\n {roc_cmd_str}\n\nwith error:\n\n {err}",)
});
Out {
stdout: String::from_utf8(output.stdout).unwrap(),
stderr: String::from_utf8(output.stderr).unwrap(),
status: output.status,
stdout: String::from_utf8(roc_cmd_output.stdout).unwrap(),
stderr: String::from_utf8(roc_cmd_output.stderr).unwrap(),
status: roc_cmd_output.status,
}
}
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>, E: IntoIterator<Item = (&'a str, &'a str)>>(
cmd_name: &str,
stdin_vals: I,
args: &[String],
env: E,
) -> Out {
let mut cmd = Command::new(cmd_name);
@ -172,6 +205,10 @@ pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
cmd.arg(arg);
}
for (env, val) in env.into_iter() {
cmd.env(env, val);
}
let mut child = cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())
@ -335,44 +372,31 @@ pub fn extract_valgrind_errors(xml: &str) -> Result<Vec<ValgrindError>, serde_xm
Ok(answer)
}
// start the dir with crates/cli_testing_examples
#[allow(dead_code)]
pub fn root_dir() -> PathBuf {
let mut path = env::current_exe().ok().unwrap();
// Get rid of the filename in target/debug/deps/cli_run-99c65e4e9a1fbd06
path.pop();
// If we're in deps/ get rid of deps/ in target/debug/deps/
if path.ends_with("deps") {
path.pop();
}
// Get rid of target/debug/ so we're back at the project root
path.pop();
path.pop();
// running cargo with --target will put us in the target dir
if path.ends_with("target") {
path.pop();
}
path
}
#[allow(dead_code)]
pub fn examples_dir(dir_name: &str) -> PathBuf {
pub fn cli_testing_dir(dir_name: &str) -> PathBuf {
let mut path = root_dir();
// Descend into examples/{dir_name}
path.push("examples");
path.extend(dir_name.split("/")); // Make slashes cross-platform
path.push("crates");
path.push("cli_testing_examples");
path.extend(dir_name.split("/")); // Make slashes cross-target
path
}
#[allow(dead_code)]
pub fn example_file(dir_name: &str, file_name: &str) -> PathBuf {
let mut path = examples_dir(dir_name);
pub fn dir_path_from_root(dir_name: &str) -> PathBuf {
let mut path = root_dir();
path.extend(dir_name.split("/")); // Make slashes cross-target
path
}
#[allow(dead_code)]
pub fn file_path_from_root(dir_name: &str, file_name: &str) -> PathBuf {
let mut path = dir_path_from_root(dir_name);
path.push(file_name);
@ -388,7 +412,7 @@ pub fn fixtures_dir(dir_name: &str) -> PathBuf {
path.push("cli");
path.push("tests");
path.push("fixtures");
path.extend(dir_name.split("/")); // Make slashes cross-platform
path.extend(dir_name.split("/")); // Make slashes cross-target
path
}

View file

@ -1,2 +1,3 @@
//! Provides shared code for cli tests and benchmarks.
pub mod bench_utils;
pub mod helpers;

View file

@ -1,6 +1,6 @@
[package]
name = "roc_code_markup"
version = "0.1.0"
version = "0.0.1"
authors = ["The Roc Contributors"]
license = "UPL-1.0"
edition = "2021"
@ -10,8 +10,7 @@ description = "Our own markup language for Roc code. Used by the editor and the
roc_ast = { path = "../ast" }
roc_module = { path = "../compiler/module" }
roc_utils = { path = "../utils" }
serde = { version = "1.0.130", features = ["derive"] }
serde = { version = "1.0.144", features = ["derive"] }
palette = "0.6.1"
snafu = { version = "0.7.1", features = ["backtraces"] }
bumpalo = { version = "3.8.0", features = ["collections"] }
itertools = "0.10.1"
bumpalo = { version = "3.11.0", features = ["collections"] }

View file

@ -1,3 +1,4 @@
//! A [markup language](https://en.wikipedia.org/wiki/Markup_language) to display Roc code in the editor.
pub mod colors;
pub mod markup;
pub mod markup_error;

View file

@ -15,7 +15,6 @@ use crate::{
syntax_highlight::HighlightStyle,
};
use itertools::Itertools;
use roc_ast::{
ast_error::ASTResult,
lang::{
@ -409,10 +408,10 @@ pub fn expr2_to_markup<'a>(
})
.collect::<ModuleResult<Vec<&str>>>()?;
let arg_mark_nodes = arg_names
let arg_mark_nodes: Vec<_> = arg_names
.iter()
.map(|arg_name| new_arg_name_mn(arg_name.to_string()))
.collect_vec();
.collect();
let args_with_commas: Vec<MarkupNode> = join_mark_nodes_commas(arg_mark_nodes);

View file

@ -12,7 +12,6 @@ use super::{
use crate::markup_error::{
ExpectedTextNodeSnafu, NestedNodeMissingChildSnafu, NestedNodeRequiredSnafu,
};
use itertools::Itertools;
use roc_ast::{
lang::{core::ast::ASTNodeId, env::Env},
mem_pool::pool_str::PoolStr,
@ -448,9 +447,9 @@ pub fn join_mark_nodes_spaces(
.collect();
if with_prepend {
join_nodes.into_iter().interleave(mark_nodes_ids).collect()
interleave(join_nodes.into_iter(), mark_nodes_ids)
} else {
mark_nodes_ids.into_iter().interleave(join_nodes).collect()
interleave(mark_nodes_ids, join_nodes.into_iter())
}
}
@ -460,7 +459,7 @@ pub fn join_mark_nodes_commas(mark_nodes: Vec<MarkupNode>) -> Vec<MarkupNode> {
.map(|_| new_comma_mn())
.collect();
mark_nodes.into_iter().interleave(join_nodes).collect()
interleave(mark_nodes.into_iter(), join_nodes)
}
pub fn mark_nodes_to_string(markup_node_ids: &[MarkNodeId], mark_node_pool: &SlowPool) -> String {
@ -493,3 +492,42 @@ pub fn node_to_string_w_children(
str_buffer.push_str(&node_content_str);
}
}
fn interleave<I, J>(i: I, j: J) -> Vec<I::Item>
where
I: IntoIterator,
J: IntoIterator<Item = I::Item>,
{
let mut output = Vec::new();
let mut flag = false;
let mut i = i.into_iter();
let mut j = j.into_iter();
loop {
flag = !flag;
if flag {
match i.next() {
None => {
output.extend(j);
break output;
}
Some(v) => {
output.push(v);
}
}
} else {
match j.next() {
None => {
output.extend(i);
break output;
}
Some(v) => {
output.push(v);
}
}
}
}
}

View file

@ -4,6 +4,7 @@ edition = "2021"
license = "UPL-1.0"
name = "roc_alias_analysis"
version = "0.0.1"
description = "Performs analysis and optimizations to remove unneeded reference counts at runtime, and supports in-place mutation."
[dependencies]
morphic_lib = {path = "../../vendor/morphic_lib"}
@ -11,3 +12,4 @@ roc_collections = {path = "../collections"}
roc_module = {path = "../module"}
roc_mono = {path = "../mono"}
roc_debug_flags = {path = "../debug_flags"}
roc_error_macros = { path = "../../error_macros" }

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