diff --git a/.earthignore b/.earthignore deleted file mode 100644 index a291dd2885..0000000000 --- a/.earthignore +++ /dev/null @@ -1,4 +0,0 @@ -AUTHORS -nix -.envrc -.gitignore \ No newline at end of file diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 58e0ee422c..7566856d52 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -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 diff --git a/.github/workflows/nix_linux_x86_64.yml b/.github/workflows/nix_linux_x86_64.yml index 55208163c2..3dc787dbcb 100644 --- a/.github/workflows/nix_linux_x86_64.yml +++ b/.github/workflows/nix_linux_x86_64.yml @@ -20,7 +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: /home/big-ci-user/.nix-profile/bin/nix develop -c cargo test --locked --release --features="wasm32-cli-run" + run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9a71508c88..83a95ca0a9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -15,10 +15,17 @@ jobs: runs-on: windows-2022 env: LLVM_SYS_130_PREFIX: C:\LLVM-13.0.1-win64 - timeout-minutes: 90 + + 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 @@ -34,5 +41,12 @@ jobs: 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 - run: cargo build + - 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 diff --git a/AUTHORS b/AUTHORS index 8146801545..0df85f2ea9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -113,3 +113,11 @@ João Mota Marcos Prieto Prajwal S N Christopher Duncan +Luke Boswell +Luca Cervello +Josh Mak +Travis Staloch +Nick Gravgaard +Keerthana Kasthuril <76804118+keerthanak-tw@users.noreply.github.com> +Salman Shaik +Austin Clements diff --git a/Cargo.lock b/Cargo.lock index b8993de844..fa521e7eca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -496,6 +496,7 @@ dependencies = [ "roc_load", "roc_module", "roc_reporting", + "roc_utils", "serde", "serde-xml-rs", "strip-ansi-escapes", @@ -3573,6 +3574,7 @@ dependencies = [ "roc_solve", "roc_types", "roc_unify", + "roc_utils", "rodio", "serde", "snafu", @@ -3594,6 +3596,7 @@ name = "roc_exhaustive" version = "0.0.1" dependencies = [ "roc_collections", + "roc_error_macros", "roc_module", "roc_region", ] @@ -3993,6 +3996,7 @@ dependencies = [ "roc_reporting", "roc_target", "roc_types", + "roc_utils", "wasi_libc_sys", "wasm-bindgen", "wasm-bindgen-futures", @@ -4860,6 +4864,7 @@ dependencies = [ "roc_target", "roc_types", "roc_unify", + "roc_utils", "target-lexicon", "tempfile", "wasi_libc_sys", @@ -5262,6 +5267,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi_libc_sys" version = "0.0.1" +dependencies = [ + "roc_utils", +] [[package]] name = "wasm-bindgen" diff --git a/Cargo.toml b/Cargo.toml index 6a5e0b066c..4106ed25cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ members = [ "crates/wasi-libc-sys", ] exclude = [ - "ci/bench-runner", + "ci/benchmarks/bench-runner", # Examples sometimes have Rust hosts in their platforms. The compiler should ignore those. "crates/cli_testing_examples", "examples", diff --git a/Earthfile b/Earthfile deleted file mode 100644 index 7c59196695..0000000000 --- a/Earthfile +++ /dev/null @@ -1,72 +0,0 @@ -FROM rust:1.61.0-slim-bullseye # make sure to update rust-toolchain.toml too so that everything uses the same rust version -WORKDIR /earthbuild - -prep-debian: - RUN apt -y update - -install-other-libs: - FROM +prep-debian - RUN apt -y install wget git - RUN apt -y install libxcb-shape0-dev libxcb-xfixes0-dev # for editor clipboard - RUN apt -y install libasound2-dev # for editor sounds - RUN apt -y install libunwind-dev pkg-config libx11-dev zlib1g-dev - RUN apt -y install unzip # for www/build.sh - -install-zig-llvm-valgrind: - FROM +install-other-libs - # editor - RUN apt -y install libxkbcommon-dev - # zig - RUN wget -c https://ziglang.org/download/0.9.1/zig-linux-x86_64-0.9.1.tar.xz --no-check-certificate - RUN tar -xf zig-linux-x86_64-0.9.1.tar.xz - RUN ln -s /earthbuild/zig-linux-x86_64-0.9.1/zig /bin/zig - # zig builtins wasm tests - RUN apt -y install build-essential - RUN cargo install wasmer-cli --features "singlepass" - RUN cargo install bindgen - # llvm - RUN apt -y install lsb-release software-properties-common gnupg - RUN wget https://apt.llvm.org/llvm.sh - RUN chmod +x llvm.sh - RUN ./llvm.sh 13 - RUN ln -s /usr/bin/clang-13 /usr/bin/clang - # use lld as linker - RUN ln -s /usr/bin/lld-13 /usr/bin/ld.lld - ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native" - # valgrind - RUN apt -y install valgrind - # wasm repl & tests - RUN rustup target add wasm32-unknown-unknown wasm32-wasi - RUN apt -y install libssl-dev - RUN OPENSSL_NO_VENDOR=1 cargo install wasm-pack - # criterion - RUN cargo install cargo-criterion - # sccache - RUN cargo install sccache - RUN sccache -V - ENV RUSTC_WRAPPER=/usr/local/cargo/bin/sccache - ENV SCCACHE_DIR=/earthbuild/sccache_dir - ENV CARGO_INCREMENTAL=0 # no need to recompile package when using new function - -copy-dirs: - FROM +install-zig-llvm-valgrind - COPY --dir crates Cargo.toml Cargo.lock version.txt www ./ - -# compile everything needed for benchmarks and output a self-contained dir from which benchmarks can be run. -prep-bench-folder: - FROM +copy-dirs - # to make use of avx, avx2, sse2, sse4.2... instructions - ENV RUSTFLAGS="-C link-arg=-fuse-ld=lld -C target-cpu=native" - ARG BENCH_SUFFIX=branch - RUN cargo criterion -V - RUN --mount=type=cache,target=$SCCACHE_DIR cd crates/cli && cargo criterion --no-run - RUN mkdir -p bench-folder/crates/cli_testing_examples/benchmarks - RUN mkdir -p bench-folder/crates/compiler/builtins/bitcode/src - RUN mkdir -p bench-folder/target/release/deps - RUN cp crates/cli_testing_examples/benchmarks/*.roc bench-folder/crates/cli_testing_examples/benchmarks/ - RUN cp -r crates/cli_testing_examples/benchmarks/platform bench-folder/crates/cli_testing_examples/benchmarks/ - RUN cp crates/compiler/builtins/bitcode/src/str.zig bench-folder/crates/compiler/builtins/bitcode/src - RUN cp target/release/roc bench-folder/target/release - # copy the most recent time bench to bench-folder - RUN cp target/release/deps/`ls -t target/release/deps/ | grep time_bench | head -n 1` bench-folder/target/release/deps/time_bench - SAVE ARTIFACT bench-folder AS LOCAL bench-folder-$BENCH_SUFFIX diff --git a/TUTORIAL.md b/TUTORIAL.md index 7e5e5e1d31..445f0e1e64 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -1,6 +1,6 @@ # Tutorial -This is a tutorial to learn how to build Roc applications. +This is a tutorial to learn how to build Roc applications. It covers the REPL, basic types like strings, lists, tags, and functions, syntax like `when` and `if then else`, and more! Enjoy! @@ -1091,6 +1091,63 @@ of the type annotation, or even the function's implementation! The only way to h Similarly, the only way to have a function whose type is `a -> a` is if the function's implementation returns its argument without modifying it in any way. This is known as [the identity function](https://en.wikipedia.org/wiki/Identity_function). +## Tag Unions + +We can also annotate types that include tags: + +```coffee +colorFromStr : Str -> [Red, Green, Yellow] +colorFromStr = \string -> + when string is + "red" -> Red + "green" -> Green + _ -> Yellow +``` + +You can read the type `[Red, Green, Yellow]` as "a *tag union* of the tags `Red`, `Green`, and `Yellow`." + +Some tag unions have only one tag in them. For example: + +```coffee +redTag : [Red] +redTag = Red +``` + +Tag unions can accumulate additional tags based on how they're used in the program. Consider this `if` expression: + +```elm +\str -> + if Str.isEmpty str then + Ok "it was empty" + else + Err ["it was not empty"] +``` + +Here, Roc sees that the first branch has the type `[Ok Str]` and that the `else` branch has +the type `[Err (List Str)]`, so it concludes that the whole `if` expression evaluates to the +combination of those two tag unions: `[Ok Str, Err (List Str)]`. + +This means the entire `\str -> …` funcion here has the type `Str -> [Ok Str, Err (List Str)]`. +However, it would be most common to annotate it as `Result Str (List Str)` instead, because +the `Result` type (for operations like `Result.withDefault`, which we saw earlier) is a type +alias for a tag union with `Ok` and `Err` tags that each have one payload: + +```haskell +Result ok err : [Ok ok, Err err] +``` + +We just saw how tag unions get combined when different branches of a conditional return different tags. Another way tag unions can get combined is through pattern matching. For example: + +```coffeescript +when color is + Red -> "red" + Yellow -> "yellow" + Green -> "green" +``` + +Here, Roc's compiler will infer that `color`'s type is `[Red, Yellow, Green]`, because +those are the three possibilities this `when` handles. + ## Numeric types Roc has different numeric types that each have different tradeoffs. @@ -1399,8 +1456,8 @@ this `imports` line tells the Roc compiler that when we call `Stdout.line`, it should look for that `line` function in the `Stdout` module of the `examples/cli/cli-platform/main.roc` package. -If we would like to include other modules in our application, say `AdditionalModule.roc` -and `AnotherModule.roc`, then they can be imported directly in `imports` like this: +If we would like to include other modules in our application, say `AdditionalModule.roc` +and `AnotherModule.roc`, then they can be imported directly in `imports` like this: ```coffee packages { pf: "examples/cli/cli-platform/main.roc" } @@ -1423,7 +1480,7 @@ interface Parser.Core ``` This says that the current .roc file is an *interface module* because it begins with the `interface` keyword. -We are naming this module when we write `interface Parser.Core`. It means that this file is in +We are naming this module when we write `interface Parser.Core`. It means that this file is in a package `Parser` and the current module is named `core`. When we write `exposes [Parser, ParseResult, ...]`, it specifies the definitions we want to *expose*. Exposing makes them importable from other modules. @@ -1469,7 +1526,7 @@ TODO ## Comments -Comments that begin with `##` will be included in generated documentation (```roc docs```). They require a single space after the `##`, and can include code blocks by adding five spaces after `##`. +Comments that begin with `##` will be included in generated documentation (```roc docs```). They require a single space after the `##`, and can include code blocks by adding five spaces after `##`. ```coffee ## This is a comment for documentation, and includes a code block. @@ -1722,6 +1779,30 @@ Some important things to note about backpassing and `await`: - Backpassing syntax does not need to be used with `await` in particular. It can be used with any function. - Roc's compiler treats functions defined with backpassing exactly the same way as functions defined the other way. The only difference between `\text ->` and `text <-` is how they look, so feel free to use whichever looks nicer to you! +### Empty Tag Unions + +If you look up the type of [`Program.exit`](https://www.roc-lang.org/examples/cli/Program#exit), +you may notice that it takes a `Task` where the error type is `[]`. What does that mean? + +Just like how `{}` is the type of an empty record, `[]` is the type of an empty tag union. +There is no way to create an empty tag union at runtime, since creating a tag union requires +making an actual tag, and an empty tag union has no tags in it! + +This means if you have a function with the type `[] -> Str`, you can be sure that it will +never execute. It requires an argument that can't be provided! Similarly, if you have a +function with the type `Str -> []`, you can call it, but you can be sure it will not terminate +normally. The only way to implement a function like that is using [infinite recursion](https://en.wikipedia.org/wiki/Infinite_loop#Infinite_recursion), which will either run indefinitely or else crash with a [stack overflow](https://en.wikipedia.org/wiki/Stack_overflow). + +Empty tag unions can be useful as type parameters. For example, a function with the type +`List [] -> Str` can be successfully called, but only if you pass it an empty list. That's because +an empty list has the type `List *`, which means it can be used wherever any type of `List` is +needed - even a `List []`! + +Similarly, a function which accepts a `Result Str []` only accepts a "Result which is always `Ok`" - so you could call that function passing something like `Ok "hello"` with no problem, +but if you tried to give it an `Err`, you'd get a type mismatch. + +Applying this to `Task`, a task with `[]` for its error type is a "task which can never fail." The only way to obtain one is by obtaining a task with an error type of `*`, since that works with any task. You can get one of these "tasks that can never fail" by using [`Task.succeed`](https://www.roc-lang.org/examples/cli/Task#succeed) or, more commonly, by handling all possible errors using [`Task.attempt`](https://www.roc-lang.org/examples/cli/Task#attempt). + ## What now? That's it, you can start writing Roc apps now! @@ -1950,200 +2031,65 @@ type that accumulates more and more fields as it progresses through a series of ### Open and Closed Tag Unions -Just like how Roc has open records and closed records, it also has open and closed tag unions. +Just like how Roc has open records and closed records, it also has open and closed tag unions. Similarly to how an open record can have other fields besides the ones explicitly listed, an open tag union can have other tags beyond the ones explicitly listed. -The *open tag union* (or *open union* for short) `[Foo Str, Bar Bool]*` represents a tag that might -be `Foo Str` and might be `Bar Bool`, but might also be some other tag whose type isn't known at compile time. - -Because an open union represents possibilities that are impossible to know ahead of time, any `when` I use on a -`[Foo Str, Bar Bool]*` value must include a catch-all `_ ->` branch. Otherwise, if one of those -unknown tags were to come up, the `when` would not know what to do with it! For example: +For example, here `[Red, Green]` is a closed union like the ones we saw earlier: ```coffee -example : [Foo Str, Bar Bool]* -> Bool -example = \tag -> - when tag is - Foo str -> Str.isEmpty str - Bar bool -> bool - _ -> Bool.false -``` +colorToStr : [Red, Green] -> String +colorToStr = \color -> + when color is + Red -> "red" + Green -> "green" -In contrast, a *closed tag union* (or *closed union*) like `[Foo Str, Bar Bool]` (without the `*`) -represents an exhaustive set of possible tags. If I use a `when` on one of these, I can match on `Foo` -only and then on `Bar` only, with no need for a catch-all branch. For example: +Now let's compare to an *open union* version: ```coffee -example : [Foo Str, Bar Bool] -> Bool -example = \tag -> - when tag is - Foo str -> Str.isEmpty str - Bar bool -> bool +colorOrOther : [Red, Green]* -> String +colorOrOther = \color -> + when color is + Red -> "red" + Green -> "green" + _ -> "other" ``` -If we were to remove the type annotations from the previous two code examples, Roc would infer the same -types for them anyway. +Two things have changed compared to the first example. +1. The `when color is` now has an extra branch: `_ -> "other"` +2. Since this branch matches any tag, the type annotation for the `color` argument changed from the closed union `[Red, Green]` to the _open union_ `[Red, Green]*`. -It would infer `tag : [Foo Str, Bar Bool]` for the latter example because the `when tag is` expression -only includes a `Foo Str` branch and a `Bar Bool` branch, and nothing else. Since the `when` doesn't handle -any other possibilities, these two tags must be the only possible ones the `tag` argument could be. - -It would infer `tag : [Foo Str, Bar Bool]*` for the former example because the `when tag is` expression -includes a `Foo Str` branch and a `Bar Bool` branch - meaning we know about at least those two specific -possibilities - but also a `_ ->` branch, indicating that there may be other tags we don't know about. Since -the `when` is flexible enough to handle all possible tags, `tag` gets inferred as an open union. - -Putting these together, whether a tag union is inferred to be open or closed depends on which possibilities -the implementation actually handles. - -> **Aside:** As with open and closed records, we can use type annotations to make tag union types less flexible -> than what would be inferred. If we added a `_ ->` branch to the second example above, the compiler would still -> accept `example : [Foo Str, Bar Bool] -> Bool` as the type annotation, even though the catch-all branch -> would permit the more flexible `example : [Foo Str, Bar Bool]* -> Bool` annotation instead. - -### Combining Open Unions - -When we make a new record, it's inferred to be a closed record. For example, in `foo { a: "hi" }`, -the type of `{ a: "hi" }` is inferred to be `{ a : Str }`. In contrast, when we make a new tag, it's inferred -to be an open union. So in `foo (Bar "hi")`, the type of `Bar "hi"` is inferred to be `[Bar Str]*`. - -This is because open unions can accumulate additional tags based on how they're used in the program, -whereas closed unions cannot. For example, let's look at this conditional: - -```elm -if x > 5 then - "foo" -else - 7 -``` - -This will be a type mismatch because the two branches have incompatible types. Strings and numbers are not -type-compatible! Now let's look at another example: - -```elm -if x > 5 then - Ok "foo" -else - Err "bar" -``` - -This shouldn't be a type mismatch, because we can see that the two branches are compatible; they are both -tags that could easily coexist in the same tag union. But if the compiler inferred the type of `Ok "foo"` to be -the closed union `[Ok Str]`, and likewise for `Err "bar"` and `[Err Str]`, then this would have to be -a type mismatch - because those two closed unions are incompatible. - -Instead, the compiler infers `Ok "foo"` to be the open union `[Ok Str]*`, and `Err "bar"` to be the open -union `[Err Str]*`. Then, when using them together in this conditional, the inferred type of the conditional -becomes `[Ok Str, Err Str]*` - that is, the combination of the unions in each of its branches. (Branches in -a `when` work the same way with open unions.) - -Earlier we saw how a function which accepts an open union must account for more possibilities, by including -catch-all `_ ->` patterns in its `when` expressions. So *accepting* an open union means you have more requirements. -In contrast, when you already *have* a value which is an open union, you have fewer requirements. A value -which is an open union (like `Ok "foo"`, which has the type `[Ok Str]*`) can be provided to anything that's -expecting a tag union (no matter whether it's open or closed), as long as the expected tag union includes at least -the tags in the open union you're providing. - -So if I have an `[Ok Str]*` value, I can pass it to functions with any of these types (among others): - -- `[Ok Str]* -> Bool` -- `[Ok Str] -> Bool` -- `[Ok Str, Err Bool]* -> Bool` -- `[Ok Str, Err Bool] -> Bool` -- `[Ok Str, Err Bool, Whatever]* -> Bool` -- `[Ok Str, Err Bool, Whatever] -> Bool` -- `Result Str Bool -> Bool` -- `[Err Bool, Whatever]* -> Bool` - -That last one works because a function accepting an open union can accept any unrecognized tag, including -`Ok Str` - even though it is not mentioned as one of the tags in `[Err Bool, Whatever]*`! Remember, when -a function accepts an open tag union, any `when` branches on that union must include a catch-all `_ ->` branch, -which is the branch that will end up handling the `Ok Str` value we pass in. - -However, I could not pass an `[Ok Str]*` to a function with a *closed* tag union argument that did not -mention `Ok Str` as one of its tags. So if I tried to pass `[Ok Str]*` to a function with the type -`[Err Bool, Whatever] -> Str`, I would get a type mismatch - because a `when` in that function could -be handling the `Err Bool` possibility and the `Whatever` possibility, and since it would not necessarily have -a catch-all `_ ->` branch, it might not know what to do with an `Ok Str` if it received one. - -> **Note:** It wouldn't be accurate to say that a function which accepts an open union handles -> "all possible tags." For example, if I have a function `[Ok Str]* -> Bool` and I pass it -> `Ok 5`, that will still be a type mismatch. If you think about it, a `when` in that function might -> have the branch `Ok str ->` which assumes there's a string inside that `Ok`, and if `Ok 5` type-checked, -> then that assumption would be false and things would break! -> -> So `[Ok Str]*` is more restrictive than `[]*`. It's basically saying "this may or may not be an `Ok` tag, -> but if it is an `Ok` tag, then it's guaranteed to have a payload of exactly `Str`." - -In summary, here's a way to think about the difference between open unions in a value you have, compared to a value you're accepting: - -- If you *have* a closed union, that means it has all the tags it ever will, and can't accumulate more. -- If you *have* an open union, that means it can accumulate more tags through conditional branches. -- If you *accept* a closed union, that means you only have to handle the possibilities listed in the union. -- If you *accept* an open union, that means you have to handle the possibility that it has a tag you can't know about. - -### Type Variables in Tag Unions - -Earlier we saw these two examples, one with an open tag union and the other with a closed one: +Also like with open records, you can name the type variable in an open tag union. For example: ```coffee -example : [Foo Str, Bar Bool]* -> Bool -example = \tag -> - when tag is - Foo str -> Str.isEmpty str - Bar bool -> bool - _ -> Bool.false -``` - -```coffee -example : [Foo Str, Bar Bool] -> Bool -example = \tag -> - when tag is - Foo str -> Str.isEmpty str - Bar bool -> bool -``` - -Similarly to how there are open records with a `*`, closed records with nothing, -and constrained records with a named type variable, we can also have *constrained tag unions* -with a named type variable. Here's an example: - -```coffee -example : [Foo Str, Bar Bool]a -> [Foo Str, Bar Bool]a -example = \tag -> - when tag is - Foo str -> Bar (Str.isEmpty str) - Bar _ -> Bar Bool.false +stopGoOther : [Red, Green]a -> [Stop, Go]a +stopGoOther = \color -> + when color is + Red -> Stop + Green -> Go other -> other ``` -This type says that the `example` function will take either a `Foo Str` tag, or a `Bar Bool` tag, -or possibly another tag we don't know about at compile time - and it also says that the function's -return type is the same as the type of its argument. +You can read this type annotation as "`stopGoOther` takes either a `Red` tag, a `Green` tag, or some other tag. It returns either a `Stop` tag, a `Go` tag, or any one of the tags it received in its argument." -So if we give this function a `[Foo Str, Bar Bool, Baz (List Str)]` argument, then it will be guaranteed -to return a `[Foo Str, Bar Bool, Baz (List Str)]` value. This is more constrained than a function that -returned `[Foo Str, Bar Bool]*` because that would say it could return *any* other tag (in addition to -the `Foo Str` and `Bar Bool` we already know about). +So let's say you called this `stopGoOther` function passing `Foo "hello"`. Then the `a` type variable would be the closed union `[Foo Str]`, and `stopGoOther` would return a union with the type `[Stop, Go][Foo Str]` - which is equivalent to `[Stop, Go, Foo Str]`. -If we removed the type annotation from `example` above, Roc's compiler would infer the same type anyway. -This may be surprising if you look closely at the body of the function, because: +Just like with records, you can replace the type variable in tag union types with a concrete type. +For example, `[Foo Str][Bar Bool][Baz (List Str)]` is equivalent to `[Foo Str, Bar Bool, Baz (List Str)]`. -- The return type includes `Foo Str`, but no branch explicitly returns `Foo`. Couldn't the return type be `[Bar Bool]a` instead? -- The argument type includes `Bar Bool` even though we never look at `Bar`'s payload. Couldn't the argument type be inferred to be `Bar *` instead of `Bar Bool`, since we never look at it? +Also just like with records, you can use this to compose tag union type aliases. For example, you can write `NetworkError : [Timeout, Disconnected]` and then `Problem : [InvalidInput, UnknownFormat]NetworkError`. -The reason it has this type is the `other -> other` branch. Take a look at that branch, and ask this question: -"What is the type of `other`?" There has to be exactly one answer! It can't be the case that `other` has one -type before the `->` and another type after it; whenever you see a named value in Roc, it is guaranteed to have -the same type everywhere it appears in that scope. +Note that that a function which accepts an open union does not accept "all possible tags." +For example, if I have a function `[Ok Str]* -> Bool` and I pass it +`Ok 5`, that will still be a type mismatch. A `when` on that function's argument might +have the branch `Ok str ->` which assumes there's a string inside that `Ok`, +and if `Ok 5` type-checked, then that assumption would be false and things would break! -For this reason, any time you see a function that only runs a `when` on its only argument, and that `when` -includes a branch like `x -> x` or `other -> other`, the function's argument type and return type must necessarily -be equivalent. +So `[Ok Str]*` is more restrictive than `[]*`. It's basically saying "this may or may not be an `Ok` tag, but if it _is_ an `Ok` tag, then it's guaranteed to have a payload of exactly `Str`." -> **Note:** Just like with records, you can also replace the type variable in tag union types with a concrete type. -> For example, `[Foo Str][Bar Bool][Baz (List Str)]` is equivalent to `[Foo Str, Bar Bool, Baz (List Str)]`. -> -> Also just like with records, you can use this to compose tag union type aliases. For example, you can write -> `NetworkError : [Timeout, Disconnected]` and then `Problem : [InvalidInput, UnknownFormat]NetworkError` +> **Note:** As with open and closed records, we can use type annotations to make tag union types less flexible +> than what the compiler would infer. For example, if we changed the type of the second +> `colorOrOther` function from the open `[Red, Green]*` to the closed `[Red, Green]`, Roc's compiler +> would accept it as a valid annotation, but it would give a warning that the `_ -> "other"` +> branch had become unreachable. ### Phantom Types diff --git a/ci/bench-runner/Cargo.lock b/ci/benchmarks/bench-runner/Cargo.lock similarity index 99% rename from ci/bench-runner/Cargo.lock rename to ci/benchmarks/bench-runner/Cargo.lock index 2ad03d6799..40c7a03862 100644 --- a/ci/bench-runner/Cargo.lock +++ b/ci/benchmarks/bench-runner/Cargo.lock @@ -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", diff --git a/ci/bench-runner/Cargo.toml b/ci/benchmarks/bench-runner/Cargo.toml similarity index 94% rename from ci/bench-runner/Cargo.toml rename to ci/benchmarks/bench-runner/Cargo.toml index ea665e8141..0534892f3c 100644 --- a/ci/bench-runner/Cargo.toml +++ b/ci/benchmarks/bench-runner/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [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" \ No newline at end of file diff --git a/ci/bench-runner/src/main.rs b/ci/benchmarks/bench-runner/src/main.rs similarity index 86% rename from ci/bench-runner/src/main.rs rename to ci/benchmarks/bench-runner/src/main.rs index 44d7e1c027..86bd30d82c 100644 --- a/ci/bench-runner/src/main.rs +++ b/ci/benchmarks/bench-runner/src/main.rs @@ -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 { 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 { )) .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 { "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(mut reader: R) -> Result { } fn sha_file(file_path: &Path) -> Result { - 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())) diff --git a/ci/benchmarks/prep_folder.sh b/ci/benchmarks/prep_folder.sh new file mode 100755 index 0000000000..926c000cc5 --- /dev/null +++ b/ci/benchmarks/prep_folder.sh @@ -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 \ No newline at end of file diff --git a/ci/enable-lld.sh b/ci/enable-lld.sh index 04377329b6..4567172783 100755 --- a/ci/enable-lld.sh +++ b/ci/enable-lld.sh @@ -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 diff --git a/ci/get_latest_release_url.sh b/ci/get_latest_release_url.sh index 4fc7f2383d..3d8e5f26b5 100755 --- a/ci/get_latest_release_url.sh +++ b/ci/get_latest_release_url.sh @@ -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" ]] diff --git a/ci/package_release.sh b/ci/package_release.sh index 646c39e9bd..ff3755f64e 100755 --- a/ci/package_release.sh +++ b/ci/package_release.sh @@ -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/helloWorld.roc examples/platform-switching examples/cli crates/roc_std diff --git a/ci/safe-earthly.sh b/ci/safe-earthly.sh deleted file mode 100755 index 0ddc13c5c0..0000000000 --- a/ci/safe-earthly.sh +++ /dev/null @@ -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 diff --git a/ci/write_version.sh b/ci/write_version.sh index 36ec1e9ffb..8e6f99fdc7 100755 --- a/ci/write_version.sh +++ b/ci/write_version.sh @@ -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 \ No newline at end of file diff --git a/ci/www-repl.sh b/ci/www-repl.sh index 221b708947..1cacbbaacc 100755 --- a/ci/www-repl.sh +++ b/ci/www-repl.sh @@ -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 diff --git a/crates/ast/src/lang/core/expr/expr_to_expr2.rs b/crates/ast/src/lang/core/expr/expr_to_expr2.rs index e47fe69bab..62d6a5d6dd 100644 --- a/crates/ast/src/lang/core/expr/expr_to_expr2.rs +++ b/crates/ast/src/lang/core/expr/expr_to_expr2.rs @@ -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); diff --git a/crates/ast/src/lang/core/expr/introduced_vars.rs b/crates/ast/src/lang/core/expr/introduced_vars.rs index 26bbe6b738..91d5b7cdbe 100644 --- a/crates/ast/src/lang/core/expr/introduced_vars.rs +++ b/crates/ast/src/lang/core/expr/introduced_vars.rs @@ -3,7 +3,7 @@ use roc_module::ident::Lowercase; use roc_module::symbol::Symbol; use roc_types::subs::Variable; -#[derive(Clone, Debug, PartialEq, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct IntroducedVariables { // Rigids must be unique within a type annotation. // E.g. in `identity : a -> a`, there should only be one diff --git a/crates/cli/src/build.rs b/crates/cli/src/build.rs index 475317ff1c..5161f6d00a 100644 --- a/crates/cli/src/build.rs +++ b/crates/cli/src/build.rs @@ -252,7 +252,9 @@ pub fn build_file<'a>( } let rebuild_timing = if linking_strategy == LinkingStrategy::Additive { - let rebuild_duration = rebuild_thread.join().unwrap(); + let rebuild_duration = rebuild_thread + .join() + .expect("Failed to (re)build platform."); if emit_timings && !prebuilt { println!( "Finished rebuilding the platform in {} ms\n", @@ -304,7 +306,7 @@ pub fn build_file<'a>( } if let HostRebuildTiming::ConcurrentWithApp(thread) = rebuild_timing { - let rebuild_duration = thread.join().unwrap(); + let rebuild_duration = thread.join().expect("Failed to (re)build platform."); if emit_timings && !prebuilt { println!( "Finished rebuilding the platform in {} ms\n", diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 3d6d62b6fd..11ed45bfe4 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -85,6 +85,12 @@ mod cli_run { let (before_first_digit, _) = err.split_at(err.rfind("found in ").unwrap()); let err = format!("{}found in ms.", before_first_digit); + // make paths consistent + let err = err.replace('\\', "/"); + + // consistency with typewriters, very important + let err = err.replace('\r', ""); + assert_multiline_str_eq!(err.as_str(), expected); } @@ -1148,7 +1154,7 @@ fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String { // .unwrap(); let store = Store::default(); - let module = Module::from_file(&store, &wasm_path).unwrap(); + let module = Module::from_file(&store, wasm_path).unwrap(); let mut fake_stdin = wasmer_wasi::Pipe::new(); let fake_stdout = wasmer_wasi::Pipe::new(); diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index 7aac57bcc4..95898767de 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -210,6 +210,7 @@ fn roc_fx_getInt_help() !i64 { const stdin = std.io.getStdIn().reader(); var buf: [40]u8 = undefined; + // 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); diff --git a/crates/cli_utils/Cargo.toml b/crates/cli_utils/Cargo.toml index ffa860fc02..5a19d43b6b 100644 --- a/crates/cli_utils/Cargo.toml +++ b/crates/cli_utils/Cargo.toml @@ -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"] } diff --git a/crates/cli_utils/src/helpers.rs b/crates/cli_utils/src/helpers.rs index 26071d7c20..0ae0157d66 100644 --- a/crates/cli_utils/src/helpers.rs +++ b/crates/cli_utils/src/helpers.rs @@ -4,6 +4,7 @@ extern crate roc_load; extern crate roc_module; extern crate tempfile; +use roc_utils::cargo; use serde::Deserialize; use serde_xml_rs::from_str; use std::env; @@ -48,7 +49,7 @@ where vec!["build", "--release", "--bin", "roc"] }; - let output = Command::new("cargo") + let output = cargo() .current_dir(root_project_dir) .args(args) .output() diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 9a9a4985e1..a31669a34f 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -4,21 +4,15 @@ use roc_builtins::bitcode; use roc_error_macros::internal_error; use roc_mono::ir::OptLevel; use roc_utils::get_lib_path; +use roc_utils::{cargo, clang, zig}; use std::collections::HashMap; use std::env; use std::io; use std::path::{Path, PathBuf}; -use std::process::{self, Child, Command, Output}; +use std::process::{self, Child, Command}; use target_lexicon::{Architecture, OperatingSystem, Triple}; use wasi_libc_sys::{WASI_COMPILER_RT_PATH, WASI_LIBC_PATH}; -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum LinkType { // These numbers correspond to the --lib and --no-link flags @@ -113,9 +107,9 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new(&zig_executable()); - command +) -> Command { + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home); @@ -130,7 +124,7 @@ pub fn build_zig_host_native( bitcode::get_builtins_host_obj_path() }; - command.args(&[ + zig_cmd.args([ "build-exe", "-fPIE", "-rdynamic", // make sure roc_alloc and friends are exposed @@ -138,12 +132,12 @@ pub fn build_zig_host_native( &builtins_obj, ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args([ zig_host_src, - emit_bin, + &format!("-femit-bin={}", emit_bin), "--pkg-begin", "str", zig_str_path, @@ -160,7 +154,7 @@ pub fn build_zig_host_native( // when we use zig 0.9. It looks like zig 0.10 is going to fix // this problem for us, so this is a temporary workaround if !target.contains("windows") { - command.args(&[ + zig_cmd.args([ // include the zig runtime "-fcompiler-rt", ]); @@ -168,16 +162,16 @@ pub fn build_zig_host_native( // valgrind does not yet support avx512 instructions, see #1963. if env::var("NO_AVX512").is_ok() { - command.args(&["-mcpu", "x86_64"]); + zig_cmd.args(["-mcpu", "x86_64"]); } if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(["-O", "ReleaseSmall"]); } - command.output().unwrap() + zig_cmd } #[cfg(windows)] @@ -191,27 +185,27 @@ pub fn build_zig_host_native( target: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new(&zig_executable()); - command +) -> Command { + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home); if let Some(shared_lib_path) = shared_lib_path { - command.args(&[ + zig_cmd.args(&[ "build-exe", // "-fPIE", PIE seems to fail on windows shared_lib_path.to_str().unwrap(), &bitcode::get_builtins_windows_obj_path(), ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(&["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args(&[ zig_host_src, - emit_bin, + &format!("-femit-bin={}", emit_bin), "--pkg-begin", "str", zig_str_path, @@ -227,12 +221,12 @@ pub fn build_zig_host_native( ]); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + zig_cmd } #[cfg(target_os = "macos")] @@ -247,14 +241,11 @@ pub fn build_zig_host_native( opt_level: OptLevel, shared_lib_path: Option<&Path>, // For compatibility with the non-macOS def above. Keep these in sync. -) -> Output { +) -> Command { use serde_json::Value; // Run `zig env` to find the location of zig's std/ directory - let zig_env_output = Command::new(&zig_executable()) - .args(&["env"]) - .output() - .unwrap(); + let zig_env_output = zig().args(&["env"]).output().unwrap(); let zig_env_json = if zig_env_output.status.success() { std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| { @@ -291,24 +282,24 @@ pub fn build_zig_host_native( zig_compiler_rt_path.push("special"); zig_compiler_rt_path.push("compiler_rt.zig"); - let mut command = Command::new(&zig_executable()); - command + let mut zig_cmd = zig(); + zig_cmd .env_clear() .env("PATH", &env_path) .env("HOME", &env_home); if let Some(shared_lib_path) = shared_lib_path { - command.args(&[ + zig_cmd.args(&[ "build-exe", "-fPIE", shared_lib_path.to_str().unwrap(), &bitcode::get_builtins_host_obj_path(), ]); } else { - command.args(&["build-obj", "-fPIC"]); + zig_cmd.args(&["build-obj", "-fPIC"]); } - command.args(&[ + zig_cmd.args(&[ zig_host_src, - emit_bin, + &format!("-femit-bin={}", emit_bin), "--pkg-begin", "str", zig_str_path, @@ -323,11 +314,12 @@ pub fn build_zig_host_native( "c", ]); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(&["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(&["-O", "ReleaseSmall"]); } - command.output().unwrap() + + zig_cmd } pub fn build_zig_host_wasm32( @@ -338,7 +330,7 @@ pub fn build_zig_host_wasm32( zig_str_path: &str, opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { +) -> Command { if shared_lib_path.is_some() { unimplemented!("Linking a shared library to wasm not yet implemented"); } @@ -358,7 +350,7 @@ pub fn build_zig_host_wasm32( // we'd like to compile with `-target wasm32-wasi` but that is blocked on // // https://github.com/ziglang/zig/issues/9414 - let mut command = Command::new(&zig_executable()); + let mut zig_cmd = zig(); let args = &[ "build-obj", zig_host_src, @@ -379,18 +371,19 @@ pub fn build_zig_host_wasm32( "--strip", ]; - command + zig_cmd .env_clear() .env("PATH", env_path) .env("HOME", env_home) .args(args); if matches!(opt_level, OptLevel::Optimize) { - command.args(&["-O", "ReleaseSafe"]); + zig_cmd.args(["-O", "ReleaseSafe"]); } else if matches!(opt_level, OptLevel::Size) { - command.args(&["-O", "ReleaseSmall"]); + zig_cmd.args(["-O", "ReleaseSmall"]); } - command.output().unwrap() + + zig_cmd } #[allow(clippy::too_many_arguments)] @@ -403,15 +396,15 @@ pub fn build_c_host_native( sources: &[&str], opt_level: OptLevel, shared_lib_path: Option<&Path>, -) -> Output { - let mut command = Command::new("clang"); - command +) -> Command { + let mut clang_cmd = clang(); + clang_cmd .env_clear() - .env("PATH", &env_path) - .env("CPATH", &env_cpath) - .env("HOME", &env_home) + .env("PATH", env_path) + .env("CPATH", env_cpath) + .env("HOME", env_home) .args(sources) - .args(&["-o", dest]); + .args(["-o", dest]); if let Some(shared_lib_path) = shared_lib_path { match target.operating_system { OperatingSystem::Windows => { @@ -432,7 +425,7 @@ pub fn build_c_host_native( ); } _ => { - command.args(&[ + clang_cmd.args([ shared_lib_path.to_str().unwrap(), // This line is commented out because // @bhansconnect: With the addition of Str.graphemes, always @@ -451,14 +444,15 @@ pub fn build_c_host_native( } } } else { - command.args(&["-fPIC", "-c"]); + clang_cmd.args(["-fPIC", "-c"]); } if matches!(opt_level, OptLevel::Optimize) { - command.arg("-O3"); + clang_cmd.arg("-O3"); } else if matches!(opt_level, OptLevel::Size) { - command.arg("-Os"); + clang_cmd.arg("-Os"); } - command.output().unwrap() + + clang_cmd } #[allow(clippy::too_many_arguments)] @@ -471,7 +465,7 @@ pub fn build_swift_host_native( shared_lib_path: Option<&Path>, objc_header_path: Option<&str>, arch: Architecture, -) -> Output { +) -> Command { if shared_lib_path.is_some() { unimplemented!("Linking a shared library to Swift not yet implemented"); } @@ -479,8 +473,8 @@ pub fn build_swift_host_native( let mut command = Command::new("arch"); command .env_clear() - .env("PATH", &env_path) - .env("HOME", &env_home); + .env("PATH", env_path) + .env("HOME", env_home); match arch { Architecture::Aarch64(_) => command.arg("-arm64"), @@ -493,10 +487,10 @@ pub fn build_swift_host_native( .args(sources) .arg("-emit-object") .arg("-parse-as-library") - .args(&["-o", dest]); + .args(["-o", dest]); if let Some(objc_header) = objc_header_path { - command.args(&["-import-objc-header", objc_header]); + command.args(["-import-objc-header", objc_header]); } if matches!(opt_level, OptLevel::Optimize) { @@ -505,7 +499,7 @@ pub fn build_swift_host_native( command.arg("-Osize"); } - command.output().unwrap() + command } pub fn rebuild_host( @@ -567,7 +561,7 @@ pub fn rebuild_host( &zig_str_path ); - let output = match target.architecture { + let zig_cmd = match target.architecture { Architecture::Wasm32 => { let emit_bin = if matches!(opt_level, OptLevel::Development) { format!("-femit-bin={}", host_dest.to_str().unwrap()) @@ -585,8 +579,6 @@ pub fn rebuild_host( ) } Architecture::X86_64 => { - let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap()); - let target = match target.operating_system { OperatingSystem::Windows => "x86_64-windows-gnu", _ => "native", @@ -595,7 +587,7 @@ pub fn rebuild_host( build_zig_host_native( &env_path, &env_home, - &emit_bin, + host_dest.to_str().unwrap(), zig_host_src.to_str().unwrap(), zig_str_path.to_str().unwrap(), target, @@ -603,37 +595,31 @@ pub fn rebuild_host( shared_lib_path, ) } - Architecture::X86_32(_) => { - let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap()); - build_zig_host_native( - &env_path, - &env_home, - &emit_bin, - zig_host_src.to_str().unwrap(), - zig_str_path.to_str().unwrap(), - "i386-linux-musl", - opt_level, - shared_lib_path, - ) - } + Architecture::X86_32(_) => build_zig_host_native( + &env_path, + &env_home, + host_dest.to_str().unwrap(), + zig_host_src.to_str().unwrap(), + zig_str_path.to_str().unwrap(), + "i386-linux-musl", + opt_level, + shared_lib_path, + ), - Architecture::Aarch64(_) => { - let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap()); - build_zig_host_native( - &env_path, - &env_home, - &emit_bin, - zig_host_src.to_str().unwrap(), - zig_str_path.to_str().unwrap(), - target_zig_str(target), - opt_level, - shared_lib_path, - ) - } + Architecture::Aarch64(_) => build_zig_host_native( + &env_path, + &env_home, + host_dest.to_str().unwrap(), + zig_host_src.to_str().unwrap(), + zig_str_path.to_str().unwrap(), + target_zig_str(target), + opt_level, + shared_lib_path, + ), _ => internal_error!("Unsupported architecture {:?}", target.architecture), }; - validate_output("host.zig", &zig_executable(), output) + run_build_command(zig_cmd, "host.zig") } else if cargo_host_src.exists() { // Compile and link Cargo.toml, if it exists let cargo_dir = host_input_path.parent().unwrap(); @@ -646,25 +632,23 @@ pub fn rebuild_host( }, ); - let mut command = Command::new("cargo"); - command.arg("build").current_dir(cargo_dir); + let mut cargo_cmd = cargo(); + cargo_cmd.arg("build").current_dir(cargo_dir); // Rust doesn't expose size without editing the cargo.toml. Instead just use release. if matches!(opt_level, OptLevel::Optimize | OptLevel::Size) { - command.arg("--release"); + cargo_cmd.arg("--release"); } let source_file = if shared_lib_path.is_some() { - command.env("RUSTFLAGS", "-C link-dead-code"); - command.args(&["--bin", "host"]); + cargo_cmd.env("RUSTFLAGS", "-C link-dead-code"); + cargo_cmd.args(["--bin", "host"]); "src/main.rs" } else { - command.arg("--lib"); + cargo_cmd.arg("--lib"); "src/lib.rs" }; - let output = command.output().unwrap(); - - validate_output(source_file, "cargo build", output); + run_build_command(cargo_cmd, source_file); if shared_lib_path.is_some() { // For surgical linking, just copy the dynamically linked rust app. @@ -674,7 +658,7 @@ pub fn rebuild_host( } else { // Cargo hosts depend on a c wrapper for the api. Compile host.c as well. - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -684,23 +668,22 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); - let output = Command::new("ld") - .env_clear() - .env("PATH", &env_path) - .args(&[ - "-r", - "-L", - cargo_out_dir.to_str().unwrap(), - c_host_dest.to_str().unwrap(), - "-lhost", - "-o", - host_dest.to_str().unwrap(), - ]) - .output() - .unwrap(); - validate_output("c_host.o", "ld", output); + run_build_command(clang_cmd, "host.c"); + + let mut ld_cmd = Command::new("ld"); + + ld_cmd.env_clear().env("PATH", &env_path).args([ + "-r", + "-L", + cargo_out_dir.to_str().unwrap(), + c_host_dest.to_str().unwrap(), + "-lhost", + "-o", + host_dest.to_str().unwrap(), + ]); + + run_build_command(ld_cmd, "c_host.o"); // Clean up c_host.o if c_host_dest.exists() { @@ -709,25 +692,24 @@ pub fn rebuild_host( } } else if rust_host_src.exists() { // Compile and link host.rs, if it exists - let mut command = Command::new("rustc"); - command.args(&[ + let mut rustc_cmd = Command::new("rustc"); + rustc_cmd.args([ rust_host_src.to_str().unwrap(), "-o", rust_host_dest.to_str().unwrap(), ]); if matches!(opt_level, OptLevel::Optimize) { - command.arg("-O"); + rustc_cmd.arg("-O"); } else if matches!(opt_level, OptLevel::Size) { - command.arg("-C opt-level=s"); + rustc_cmd.arg("-C opt-level=s"); } - let output = command.output().unwrap(); - validate_output("host.rs", "rustc", output); + run_build_command(rustc_cmd, "host.rs"); // Rust hosts depend on a c wrapper for the api. Compile host.c as well. if shared_lib_path.is_some() { // If compiling to executable, let c deal with linking as well. - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -740,9 +722,9 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); + run_build_command(clang_cmd, "host.c"); } else { - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -753,21 +735,19 @@ pub fn rebuild_host( shared_lib_path, ); - validate_output("host.c", "clang", output); - let output = Command::new("ld") - .env_clear() - .env("PATH", &env_path) - .args(&[ - "-r", - c_host_dest.to_str().unwrap(), - rust_host_dest.to_str().unwrap(), - "-o", - host_dest.to_str().unwrap(), - ]) - .output() - .unwrap(); + run_build_command(clang_cmd, "host.c"); - validate_output("rust_host.o", "ld", output); + let mut ld_cmd = Command::new("ld"); + + ld_cmd.env_clear().env("PATH", &env_path).args([ + "-r", + c_host_dest.to_str().unwrap(), + rust_host_dest.to_str().unwrap(), + "-o", + host_dest.to_str().unwrap(), + ]); + + run_build_command(ld_cmd, "rust_host.o"); } // Clean up rust_host.o and c_host.o @@ -779,7 +759,7 @@ pub fn rebuild_host( } } else if c_host_src.exists() { // Compile host.c, if it exists - let output = build_c_host_native( + let clang_cmd = build_c_host_native( target, &env_path, &env_home, @@ -789,10 +769,11 @@ pub fn rebuild_host( opt_level, shared_lib_path, ); - validate_output("host.c", "clang", output); + + run_build_command(clang_cmd, "host.c"); } else if swift_host_src.exists() { // Compile host.swift, if it exists - let output = build_swift_host_native( + let swiftc_cmd = build_swift_host_native( &env_path, &env_home, host_dest.to_str().unwrap(), @@ -804,7 +785,8 @@ pub fn rebuild_host( .then(|| swift_host_header_src.to_str().unwrap()), target.architecture, ); - validate_output("host.swift", "swiftc", output); + + run_build_command(swiftc_cmd, "host.swift"); } host_dest @@ -873,10 +855,10 @@ fn link_linux( if let Architecture::X86_32(_) = target.architecture { return Ok(( - Command::new(&zig_executable()) - .args(&["build-exe"]) + zig() + .args(["build-exe"]) .args(input_paths) - .args(&[ + .args([ "-target", "i386-linux-musl", "-lc", @@ -1029,7 +1011,7 @@ fn link_linux( .filter(|&(ref k, _)| k.starts_with("NIX_")) .collect::>(), ) - .args(&[ + .args([ "--gc-sections", "--eh-frame-hdr", "-A", @@ -1039,11 +1021,11 @@ fn link_linux( &*crtn_path.to_string_lossy(), ]) .args(&base_args) - .args(&["-dynamic-linker", ld_linux]) + .args(["-dynamic-linker", ld_linux]) .args(input_paths) // ld.lld requires this argument, and does not accept --arch // .args(&["-L/usr/lib/x86_64-linux-gnu"]) - .args(&[ + .args([ // Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496365925 // for discussion and further references "-lc", @@ -1094,7 +1076,7 @@ fn link_macos( // The `-l` flags should go after the `.o` arguments // Don't allow LD_ env vars to affect this .env_clear() - .args(&[ + .args([ // NOTE: we don't do --gc-sections on macOS because the default // macOS linker doesn't support it, but it's a performance // optimization, so if we ever switch to a different linker, @@ -1126,7 +1108,7 @@ fn link_macos( ld_command.arg(roc_link_flag); } - ld_command.args(&[ + ld_command.args([ // Libraries - see https://github.com/roc-lang/roc/pull/554#discussion_r496392274 // for discussion and further references "-lSystem", @@ -1166,7 +1148,7 @@ fn link_macos( Architecture::Aarch64(_) => { ld_child.wait()?; let codesign_child = Command::new("codesign") - .args(&["-s", "-", output_path.to_str().unwrap()]) + .args(["-s", "-", output_path.to_str().unwrap()]) .spawn()?; Ok((codesign_child, output_path)) @@ -1202,10 +1184,10 @@ fn link_wasm32( let zig_str_path = find_zig_str_path(); let wasi_libc_path = find_wasi_libc_path(); - let child = Command::new(&zig_executable()) + let child = zig() // .env_clear() // .env("PATH", &env_path) - .args(&["build-exe"]) + .args(["build-exe"]) .args(input_paths) .args([ // include wasi libc @@ -1239,8 +1221,8 @@ fn link_windows( match link_type { LinkType::Dylib => { - let child = Command::new(&zig_executable()) - .args(&["build-lib"]) + let child = zig() + .args(["build-lib"]) .args(input_paths) .args([ "-lc", @@ -1261,8 +1243,8 @@ fn link_windows( Ok((child, output_path)) } LinkType::Executable => { - let child = Command::new(&zig_executable()) - .args(&["build-exe"]) + let child = zig() + .args(["build-exe"]) .args(input_paths) .args([ "-target", @@ -1349,7 +1331,7 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P (but seems to be an unofficial API) */ - let mut command = Command::new(&zig_executable()); + let mut zig_cmd = zig(); let args = &[ "wasm-ld", &bitcode::get_builtins_wasm32_obj_path(), @@ -1364,28 +1346,30 @@ pub fn preprocess_host_wasm32(host_input_path: &Path, preprocessed_host_path: &P "--relocatable", ]; - command.args(args); + zig_cmd.args(args); // println!("\npreprocess_host_wasm32"); // println!("zig {}\n", args.join(" ")); - let output = command.output().unwrap(); - validate_output(output_file, "zig", output) + run_build_command(zig_cmd, output_file) } -fn validate_output(file_name: &str, cmd_name: &str, output: Output) { - if !output.status.success() { - match std::str::from_utf8(&output.stderr) { +fn run_build_command(mut command: Command, file_to_build: &str) { + let cmd_str = format!("{:?}", &command); + let cmd_output = command.output().unwrap(); + + if !cmd_output.status.success() { + match std::str::from_utf8(&cmd_output.stderr) { Ok(stderr) => internal_error!( - "Failed to rebuild {} - stderr of the `{}` command was:\n{}", - file_name, - cmd_name, + "Error:\n Failed to rebuild {}:\n The executed command was:\n {}\n stderr of that command:\n {}", + file_to_build, + cmd_str, stderr ), Err(utf8_err) => internal_error!( - "Failed to rebuild {} - stderr of the `{}` command was invalid utf8 ({:?})", - file_name, - cmd_name, + "Error:\n Failed to rebuild {}:\n The executed command was:\n {}\n stderr of that command could not be parsed as valid utf8:\n {}", + file_to_build, + cmd_str, utf8_err ), } diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 50ddf387c0..99bb3d62fa 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -347,7 +347,7 @@ fn gen_from_mono_module_llvm( // run the debugir https://github.com/vaivaswatha/debugir tool match Command::new("debugir") - .args(&["-instnamer", app_ll_file.to_str().unwrap()]) + .args(["-instnamer", app_ll_file.to_str().unwrap()]) .output() { Ok(_) => {} @@ -369,7 +369,7 @@ fn gen_from_mono_module_llvm( | Architecture::Aarch64(_) | Architecture::Wasm32 => { let ll_to_bc = Command::new("llvm-as") - .args(&[ + .args([ app_ll_dbg_file.to_str().unwrap(), "-o", app_bc_file.to_str().unwrap(), diff --git a/crates/compiler/builtins/Cargo.toml b/crates/compiler/builtins/Cargo.toml index 840cce6f60..1c63f517bf 100644 --- a/crates/compiler/builtins/Cargo.toml +++ b/crates/compiler/builtins/Cargo.toml @@ -16,6 +16,7 @@ lazy_static = "1.4.0" [build-dependencies] # dunce can be removed once ziglang/zig#5109 is fixed dunce = "1.0.3" +roc_utils = { path = "../../utils" } [target.'cfg(target_os = "macos")'.build-dependencies] tempfile = "3.2.0" diff --git a/crates/compiler/builtins/bitcode/benchmark.sh b/crates/compiler/builtins/bitcode/benchmark.sh index ae6ff3297b..410188d451 100755 --- a/crates/compiler/builtins/bitcode/benchmark.sh +++ b/crates/compiler/builtins/bitcode/benchmark.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail zig build-exe benchmark/dec.zig -O ReleaseFast --main-pkg-path . diff --git a/crates/compiler/builtins/build.rs b/crates/compiler/builtins/build.rs index 63b8ceb4cf..b72aa3673f 100644 --- a/crates/compiler/builtins/build.rs +++ b/crates/compiler/builtins/build.rs @@ -1,6 +1,5 @@ -use std::convert::AsRef; +use roc_utils::zig; use std::env; -use std::ffi::OsStr; use std::fs; use std::io; use std::path::Path; @@ -14,13 +13,6 @@ use tempfile::tempdir; /// To debug the zig code with debug prints, we need to disable the wasm code gen const DEBUG: bool = false; -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -95,12 +87,13 @@ fn generate_object_file(bitcode_path: &Path, zig_object: &str, object_file_name: println!("Compiling zig object `{}` to: {}", zig_object, src_obj); if !DEBUG { - run_command( - &bitcode_path, - &zig_executable(), - &["build", zig_object, "-Drelease=true"], - 0, - ); + let mut zig_cmd = zig(); + + zig_cmd + .current_dir(bitcode_path) + .args(["build", zig_object, "-Drelease=true"]); + + run_command(zig_cmd, 0); println!("Moving zig object `{}` to: {}", zig_object, dest_obj); @@ -130,12 +123,13 @@ fn generate_bc_file(bitcode_path: &Path, zig_object: &str, file_name: &str) { #[cfg(target_os = "macos")] let _ = fs::remove_dir_all("./bitcode/zig-cache"); - run_command( - &bitcode_path, - &zig_executable(), - &["build", zig_object, "-Drelease=true"], - 0, - ); + let mut zig_cmd = zig(); + + zig_cmd + .current_dir(bitcode_path) + .args(["build", zig_object, "-Drelease=true"]); + + run_command(zig_cmd, 0); } pub fn get_lib_dir() -> PathBuf { @@ -174,7 +168,7 @@ fn copy_zig_builtins_to_target_dir(bitcode_path: &Path) { // recursively copy all the .zig files from this directory, but do *not* recurse into zig-cache/ fn cp_unless_zig_cache(src_dir: &Path, target_dir: &Path) -> io::Result<()> { // Make sure the destination directory exists before we try to copy anything into it. - std::fs::create_dir_all(&target_dir).unwrap_or_else(|err| { + std::fs::create_dir_all(target_dir).unwrap_or_else(|err| { panic!( "Failed to create output library directory for zig bitcode {:?}: {:?}", target_dir, err @@ -204,19 +198,10 @@ fn cp_unless_zig_cache(src_dir: &Path, target_dir: &Path) -> io::Result<()> { Ok(()) } -fn run_command + Copy>( - path: P, - command_str: &str, - args: I, - flaky_fail_counter: usize, -) where - I: IntoIterator, - S: AsRef, -{ - let output_result = Command::new(OsStr::new(&command_str)) - .current_dir(path) - .args(args) - .output(); +fn run_command(mut command: Command, flaky_fail_counter: usize) { + let command_str = format!("{:?}", &command); + + let output_result = command.output(); match output_result { Ok(output) => match output.status.success() { @@ -227,14 +212,14 @@ fn run_command + Copy>( Err(_) => format!("Failed to run \"{}\"", command_str), }; - // flaky test error that only occurs sometimes inside MacOS ci run + // Flaky test errors that only occur sometimes on MacOS ci server. if error_str.contains("FileNotFound") || error_str.contains("unable to save cached ZIR code") { if flaky_fail_counter == 10 { panic!("{} failed 10 times in a row. The following error is unlikely to be a flaky error: {}", command_str, error_str); } else { - run_command(path, command_str, args, flaky_fail_counter + 1) + run_command(command, flaky_fail_counter + 1) } } else { panic!("{} failed: {}", command_str, error_str); diff --git a/crates/compiler/builtins/roc/Dict.roc b/crates/compiler/builtins/roc/Dict.roc index cb83d06b7c..e217f87e0d 100644 --- a/crates/compiler/builtins/roc/Dict.roc +++ b/crates/compiler/builtins/roc/Dict.roc @@ -223,18 +223,18 @@ expect update (single "a" Bool.true) "a" alterValue == empty ## Dict.empty ## |> Dict.insert 1234 "5678" ## |> Dict.contains 1234 -## |> Bool.isEq Bool.true contains : Dict k v, k -> Bool | k has Eq contains = \@Dict list, needle -> - step = \_, Pair key _val -> - if key == needle then - Break {} - else - Continue {} + List.any list \Pair key _val -> key == needle - when List.iterate list {} step is - Continue _ -> Bool.false - Break _ -> Bool.true +expect contains empty "a" == Bool.false +expect contains (single "a" {}) "a" == Bool.true +expect contains (single "b" {}) "a" == Bool.false +expect + Dict.empty + |> Dict.insert 1234 "5678" + |> Dict.contains 1234 + |> Bool.isEq Bool.true ## Returns a dictionary containing the key and value provided as input. ## diff --git a/crates/compiler/builtins/roc/List.roc b/crates/compiler/builtins/roc/List.roc index 0e06253a22..e968d35712 100644 --- a/crates/compiler/builtins/roc/List.roc +++ b/crates/compiler/builtins/roc/List.roc @@ -8,7 +8,6 @@ interface List map, len, withCapacity, - iterate, walkBackwards, concat, first, diff --git a/crates/compiler/can/src/abilities.rs b/crates/compiler/can/src/abilities.rs index cab1a7d929..1dde05adab 100644 --- a/crates/compiler/can/src/abilities.rs +++ b/crates/compiler/can/src/abilities.rs @@ -28,7 +28,7 @@ pub struct MemberVariables { /// The member and its signature is defined locally, in the module the store is created for. /// We need to instantiate and introduce this during solving. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ResolvedMemberType(Variable); /// Member type information that needs to be resolved from imports. @@ -56,7 +56,7 @@ impl ResolvePhase for Pending { type MemberType = PendingMemberType; } -#[derive(Default, Debug, Clone, Copy, PartialEq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub struct Resolved; impl ResolvePhase for Resolved { type MemberType = ResolvedMemberType; diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 7e8842901c..a68f5d6f28 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -13,13 +13,14 @@ use roc_types::types::{Category, PatternCategory, Type}; pub struct Constraints { pub constraints: Vec, pub types: Vec>, + pub type_slices: Vec, pub variables: Vec, pub loc_symbols: Vec<(Symbol, Region)>, pub let_constraints: Vec, pub categories: Vec, pub pattern_categories: Vec, - pub expectations: Vec>>, - pub pattern_expectations: Vec>>, + pub expectations: Vec>, + pub pattern_expectations: Vec>, pub includes_tags: Vec, pub strings: Vec<&'static str>, pub sketched_rows: Vec, @@ -33,6 +34,7 @@ impl std::fmt::Debug for Constraints { f.debug_struct("Constraints") .field("constraints", &self.constraints) .field("types", &"") + .field("type_slices", &self.type_slices) .field("variables", &self.variables) .field("loc_symbols", &self.loc_symbols) .field("let_constraints", &self.let_constraints) @@ -56,15 +58,15 @@ impl Default for Constraints { } } -pub type TypeIndex = Index>; -pub type ExpectedTypeIndex = Index>>; -pub type PExpectedTypeIndex = Index>>; +pub type ExpectedTypeIndex = Index>; +pub type PExpectedTypeIndex = Index>; pub type TypeOrVar = EitherIndex, Variable>; impl Constraints { pub fn new() -> Self { let constraints = Vec::new(); let mut types = Vec::new(); + let type_slices = Vec::with_capacity(16); let variables = Vec::new(); let loc_symbols = Vec::new(); let let_constraints = Vec::new(); @@ -119,6 +121,7 @@ impl Constraints { Self { constraints, types, + type_slices, variables, loc_symbols, let_constraints, @@ -211,12 +214,12 @@ impl Constraints { EitherIndex::from_right(index) } - pub fn push_expected_type(&mut self, expected: Expected) -> ExpectedTypeIndex { - Index::push_new(&mut self.expectations, expected.map(Cell::new)) + pub fn push_expected_type(&mut self, expected: Expected) -> ExpectedTypeIndex { + Index::push_new(&mut self.expectations, expected) } - pub fn push_pat_expected_type(&mut self, expected: PExpected) -> PExpectedTypeIndex { - Index::push_new(&mut self.pattern_expectations, expected.map(Cell::new)) + pub fn push_pat_expected_type(&mut self, expected: PExpected) -> PExpectedTypeIndex { + Index::push_new(&mut self.pattern_expectations, expected) } #[inline(always)] @@ -372,24 +375,24 @@ impl Constraints { fn def_types_slice(&mut self, it: I) -> DefTypes where - I: IntoIterator)>, + I: IntoIterator)>, I::IntoIter: ExactSizeIterator, { let it = it.into_iter(); - let types_start = self.types.len(); + let types_start = self.type_slices.len(); let loc_symbols_start = self.loc_symbols.len(); // because we have an ExactSizeIterator, we can reserve space here let length = it.len(); - self.types.reserve(length); + self.type_slices.reserve(length); self.loc_symbols.reserve(length); for (symbol, loc_type) in it { let Loc { region, value } = loc_type; - self.types.push(Cell::new(value)); + self.type_slices.push(value); self.loc_symbols.push((symbol, region)); } @@ -460,7 +463,7 @@ impl Constraints { where I1: IntoIterator, I2: IntoIterator, - I3: IntoIterator)>, + I3: IntoIterator)>, I3::IntoIter: ExactSizeIterator, { // defs and ret constraint are stored consequtively, so we only need to store one index @@ -506,7 +509,7 @@ impl Constraints { ) -> Constraint where I1: IntoIterator, - I2: IntoIterator)>, + I2: IntoIterator)>, I2::IntoIter: ExactSizeIterator, { // defs and ret constraint are stored consequtively, so we only need to store one index @@ -615,8 +618,8 @@ impl Constraints { real_var: Variable, real_region: Region, category_and_expectation: Result< - (Category, Expected), - (PatternCategory, PExpected), + (Category, ExpectedTypeIndex), + (PatternCategory, PExpectedTypeIndex), >, sketched_rows: SketchedRows, context: ExhaustiveContext, @@ -628,15 +631,12 @@ impl Constraints { let equality = match category_and_expectation { Ok((category, expected)) => { let category = Index::push_new(&mut self.categories, category); - let expected = Index::push_new(&mut self.expectations, expected.map(Cell::new)); let equality = Eq(real_var, expected, category, real_region); let equality = Index::push_new(&mut self.eq, equality); Ok(equality) } Err((category, expected)) => { let category = Index::push_new(&mut self.pattern_categories, category); - let expected = - Index::push_new(&mut self.pattern_expectations, expected.map(Cell::new)); let equality = PatternEq(real_var, expected, category, real_region); let equality = Index::push_new(&mut self.pattern_eq, equality); Err(equality) @@ -679,7 +679,7 @@ roc_error_macros::assert_sizeof_aarch64!(Constraint, 3 * 8); #[derive(Clone, Copy, Debug)] pub struct Eq( pub TypeOrVar, - pub Index>>, + pub ExpectedTypeIndex, pub Index, pub Region, ); @@ -759,7 +759,7 @@ pub enum Constraint { #[derive(Debug, Clone, Copy, Default)] pub struct DefTypes { - pub types: Slice, + pub types: Slice, pub loc_symbols: Slice<(Symbol, Region)>, } diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index d259d37699..b44952ce50 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -1,7 +1,7 @@ use crate::{ def::Def, expr::{AccessorData, ClosureData, Expr, Field, OpaqueWrapFunctionData, WhenBranchPattern}, - pattern::{DestructType, Pattern, RecordDestruct}, + pattern::{DestructType, ListPatterns, Pattern, RecordDestruct}, }; use roc_module::{ ident::{Lowercase, TagName}, @@ -707,6 +707,18 @@ fn deep_copy_pattern_help( }) .collect(), }, + List { + list_var, + elem_var, + patterns: ListPatterns { patterns, opt_rest }, + } => List { + list_var: sub!(*list_var), + elem_var: sub!(*elem_var), + patterns: ListPatterns { + patterns: patterns.iter().map(|lp| lp.map(|p| go_help!(p))).collect(), + opt_rest: *opt_rest, + }, + }, NumLiteral(var, s, n, bound) => NumLiteral(sub!(*var), s.clone(), *n, *bound), IntLiteral(v1, v2, s, n, bound) => IntLiteral(sub!(*v1), sub!(*v2), s.clone(), *n, *bound), FloatLiteral(v1, v2, s, n, bound) => { diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index b5a86c348c..24fba9813d 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1821,7 +1821,7 @@ pub(crate) fn sort_can_defs( .strongly_connected_components_subset(group); debug_assert!( - !group.iter_ones().any(|index| matches!((&defs[index]).as_ref().unwrap().loc_pattern.value, Pattern::AbilityMemberSpecialization{..})), + !group.iter_ones().any(|index| matches!(defs[index].as_ref().unwrap().loc_pattern.value, Pattern::AbilityMemberSpecialization{..})), "A specialization is involved in a recursive cycle - this should not be knowable until solving"); let declaration = if direct_sccs.groups().count() == 1 { @@ -1921,6 +1921,14 @@ fn pattern_to_vars_by_symbol( } } + List { + patterns, elem_var, .. + } => { + for pat in patterns.patterns.iter() { + pattern_to_vars_by_symbol(vars_by_symbol, &pat.value, *elem_var); + } + } + NumLiteral(..) | IntLiteral(..) | FloatLiteral(..) diff --git a/crates/compiler/can/src/exhaustive.rs b/crates/compiler/can/src/exhaustive.rs index b094d6b7a5..476fef14d1 100644 --- a/crates/compiler/can/src/exhaustive.rs +++ b/crates/compiler/can/src/exhaustive.rs @@ -4,11 +4,14 @@ use roc_collections::all::HumanIndex; use roc_collections::VecMap; use roc_error_macros::internal_error; use roc_exhaustive::{ - is_useful, Ctor, CtorName, Error, Guard, Literal, Pattern, RenderAs, TagId, Union, + is_useful, Ctor, CtorName, Error, Guard, ListArity, Literal, Pattern, RenderAs, TagId, Union, }; -use roc_module::ident::{TagIdIntType, TagName}; +use roc_module::ident::{Lowercase, TagIdIntType, TagName}; +use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; -use roc_types::subs::{Content, FlatType, RedundantMark, Subs, SubsFmtContent, Variable}; +use roc_types::subs::{ + Content, FlatType, GetSubsSlice, RedundantMark, Subs, SubsFmtContent, Variable, +}; use roc_types::types::AliasKind; pub use roc_exhaustive::Context as ExhaustiveContext; @@ -22,12 +25,19 @@ pub struct ExhaustiveSummary { pub redundancies: Vec, } +#[derive(Debug)] +pub struct TypeError; + +/// Exhaustiveness-checks [sketched rows][SketchedRows] against an expected type. +/// +/// Returns an error if the sketch has a type error, in which case exhautiveness checking will not +/// have been performed. pub fn check( subs: &Subs, real_var: Variable, sketched_rows: SketchedRows, context: ExhaustiveContext, -) -> ExhaustiveSummary { +) -> Result { let overall_region = sketched_rows.overall_region; let mut all_errors = Vec::with_capacity(1); @@ -35,7 +45,7 @@ pub fn check( non_redundant_rows, errors, redundancies, - } = sketched_rows.reify_to_non_redundant(subs, real_var); + } = sketched_rows.reify_to_non_redundant(subs, real_var)?; all_errors.extend(errors); let exhaustive = match roc_exhaustive::check(overall_region, context, non_redundant_rows) { @@ -46,11 +56,11 @@ pub fn check( } }; - ExhaustiveSummary { + Ok(ExhaustiveSummary { errors: all_errors, exhaustive, redundancies, - } + }) } #[derive(Clone, Debug, PartialEq, Eq)] @@ -60,7 +70,8 @@ enum SketchedPattern { /// A constructor whose expected union is not yet known. /// We'll know the whole union when reifying the sketched pattern against an expected case type. Ctor(TagName, Vec), - KnownCtor(Union, IndexCtor<'static>, TagId, Vec), + KnownCtor(Union, TagId, Vec), + List(ListArity, Vec), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -68,12 +79,42 @@ enum IndexCtor<'a> { /// Index an opaque type. There should be one argument. Opaque, /// Index a record type. The arguments are the types of the record fields. - Record, + Record(&'a [Lowercase]), /// Index a guard constructor. The arguments are a faux guard pattern, and then the real /// pattern being guarded. E.g. `A B if g` becomes Guard { [True, (A B)] }. Guard, /// Index a tag union with the given tag constructor. Tag(&'a TagName), + /// Index a list type. The argument is the element type. + List, +} + +impl<'a> IndexCtor<'a> { + fn of_union(un: &'a Union, tag_id: TagId) -> Self { + let Union { + alternatives, + render_as, + } = un; + + match render_as { + RenderAs::Tag => { + let tag_name = alternatives + .iter() + .find(|ctor| ctor.tag_id == tag_id) + .map(|Ctor { name, .. }| match name { + CtorName::Tag(tag) => tag, + CtorName::Opaque(_) => { + internal_error!("tag union should never have opaque alternative") + } + }) + .expect("indexable tag ID must be known to alternatives"); + Self::Tag(tag_name) + } + RenderAs::Opaque => Self::Opaque, + RenderAs::Record(fields) => Self::Record(fields), + RenderAs::Guard => Self::Guard, + } + } } /// Index a variable as a certain constructor, to get the expected argument types of that constructor. @@ -82,11 +123,11 @@ fn index_var( mut var: Variable, ctor: IndexCtor, render_as: &RenderAs, -) -> Vec { +) -> Result, TypeError> { if matches!(ctor, IndexCtor::Guard) { // `A B if g` becomes Guard { [True, (A B)] }, so the arguments are a bool, and the type // of the pattern. - return vec![Variable::BOOL, var]; + return Ok(vec![Variable::BOOL, var]); } loop { match subs.get_content_without_compacting(var) { @@ -95,10 +136,8 @@ fn index_var( | Content::FlexAbleVar(_, _) | Content::RigidAbleVar(_, _) | Content::LambdaSet(_) - | Content::RangedNumber(..) => internal_error!("not a indexable constructor"), - Content::Error => { - internal_error!("errors should not be reachable during exhautiveness checking") - } + | Content::RangedNumber(..) => return Err(TypeError), + Content::Error => return Err(TypeError), Content::RecursionVar { structure, opt_name: _, @@ -106,14 +145,19 @@ fn index_var( var = *structure; } Content::Structure(structure) => match structure { - FlatType::Apply(_, _) - | FlatType::Func(_, _, _) - | FlatType::FunctionOrTagUnion(_, _, _) => { - internal_error!("not an indexable constructor") + FlatType::Func(_, _, _) | FlatType::FunctionOrTagUnion(_, _, _) => { + return Err(TypeError) } - FlatType::Erroneous(_) => { - internal_error!("errors should not be reachable during exhautiveness checking") + FlatType::Erroneous(_) => return Err(TypeError), + FlatType::Apply(Symbol::LIST_LIST, args) => { + match (subs.get_subs_slice(*args), ctor) { + ([elem_var], IndexCtor::List) => { + return Ok(vec![*elem_var]); + } + _ => internal_error!("list types can only be indexed by list patterns"), + } } + FlatType::Apply(..) => internal_error!("not an indexable constructor"), FlatType::Record(fields, ext) => { let fields_order = match render_as { RenderAs::Record(fields) => fields, @@ -137,7 +181,7 @@ fn index_var( }) .collect(); - return field_types; + return Ok(field_types); } FlatType::TagUnion(tags, ext) | FlatType::RecursiveTagUnion(_, tags, ext) => { let tag_ctor = match ctor { @@ -155,10 +199,10 @@ fn index_var( } }); let vars = opt_vars.expect("constructor must be known in the indexable type if we are exhautiveness checking"); - return vars; + return Ok(vars); } FlatType::EmptyRecord => { - debug_assert!(matches!(ctor, IndexCtor::Record)); + debug_assert!(matches!(ctor, IndexCtor::Record(..))); // If there are optional record fields we don't unify them, but we need to // cover them. Since optional fields correspond to "any" patterns, we can pass // through arbitrary types. @@ -168,7 +212,7 @@ fn index_var( "record constructors must always be rendered as records" ), }; - return std::iter::repeat(Variable::NULL).take(num_fields).collect(); + return Ok(std::iter::repeat(Variable::NULL).take(num_fields).collect()); } FlatType::EmptyTagUnion => { internal_error!("empty tag unions are not indexable") @@ -176,7 +220,7 @@ fn index_var( }, Content::Alias(_, _, var, AliasKind::Opaque) => { debug_assert!(matches!(ctor, IndexCtor::Opaque)); - return vec![*var]; + return Ok(vec![*var]); } Content::Alias(_, _, inner, AliasKind::Structural) => { var = *inner; @@ -186,35 +230,44 @@ fn index_var( } impl SketchedPattern { - fn reify(self, subs: &Subs, real_var: Variable) -> Pattern { + fn reify(self, subs: &Subs, real_var: Variable) -> Result { match self { - Self::Anything => Pattern::Anything, - Self::Literal(lit) => Pattern::Literal(lit), - Self::KnownCtor(union, index_ctor, tag_id, patterns) => { - let arg_vars = index_var(subs, real_var, index_ctor, &union.render_as); + Self::Anything => Ok(Pattern::Anything), + Self::Literal(lit) => Ok(Pattern::Literal(lit)), + Self::KnownCtor(union, tag_id, patterns) => { + let index_ctor = IndexCtor::of_union(&union, tag_id); + let arg_vars = index_var(subs, real_var, index_ctor, &union.render_as)?; debug_assert!(arg_vars.len() == patterns.len()); let args = (patterns.into_iter()) .zip(arg_vars) - .map(|(pat, var)| { - // FIXME - pat.reify(subs, var) - }) - .collect(); + .map(|(pat, var)| pat.reify(subs, var)) + .collect::, _>>()?; - Pattern::Ctor(union, tag_id, args) + Ok(Pattern::Ctor(union, tag_id, args)) } Self::Ctor(tag_name, patterns) => { - let arg_vars = index_var(subs, real_var, IndexCtor::Tag(&tag_name), &RenderAs::Tag); + let arg_vars = + index_var(subs, real_var, IndexCtor::Tag(&tag_name), &RenderAs::Tag)?; let (union, tag_id) = convert_tag(subs, real_var, &tag_name); debug_assert!(arg_vars.len() == patterns.len()); let args = (patterns.into_iter()) .zip(arg_vars) .map(|(pat, var)| pat.reify(subs, var)) - .collect(); + .collect::, _>>()?; - Pattern::Ctor(union, tag_id, args) + Ok(Pattern::Ctor(union, tag_id, args)) + } + Self::List(arity, patterns) => { + let elem_var = index_var(subs, real_var, IndexCtor::List, &RenderAs::Tag)?[0]; + + let patterns = patterns + .into_iter() + .map(|pat| pat.reify(subs, elem_var)) + .collect::, _>>()?; + + Ok(Pattern::List(arity, patterns)) } } } @@ -235,7 +288,11 @@ pub struct SketchedRows { } impl SketchedRows { - fn reify_to_non_redundant(self, subs: &Subs, real_var: Variable) -> NonRedundantSummary { + fn reify_to_non_redundant( + self, + subs: &Subs, + real_var: Variable, + ) -> Result { to_nonredundant_rows(subs, real_var, self) } } @@ -283,7 +340,23 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern { }], }; - SP::KnownCtor(union, IndexCtor::Record, tag_id, patterns) + SP::KnownCtor(union, tag_id, patterns) + } + + List { + patterns, + list_var: _, + elem_var: _, + } => { + let arity = patterns.arity(); + + let sketched_elem_patterns = patterns + .patterns + .iter() + .map(|p| sketch_pattern(&p.value)) + .collect(); + + SP::List(arity, sketched_elem_patterns) } AppliedTag { @@ -315,12 +388,7 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern { }], }; - SP::KnownCtor( - union, - IndexCtor::Opaque, - tag_id, - vec![sketch_pattern(&argument.value)], - ) + SP::KnownCtor(union, tag_id, vec![sketch_pattern(&argument.value)]) } // Treat this like a literal so we mark it as non-exhaustive @@ -390,7 +458,6 @@ pub fn sketch_when_branches(region: Region, patterns: &[expr::WhenBranch]) -> Sk vec![SP::KnownCtor( union, - IndexCtor::Guard, tag_id, // NB: ordering the guard pattern first seems to be better at catching // non-exhaustive constructors in the second argument; see the paper to see if @@ -445,7 +512,7 @@ fn to_nonredundant_rows( subs: &Subs, real_var: Variable, rows: SketchedRows, -) -> NonRedundantSummary { +) -> Result { let SketchedRows { rows, overall_region, @@ -468,7 +535,7 @@ fn to_nonredundant_rows( let next_row: Vec = patterns .into_iter() .map(|pattern| pattern.reify(subs, real_var)) - .collect(); + .collect::>()?; let redundant_err = if !is_inhabited_row(&next_row) { Some(Error::Unmatchable { @@ -499,11 +566,11 @@ fn to_nonredundant_rows( } } - NonRedundantSummary { + Ok(NonRedundantSummary { non_redundant_rows: checked_rows, redundancies, errors, - } + }) } fn is_inhabited_row(patterns: &[Pattern]) -> bool { @@ -518,10 +585,16 @@ fn is_inhabited_pattern(pat: &Pattern) -> bool { Pattern::Literal(_) => {} Pattern::Ctor(union, id, pats) => { if !union.alternatives.iter().any(|alt| alt.tag_id == *id) { + // The tag ID was dropped from the union, which means that this tag ID is one + // that is not material to the union, and so is uninhabited! return false; } stack.extend(pats); } + Pattern::List(_, pats) => { + // List is uninhabited if any element is uninhabited. + stack.extend(pats); + } } } true diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 4e7e9d13d4..fad9e313df 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -65,7 +65,7 @@ impl Output { } } -#[derive(Clone, Debug, PartialEq, Copy)] +#[derive(Clone, Debug, PartialEq, Eq, Copy)] pub enum IntValue { I128([u8; 16]), U128([u8; 16]), @@ -345,7 +345,7 @@ pub struct ClosureData { /// /// We distinguish them from closures so we can have better error messages /// during constraint generation. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct AccessorData { pub name: Symbol, pub function_var: Variable, @@ -485,7 +485,7 @@ pub struct Field { pub loc_expr: Box>, } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Recursive { NotRecursive = 0, Recursive = 1, @@ -888,7 +888,7 @@ pub fn canonicalize_expr<'a>( var_store, inner_scope, region, - *branch, + branch, &mut output, ) }); @@ -1493,7 +1493,7 @@ fn canonicalize_fields<'a>( let mut output = Output::default(); for loc_field in fields.iter() { - match canonicalize_field(env, var_store, scope, &loc_field.value, loc_field.region) { + match canonicalize_field(env, var_store, scope, &loc_field.value) { Ok((label, field_expr, field_out, field_var)) => { let field = Field { var: field_var, @@ -1546,7 +1546,6 @@ fn canonicalize_field<'a>( var_store: &mut VarStore, scope: &mut Scope, field: &'a ast::AssignedField<'a, ast::Expr<'a>>, - region: Region, ) -> Result<(Lowercase, Loc, Output, Variable), CanonicalizeFieldProblem> { use roc_parse::ast::AssignedField::*; @@ -1576,7 +1575,7 @@ fn canonicalize_field<'a>( } SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => { - canonicalize_field(env, var_store, scope, sub_field, region) + canonicalize_field(env, var_store, scope, sub_field) } Malformed(_string) => { @@ -1652,7 +1651,7 @@ fn canonicalize_var_lookup( } /// Currently uses the heuristic of "only inline if it's a builtin" -pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> Expr { +pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { use Expr::*; match expr { @@ -1681,7 +1680,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let mut new_elems = Vec::with_capacity(loc_elems.len()); for loc_elem in loc_elems { - let value = inline_calls(var_store, scope, loc_elem.value); + let value = inline_calls(var_store, loc_elem.value); new_elems.push(Loc { value, @@ -1706,20 +1705,20 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } => { let loc_cond = Box::new(Loc { region: loc_cond.region, - value: inline_calls(var_store, scope, loc_cond.value), + value: inline_calls(var_store, loc_cond.value), }); let mut new_branches = Vec::with_capacity(branches.len()); for branch in branches { let value = Loc { - value: inline_calls(var_store, scope, branch.value.value), + value: inline_calls(var_store, branch.value.value), region: branch.value.region, }; let guard = match branch.guard { Some(loc_expr) => Some(Loc { region: loc_expr.region, - value: inline_calls(var_store, scope, loc_expr.value), + value: inline_calls(var_store, loc_expr.value), }), None => None, }; @@ -1753,12 +1752,12 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> for (loc_cond, loc_expr) in branches { let loc_cond = Loc { - value: inline_calls(var_store, scope, loc_cond.value), + value: inline_calls(var_store, loc_cond.value), region: loc_cond.region, }; let loc_expr = Loc { - value: inline_calls(var_store, scope, loc_expr.value), + value: inline_calls(var_store, loc_expr.value), region: loc_expr.region, }; @@ -1767,7 +1766,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let final_else = Box::new(Loc { region: final_else.region, - value: inline_calls(var_store, scope, final_else.value), + value: inline_calls(var_store, final_else.value), }); If { @@ -1785,12 +1784,12 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } => { let loc_condition = Loc { region: loc_condition.region, - value: inline_calls(var_store, scope, loc_condition.value), + value: inline_calls(var_store, loc_condition.value), }; let loc_continuation = Loc { region: loc_continuation.region, - value: inline_calls(var_store, scope, loc_continuation.value), + value: inline_calls(var_store, loc_continuation.value), }; Expect { @@ -1807,12 +1806,12 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> } => { let loc_condition = Loc { region: loc_condition.region, - value: inline_calls(var_store, scope, loc_condition.value), + value: inline_calls(var_store, loc_condition.value), }; let loc_continuation = Loc { region: loc_continuation.region, - value: inline_calls(var_store, scope, loc_continuation.value), + value: inline_calls(var_store, loc_continuation.value), }; ExpectFx { @@ -1830,7 +1829,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> loc_pattern: def.loc_pattern, loc_expr: Loc { region: def.loc_expr.region, - value: inline_calls(var_store, scope, def.loc_expr.value), + value: inline_calls(var_store, def.loc_expr.value), }, expr_var: def.expr_var, pattern_vars: def.pattern_vars, @@ -1840,7 +1839,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let loc_expr = Loc { region: loc_expr.region, - value: inline_calls(var_store, scope, loc_expr.value), + value: inline_calls(var_store, loc_expr.value), }; LetRec(new_defs, Box::new(loc_expr), mark) @@ -1851,7 +1850,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> loc_pattern: def.loc_pattern, loc_expr: Loc { region: def.loc_expr.region, - value: inline_calls(var_store, scope, def.loc_expr.value), + value: inline_calls(var_store, def.loc_expr.value), }, expr_var: def.expr_var, pattern_vars: def.pattern_vars, @@ -1860,7 +1859,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let loc_expr = Loc { region: loc_expr.region, - value: inline_calls(var_store, scope, loc_expr.value), + value: inline_calls(var_store, loc_expr.value), }; LetNonRec(Box::new(def), Box::new(loc_expr)) @@ -1878,7 +1877,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> }) => { let loc_expr = *loc_body; let loc_expr = Loc { - value: inline_calls(var_store, scope, loc_expr.value), + value: inline_calls(var_store, loc_expr.value), region: loc_expr.region, }; @@ -1938,7 +1937,7 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) -> let (var, loc_expr) = *argument; let argument = Box::new(( var, - loc_expr.map_owned(|expr| inline_calls(var_store, scope, expr)), + loc_expr.map_owned(|expr| inline_calls(var_store, expr)), )); OpaqueRef { @@ -2737,7 +2736,7 @@ fn get_lookup_symbols(expr: &Expr) -> Vec { | Expr::ExpectFx { loc_continuation, .. } => { - stack.push(&(*loc_continuation).value); + stack.push(&loc_continuation.value); // Intentionally ignore the lookups in the nested `expect` condition itself, // because they couldn't possibly influence the outcome of this `expect`! diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index aacf542445..914c1b06e4 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -898,6 +898,15 @@ fn fix_values_captured_in_closure_pattern( } } } + List { patterns, .. } => { + for loc_pat in patterns.patterns.iter_mut() { + fix_values_captured_in_closure_pattern( + &mut loc_pat.value, + no_capture_symbols, + closure_captures, + ); + } + } Identifier(_) | NumLiteral(..) | IntLiteral(..) diff --git a/crates/compiler/can/src/operator.rs b/crates/compiler/can/src/operator.rs index 9a46592f0a..fac3cf2b37 100644 --- a/crates/compiler/can/src/operator.rs +++ b/crates/compiler/can/src/operator.rs @@ -79,7 +79,7 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a> ann_pattern, ann_type, comment: *comment, - body_pattern: *body_pattern, + body_pattern, body_expr: desugar_expr(arena, body_expr), }, Expect { diff --git a/crates/compiler/can/src/pattern.rs b/crates/compiler/can/src/pattern.rs index e70dd515e8..e2e96ee52d 100644 --- a/crates/compiler/can/src/pattern.rs +++ b/crates/compiler/can/src/pattern.rs @@ -6,6 +6,7 @@ use crate::num::{ ParsedNumResult, }; use crate::scope::{PendingAbilitiesInScope, Scope}; +use roc_exhaustive::ListArity; use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::symbol::Symbol; use roc_parse::ast::{self, StrLiteral, StrSegment}; @@ -56,6 +57,11 @@ pub enum Pattern { ext_var: Variable, destructs: Vec>, }, + List { + list_var: Variable, + elem_var: Variable, + patterns: ListPatterns, + }, NumLiteral(Variable, Box, IntValue, NumBound), IntLiteral(Variable, Variable, Box, IntValue, IntBound), FloatLiteral(Variable, Variable, Box, f64, FloatBound), @@ -92,6 +98,10 @@ impl Pattern { AppliedTag { whole_var, .. } => Some(*whole_var), UnwrappedOpaque { whole_var, .. } => Some(*whole_var), RecordDestructure { whole_var, .. } => Some(*whole_var), + List { + list_var: whole_var, + .. + } => Some(*whole_var), NumLiteral(var, ..) => Some(*var), IntLiteral(var, ..) => Some(*var), FloatLiteral(var, ..) => Some(*var), @@ -119,6 +129,7 @@ impl Pattern { | MalformedPattern(..) | AbilityMemberSpecialization { .. } => true, RecordDestructure { destructs, .. } => destructs.is_empty(), + List { patterns, .. } => patterns.surely_exhaustive(), AppliedTag { .. } | NumLiteral(..) | IntLiteral(..) @@ -145,6 +156,7 @@ impl Pattern { UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque), RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord, RecordDestructure { .. } => C::Record, + List { .. } => C::List, NumLiteral(..) => C::Num, IntLiteral(..) => C::Int, FloatLiteral(..) => C::Float, @@ -161,6 +173,36 @@ impl Pattern { } } +#[derive(Clone, Debug)] +pub struct ListPatterns { + pub patterns: Vec>, + /// Where a rest pattern splits patterns before and after it, if it does at all. + /// If present, patterns at index >= the rest index appear after the rest pattern. + /// For example: + /// [ .., A, B ] -> patterns = [A, B], rest = 0 + /// [ A, .., B ] -> patterns = [A, B], rest = 1 + /// [ A, B, .. ] -> patterns = [A, B], rest = 2 + pub opt_rest: Option, +} + +impl ListPatterns { + /// Is this list pattern the trivially-exhaustive pattern `[..]`? + fn surely_exhaustive(&self) -> bool { + self.patterns.is_empty() && matches!(self.opt_rest, Some(0)) + } + + pub fn arity(&self) -> ListArity { + match self.opt_rest { + Some(i) => { + let before = i; + let after = self.patterns.len() - before; + ListArity::Slice(before, after) + } + None => ListArity::Exact(self.patterns.len()), + } + } +} + #[derive(Clone, Debug)] pub struct RecordDestruct { pub var: Variable, @@ -621,8 +663,75 @@ pub fn canonicalize_pattern<'a>( unreachable!("should have been handled in RecordDestructure"); } - List(..) => todo!(), - ListRest => todo!(), + List(patterns) => { + // We want to admit the following cases: + // + // [] + // [..] + // [.., P_1,* P_n] + // [P_1,* P_n, ..] + // [P_1,* P_m, .., P_n,* P_q] + // [P_1,* P_n] + // + // So, a list-rest pattern can appear anywhere in a list pattern, but can appear at + // most once. + let elem_var = var_store.fresh(); + let list_var = var_store.fresh(); + + let mut rest_index = None; + let mut can_pats = Vec::with_capacity(patterns.len()); + let mut opt_erroneous = None; + + for (i, loc_pattern) in patterns.iter().enumerate() { + match &loc_pattern.value { + ListRest => match rest_index { + None => { + rest_index = Some(i); + } + Some(_) => { + env.problem(Problem::MultipleListRestPattern { + region: loc_pattern.region, + }); + + opt_erroneous = Some(Pattern::MalformedPattern( + MalformedPatternProblem::DuplicateListRestPattern, + loc_pattern.region, + )); + } + }, + pattern => { + let pat = canonicalize_pattern( + env, + var_store, + scope, + output, + pattern_type, + pattern, + loc_pattern.region, + permit_shadows, + ); + can_pats.push(pat); + } + } + } + + // If we encountered an erroneous pattern (e.g. one with shadowing), + // use the resulting RuntimeError. Otherwise, return a successful record destructure. + opt_erroneous.unwrap_or(Pattern::List { + list_var, + elem_var, + patterns: ListPatterns { + patterns: can_pats, + opt_rest: rest_index, + }, + }) + } + ListRest => { + // Parsing should make sure these only appear in list patterns, where we will generate + // better contextual errors. + let problem = MalformedPatternProblem::Unknown; + malformed_pattern(env, problem, region) + } Malformed(_str) => { let problem = MalformedPatternProblem::Unknown; @@ -739,6 +848,9 @@ impl<'a> BindingsFromPattern<'a> { | MalformedPattern(_, _) | UnsupportedPattern(_) | OpaqueNotInScope(..) => (), + List { patterns, .. } => { + stack.extend(patterns.patterns.iter().rev().map(Pattern)); + } } } BindingsFromPatternWork::Destruct(loc_destruct) => { diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 6795923cec..28c5ec6b78 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -472,6 +472,12 @@ pub fn walk_pattern(visitor: &mut V, pattern: &Pattern) { RecordDestructure { destructs, .. } => destructs .iter() .for_each(|d| visitor.visit_record_destruct(&d.value, d.region)), + List { + patterns, elem_var, .. + } => patterns + .patterns + .iter() + .for_each(|p| visitor.visit_pattern(&p.value, p.region, Some(*elem_var))), NumLiteral(..) => { /* terminal */ } IntLiteral(..) => { /* terminal */ } FloatLiteral(..) => { /* terminal */ } diff --git a/crates/compiler/collections/src/vec_set.rs b/crates/compiler/collections/src/vec_set.rs index d5fd02a63c..8415bbc102 100644 --- a/crates/compiler/collections/src/vec_set.rs +++ b/crates/compiler/collections/src/vec_set.rs @@ -1,6 +1,6 @@ use std::{borrow::Borrow, iter::FromIterator}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct VecSet { elements: Vec, } diff --git a/crates/compiler/constrain/src/builtins.rs b/crates/compiler/constrain/src/builtins.rs index ad307261c2..3aee4ab16b 100644 --- a/crates/compiler/constrain/src/builtins.rs +++ b/crates/compiler/constrain/src/builtins.rs @@ -1,5 +1,5 @@ use arrayvec::ArrayVec; -use roc_can::constraint::{Constraint, Constraints}; +use roc_can::constraint::{Constraint, Constraints, TypeOrVar}; use roc_can::expected::Expected::{self, *}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand}; use roc_module::symbol::Symbol; @@ -30,7 +30,7 @@ pub fn add_numeric_bound_constr( num_num(Variable(num_var)) } NumericBound::FloatExact(width) => { - let actual_type = Variable(float_width_to_variable(width)); + let actual_type = constraints.push_type(Variable(float_width_to_variable(width))); let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region); let type_index = constraints.push_type(Variable(num_var)); let expected_index = constraints.push_expected_type(expected); @@ -42,7 +42,7 @@ pub fn add_numeric_bound_constr( Variable(num_var) } NumericBound::IntExact(width) => { - let actual_type = Variable(int_lit_width_to_variable(width)); + let actual_type = constraints.push_type(Variable(int_lit_width_to_variable(width))); let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region); let type_index = constraints.push_type(Variable(num_var)); let expected_index = constraints.push_expected_type(expected); @@ -54,11 +54,10 @@ pub fn add_numeric_bound_constr( Variable(num_var) } NumericBound::Range(range) => { - let actual_type = Variable(precision_var); - let expected = Expected::NoExpectation(RangedNumber(range)); - let type_index = constraints.push_type(actual_type); + let precision_type = constraints.push_type(Variable(precision_var)); + let expected = Expected::NoExpectation(constraints.push_type(RangedNumber(range))); let expected_index = constraints.push_expected_type(expected); - let constr = constraints.equal_types(type_index, expected_index, category, region); + let constr = constraints.equal_types(precision_type, expected_index, category, region); num_constraints.extend([constr]); @@ -72,7 +71,7 @@ pub fn int_literal( constraints: &mut Constraints, num_var: Variable, precision_var: Variable, - expected: Expected, + expected: Expected, region: Region, bound: IntBound, ) -> Constraint { @@ -91,11 +90,10 @@ pub fn int_literal( ); let num_type_index = constraints.push_type(num_type); - let expect_precision_var = constraints.push_expected_type(ForReason( - reason, - num_int(Type::Variable(precision_var)), - region, - )); + let int_precision_type = constraints.push_type(num_int(Type::Variable(precision_var))); + + let expect_precision_var = + constraints.push_expected_type(ForReason(reason, int_precision_type, region)); constrs.extend([ constraints.equal_types(num_type_index, expect_precision_var, Category::Int, region), @@ -114,7 +112,7 @@ pub fn single_quote_literal( constraints: &mut Constraints, num_var: Variable, precision_var: Variable, - expected: Expected, + expected: Expected, region: Region, bound: SingleQuoteBound, ) -> Constraint { @@ -133,11 +131,10 @@ pub fn single_quote_literal( ); let num_type_index = constraints.push_type(num_type); - let expect_precision_var = constraints.push_expected_type(ForReason( - reason, - num_int(Type::Variable(precision_var)), - region, - )); + let int_precision_type = constraints.push_type(num_int(Type::Variable(precision_var))); + + let expect_precision_var = + constraints.push_expected_type(ForReason(reason, int_precision_type, region)); constrs.extend([ constraints.equal_types( @@ -161,7 +158,7 @@ pub fn float_literal( constraints: &mut Constraints, num_var: Variable, precision_var: Variable, - expected: Expected, + expected: Expected, region: Region, bound: FloatBound, ) -> Constraint { @@ -179,11 +176,10 @@ pub fn float_literal( ); let num_type_index = constraints.push_type(num_type); - let expect_precision_var = constraints.push_expected_type(ForReason( - reason, - num_float(Type::Variable(precision_var)), - region, - )); + let float_precision_type = constraints.push_type(num_float(Type::Variable(precision_var))); + + let expect_precision_var = + constraints.push_expected_type(ForReason(reason, float_precision_type, region)); constrs.extend([ constraints.equal_types(num_type_index, expect_precision_var, Category::Frac, region), @@ -201,7 +197,7 @@ pub fn float_literal( pub fn num_literal( constraints: &mut Constraints, num_var: Variable, - expected: Expected, + expected: Expected, region: Region, bound: NumBound, ) -> Constraint { diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 54a71a9eb3..1edb6aa2d0 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -6,7 +6,7 @@ use crate::builtins::{ }; use crate::pattern::{constrain_pattern, PatternState}; use roc_can::annotation::IntroducedVariables; -use roc_can::constraint::{Constraint, Constraints, OpportunisticResolve}; +use roc_can::constraint::{Constraint, Constraints, OpportunisticResolve, TypeOrVar}; use roc_can::def::Def; use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext}; use roc_can::expected::Expected::{self, *}; @@ -35,7 +35,7 @@ use roc_types::types::{ pub struct Info { pub vars: Vec, pub constraints: Vec, - pub def_types: VecMap>, + pub def_types: VecMap>, } impl Info { @@ -72,8 +72,9 @@ fn constrain_untyped_args( // Untyped args don't need exhaustiveness checking because they are the source of truth! let _ = annotated_mark; - let pattern_type = Type::Variable(*pattern_var); - let pattern_expected = PExpected::NoExpectation(pattern_type.clone()); + let pattern_type = Variable(*pattern_var); + let pattern_type_index = constraints.push_type(Variable(*pattern_var)); + let pattern_expected = PExpected::NoExpectation(pattern_type_index); pattern_types.push(pattern_type); @@ -100,7 +101,7 @@ fn constrain_untyped_closure( constraints: &mut Constraints, env: &mut Env, region: Region, - expected: Expected, + expected: Expected, fn_var: Variable, closure_var: Variable, @@ -112,19 +113,15 @@ fn constrain_untyped_closure( ) -> Constraint { let closure_type = Type::Variable(closure_var); let return_type = Type::Variable(ret_var); - let (mut vars, pattern_state, function_type) = constrain_untyped_args( - constraints, - env, - arguments, - closure_type, - return_type.clone(), - ); + let return_type_index = constraints.push_type(Type::Variable(ret_var)); + let (mut vars, pattern_state, function_type) = + constrain_untyped_args(constraints, env, arguments, closure_type, return_type); vars.push(ret_var); vars.push(closure_var); vars.push(fn_var); - let body_type = NoExpectation(return_type); + let body_type = NoExpectation(return_type_index); let ret_constraint = constrain_expr( constraints, env, @@ -181,7 +178,7 @@ pub fn constrain_expr( env: &mut Env, region: Region, expr: &Expr, - expected: Expected, + expected: Expected, ) -> Constraint { match expr { &Int(var, precision, _, _, bound) => { @@ -257,9 +254,11 @@ pub fn constrain_expr( cons.push(con); } - let fields_type = - Type::Record(fields, TypeExtension::from_type(Type::Variable(*ext_var))); - let record_type = Type::Variable(*record_var); + let fields_type = constraints.push_type(Type::Record( + fields, + TypeExtension::from_type(Type::Variable(*ext_var)), + )); + let record_type = constraints.push_type(Type::Variable(*record_var)); // NOTE from elm compiler: fields_type is separate so that Error propagates better let fields_type_expected = constraints.push_expected_type(NoExpectation(fields_type)); @@ -327,6 +326,7 @@ pub fn constrain_expr( constraints.exists(vec![*elem_var], eq) } else { let list_elem_type = Type::Variable(*elem_var); + let list_elem_type_index = constraints.push_type(list_elem_type.clone()); let mut list_constraints = Vec::with_capacity(1 + loc_elems.len()); for (index, loc_elem) in loc_elems.iter().enumerate() { @@ -334,7 +334,7 @@ pub fn constrain_expr( Reason::ElemInList { index: HumanIndex::zero_based(index), }, - list_elem_type.clone(), + list_elem_type_index, loc_elem.region, ); let constraint = constrain_expr( @@ -371,9 +371,9 @@ pub fn constrain_expr( None }; - let fn_type = Variable(*fn_var); + let fn_type_index = constraints.push_type(Variable(*fn_var)); let fn_region = loc_fn.region; - let fn_expected = NoExpectation(fn_type); + let fn_expected = NoExpectation(fn_type_index); let fn_reason = Reason::FnCall { name: opt_symbol, @@ -402,12 +402,13 @@ pub fn constrain_expr( for (index, (arg_var, loc_arg)) in loc_args.iter().enumerate() { let region = loc_arg.region; let arg_type = Variable(*arg_var); + let arg_type_index = constraints.push_type(Variable(*arg_var)); let reason = Reason::FnArg { name: opt_symbol, arg_index: HumanIndex::zero_based(index), }; - let expected_arg = ForReason(reason, arg_type.clone(), region); + let expected_arg = ForReason(reason, arg_type_index, region); let arg_con = constrain_expr( constraints, env, @@ -421,11 +422,13 @@ pub fn constrain_expr( arg_cons.push(arg_con); } - let expected_fn_type = constraints.push_expected_type(ForReason( - fn_reason, - Function(arg_types, Box::new(closure_type), Box::new(ret_type)), - region, + let expected_fn_index = constraints.push_type(Function( + arg_types, + Box::new(closure_type), + Box::new(ret_type), )); + let expected_fn_type = + constraints.push_expected_type(ForReason(fn_reason, expected_fn_index, region)); let expected_final_type = constraints.push_expected_type(expected); @@ -443,25 +446,28 @@ pub fn constrain_expr( } Var(symbol, variable) => { // Save the expectation in the variable, then lookup the symbol's type in the environment - let expected_type = constraints.push_type(expected.get_type_ref().clone()); + let expected_type = *expected.get_type_ref(); let store_expected = constraints.store(expected_type, *variable, file!(), line!()); - let store_into_var = - constraints.push_expected_type(expected.replace(Type::Variable(*variable))); + let stored_index = constraints.push_type(Variable(*variable)); + let stored_type = constraints.push_expected_type(expected.replace(stored_index)); - let lookup_constr = constraints.lookup(*symbol, store_into_var, region); + let lookup_constr = constraints.lookup(*symbol, stored_type, region); constraints.and_constraint([store_expected, lookup_constr]) } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then // lookup the member in the environment. - let expected_type = constraints.push_type(expected.get_type_ref().clone()); + let expected_type = *expected.get_type_ref(); let store_expected = constraints.store(expected_type, specialization_var, file!(), line!()); - let store_specialization_var = constraints - .push_expected_type(Expected::NoExpectation(Type::Variable(specialization_var))); - let lookup_constr = constraints.lookup(symbol, store_specialization_var, region); + + let stored_index = constraints.push_type(Variable(specialization_var)); + let stored_specialization_var = + constraints.push_expected_type(Expected::NoExpectation(stored_index)); + + let lookup_constr = constraints.lookup(symbol, stored_specialization_var, region); // Make sure we attempt to resolve the specialization, if we can. if let Some(specialization_id) = specialization_id { @@ -505,9 +511,9 @@ pub fn constrain_expr( loc_continuation, lookups_in_cond, } => { - let expect_bool = |region| { - let bool_type = Type::Variable(Variable::BOOL); - Expected::ForReason(Reason::ExpectCondition, bool_type, region) + let expected_bool = { + let bool_type = constraints.push_type(Type::Variable(Variable::BOOL)); + Expected::ForReason(Reason::ExpectCondition, bool_type, loc_condition.region) }; let cond_con = constrain_expr( @@ -515,7 +521,7 @@ pub fn constrain_expr( env, loc_condition.region, &loc_condition.value, - expect_bool(loc_condition.region), + expected_bool, ); let continuation_con = constrain_expr( @@ -542,8 +548,9 @@ pub fn constrain_expr( { vars.push(*var); - let store_into = - constraints.push_expected_type(NoExpectation(Type::Variable(*var))); + let var_index = constraints.push_type(Variable(*var)); + let store_into = constraints.push_expected_type(NoExpectation(var_index)); + all_constraints.push(constraints.lookup(*symbol, store_into, Region::zero())); } @@ -555,9 +562,9 @@ pub fn constrain_expr( loc_continuation, lookups_in_cond, } => { - let expect_bool = |region| { - let bool_type = Type::Variable(Variable::BOOL); - Expected::ForReason(Reason::ExpectCondition, bool_type, region) + let expected_bool = { + let bool_type = constraints.push_type(Type::Variable(Variable::BOOL)); + Expected::ForReason(Reason::ExpectCondition, bool_type, loc_condition.region) }; let cond_con = constrain_expr( @@ -565,7 +572,7 @@ pub fn constrain_expr( env, loc_condition.region, &loc_condition.value, - expect_bool(loc_condition.region), + expected_bool, ); let continuation_con = constrain_expr( @@ -592,8 +599,8 @@ pub fn constrain_expr( { vars.push(*var); - let store_into = - constraints.push_expected_type(NoExpectation(Type::Variable(*var))); + let var_index = constraints.push_type(Variable(*var)); + let store_into = constraints.push_expected_type(NoExpectation(var_index)); all_constraints.push(constraints.lookup(*symbol, store_into, Region::zero())); } @@ -607,15 +614,18 @@ pub fn constrain_expr( branches, final_else, } => { - let expect_bool = |region| { - let bool_type = Type::Variable(Variable::BOOL); + let expect_bool = |constraints: &mut Constraints, region| { + let bool_type = constraints.push_type(Type::Variable(Variable::BOOL)); Expected::ForReason(Reason::IfCondition, bool_type, region) }; let mut branch_cons = Vec::with_capacity(2 * branches.len() + 3); // TODO why does this cond var exist? is it for error messages? let first_cond_region = branches[0].0.region; - let expected_bool = constraints.push_expected_type(expect_bool(first_cond_region)); + let expected_bool = { + let expected = expect_bool(constraints, first_cond_region); + constraints.push_expected_type(expected) + }; let cond_var_is_bool_con = constraints.equal_types_var( *cond_var, expected_bool, @@ -629,12 +639,13 @@ pub fn constrain_expr( FromAnnotation(name, arity, ann_source, tipe) => { let num_branches = branches.len() + 1; for (index, (loc_cond, loc_body)) in branches.iter().enumerate() { + let expected_bool = expect_bool(constraints, loc_cond.region); let cond_con = constrain_expr( constraints, env, loc_cond.region, &loc_cond.value, - expect_bool(loc_cond.region), + expected_bool, ); let then_con = constrain_expr( @@ -650,7 +661,7 @@ pub fn constrain_expr( num_branches, region: ann_source.region(), }, - tipe.clone(), + tipe, ), ); @@ -671,7 +682,7 @@ pub fn constrain_expr( num_branches, region: ann_source.region(), }, - tipe.clone(), + tipe, ), ); @@ -690,13 +701,16 @@ pub fn constrain_expr( constraints.exists_many([*cond_var, *branch_var], branch_cons) } _ => { + let branch_var_index = constraints.push_type(Variable(*branch_var)); + for (index, (loc_cond, loc_body)) in branches.iter().enumerate() { + let expected_bool = expect_bool(constraints, loc_cond.region); let cond_con = constrain_expr( constraints, env, loc_cond.region, &loc_cond.value, - expect_bool(loc_cond.region), + expected_bool, ); let then_con = constrain_expr( @@ -709,7 +723,7 @@ pub fn constrain_expr( index: HumanIndex::zero_based(index), total_branches: branches.len(), }, - Type::Variable(*branch_var), + branch_var_index, loc_body.region, ), ); @@ -727,7 +741,7 @@ pub fn constrain_expr( index: HumanIndex::zero_based(branches.len()), total_branches: branches.len() + 1, }, - Type::Variable(*branch_var), + branch_var_index, final_else.region, ), ); @@ -756,10 +770,10 @@ pub fn constrain_expr( .. } => { let branches_cond_var = *branches_cond_var; - let branches_cond_type = Variable(branches_cond_var); + let branches_cond_index = constraints.push_type(Variable(branches_cond_var)); let body_var = *expr_var; - let body_type = Variable(body_var); + let body_type_index = constraints.push_type(Variable(body_var)); let branches_region = { debug_assert!(!branches.is_empty()); @@ -767,7 +781,7 @@ pub fn constrain_expr( }; let branch_expr_reason = - |expected: &Expected, index, branch_region| match expected { + |expected: &Expected, index, branch_region| match expected { FromAnnotation(name, arity, ann_source, _typ) => { // NOTE deviation from elm. // @@ -780,15 +794,11 @@ pub fn constrain_expr( index, region: ann_source.region(), }, - body_type.clone(), + body_type_index, ) } - _ => ForReason( - Reason::WhenBranch { index }, - body_type.clone(), - branch_region, - ), + _ => ForReason(Reason::WhenBranch { index }, body_type_index, branch_region), }; // Our goal is to constrain and introduce variables in all pattern when branch patterns before @@ -825,7 +835,7 @@ pub fn constrain_expr( index: HumanIndex::zero_based(index), sub_pattern, }, - branches_cond_type.clone(), + branches_cond_index, sub_region, ) }; @@ -890,7 +900,7 @@ pub fn constrain_expr( // // First, solve the condition type. let real_cond_var = *real_cond_var; - let real_cond_type = Type::Variable(real_cond_var); + let real_cond_type = constraints.push_type(Type::Variable(real_cond_var)); let cond_constraint = constrain_expr( constraints, env, @@ -902,13 +912,15 @@ pub fn constrain_expr( // Now check the condition against the type expected by the branches. let sketched_rows = sketch_when_branches(branches_region, branches); + let expected_by_branches = constraints.push_expected_type(Expected::ForReason( + Reason::WhenBranches, + branches_cond_index, + branches_region, + )); let cond_matches_branches_constraint = constraints.exhaustive( real_cond_var, loc_cond.region, - Ok(( - loc_cond.value.category(), - Expected::ForReason(Reason::WhenBranches, branches_cond_type, branches_region), - )), + Ok((loc_cond.value.category(), expected_by_branches)), sketched_rows, ExhaustiveContext::BadCase, *exhaustive, @@ -957,9 +969,11 @@ pub fn constrain_expr( let label = field.clone(); rec_field_types.insert(label, RecordField::Demanded(field_type)); - let record_type = Type::Record(rec_field_types, TypeExtension::from_type(ext_type)); - let record_expected = - constraints.push_expected_type(NoExpectation(record_type.clone())); + let record_type = constraints.push_type(Type::Record( + rec_field_types, + TypeExtension::from_type(ext_type), + )); + let record_expected = constraints.push_expected_type(NoExpectation(record_type)); let category = Category::Access(field.clone()); @@ -999,19 +1013,22 @@ pub fn constrain_expr( let label = field.clone(); field_types.insert(label, RecordField::Demanded(field_type.clone())); let record_type = Type::Record(field_types, TypeExtension::from_type(ext_type)); + let record_type_index = constraints.push_type(record_type.clone()); let category = Category::Accessor(field.clone()); - let record_expected = - constraints.push_expected_type(NoExpectation(record_type.clone())); + let record_expected = constraints.push_expected_type(NoExpectation(record_type_index)); let record_con = constraints.equal_types_var(*record_var, record_expected, category.clone(), region); - let lambda_set = constraints.push_expected_type(NoExpectation(Type::ClosureTag { - name: *closure_name, - captures: vec![], - ambient_function: *function_var, - })); + let expected_lambda_set = { + let lambda_set_ty = constraints.push_type(Type::ClosureTag { + name: *closure_name, + captures: vec![], + ambient_function: *function_var, + }); + constraints.push_expected_type(NoExpectation(lambda_set_ty)) + }; let closure_type = Type::Variable(*closure_var); @@ -1022,7 +1039,12 @@ pub fn constrain_expr( )); let cons = [ - constraints.equal_types_var(*closure_var, lambda_set, category.clone(), region), + constraints.equal_types_var( + *closure_var, + expected_lambda_set, + category.clone(), + region, + ), { let expected_index = constraints.push_expected_type(expected); constraints.equal_types( @@ -1033,11 +1055,12 @@ pub fn constrain_expr( ) }, { - let function_var_index = - constraints.push_expected_type(NoExpectation(Variable(*function_var))); + let store_fn_var_index = constraints.push_type(Variable(*function_var)); + let store_fn_var_expected = + constraints.push_expected_type(NoExpectation(store_fn_var_index)); constraints.equal_types( function_type_index, - function_var_index, + store_fn_var_expected, category, region, ) @@ -1100,12 +1123,13 @@ pub fn constrain_expr( let mut arg_cons = Vec::with_capacity(arguments.len()); for (var, loc_expr) in arguments { + let var_index = constraints.push_type(Variable(*var)); let arg_con = constrain_expr( constraints, env, loc_expr.region, &loc_expr.value, - Expected::NoExpectation(Type::Variable(*var)), + Expected::NoExpectation(var_index), ); arg_cons.push(arg_con); @@ -1171,6 +1195,7 @@ pub fn constrain_expr( } => { let (arg_var, arg_loc_expr) = &**argument; let arg_type = Type::Variable(*arg_var); + let arg_type_index = constraints.push_type(Variable(*arg_var)); let opaque_type = constraints.push_type(Type::Alias { symbol: *name, @@ -1183,7 +1208,7 @@ pub fn constrain_expr( .collect(), lambda_set_variables: lambda_set_variables.clone(), infer_ext_in_output_types: vec![], - actual: Box::new(arg_type.clone()), + actual: Box::new(arg_type), kind: AliasKind::Opaque, }); @@ -1193,7 +1218,7 @@ pub fn constrain_expr( env, arg_loc_expr.region, &arg_loc_expr.value, - Expected::NoExpectation(arg_type.clone()), + Expected::NoExpectation(arg_type_index), ); // Link the entire wrapped opaque type (with the now-constrained argument) to the @@ -1211,11 +1236,12 @@ pub fn constrain_expr( // variables of the opaque type // TODO: better expectation here let link_type_variables_con = { - let type_index = constraints.push_type(arg_type); - let expected_index = constraints - .push_expected_type(Expected::NoExpectation((**specialized_def_type).clone())); + let specialized_type_index = + constraints.push_type((**specialized_def_type).clone()); + let expected_index = + constraints.push_expected_type(Expected::NoExpectation(specialized_type_index)); constraints.equal_types( - type_index, + arg_type_index, expected_index, Category::OpaqueArg, arg_loc_expr.region, @@ -1244,7 +1270,7 @@ pub fn constrain_expr( }) => { let argument_type = Type::Variable(*argument_var); - let opaque_type = Type::Alias { + let opaque_type = constraints.push_type(Type::Alias { symbol: *opaque_name, type_arguments: type_arguments .iter() @@ -1257,7 +1283,7 @@ pub fn constrain_expr( infer_ext_in_output_types: vec![], actual: Box::new(argument_type.clone()), kind: AliasKind::Opaque, - }; + }); let expected_opaque_type = constraints.push_expected_type(NoExpectation(opaque_type)); @@ -1272,8 +1298,9 @@ pub fn constrain_expr( // Tie the type of the value wrapped by the opaque to the opaque's type variables. let link_type_variables_con = { let arg_type_index = constraints.push_type(argument_type.clone()); - let expected_specialized = constraints - .push_expected_type(Expected::NoExpectation((*specialized_def_type).clone())); + let specialized_type_index = constraints.push_type((*specialized_def_type).clone()); + let expected_specialized = + constraints.push_expected_type(Expected::NoExpectation(specialized_type_index)); constraints.equal_types( arg_type_index, expected_specialized, @@ -1282,21 +1309,27 @@ pub fn constrain_expr( ) }; - let lambda_set = constraints.push_expected_type(NoExpectation(Type::ClosureTag { - name: *function_name, - captures: vec![], - ambient_function: *function_var, - })); + let lambda_set = { + let lambda_set_index = constraints.push_type(Type::ClosureTag { + name: *function_name, + captures: vec![], + ambient_function: *function_var, + }); + constraints.push_expected_type(NoExpectation(lambda_set_index)) + }; let closure_type = Type::Variable(*closure_var); let opaque_type = Type::Variable(*opaque_var); - let function_type = constraints.push_expected_type(NoExpectation(Type::Function( - vec![argument_type], - Box::new(closure_type), - Box::new(opaque_type), - ))); + let expected_function_type = { + let fn_type = constraints.push_type(Type::Function( + vec![argument_type], + Box::new(closure_type), + Box::new(opaque_type), + )); + constraints.push_expected_type(NoExpectation(fn_type)) + }; let expected = constraints.push_expected_type(expected); @@ -1311,7 +1344,7 @@ pub fn constrain_expr( ), constraints.equal_types_var( *function_var, - function_type, + expected_function_type, Category::OpaqueWrap(*opaque_name), region, ), @@ -1347,12 +1380,12 @@ pub fn constrain_expr( let mut arg_types = Vec::with_capacity(args.len()); let mut arg_cons = Vec::with_capacity(args.len()); - let mut add_arg = |index, arg_type: Type, arg| { + let mut add_arg = |constraints: &mut Constraints, index, arg_type: TypeOrVar, arg| { let reason = Reason::LowLevelOpArg { op: *op, arg_index: HumanIndex::zero_based(index), }; - let expected_arg = ForReason(reason, arg_type.clone(), Region::zero()); + let expected_arg = ForReason(reason, arg_type, Region::zero()); let arg_con = constrain_expr(constraints, env, Region::zero(), arg, expected_arg); arg_types.push(arg_type); @@ -1361,8 +1394,9 @@ pub fn constrain_expr( for (index, (arg_var, arg)) in args.iter().enumerate() { vars.push(*arg_var); + let arg_var_index = constraints.push_type(Variable(*arg_var)); - add_arg(index, Variable(*arg_var), arg); + add_arg(constraints, index, arg_var_index, arg); } let category = Category::LowLevelOpResult(*op); @@ -1388,12 +1422,12 @@ pub fn constrain_expr( let mut arg_types = Vec::with_capacity(args.len()); let mut arg_cons = Vec::with_capacity(args.len()); - let mut add_arg = |index, arg_type: Type, arg| { + let mut add_arg = |constraints: &mut Constraints, index, arg_type: TypeOrVar, arg| { let reason = Reason::ForeignCallArg { foreign_symbol: foreign_symbol.clone(), arg_index: HumanIndex::zero_based(index), }; - let expected_arg = ForReason(reason, arg_type.clone(), Region::zero()); + let expected_arg = ForReason(reason, arg_type, Region::zero()); let arg_con = constrain_expr(constraints, env, Region::zero(), arg, expected_arg); arg_types.push(arg_type); @@ -1402,8 +1436,9 @@ pub fn constrain_expr( for (index, (arg_var, arg)) in args.iter().enumerate() { vars.push(*arg_var); + let arg_var_index = constraints.push_type(Variable(*arg_var)); - add_arg(index, Variable(*arg_var), arg); + add_arg(constraints, index, arg_var_index, arg); } let category = Category::ForeignCall; @@ -1425,8 +1460,14 @@ pub fn constrain_expr( ) } RuntimeError(_) => { - // Runtime Errors have no constraints because they're going to crash. - Constraint::True + // Runtime Errors are always going to crash, so they don't introduce any new + // constraints. + // Instead, trivially equate the expected type to itself. This will never yield + // unification errors but it will catch errors in type translation, including ability + // obligations. + let trivial_type = *expected.get_type_ref(); + let expected = constraints.push_expected_type(expected); + constraints.equal_types(trivial_type, expected, Category::Unknown, region) } } } @@ -1442,6 +1483,7 @@ fn constrain_function_def( let loc_expr = &declarations.expressions[index]; let loc_symbol = declarations.symbols[index]; let expr_var = declarations.variables[index]; + let expr_var_index = constraints.push_type(Type::Variable(expr_var)); let opt_annotation = &declarations.annotations[index]; let loc_function_def = &declarations.function_bodies[function_def_index.index()]; @@ -1466,6 +1508,8 @@ fn constrain_function_def( &mut ftv, ); + let signature_index = constraints.push_type(signature.clone()); + let (arg_types, signature_closure_type, ret_type) = match &signature { Type::Function(arg_types, signature_closure_type, ret_type) => { (arg_types, signature_closure_type, ret_type) @@ -1481,7 +1525,7 @@ fn constrain_function_def( Loc { region: loc_function_def.region, // todo can we use Type::Variable(expr_var) here? - value: signature.clone(), + value: signature_index, }, ); @@ -1493,15 +1537,14 @@ fn constrain_function_def( AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); { - let expr_type_index = constraints.push_type(Type::Variable(expr_var)); let expected_index = constraints.push_expected_type(annotation_expected); def_pattern_state.constraints.push(constraints.equal_types( - expr_type_index, + expr_var_index, expected_index, Category::Storage(std::file!(), std::line!()), Region::span_across(&annotation.region, &loc_body_expr.region), @@ -1517,7 +1560,7 @@ fn constrain_function_def( AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); let ret_constraint = constrain_untyped_closure( @@ -1537,8 +1580,6 @@ fn constrain_function_def( let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint); - let signature_index = constraints.push_type(signature); - let cons = [ ret_constraint, // Store type into AST vars. We use Store so errors aren't reported twice @@ -1575,8 +1616,7 @@ fn constrain_function_def( let ret_var = function_def.return_type; let closure_var = function_def.closure_type; - let ret_type = *ret_type.clone(); - let ret_type_index = constraints.push_type(ret_type.clone()); + let ret_type_index = constraints.push_type(*ret_type.clone()); vars.push(ret_var); vars.push(closure_var); @@ -1592,7 +1632,7 @@ fn constrain_function_def( // annotation type may be instantiated into a fresh type variable that is // disassociated fromt the rest of the program. // Below, we'll check that the function actually matches the annotation. - value: Type::Variable(expr_var), + value: expr_var_index, }, ); @@ -1604,7 +1644,7 @@ fn constrain_function_def( AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); { @@ -1644,7 +1684,7 @@ fn constrain_function_def( AnnotationSource::TypedBody { region: annotation.region, }, - ret_type, + ret_type_index, ); let ret_constraint = constrain_expr( @@ -1659,14 +1699,18 @@ fn constrain_function_def( vars.push(expr_var); let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints); - let signature_closure_type = constraints.push_expected_type(Expected::FromAnnotation( - loc_pattern, - arity, - AnnotationSource::TypedBody { - region: annotation.region, - }, - *signature_closure_type.clone(), - )); + let signature_closure_type = { + let signature_closure_type_index = + constraints.push_type(*signature_closure_type.clone()); + constraints.push_expected_type(Expected::FromAnnotation( + loc_pattern, + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + signature_closure_type_index, + )) + }; let signature_index = constraints.push_type(signature); let cons = [ constraints.let_constraint( @@ -1699,7 +1743,7 @@ fn constrain_function_def( ) } None => { - let expr_type = Type::Variable(expr_var); + let expr_type = constraints.push_type(Variable(expr_var)); let expr_con = constrain_untyped_closure( constraints, @@ -1725,7 +1769,7 @@ fn constrain_function_def( body_con, loc_symbol, expr_var, - Type::Variable(expr_var), + expr_type, ) } } @@ -1741,13 +1785,14 @@ fn constrain_destructure_def( ) -> Constraint { let loc_expr = &declarations.expressions[index]; let expr_var = declarations.variables[index]; + let expr_var_index = constraints.push_type(Variable(expr_var)); let opt_annotation = &declarations.annotations[index]; let destructure_def = &declarations.destructs[destructure_def_index.index()]; let loc_pattern = &destructure_def.loc_pattern; let mut def_pattern_state = - constrain_def_pattern(constraints, env, loc_pattern, Type::Variable(expr_var)); + constrain_def_pattern(constraints, env, loc_pattern, expr_var_index); def_pattern_state.vars.push(expr_var); @@ -1762,6 +1807,7 @@ fn constrain_destructure_def( new_rigid_variables, new_infer_variables, } = instantiate_rigids( + constraints, &annotation.signature, &annotation.introduced_variables, loc_pattern, @@ -1775,13 +1821,15 @@ fn constrain_destructure_def( resolutions_to_make: vec![], }; + let signature_index = constraints.push_type(signature); + let annotation_expected = FromAnnotation( loc_pattern.clone(), arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); // This will fill in inference variables in the `signature` as well, so that we can @@ -1795,8 +1843,6 @@ fn constrain_destructure_def( annotation_expected, ); - let signature_index = constraints.push_type(signature); - let cons = [ ret_constraint, // Store type into AST vars. We use Store so errors aren't reported twice @@ -1814,7 +1860,7 @@ fn constrain_destructure_def( ) } None => { - let expr_type = Type::Variable(expr_var); + let expr_type = constraints.push_type(Variable(expr_var)); let expr_con = constrain_expr( constraints, @@ -1872,13 +1918,15 @@ fn constrain_value_def( let loc_pattern = Loc::at(loc_symbol.region, Pattern::Identifier(loc_symbol.value)); + let signature_index = constraints.push_type(signature); + let annotation_expected = FromAnnotation( loc_pattern, arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); // This will fill in inference variables in the `signature` as well, so that we can @@ -1893,8 +1941,6 @@ fn constrain_value_def( ); let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint); - let signature_index = constraints.push_type(signature.clone()); - let cons = [ ret_constraint, // Store type into AST vars. We use Store so errors aren't reported twice @@ -1910,11 +1956,11 @@ fn constrain_value_def( body_con, loc_symbol, expr_var, - signature, + signature_index, ) } None => { - let expr_type = Type::Variable(expr_var); + let expr_type = constraints.push_type(Type::Variable(expr_var)); let expr_con = constrain_expr( constraints, @@ -1934,7 +1980,7 @@ fn constrain_value_def( body_con, loc_symbol, expr_var, - Type::Variable(expr_var), + expr_type, ) } } @@ -1942,7 +1988,7 @@ fn constrain_value_def( struct ConstrainedBranch { vars: Vec, - headers: VecMap>, + headers: VecMap>, pattern_constraints: Constraint, is_open_constrains: Vec, body_constraints: Constraint, @@ -1956,8 +2002,8 @@ fn constrain_when_branch_help( env: &mut Env, region: Region, when_branch: &WhenBranch, - pattern_expected: impl Fn(HumanIndex, Region) -> PExpected, - expr_expected: Expected, + pattern_expected: impl Fn(HumanIndex, Region) -> PExpected, + expr_expected: Expected, ) -> ConstrainedBranch { let ret_constraint = constrain_expr( constraints, @@ -2000,9 +2046,9 @@ fn constrain_when_branch_help( // Make sure the bound variables in the patterns on the same branch agree in their types. for (sym, all_branches_bound_typ) in state.headers.iter() { if let Some(this_bound_typ) = partial_state.headers.get(sym) { - let whole_typ = constraints.push_type(all_branches_bound_typ.value.clone()); + let whole_typ = all_branches_bound_typ.value; let this_typ = constraints - .push_expected_type(Expected::NoExpectation(this_bound_typ.value.clone())); + .push_expected_type(Expected::NoExpectation(this_bound_typ.value)); state.constraints.push(constraints.equal_types( whole_typ, @@ -2029,16 +2075,14 @@ fn constrain_when_branch_help( let (pattern_constraints, delayed_is_open_constraints, body_constraints) = if let Some(loc_guard) = &when_branch.guard { + let bool_index = constraints.push_type(Variable(Variable::BOOL)); + let guard_constraint = constrain_expr( constraints, env, region, &loc_guard.value, - Expected::ForReason( - Reason::WhenGuard, - Type::Variable(Variable::BOOL), - loc_guard.region, - ), + Expected::ForReason(Reason::WhenGuard, bool_index, loc_guard.region), ); // must introduce the headers from the pattern before constraining the guard @@ -2072,8 +2116,8 @@ fn constrain_field( field_var: Variable, loc_expr: &Loc, ) -> (Type, Constraint) { - let field_type = Variable(field_var); - let field_expected = NoExpectation(field_type.clone()); + let field_type = constraints.push_type(Variable(field_var)); + let field_expected = NoExpectation(field_type); let constraint = constrain_expr( constraints, env, @@ -2082,14 +2126,14 @@ fn constrain_field( field_expected, ); - (field_type, constraint) + (Variable(field_var), constraint) } #[inline(always)] fn constrain_empty_record( constraints: &mut Constraints, region: Region, - expected: Expected, + expected: Expected, ) -> Constraint { let record_type_index = constraints.push_type(Type::EmptyRec); let expected_index = constraints.push_expected_type(expected); @@ -2129,7 +2173,7 @@ pub fn constrain_decls( Expectation => { let loc_expr = &declarations.expressions[index]; - let bool_type = Type::Variable(Variable::BOOL); + let bool_type = constraints.push_type(Variable(Variable::BOOL)); let expected = Expected::ForReason(Reason::ExpectCondition, bool_type, loc_expr.region); @@ -2146,7 +2190,7 @@ pub fn constrain_decls( ExpectationFx => { let loc_expr = &declarations.expressions[index]; - let bool_type = Type::Variable(Variable::BOOL); + let bool_type = constraints.push_type(Variable(Variable::BOOL)); let expected = Expected::ForReason(Reason::ExpectCondition, bool_type, loc_expr.region); @@ -2221,7 +2265,7 @@ pub(crate) fn constrain_def_pattern( constraints: &mut Constraints, env: &mut Env, loc_pattern: &Loc, - expr_type: Type, + expr_type: TypeOrVar, ) -> PatternState { let pattern_expected = PExpected::NoExpectation(expr_type); @@ -2253,10 +2297,10 @@ fn constrain_typed_def( annotation: &roc_can::def::Annotation, ) -> Constraint { let expr_var = def.expr_var; - let expr_type = Type::Variable(expr_var); + let expr_type_index = constraints.push_type(Variable(expr_var)); let mut def_pattern_state = - constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type.clone()); + constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type_index); def_pattern_state.vars.push(expr_var); @@ -2269,6 +2313,7 @@ fn constrain_typed_def( new_rigid_variables, new_infer_variables, } = instantiate_rigids( + constraints, &annotation.signature, &annotation.introduced_variables, &def.loc_pattern, @@ -2282,25 +2327,23 @@ fn constrain_typed_def( rigids: ftv, }; - let annotation_expected = FromAnnotation( + let signature_index = constraints.push_type(signature.clone()); + + let annotation_expected = constraints.push_expected_type(FromAnnotation( def.loc_pattern.clone(), arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), - ); + signature_index, + )); - { - let type_index = constraints.push_type(expr_type.clone()); - let expected_index = constraints.push_expected_type(annotation_expected); - def_pattern_state.constraints.push(constraints.equal_types( - type_index, - expected_index, - Category::Storage(std::file!(), std::line!()), - Region::span_across(&annotation.region, &def.loc_expr.region), - )); - } + def_pattern_state.constraints.push(constraints.equal_types( + expr_type_index, + annotation_expected, + Category::Storage(std::file!(), std::line!()), + Region::span_across(&annotation.region, &def.loc_expr.region), + )); // when a def is annotated, and its body is a closure, treat this // as a named function (in elm terms) for error messages. @@ -2336,8 +2379,7 @@ fn constrain_typed_def( let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = *ret_var; let closure_var = *closure_var; - let ret_type = *ret_type.clone(); - let ret_type_index = constraints.push_type(ret_type.clone()); + let ret_type_index = constraints.push_type(*ret_type.clone()); vars.push(ret_var); vars.push(closure_var); @@ -2368,7 +2410,7 @@ fn constrain_typed_def( AnnotationSource::TypedBody { region: annotation.region, }, - ret_type, + ret_type_index, ); let ret_constraint = constrain_expr( @@ -2383,15 +2425,18 @@ fn constrain_typed_def( vars.push(*fn_var); let defs_constraint = constraints.and_constraint(argument_pattern_state.constraints); - let signature_closure_type = constraints.push_expected_type(Expected::FromAnnotation( - def.loc_pattern.clone(), - arity, - AnnotationSource::TypedBody { - region: annotation.region, - }, - *signature_closure_type.clone(), - )); - let signature_index = constraints.push_type(signature); + let signature_closure_type = { + let signature_closure_type_index = + constraints.push_type(*signature_closure_type.clone()); + constraints.push_expected_type(Expected::FromAnnotation( + def.loc_pattern.clone(), + arity, + AnnotationSource::TypedBody { + region: annotation.region, + }, + signature_closure_type_index, + )) + }; let cons = [ constraints.let_constraint( [], @@ -2431,7 +2476,7 @@ fn constrain_typed_def( AnnotationSource::TypedBody { region: annotation.region, }, - expr_type, + expr_type_index, ); let ret_constraint = constrain_expr( @@ -2473,6 +2518,9 @@ fn constrain_typed_function_arguments( let it = arguments.iter().zip(arg_types.iter()).enumerate(); for (index, ((pattern_var, annotated_mark, loc_pattern), ann)) in it { + let pattern_var_index = constraints.push_type(Variable(*pattern_var)); + let ann_index = constraints.push_type(ann.clone()); + if loc_pattern.value.surely_exhaustive() { // OPT: we don't need to perform any type-level exhaustiveness checking. // Check instead only that the pattern unifies with the annotation type. @@ -2481,7 +2529,7 @@ fn constrain_typed_function_arguments( index: HumanIndex::zero_based(index), opt_name: opt_label, }, - ann.clone(), + ann_index, loc_pattern.region, ); @@ -2500,7 +2548,7 @@ fn constrain_typed_function_arguments( def_pattern_state.vars.push(*pattern_var); let ann_expected = - constraints.push_expected_type(Expected::NoExpectation(ann.clone())); + constraints.push_expected_type(Expected::NoExpectation(ann_index)); let pattern_con = constraints.equal_types_var( *pattern_var, ann_expected, @@ -2523,7 +2571,7 @@ fn constrain_typed_function_arguments( { // First, solve the type that the pattern is expecting to match in this // position. - let pattern_expected = PExpected::NoExpectation(Type::Variable(*pattern_var)); + let pattern_expected = PExpected::NoExpectation(pattern_var_index); constrain_pattern( constraints, env, @@ -2537,7 +2585,7 @@ fn constrain_typed_function_arguments( { // Store the actual type in a variable. let ann_expected = - constraints.push_expected_type(Expected::NoExpectation(ann.clone())); + constraints.push_expected_type(Expected::NoExpectation(ann_index)); argument_pattern_state .constraints .push(constraints.equal_types_var( @@ -2562,14 +2610,14 @@ fn constrain_typed_function_arguments( // annotation wants. let sketched_rows = sketch_pattern_to_rows(loc_pattern.region, &loc_pattern.value); let category = loc_pattern.value.category(); - let expected = PExpected::ForReason( + let expected = constraints.push_pat_expected_type(PExpected::ForReason( PReason::TypedArg { index: HumanIndex::zero_based(index), opt_name: opt_label, }, - Type::Variable(*pattern_var), + pattern_var_index, loc_pattern.region, - ); + )); let exhaustive_constraint = constraints.exhaustive( annotation_var, loc_pattern.region, @@ -2597,6 +2645,9 @@ fn constrain_typed_function_arguments_simple( ) { let it = arguments.iter().zip(arg_types.iter()).enumerate(); for (index, ((pattern_var, annotated_mark, loc_pattern), ann)) in it { + let pattern_var_index = constraints.push_type(Variable(*pattern_var)); + let ann_index = constraints.push_type(ann.clone()); + if loc_pattern.value.surely_exhaustive() { // OPT: we don't need to perform any type-level exhaustiveness checking. // Check instead only that the pattern unifies with the annotation type. @@ -2605,7 +2656,7 @@ fn constrain_typed_function_arguments_simple( index: HumanIndex::zero_based(index), opt_name: Some(symbol), }, - ann.clone(), + ann_index, loc_pattern.region, ); @@ -2624,7 +2675,7 @@ fn constrain_typed_function_arguments_simple( def_pattern_state.vars.push(*pattern_var); let ann_expected = - constraints.push_expected_type(Expected::NoExpectation(ann.clone())); + constraints.push_expected_type(Expected::NoExpectation(ann_index)); let pattern_con = constraints.equal_types_var( *pattern_var, ann_expected, @@ -2647,7 +2698,7 @@ fn constrain_typed_function_arguments_simple( { // First, solve the type that the pattern is expecting to match in this // position. - let pattern_expected = PExpected::NoExpectation(Type::Variable(*pattern_var)); + let pattern_expected = PExpected::NoExpectation(pattern_var_index); constrain_pattern( constraints, env, @@ -2661,7 +2712,7 @@ fn constrain_typed_function_arguments_simple( { // Store the actual type in a variable. let expected_annotation = - constraints.push_expected_type(Expected::NoExpectation(ann.clone())); + constraints.push_expected_type(Expected::NoExpectation(ann_index)); argument_pattern_state .constraints .push(constraints.equal_types_var( @@ -2677,14 +2728,14 @@ fn constrain_typed_function_arguments_simple( // annotation wants. let sketched_rows = sketch_pattern_to_rows(loc_pattern.region, &loc_pattern.value); let category = loc_pattern.value.category(); - let expected = PExpected::ForReason( + let expected = constraints.push_pat_expected_type(PExpected::ForReason( PReason::TypedArg { index: HumanIndex::zero_based(index), opt_name: Some(symbol), }, - Type::Variable(*pattern_var), + pattern_var_index, loc_pattern.region, - ); + )); let exhaustive_constraint = constraints.exhaustive( annotation_var, loc_pattern.region, @@ -2722,10 +2773,10 @@ fn constrain_def( Some(annotation) => constrain_typed_def(constraints, env, def, body_con, annotation), None => { let expr_var = def.expr_var; - let expr_type = Type::Variable(expr_var); + let expr_type_index = constraints.push_type(Variable(expr_var)); let mut def_pattern_state = - constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type.clone()); + constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type_index); def_pattern_state.vars.push(expr_var); // no annotation, so no extra work with rigids @@ -2735,7 +2786,7 @@ fn constrain_def( env, def.loc_expr.region, &def.loc_expr.value, - NoExpectation(expr_type), + NoExpectation(expr_type_index), ); let expr_con = attach_resolution_constraints(constraints, env, expr_con); @@ -2784,7 +2835,7 @@ fn constrain_value_def_make_constraint( body_con: Constraint, symbol: Loc, expr_var: Variable, - expr_type: Type, + expr_type: TypeOrVar, ) -> Constraint { let def_con = constraints.let_constraint( [], @@ -2848,18 +2899,18 @@ fn constrain_closure_size( captured_types.push(Type::Variable(*var)); // make the variable equal to the looked-up type of symbol - let store_into = - constraints.push_expected_type(Expected::NoExpectation(Type::Variable(*var))); + let store_var_index = constraints.push_type(Variable(*var)); + let store_into = constraints.push_expected_type(Expected::NoExpectation(store_var_index)); captured_symbols_constraints.push(constraints.lookup(*symbol, store_into, Region::zero())); } let finalizer = { // pick a more efficient representation if we don't actually capture anything - let closure_type = Type::ClosureTag { + let closure_type = constraints.push_type(Type::ClosureTag { name, captures: captured_types, ambient_function, - }; + }); let clos_type = constraints.push_expected_type(NoExpectation(closure_type)); constraints.equal_types_var(closure_var, clos_type, Category::ClosureSize, region) }; @@ -2876,11 +2927,12 @@ pub struct InstantiateRigids { } fn instantiate_rigids( + constraints: &mut Constraints, annotation: &Type, introduced_vars: &IntroducedVariables, loc_pattern: &Loc, ftv: &mut MutMap, // rigids defined before the current annotation - headers: &mut VecMap>, + headers: &mut VecMap>, ) -> InstantiateRigids { let mut annotation = annotation.clone(); let mut new_rigid_variables: Vec = Vec::new(); @@ -2926,10 +2978,13 @@ fn instantiate_rigids( // Skipping all of this cloning here would be neat! let loc_annotation_ref = Loc::at(loc_pattern.region, &annotation); if let Pattern::Identifier(symbol) = loc_pattern.value { - headers.insert(symbol, Loc::at(loc_pattern.region, annotation.clone())); - } else if let Some(new_headers) = - crate::pattern::headers_from_annotation(&loc_pattern.value, &loc_annotation_ref) - { + let annotation_index = constraints.push_type(annotation.clone()); + headers.insert(symbol, Loc::at(loc_pattern.region, annotation_index)); + } else if let Some(new_headers) = crate::pattern::headers_from_annotation( + constraints, + &loc_pattern.value, + &loc_annotation_ref, + ) { headers.extend(new_headers) } @@ -3020,13 +3075,13 @@ fn constraint_recursive_function( match opt_annotation { None => { - let expr_type = Type::Variable(expr_var); + let expr_type_index = constraints.push_type(Type::Variable(expr_var)); let expr_con = constrain_untyped_closure( constraints, env, loc_function_def.region, - NoExpectation(expr_type), + NoExpectation(expr_type_index), expr_var, function_def.closure_type, function_def.return_type, @@ -3043,7 +3098,7 @@ fn constraint_recursive_function( flex_info.constraints.push(def_con); flex_info.def_types.insert( loc_symbol.value, - Loc::at(loc_symbol.region, Type::Variable(expr_var)), + Loc::at(loc_symbol.region, expr_type_index), ); } @@ -3066,14 +3121,16 @@ fn constraint_recursive_function( flex_info.vars.extend(new_infer_variables); - let annotation_expected = FromAnnotation( + let signature_index = constraints.push_type(signature.clone()); + + let annotation_expected = constraints.push_expected_type(FromAnnotation( loc_pattern, arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), - ); + signature_index, + )); let (arg_types, _signature_closure_type, ret_type) = match &signature { Type::Function(arg_types, signature_closure_type, ret_type) => { @@ -3082,7 +3139,6 @@ fn constraint_recursive_function( _ => todo!("TODO {:?}", (loc_symbol, &signature)), }; - let expected = annotation_expected; let region = loc_function_def.region; let loc_body_expr = loc_expr; @@ -3095,8 +3151,7 @@ fn constraint_recursive_function( let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1); let ret_var = function_def.return_type; let closure_var = function_def.closure_type; - let ret_type = *ret_type.clone(); - let ret_type_index = constraints.push_type(ret_type.clone()); + let ret_type_index = constraints.push_type(*ret_type.clone()); vars.push(ret_var); vars.push(closure_var); @@ -3107,7 +3162,11 @@ fn constraint_recursive_function( loc_symbol.value, Loc { region, - value: signature.clone(), + // TODO coalesce with other `signature_index`. + // This doesn't yet work; needs investigation as to why. + // My guess is that when types SoA lands, this might just resolve itself, since + // types will be composed from variables to begin with. + value: constraints.push_type(signature.clone()), }, ); @@ -3137,24 +3196,25 @@ fn constraint_recursive_function( &mut vars, ); - let fn_type = Type::Function( + let fn_type = constraints.push_type(Type::Function( pattern_types, Box::new(Type::Variable(closure_var)), - Box::new(ret_type.clone()), - ); + Box::new(*ret_type.clone()), + )); - let expr_con = constrain_expr( - constraints, - env, - loc_body_expr.region, - &loc_body_expr.value, - NoExpectation(ret_type), - ); + let expr_con = { + constrain_expr( + constraints, + env, + loc_body_expr.region, + &loc_body_expr.value, + NoExpectation(ret_type_index), + ) + }; let expr_con = attach_resolution_constraints(constraints, env, expr_con); vars.push(expr_var); - let signature_index = constraints.push_type(signature); let state_constraints = constraints.and_constraint(argument_pattern_state.constraints); let cons = [ constraints.let_constraint( @@ -3164,11 +3224,7 @@ fn constraint_recursive_function( state_constraints, expr_con, ), - { - let type_index = constraints.push_type(fn_type); - let expected_index = constraints.push_expected_type(expected); - constraints.equal_types(type_index, expected_index, Category::Lambda, region) - }, + constraints.equal_types(fn_type, annotation_expected, Category::Lambda, region), // "fn_var is equal to the closure's type" - fn_var is used in code gen // Store type into AST vars. We use Store so errors aren't reported twice constraints.store(signature_index, expr_var, std::file!(), std::line!()), @@ -3225,6 +3281,7 @@ pub fn rec_defs_help_simple( match declarations.declarations[index] { DeclarationTag::Value => { let expr_var = declarations.variables[index]; + let expr_var_index = constraints.push_type(Type::Variable(expr_var)); let opt_annotation = &declarations.annotations[index]; let loc_expr = &declarations.expressions[index]; @@ -3237,7 +3294,7 @@ pub fn rec_defs_help_simple( env, loc_expr.region, &loc_expr.value, - NoExpectation(Type::Variable(expr_var)), + NoExpectation(expr_var_index), ); let expr_con = attach_resolution_constraints(constraints, env, expr_con); @@ -3245,10 +3302,9 @@ pub fn rec_defs_help_simple( hybrid_and_flex_info.vars.push(expr_var); hybrid_and_flex_info.constraints.push(def_con); - hybrid_and_flex_info.def_types.insert( - loc_symbol.value, - Loc::at(loc_symbol.region, Type::Variable(expr_var)), - ); + hybrid_and_flex_info + .def_types + .insert(loc_symbol.value, Loc::at(loc_symbol.region, expr_var_index)); } Some(annotation) => { let arity = annotation.signature.arity(); @@ -3272,13 +3328,15 @@ pub fn rec_defs_help_simple( hybrid_and_flex_info.vars.extend(new_infer_variables); + let signature_index = constraints.push_type(signature.clone()); + let annotation_expected = FromAnnotation( loc_pattern.clone(), arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); let expected = annotation_expected; @@ -3293,8 +3351,6 @@ pub fn rec_defs_help_simple( let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint); - let signature_index = constraints.push_type(signature.clone()); - let cons = [ ret_constraint, // Store type into AST vars. We use Store so errors aren't reported twice @@ -3307,7 +3363,7 @@ pub fn rec_defs_help_simple( ]; let def_con = constraints.and_constraint(cons); - let loc_type = Loc::at(loc_symbol.region, signature); + let loc_type = Loc::at(loc_symbol.region, signature_index); if is_hybrid { hybrid_and_flex_info.vars.extend(&new_rigid_variables); hybrid_and_flex_info.constraints.push(def_con); @@ -3436,10 +3492,10 @@ fn rec_defs_help( for def in defs { let expr_var = def.expr_var; - let expr_type = Type::Variable(expr_var); + let expr_type_index = constraints.push_type(Variable(expr_var)); let mut def_pattern_state = - constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type.clone()); + constrain_def_pattern(constraints, env, &def.loc_pattern, expr_type_index); def_pattern_state.vars.push(expr_var); @@ -3450,7 +3506,7 @@ fn rec_defs_help( env, def.loc_expr.region, &def.loc_expr.value, - NoExpectation(expr_type), + NoExpectation(expr_type_index), ); let expr_con = attach_resolution_constraints(constraints, env, expr_con); @@ -3472,6 +3528,7 @@ fn rec_defs_help( new_rigid_variables, new_infer_variables, } = instantiate_rigids( + constraints, &annotation.signature, &annotation.introduced_variables, &def.loc_pattern, @@ -3483,13 +3540,15 @@ fn rec_defs_help( hybrid_and_flex_info.vars.extend(new_infer_variables); + let signature_index = constraints.push_type(signature.clone()); + let annotation_expected = FromAnnotation( def.loc_pattern.clone(), arity, AnnotationSource::TypedBody { region: annotation.region, }, - signature.clone(), + signature_index, ); // when a def is annotated, and it's body is a closure, treat this @@ -3527,8 +3586,7 @@ fn rec_defs_help( let mut vars = Vec::with_capacity(state.vars.capacity() + 1); let ret_var = *ret_var; let closure_var = *closure_var; - let ret_type = *ret_type.clone(); - let ret_type_index = constraints.push_type(ret_type.clone()); + let ret_type_index = constraints.push_type(*ret_type.clone()); vars.push(ret_var); vars.push(closure_var); @@ -3557,9 +3615,9 @@ fn rec_defs_help( let fn_type_index = constraints.push_type(Type::Function( pattern_types, Box::new(Type::Variable(closure_var)), - Box::new(ret_type.clone()), + Box::new(*ret_type.clone()), )); - let body_type = NoExpectation(ret_type.clone()); + let body_type = NoExpectation(ret_type_index); let expr_con = constrain_expr( constraints, env, @@ -3571,7 +3629,6 @@ fn rec_defs_help( vars.push(*fn_var); - let signature_index = constraints.push_type(signature); let state_constraints = constraints.and_constraint(state.constraints); let expected_index = constraints.push_expected_type(expected); let cons = [ @@ -3639,8 +3696,6 @@ fn rec_defs_help( let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint); - let signature_index = constraints.push_type(signature); - let cons = [ ret_constraint, // Store type into AST vars. We use Store so errors aren't reported twice @@ -3758,10 +3813,10 @@ fn constrain_field_update( field: Lowercase, loc_expr: &Loc, ) -> (Variable, Type, Constraint) { - let field_type = Type::Variable(var); + let field_type = constraints.push_type(Variable(var)); let reason = Reason::RecordUpdateValue(field); - let expected = ForReason(reason, field_type.clone(), region); + let expected = ForReason(reason, field_type, region); let con = constrain_expr(constraints, env, loc_expr.region, &loc_expr.value, expected); - (var, field_type, con) + (var, Variable(var), con) } diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index 76f9047d36..6b640065a4 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -50,8 +50,9 @@ fn constrain_symbols_from_requires( }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(loc_symbol.value)); + let type_index = constraints.push_type(loc_type.value); let def_pattern_state = - constrain_def_pattern(constraints, &mut env, &pattern, loc_type.value); + constrain_def_pattern(constraints, &mut env, &pattern, type_index); debug_assert!(env.resolutions_to_make.is_empty()); @@ -69,13 +70,15 @@ fn constrain_symbols_from_requires( // Otherwise, this symbol comes from an app module - we want to check that the type // provided by the app is in fact what the package module requires. let arity = loc_type.value.arity(); + let typ = loc_type.value; + let type_index = constraints.push_type(typ); let expected = constraints.push_expected_type(Expected::FromAnnotation( loc_symbol.map(|&s| Pattern::Identifier(s)), arity, AnnotationSource::RequiredSymbol { region: loc_type.region, }, - loc_type.value, + type_index, )); let provided_eq_requires_constr = constraints.lookup(loc_symbol.value, expected, loc_type.region); @@ -106,12 +109,10 @@ pub fn frontload_ability_constraints( }; let pattern = Loc::at_zero(roc_can::pattern::Pattern::Identifier(*member_name)); - let mut def_pattern_state = constrain_def_pattern( - constraints, - &mut env, - &pattern, - Type::Variable(signature_var), - ); + let signature_index = constraints.push_type(signature.clone()); + + let mut def_pattern_state = + constrain_def_pattern(constraints, &mut env, &pattern, signature_index); debug_assert!(env.resolutions_to_make.is_empty()); @@ -121,7 +122,7 @@ pub fn frontload_ability_constraints( let infer_variables = vars.flex_vars.iter().copied(); let signature_expectation = - constraints.push_expected_type(Expected::NoExpectation(signature.clone())); + constraints.push_expected_type(Expected::NoExpectation(signature_index)); def_pattern_state .constraints diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index 8ece500a1f..e1a79fb46d 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -1,9 +1,9 @@ use crate::builtins; use crate::expr::{constrain_expr, Env}; -use roc_can::constraint::{Constraint, Constraints}; +use roc_can::constraint::{Constraint, Constraints, TypeOrVar}; use roc_can::expected::{Expected, PExpected}; use roc_can::pattern::Pattern::{self, *}; -use roc_can::pattern::{DestructType, RecordDestruct}; +use roc_can::pattern::{DestructType, ListPatterns, RecordDestruct}; use roc_collections::all::{HumanIndex, SendMap}; use roc_collections::VecMap; use roc_module::ident::Lowercase; @@ -17,7 +17,7 @@ use roc_types::types::{ #[derive(Default, Debug)] pub struct PatternState { - pub headers: VecMap>, + pub headers: VecMap>, pub vars: Vec, pub constraints: Vec, pub delayed_is_open_constraints: Vec, @@ -31,14 +31,16 @@ pub struct PatternState { /// Would add `x => <42>` to the headers (i.e., symbol points to a type variable). If the /// definition has an annotation, we instead now add `x => Int`. pub fn headers_from_annotation( + constraints: &mut Constraints, pattern: &Pattern, annotation: &Loc<&Type>, -) -> Option>> { +) -> Option>> { let mut headers = VecMap::default(); // Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int` // in such incorrect cases we don't put the full annotation in headers, just a variable, and let // inference generate a proper error. - let is_structurally_valid = headers_from_annotation_help(pattern, annotation, &mut headers); + let is_structurally_valid = + headers_from_annotation_help(constraints, pattern, annotation, &mut headers); if is_structurally_valid { Some(headers) @@ -48,9 +50,10 @@ pub fn headers_from_annotation( } fn headers_from_annotation_help( + constraints: &mut Constraints, pattern: &Pattern, annotation: &Loc<&Type>, - headers: &mut VecMap>, + headers: &mut VecMap>, ) -> bool { match pattern { Identifier(symbol) @@ -60,7 +63,8 @@ fn headers_from_annotation_help( ident: symbol, specializes: _, } => { - let typ = Loc::at(annotation.region, annotation.value.clone()); + let annotation_index = constraints.push_type(annotation.value.clone()); + let typ = Loc::at(annotation.region, annotation_index); headers.insert(*symbol, typ); true } @@ -87,9 +91,10 @@ fn headers_from_annotation_help( // `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases // the type of `x` within the binding itself is the same. if let Some(field_type) = fields.get(&destruct.label) { + let field_type_index = constraints.push_type(field_type.as_inner().clone()); headers.insert( destruct.symbol, - Loc::at(annotation.region, field_type.clone().into_inner()), + Loc::at(annotation.region, field_type_index), ); } else { return false; @@ -101,6 +106,14 @@ fn headers_from_annotation_help( _ => false, }, + List { .. } => { + // There are no interesting headers to introduce for list patterns, since the only + // exhaustive list pattern is + // \[..] -> + // which does not introduce any symbols. + false + }, + AppliedTag { tag_name, arguments, @@ -117,6 +130,7 @@ fn headers_from_annotation_help( .zip(arg_types.iter()) .all(|(arg_pattern, arg_type)| { headers_from_annotation_help( + constraints, &arg_pattern.1.value, &Loc::at(annotation.region, arg_type), headers, @@ -148,11 +162,13 @@ fn headers_from_annotation_help( && type_arguments.len() == pat_type_arguments.len() && lambda_set_variables.len() == pat_lambda_set_variables.len() => { - let typ = Loc::at(annotation.region, annotation.value.clone()); + let annotation_index = constraints.push_type(annotation.value.clone()); + let typ = Loc::at(annotation.region, annotation_index); headers.insert(*opaque, typ); let (_, argument_pat) = &**argument; headers_from_annotation_help( + constraints, &argument_pat.value, &Loc::at(annotation.region, actual), headers, @@ -171,7 +187,7 @@ pub fn constrain_pattern( env: &mut Env, pattern: &Pattern, region: Region, - expected: PExpected, + expected: PExpected, state: &mut PatternState, ) { match pattern { @@ -182,8 +198,8 @@ pub fn constrain_pattern( // A -> "" // _ -> "" // so, we know that "x" (in this case, a tag union) must be open. - if could_be_a_tag_union(expected.get_type_ref()) { - let type_index = constraints.push_type(expected.get_type()); + if could_be_a_tag_union(constraints, *expected.get_type_ref()) { + let type_index = expected.get_type(); state .delayed_is_open_constraints @@ -195,9 +211,9 @@ pub fn constrain_pattern( } Identifier(symbol) | Shadowed(_, _, symbol) => { - if could_be_a_tag_union(expected.get_type_ref()) { - let type_index = constraints.push_type(expected.get_type_ref().clone()); + let type_index = *expected.get_type_ref(); + if could_be_a_tag_union(constraints, type_index) { state .delayed_is_open_constraints .push(constraints.is_open_type(type_index)); @@ -207,7 +223,7 @@ pub fn constrain_pattern( *symbol, Loc { region, - value: expected.get_type(), + value: type_index, }, ); } @@ -216,9 +232,9 @@ pub fn constrain_pattern( ident: symbol, specializes: _, } => { - if could_be_a_tag_union(expected.get_type_ref()) { - let type_index = constraints.push_type(expected.get_type_ref().clone()); + let type_index = *expected.get_type_ref(); + if could_be_a_tag_union(constraints, type_index) { state.constraints.push(constraints.is_open_type(type_index)); } @@ -226,7 +242,7 @@ pub fn constrain_pattern( *symbol, Loc { region, - value: expected.get_type(), + value: type_index, }, ); } @@ -270,7 +286,7 @@ pub fn constrain_pattern( let num_type = constraints.push_type(num_type); // Link the free num var with the int var and our expectation. - let int_type = builtins::num_int(Type::Variable(precision_var)); + let int_type = constraints.push_type(builtins::num_int(Type::Variable(precision_var))); state.constraints.push({ let expected_index = @@ -300,10 +316,11 @@ pub fn constrain_pattern( region, Category::Frac, ); + let num_type_index = constraints.push_type(num_type); // TODO check me if something breaks! // Link the free num var with the float var and our expectation. - let float_type = builtins::num_float(Type::Variable(precision_var)); - let num_type_index = constraints.push_type(num_type); // TODO check me if something breaks! + let float_type = + constraints.push_type(builtins::num_float(Type::Variable(precision_var))); state.constraints.push({ let expected_index = @@ -345,11 +362,11 @@ pub fn constrain_pattern( Category::Int, ); - // Link the free num var with the int var and our expectation. - let int_type = builtins::num_int(Type::Variable(precision_var)); - let num_type_index = constraints.push_type(num_type); + // Link the free num var with the int var and our expectation. + let int_type = constraints.push_type(builtins::num_int(Type::Variable(precision_var))); + state.constraints.push({ let expected_index = constraints.push_expected_type(Expected::NoExpectation(int_type)); @@ -394,12 +411,13 @@ pub fn constrain_pattern( } in destructs { let pat_type = Type::Variable(*var); - let expected = PExpected::NoExpectation(pat_type.clone()); + let pat_type_index = constraints.push_type(pat_type.clone()); + let expected = PExpected::NoExpectation(pat_type_index); if !state.headers.contains_key(symbol) { state .headers - .insert(*symbol, Loc::at(region, pat_type.clone())); + .insert(*symbol, Loc::at(region, pat_type_index)); } let field_type = match typ { @@ -408,7 +426,7 @@ pub fn constrain_pattern( let expected_pat = constraints.push_pat_expected_type(PExpected::ForReason( PReason::PatternGuard, - pat_type.clone(), + pat_type_index, loc_guard.region, )); @@ -436,7 +454,7 @@ pub fn constrain_pattern( let expected_pat = constraints.push_pat_expected_type(PExpected::ForReason( PReason::OptionalField, - pat_type.clone(), + pat_type_index, loc_expr.region, )); @@ -451,7 +469,7 @@ pub fn constrain_pattern( let expr_expected = Expected::ForReason( Reason::RecordDefaultField(label.clone()), - pat_type.clone(), + pat_type_index, loc_expr.region, ); @@ -477,7 +495,10 @@ pub fn constrain_pattern( state.vars.push(*var); } - let record_type = Type::Record(field_types, TypeExtension::from_type(ext_type)); + let record_type = constraints.push_type(Type::Record( + field_types, + TypeExtension::from_type(ext_type), + )); let whole_var_index = constraints.push_type(Type::Variable(*whole_var)); let expected_record = @@ -501,6 +522,54 @@ pub fn constrain_pattern( state.constraints.push(whole_con); state.constraints.push(record_con); } + + List { + list_var, + elem_var, + patterns: + ListPatterns { + patterns, + opt_rest: _, + }, + } => { + let elem_var_index = constraints.push_type(Type::Variable(*elem_var)); + + for loc_pat in patterns.iter() { + let expected = + PExpected::ForReason(PReason::ListElem, elem_var_index, loc_pat.region); + + constrain_pattern( + constraints, + env, + &loc_pat.value, + loc_pat.region, + expected, + state, + ); + } + + let list_var_index = constraints.push_type(Type::Variable(*list_var)); + let solved_list = constraints.push_type(Type::Apply( + Symbol::LIST_LIST, + vec![Loc::at(region, Type::Variable(*elem_var))], + region, + )); + let store_solved_list = constraints.store(solved_list, *list_var, file!(), line!()); + + let expected = constraints.push_pat_expected_type(expected); + let expected_constraint = constraints.pattern_presence( + list_var_index, + expected, + PatternCategory::List, + region, + ); + + state.vars.push(*list_var); + state.vars.push(*elem_var); + state.constraints.push(store_solved_list); + state.constraints.push(expected_constraint); + } + AppliedTag { whole_var, ext_var, @@ -512,7 +581,7 @@ pub fn constrain_pattern( for (index, (pattern_var, loc_pattern)) in arguments.iter().enumerate() { state.vars.push(*pattern_var); - let pattern_type = Type::Variable(*pattern_var); + let pattern_type = constraints.push_type(Type::Variable(*pattern_var)); let expected = PExpected::ForReason( PReason::TagArg { @@ -533,7 +602,7 @@ pub fn constrain_pattern( } let pat_category = PatternCategory::Ctor(tag_name.clone()); - let expected_type = constraints.push_type(expected.get_type_ref().clone()); + let expected_type = *expected.get_type_ref(); let whole_con = constraints.includes_tag( expected_type, @@ -565,8 +634,9 @@ pub fn constrain_pattern( // Suppose we are constraining the pattern \@Id who, where Id n := [Id U64 n] let (arg_pattern_var, loc_arg_pattern) = &**argument; let arg_pattern_type = Type::Variable(*arg_pattern_var); + let arg_pattern_type_index = constraints.push_type(Type::Variable(*arg_pattern_var)); - let opaque_type = Type::Alias { + let opaque_type = constraints.push_type(Type::Alias { symbol: *opaque, type_arguments: type_arguments .iter() @@ -579,10 +649,10 @@ pub fn constrain_pattern( infer_ext_in_output_types: vec![], actual: Box::new(arg_pattern_type.clone()), kind: AliasKind::Opaque, - }; + }); // First, add a constraint for the argument "who" - let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone()); + let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type_index); constrain_pattern( constraints, env, @@ -617,11 +687,13 @@ pub fn constrain_pattern( // `[A k1, B k1] += typeof (A s)`, because we are in a destructure position and not // all constructors are covered in this branch! let arg_pattern_type = constraints.push_type(arg_pattern_type); - let specialized_type = constraints - .push_pat_expected_type(PExpected::NoExpectation((**specialized_def_type).clone())); + let specialized_type_index = constraints.push_type((**specialized_def_type).clone()); + let specialized_type_expected = constraints + .push_pat_expected_type(PExpected::NoExpectation(specialized_type_index)); + let link_type_variables_con = constraints.pattern_presence( arg_pattern_type, - specialized_type, + specialized_type_expected, PatternCategory::Opaque(*opaque), loc_arg_pattern.region, ); @@ -654,6 +726,18 @@ pub fn constrain_pattern( } } -fn could_be_a_tag_union(typ: &Type) -> bool { - !matches!(typ, Type::Apply(..) | Type::Function(..) | Type::Record(..)) +fn could_be_a_tag_union(constraints: &mut Constraints, typ: TypeOrVar) -> bool { + match typ.split() { + Ok(typ_index) => { + let typ_cell = &mut constraints.types[typ_index.index()]; + !matches!( + typ_cell.get_mut(), + Type::Apply(..) | Type::Function(..) | Type::Record(..) + ) + } + Err(_) => { + // Variables are opaque at this point, assume yes + true + } + } } diff --git a/crates/compiler/derive/src/lib.rs b/crates/compiler/derive/src/lib.rs index 1f63ac3734..43d547ddb5 100644 --- a/crates/compiler/derive/src/lib.rs +++ b/crates/compiler/derive/src/lib.rs @@ -99,14 +99,11 @@ impl DerivedModule { exposed_by_module: &ExposedByModule, key: DeriveKey, ) -> &(Symbol, Def, SpecializationLambdaSets) { - match self.map.get(&key) { - Some(entry) => { - // rustc won't let us return an immutable reference *and* continue using - // `self.map` immutably below, but this is safe, because we are not returning - // an immutable reference to the entry. - return unsafe { std::mem::transmute(entry) }; - } - None => {} + if let Some(entry) = self.map.get(&key) { + // rustc won't let us return an immutable reference *and* continue using + // `self.map` immutably below, but this is safe, because we are not returning + // an immutable reference to the entry. + return unsafe { std::mem::transmute(entry) }; } let ident_id = if cfg!(debug_assertions) || cfg!(feature = "debug-derived-symbols") { diff --git a/crates/compiler/derive_key/src/lib.rs b/crates/compiler/derive_key/src/lib.rs index 65a0062a7d..a994a622e3 100644 --- a/crates/compiler/derive_key/src/lib.rs +++ b/crates/compiler/derive_key/src/lib.rs @@ -25,7 +25,7 @@ use hash::{FlatHash, FlatHashKey}; use roc_module::symbol::Symbol; use roc_types::subs::{Subs, Variable}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum DeriveError { /// Unbound variable present in the type-to-derive. It may be possible to derive for this type /// once the unbound variable is resolved. diff --git a/crates/compiler/exhaustive/Cargo.toml b/crates/compiler/exhaustive/Cargo.toml index 5a1fa3a271..225dbb15cb 100644 --- a/crates/compiler/exhaustive/Cargo.toml +++ b/crates/compiler/exhaustive/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" roc_collections = { path = "../collections" } roc_region = { path = "../region" } roc_module = { path = "../module" } +roc_error_macros = { path = "../../error_macros" } diff --git a/crates/compiler/exhaustive/src/lib.rs b/crates/compiler/exhaustive/src/lib.rs index 631b2d8539..1b96b0a18f 100644 --- a/crates/compiler/exhaustive/src/lib.rs +++ b/crates/compiler/exhaustive/src/lib.rs @@ -2,6 +2,7 @@ //! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf use roc_collections::all::{HumanIndex, MutMap}; +use roc_error_macros::internal_error; use roc_module::{ ident::{Lowercase, TagIdIntType, TagName}, symbol::Symbol, @@ -69,6 +70,54 @@ pub enum Pattern { Anything, Literal(Literal), Ctor(Union, TagId, std::vec::Vec), + List(ListArity, std::vec::Vec), +} + +/// The arity of list pattern. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ListArity { + /// A list pattern of an exact size. + Exact(usize), + /// A list pattern matching a variable size, where `Slice(before, after)` refers to the number + /// of elements that must be present before and after the variable rest pattern, respectively. + /// + /// For example, + /// [..] => Slice(0, 0) + /// [A, .., B] => Slice(1, 1) + /// [A, B, ..] => Slice(2, 0) + /// [.., A, B] => Slice(0, 2) + Slice(usize, usize), +} + +impl ListArity { + /// The trivially-exhaustive list pattern `[..]` + const ANY: ListArity = ListArity::Slice(0, 0); + + pub fn min_len(&self) -> usize { + match self { + ListArity::Exact(n) => *n, + ListArity::Slice(l, r) => l + r, + } + } + + /// Could this list pattern include list pattern arity `other`? + fn covers_arities_of(&self, other: &Self) -> bool { + self.covers_length(other.min_len()) + } + + pub fn covers_length(&self, length: usize) -> bool { + match self { + ListArity::Exact(l) => { + // [_, _, _] can only cover [_, _, _] + *l == length + } + ListArity::Slice(head, tail) => { + // [_, _, .., _] can cover infinite arities >=3 , including + // [_, _, .., _], [_, .., _, _], [_, _, .., _, _], [_, _, _, .., _, _], and so on + head + tail <= length + } + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -141,15 +190,16 @@ pub fn check( /// The initial count of items per row "n" is also 1 /// The resulting rows are examples of missing patterns fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix { - if matrix.is_empty() { - vec![std::iter::repeat(Anything).take(n).collect()] + let ctors = if matrix.is_empty() { + return vec![std::iter::repeat(Anything).take(n).collect()]; } else if n == 0 { - vec![] + return vec![]; } else { - let ctors = collect_ctors(matrix); - let num_seen = ctors.len(); + collect_ctors(matrix) + }; - if num_seen == 0 { + match ctors { + CollectedCtors::NonExhaustiveAny => { let new_matrix: Vec<_> = matrix .iter() .filter_map(|row| specialize_row_by_anything(row)) @@ -161,7 +211,11 @@ fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix { } rest - } else { + } + CollectedCtors::Ctors(ctors) => { + debug_assert!(!ctors.is_empty()); + + let num_seen = ctors.len(); let alts = ctors.iter().next().unwrap().1; let alt_list = &alts.alternatives; @@ -193,7 +247,7 @@ fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix { let is_alt_exhaustive = |Ctor { arity, tag_id, .. }| { let new_matrix: Vec<_> = matrix .iter() - .filter_map(|r| specialize_row_by_ctor(tag_id, arity, r)) + .filter_map(|r| specialize_row_by_ctor(tag_id, arity, r.to_owned())) .collect(); let rest: Vec> = is_exhaustive(&new_matrix, arity + n - 1); @@ -212,6 +266,21 @@ fn is_exhaustive(matrix: &RefPatternMatrix, n: usize) -> PatternMatrix { .collect() } } + CollectedCtors::NonExhaustiveList(alt_lists) => { + let is_alt_exhaustive = |arity: ListArity| { + let new_matrix: Vec<_> = matrix + .iter() + .filter_map(|row| specialize_row_by_list(arity, row.to_owned())) + .collect(); + + let rest = is_exhaustive(&new_matrix, arity.min_len() + n - 1); + + rest.into_iter() + .map(move |row_not_covered| recover_list(arity, row_not_covered)) + }; + + alt_lists.into_iter().flat_map(is_alt_exhaustive).collect() + } } } @@ -232,14 +301,23 @@ fn recover_ctor( arity: usize, mut patterns: Vec, ) -> Vec { - let mut rest = patterns.split_off(arity); - let args = patterns; + let args = patterns.split_off(patterns.len() - arity); + let mut rest = patterns; rest.push(Ctor(union, tag_id, args)); rest } +fn recover_list(arity: ListArity, mut patterns: Vec) -> Vec { + let list_elems = patterns.split_off(patterns.len() - arity.min_len()); + let mut rest = patterns; + + rest.push(List(arity, list_elems)); + + rest +} + /// Check if a new row "vector" is useful given previous rows "matrix" pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { let mut matrix = Vec::with_capacity(old_matrix.len()); @@ -262,13 +340,53 @@ pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { match first_pattern { // keep checking rows that start with this Ctor or Anything Ctor(_, id, args) => { - specialize_row_by_ctor2(id, args.len(), &mut old_matrix, &mut matrix); + specialize_matrix_by_ctor(id, args.len(), &mut old_matrix, &mut matrix); std::mem::swap(&mut old_matrix, &mut matrix); vector.extend(args); } + List(arity, args) => { + // Check if there any specialized constructor of this list pattern + // that is useful. + let spec_list_ctors = build_list_ctors_covering_patterns( + arity, + filter_matrix_list_ctors(&old_matrix), + ); + debug_assert!(!spec_list_ctors.is_empty()); + + if spec_list_ctors.len() == 1 { + specialize_matrix_by_list( + spec_list_ctors[0], + &mut old_matrix, + &mut matrix, + ); + + std::mem::swap(&mut old_matrix, &mut matrix); + + vector.extend(args); + } else { + // TODO turn this into an iteration over the outer loop rather than bouncing + vector.extend(args); + for list_ctor in spec_list_ctors { + let mut old_matrix = old_matrix.clone(); + let mut spec_matrix = Vec::with_capacity(old_matrix.len()); + + specialize_matrix_by_list( + list_ctor, + &mut old_matrix, + &mut spec_matrix, + ); + + if is_useful(spec_matrix, vector.clone()) { + return true; + } + } + return false; + } + } + Anything => { // check if all alternatives appear in matrix match is_complete(&old_matrix) { @@ -293,7 +411,7 @@ pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { let mut old_matrix = old_matrix.clone(); let mut matrix = vec![]; - specialize_row_by_ctor2( + specialize_matrix_by_ctor( tag_id, arity, &mut old_matrix, @@ -330,6 +448,8 @@ pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { } Some(Anything) => matrix.push(patterns), + Some(List(..)) => internal_error!("After type checking, lists and literals should never align in exhaustiveness checking"), + Some(Ctor(_, _, _)) => panic!( r#"Compiler bug! After type checking, constructors and literals should never align in pattern match exhaustiveness checks."# ), @@ -347,67 +467,118 @@ pub fn is_useful(mut old_matrix: PatternMatrix, mut vector: Row) -> bool { } } -/// INVARIANT: (length row == N) ==> (length result == arity + N - 1) -fn specialize_row_by_ctor2( - tag_id: TagId, - arity: usize, +// Specialize rows in the matrix that match a list's constructor(s). +// +// See the docs on [build_list_ctors_covering_patterns] for more information on how list +// constructors are built up. +fn specialize_matrix_by_list( + spec_arity: ListArity, old_matrix: &mut PatternMatrix, - matrix: &mut PatternMatrix, + spec_matrix: &mut PatternMatrix, ) { - for mut row in old_matrix.drain(..) { - let head = row.pop(); - let mut patterns = row; - - match head { - Some(Ctor(_, id, args)) => - if id == tag_id { - patterns.extend(args); - matrix.push(patterns); - } else { - // do nothing - } - Some(Anything) => { - // TODO order! - patterns.extend(std::iter::repeat(Anything).take(arity)); - matrix.push(patterns); - } - Some(Literal(_)) => panic!( "Compiler bug! After type checking, constructors and literal should never align in pattern match exhaustiveness checks."), - None => panic!("Compiler error! Empty matrices should not get specialized."), - } + for row in old_matrix.drain(..) { + if let Some(spec_row) = specialize_row_by_list(spec_arity, row) { + spec_matrix.push(spec_row); + } } } -/// INVARIANT: (length row == N) ==> (length result == arity + N - 1) -fn specialize_row_by_ctor(tag_id: TagId, arity: usize, row: &RefRow) -> Option { - let mut row = row.to_vec(); - +// Specialize a row that matches a list's constructor(s). +// +// See the docs on [build_list_ctors_covering_patterns] for more information on how list +// constructors are built up. +fn specialize_row_by_list(spec_arity: ListArity, mut row: Row) -> Option { let head = row.pop(); - let patterns = row; + let mut spec_patterns = row; match head { - Some(Ctor(_, id, args)) => { - if id == tag_id { - // TODO order! - let mut new_patterns = Vec::new(); - new_patterns.extend(args); - new_patterns.extend(patterns); - Some(new_patterns) + Some(List(this_arity, args)) => { + if this_arity.covers_arities_of(&spec_arity) { + // This pattern covers the constructor we are specializing, so add on the + // specialized fields of this pattern relative to the given constructor. + if spec_arity.min_len() != this_arity.min_len() { + // This list pattern covers the list we are specializing, so it must be + // a variable-length slice, i.e. of the form `[before, .., after]`. + // + // Hence, the list we're specializing for must have at least a larger minimum length. + // So we fill the middle part with enough wildcards to reach the length of + // list constructor we're specializing for. + debug_assert!(spec_arity.min_len() > this_arity.min_len()); + match this_arity { + ListArity::Exact(_) => internal_error!("exact-sized lists cannot cover lists of other minimum length"), + ListArity::Slice(before, after) => { + let before = &args[..before]; + let after = &args[this_arity.min_len() - after..]; + let num_extra_wildcards = spec_arity.min_len() - this_arity.min_len(); + let extra_wildcards = std::iter::repeat(&Anything).take(num_extra_wildcards); + + let new_pats = (before.iter().chain(extra_wildcards).chain(after)).cloned(); + + spec_patterns.extend(new_pats); + } + } + } else { + debug_assert_eq!(this_arity.min_len(), spec_arity.min_len()); + + spec_patterns.extend(args); + } + + Some(spec_patterns) } else { None } } Some(Anything) => { - // TODO order! - let new_patterns = std::iter::repeat(Anything) - .take(arity) - .chain(patterns) - .collect(); - Some(new_patterns) + // The specialized fields for a `Anything` pattern with a list constructor is just + // `Anything` repeated for the number of times we want to see the list pattern. + spec_patterns.extend(std::iter::repeat(Anything).take(spec_arity.min_len())); + Some(spec_patterns) } - Some(Literal(_)) => unreachable!( - r#"Compiler bug! After type checking, a constructor can never align with a literal: that should be a type error!"# + Some(Ctor(..)) => internal_error!("After type checking, lists and constructors should never align in exhaustiveness checking"), + Some(Literal(..)) => internal_error!("After type checking, lists and literals should never align in exhaustiveness checking"), + None => internal_error!("Empty matrices should not get specialized"), + } +} + +/// INVARIANT: (length row == N) ==> (length result == arity + N - 1) +fn specialize_matrix_by_ctor( + tag_id: TagId, + arity: usize, + old_matrix: &mut PatternMatrix, + matrix: &mut PatternMatrix, +) { + for row in old_matrix.drain(..) { + if let Some(spec_row) = specialize_row_by_ctor(tag_id, arity, row) { + matrix.push(spec_row); + } + } +} + +/// INVARIANT: (length row == N) ==> (length result == arity + N - 1) +fn specialize_row_by_ctor(tag_id: TagId, arity: usize, mut row: Row) -> Option { + let head = row.pop(); + let mut spec_patterns = row; + + match head { + Some(Ctor(_, id, args)) => { + if id == tag_id { + spec_patterns.extend(args); + Some(spec_patterns) + } else { + None + } + } + Some(Anything) => { + spec_patterns.extend(std::iter::repeat(Anything).take(arity)); + Some(spec_patterns) + } + Some(List(..)) => { + internal_error!(r#"After type checking, a constructor can never align with a list"#) + } + Some(Literal(_)) => internal_error!( + r#"After type checking, a constructor can never align with a literal: that should be a type error!"# ), - None => panic!("Compiler error! Empty matrices should not get specialized."), + None => internal_error!("Empty matrices should not get specialized."), } } @@ -430,16 +601,21 @@ pub enum Complete { fn is_complete(matrix: &RefPatternMatrix) -> Complete { let ctors = collect_ctors(matrix); - let length = ctors.len(); - let mut it = ctors.into_iter(); + match ctors { + CollectedCtors::NonExhaustiveAny | CollectedCtors::NonExhaustiveList(_) => Complete::No, + CollectedCtors::Ctors(ctors) => { + let length = ctors.len(); + let mut it = ctors.into_iter(); - match it.next() { - None => Complete::No, - Some((_, Union { alternatives, .. })) => { - if length == alternatives.len() { - Complete::Yes(alternatives) - } else { - Complete::No + match it.next() { + None => Complete::No, + Some((_, Union { alternatives, .. })) => { + if length == alternatives.len() { + Complete::Yes(alternatives) + } else { + Complete::No + } + } } } } @@ -452,14 +628,219 @@ type PatternMatrix = Vec>; type RefRow = [Pattern]; type Row = Vec; -fn collect_ctors(matrix: &RefPatternMatrix) -> MutMap { - let mut ctors = MutMap::default(); +enum CollectedCtors { + NonExhaustiveAny, + NonExhaustiveList(Vec), + Ctors(MutMap), +} - for row in matrix { - if let Some(Ctor(union, id, _)) = row.last() { - ctors.insert(*id, union.clone()); - } +fn collect_ctors(matrix: &RefPatternMatrix) -> CollectedCtors { + if matrix.is_empty() { + return CollectedCtors::NonExhaustiveAny; } - ctors + let first_row = &matrix[0]; + + if let Some(ctor) = first_row.last() { + match ctor { + Anything => CollectedCtors::NonExhaustiveAny, + Pattern::Literal(_) => CollectedCtors::NonExhaustiveAny, + List(_, _) => { + let list_ctors = build_list_ctors_covering_patterns( + ListArity::ANY, + filter_matrix_list_ctors(matrix), + ); + + CollectedCtors::NonExhaustiveList(list_ctors) + } + Pattern::Ctor(_, _, _) => { + let mut ctors = MutMap::default(); + + for row in matrix { + if let Some(Ctor(union, id, _)) = row.last() { + ctors.insert(*id, union.clone()); + } + } + + CollectedCtors::Ctors(ctors) + } + } + } else { + CollectedCtors::NonExhaustiveAny + } +} + +/// Largely derived from Rust's list-pattern exhaustiveness checking algorithm: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/usefulness/index.html +/// Dual-licensed under MIT and Apache licenses. +/// Thank you, Rust contributors. +/// +/// Calculates the list constructors that are covered by a given [slice constructor][ListArity::Slice], +/// relative to other list constructors matched by a series of patterns. +/// +/// This is relevant for both exhaustiveness and redundancy checking; to understand the motivation, +/// let's start with the exhaustiveness checking case. +/// +/// # Exhaustiveness Checking +/// +/// All list constructors are exausted by the pattern [..], which actually represents the infinite +/// series of constructors +/// [] +/// [_] +/// [_, _] +/// ... +/// +/// But we don't need to enumerate that infinite series to check if a series of list patterns is exhaustive - +/// we only need to enumerate a finite number of constructors, up to the largest exact-size list +/// pattern not covered by the patterns, or the largest slice pattern covered by the patterns. +/// +/// ## Exact-sized patterns +/// +/// Say we have patterns +/// [_] -> .. +/// [_, _] -> .. +/// To exhaustiveness-check these patterns, we only need to build the subset of `[..]` constructors +/// [] +/// [_] +/// [_, _] +/// [_, _, _, ..] +/// to cover all list constructors that may or may not be matched by the patterns (in this case +/// not, because `[]` is not matched, and the last constructor `[_, _, _, ..]` is not matched). +/// +/// We include `[_, _, _, ..]` here because during exhaustiveness checking, we specialize list +/// patterns **by exact size**, not by ranges. That means that is we stopped enumerating the +/// constructors needed at `[_, _, ..]`, when specializing the list patterns against `[_, _, ..]`, +/// we would see that the last pattern `[_, _] -> ..` exhausts it. +/// +/// So, in the presence of exact-size constructors, we want to include a slice constructor that is +/// larger than all other exact-size list pattern. +/// +/// ## Slice patterns +/// +/// Say we have patterns +/// [1] -> .. +/// [2, ..] -> .. +/// now it's enough to just build +/// [] +/// [_, ..] +/// as possible constructors, since the last constructor `[_, ..]` will specialize both patterns to +/// [1] -> .. +/// [2] -> .. +/// and if these patterns are exhaustive w.r.t. their arguments (`1` and `2`, which they are not, +/// since number literals are not exhaustive), then the whole pattern must be exhaustive, since the +/// largest slice constructor `[_, ..]` will cover the remaining infinite number of list constructors. +/// +/// You can see that this holds with slice constructors that match elements at their head and tail +/// as well: +/// [{}, ..] -> .. +/// [.., {}] -> .. +/// Here again it's enough to just build the constructors [] and [_, ..] to match against - +/// notice that above slices of arity `1`, the patterns above do not provide any more information, +/// since they match any additional elements at the tail and head, respectively. +/// +/// So, if they are exhaustive at arity `1`, they must be exhaustive at any higher arity. +/// +/// In fact, in this case, if we are matching against `List {}`, the second pattern redundant! +/// +/// # Redundancy checking +/// +/// Redundancy checking (in general, and for list patterns) is the same as exhaustiveness checking, +/// except that instead of checking whether `[..]` is covered by all patterns, we want to check if +/// the list constructor of a pattern introduces any more information than previous patterns we've +/// seen. +/// +/// Let's say we're redundancy checking the pattern marked by `*` +/// [] -> .. +/// [_] -> .. +/// (*) [.., _] -> .. +/// +/// The list constructors this pattern introduces are the infinite series [_], [_, _], ... +/// But the only ones relevant, relevant to the patterns we've already seen, are +/// [_] +/// [_, _] +/// (Notice that the enumeration algorithm is the same as for `[..]` in the presence of exact-size +/// slices, just that the starting size differs - due to the tail matched by this pattern) +/// +/// During checking we'll see that the `[_, _]` pattern is not already covered, so `[.., _]` is in +/// fact not redundant. +/// +/// On the other hand, suppose we have +/// [] -> .. +/// [_, ..] -> .. +/// (*) [.., _] -> .. +/// +/// Again enumerating the relevant constructors of `[.., _]` relative to the other patterns, we find +/// them to be +/// [] +/// [.., _] +/// the first is already matched by the first pattern `[] -> ..`, and the latter specialized to +/// `[_]`, which in fact is covered by the second pattern `[_, ..] -> ..`. So the pattern marked by (*) +/// is indeed redundant. +/// +/// # All together +/// +/// So the idea to cover the infinite # of list constructors enumerated by a [slice][ListArity::Slice], +/// while specializing to the constructors that the user has provided, is as follows: +/// - Build [exact][ListArity::Exact] constructor variants for everything up to the max slice +/// constructor size, L. +/// - Then, the infinite # of list constructors is covered by the [0..L) exact-size constructors, and +/// the last slice constructor, that covers size [L..∞). +/// +/// If we might only see [exact][ListArity::Exact] constructors along the way, we want to pick the +/// max slice size L that is larger than all of those exact size constructors. +/// +/// But for slice constructors, we can just pick the largest slice, since that will cover slices of +/// that size, and any larger size. +/// +/// Putting that together, we calculate L via +/// +/// L = max(max_exact_len + 1, max_prefix_len + max_suffix_len) +fn build_list_ctors_covering_patterns( + list_arity: ListArity, + list_pattern_arities: impl IntoIterator, +) -> std::vec::Vec { + match list_arity { + ListArity::Exact(_) => { + // Exact-size lists can only cover themselves.. + vec![list_arity] + } + ListArity::Slice(prefix_len, suffix_len) => { + let min_len = prefix_len + suffix_len; + + let mut max_exact_len = 0; + let mut max_prefix_len = prefix_len; + let mut max_suffix_len = suffix_len; + + for arity in list_pattern_arities { + match arity { + ListArity::Exact(n) => max_exact_len = max_exact_len.max(n), + ListArity::Slice(prefix, suffix) => { + max_prefix_len = max_prefix_len.max(prefix); + max_suffix_len = max_suffix_len.max(suffix); + } + } + } + + let (inf_cover_prefix, inf_cover_suffix) = { + if max_exact_len + 1 >= max_prefix_len + max_suffix_len { + max_prefix_len = max_exact_len + 1 - max_suffix_len; + } + (max_prefix_len, max_suffix_len) + }; + let l = inf_cover_prefix + inf_cover_suffix; + + let exact_size_lists = (min_len..l) // exclusive + .map(ListArity::Exact); + + exact_size_lists + .chain([ListArity::Slice(inf_cover_prefix, inf_cover_suffix)]) + .collect() + } + } +} + +fn filter_matrix_list_ctors(matrix: &RefPatternMatrix) -> impl Iterator + '_ { + matrix.iter().filter_map(|ctor| match ctor.last() { + Some(List(ar, _)) => Some(*ar), + _ => None, + }) } diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index 9fcdb4bf12..e3cd669f67 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -166,8 +166,8 @@ impl<'a> Formattable for TypeAnnotation<'a> { Wildcard | Inferred | BoundVariable(_) | Malformed(_) => false, Function(args, result) => { - (&result.value).is_multiline() - || args.iter().any(|loc_arg| (&loc_arg.value).is_multiline()) + result.value.is_multiline() + || args.iter().any(|loc_arg| loc_arg.value.is_multiline()) } Apply(_, _, args) => args.iter().any(|loc_arg| loc_arg.value.is_multiline()), As(lhs, _, _) => lhs.value.is_multiline(), @@ -226,7 +226,7 @@ impl<'a> Formattable for TypeAnnotation<'a> { buf.newline(); } - (&argument.value).format_with_options( + argument.value.format_with_options( buf, Parens::InFunctionType, Newlines::No, @@ -251,7 +251,8 @@ impl<'a> Formattable for TypeAnnotation<'a> { buf.push_str("->"); buf.spaces(1); - (&ret.value).format_with_options(buf, Parens::InFunctionType, Newlines::No, indent); + ret.value + .format_with_options(buf, Parens::InFunctionType, Newlines::No, indent); if needs_parens { buf.push(')') @@ -275,12 +276,9 @@ impl<'a> Formattable for TypeAnnotation<'a> { for argument in *arguments { buf.spaces(1); - (&argument.value).format_with_options( - buf, - Parens::InApply, - Newlines::No, - indent, - ); + argument + .value + .format_with_options(buf, Parens::InApply, Newlines::No, indent); } if write_parens { @@ -371,12 +369,12 @@ impl<'a> Formattable for AssignedField<'a, TypeAnnotation<'a>> { fn format_with_options<'buf>( &self, buf: &mut Buf<'buf>, - parens: Parens, + _parens: Parens, newlines: Newlines, indent: u16, ) { // we abuse the `Newlines` type to decide between multiline or single-line layout - format_assigned_field_help(self, buf, parens, indent, 1, newlines == Newlines::Yes); + format_assigned_field_help(self, buf, indent, 1, newlines == Newlines::Yes); } } @@ -388,12 +386,12 @@ impl<'a> Formattable for AssignedField<'a, Expr<'a>> { fn format_with_options<'buf>( &self, buf: &mut Buf<'buf>, - parens: Parens, + _parens: Parens, newlines: Newlines, indent: u16, ) { // we abuse the `Newlines` type to decide between multiline or single-line layout - format_assigned_field_help(self, buf, parens, indent, 0, newlines == Newlines::Yes); + format_assigned_field_help(self, buf, indent, 0, newlines == Newlines::Yes); } } @@ -413,7 +411,6 @@ fn is_multiline_assigned_field_help(afield: &AssignedField<'_, T fn format_assigned_field_help<'a, 'buf, T>( zelf: &AssignedField<'a, T>, buf: &mut Buf<'buf>, - parens: Parens, indent: u16, separator_spaces: usize, is_multiline: bool, @@ -466,24 +463,10 @@ fn format_assigned_field_help<'a, 'buf, T>( } AssignedField::SpaceBefore(sub_field, spaces) => { fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); - format_assigned_field_help( - sub_field, - buf, - parens, - indent, - separator_spaces, - is_multiline, - ); + format_assigned_field_help(sub_field, buf, indent, separator_spaces, is_multiline); } AssignedField::SpaceAfter(sub_field, spaces) => { - format_assigned_field_help( - sub_field, - buf, - parens, - indent, - separator_spaces, - is_multiline, - ); + format_assigned_field_help(sub_field, buf, indent, separator_spaces, is_multiline); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); } Malformed(raw) => { @@ -497,7 +480,7 @@ impl<'a> Formattable for Tag<'a> { use self::Tag::*; match self { - Apply { args, .. } => args.iter().any(|arg| (&arg.value).is_multiline()), + Apply { args, .. } => args.iter().any(|arg| arg.value.is_multiline()), Tag::SpaceBefore(_, _) | Tag::SpaceAfter(_, _) => true, Malformed(text) => text.chars().any(|c| c == '\n'), } diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index f5395e6d24..6cb1709863 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -542,7 +542,7 @@ fn fmt_binops<'a, 'buf>( indent: u16, ) { let is_multiline = part_of_multi_line_binops - || (&loc_right_side.value).is_multiline() + || loc_right_side.value.is_multiline() || lefts.iter().any(|(expr, _)| expr.value.is_multiline()); for (loc_left_side, loc_binop) in lefts { @@ -1045,7 +1045,7 @@ fn fmt_closure<'a, 'buf>( buf.push_str("->"); - let is_multiline = (&loc_ret.value).is_multiline(); + let is_multiline = loc_ret.value.is_multiline(); // If the body is multiline, go down a line and indent. let body_indent = if is_multiline { @@ -1156,7 +1156,7 @@ fn fmt_backpassing<'a, 'buf>( buf.push_str("<-"); - let is_multiline = (&loc_ret.value).is_multiline(); + let is_multiline = loc_ret.value.is_multiline(); // If the body is multiline, go down a line and indent. let body_indent = if is_multiline { diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index a69655f1a5..897d4ff767 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -152,6 +152,7 @@ impl<'a> Formattable for Pattern<'a> { } StrLiteral(literal) => fmt_str_literal(buf, *literal, indent), SingleQuote(string) => { + buf.indent(indent); buf.push('\''); buf.push_str(string); buf.push('\''); diff --git a/crates/compiler/fmt/tests/test_fmt.rs b/crates/compiler/fmt/tests/test_fmt.rs index 235e24a18b..e53a8a7275 100644 --- a/crates/compiler/fmt/tests/test_fmt.rs +++ b/crates/compiler/fmt/tests/test_fmt.rs @@ -3864,6 +3864,17 @@ mod test_fmt { ); } + #[test] + fn when_with_single_quote_char() { + expr_formats_same(indoc!( + r#" + when x is + '0' -> 0 + '1' -> 1 + "# + )); + } + // NEWLINES #[test] diff --git a/crates/compiler/gen_dev/src/generic64/disassembler_test_macro.rs b/crates/compiler/gen_dev/src/generic64/disassembler_test_macro.rs index f6af4bd81a..5461c54fd0 100644 --- a/crates/compiler/gen_dev/src/generic64/disassembler_test_macro.rs +++ b/crates/compiler/gen_dev/src/generic64/disassembler_test_macro.rs @@ -19,7 +19,7 @@ macro_rules! disassembler_test { // TODO: Not sure if there is a better way to merge these together, // but I like the end use of this a lot better than the old tests. ($assemble_fn: expr, $format_fn: expr) => {{ - use crate::generic64::disassembler_test_macro::merge_instructions_without_line_numbers; + use $crate::generic64::disassembler_test_macro::merge_instructions_without_line_numbers; let arena = bumpalo::Bump::new(); let (mut buf, cs) = setup_capstone_and_arena(&arena); $assemble_fn(&mut buf); diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index fce88a8178..37ee037557 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -1011,7 +1011,7 @@ impl Assembler for X86_64Assembler { #[inline(always)] fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String) { - buf.extend(&[0xE8, 0x00, 0x00, 0x00, 0x00]); + buf.extend([0xE8, 0x00, 0x00, 0x00, 0x00]); relocs.push(Relocation::LinkedFunction { offset: buf.len() as u64 - 4, name: fn_name, @@ -1478,7 +1478,7 @@ fn binop_reg64_reg64( let rex = add_reg_extension(src, rex); let dst_mod = dst as u8 % 8; let src_mod = (src as u8 % 8) << 3; - buf.extend(&[rex, op_code, 0xC0 | dst_mod | src_mod]); + buf.extend([rex, op_code, 0xC0 | dst_mod | src_mod]); } #[inline(always)] @@ -1493,7 +1493,7 @@ fn extended_binop_reg64_reg64( let rex = add_reg_extension(src, rex); let dst_mod = dst as u8 % 8; let src_mod = (src as u8 % 8) << 3; - buf.extend(&[rex, op_code1, op_code2, 0xC0 | dst_mod | src_mod]); + buf.extend([rex, op_code1, op_code2, 0xC0 | dst_mod | src_mod]); } // Below here are the functions for all of the assembly instructions. @@ -1508,8 +1508,8 @@ fn add_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) { let rex = add_rm_extension(dst, REX_W); let dst_mod = dst as u8 % 8; buf.reserve(7); - buf.extend(&[rex, 0x81, 0xC0 | dst_mod]); - buf.extend(&imm.to_le_bytes()); + buf.extend([rex, 0x81, 0xC0 | dst_mod]); + buf.extend(imm.to_le_bytes()); } /// `ADD r/m64,r64` -> Add r64 to r/m64. @@ -1547,7 +1547,7 @@ fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF2, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1555,7 +1555,7 @@ fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF2, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF2, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1567,7 +1567,7 @@ fn addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF3, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1575,7 +1575,7 @@ fn addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF3, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF3, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1587,7 +1587,7 @@ fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF2, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1595,7 +1595,7 @@ fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF2, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF2, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1607,7 +1607,7 @@ fn divss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF3, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1615,7 +1615,7 @@ fn divss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF3, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF3, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1627,7 +1627,7 @@ fn divsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF2, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1635,7 +1635,7 @@ fn divsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF2, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF2, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1647,7 +1647,7 @@ fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF3, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1655,7 +1655,7 @@ fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF3, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF3, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1667,7 +1667,7 @@ fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0x66, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1675,7 +1675,7 @@ fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0x66, 0x0F, 0x54, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0x66, 0x0F, 0x54, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1684,7 +1684,7 @@ fn andpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl fn and_reg64_imm8(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i8) { let rex = add_rm_extension(dst, REX_W); let dst_mod = dst as u8 % 8; - buf.extend(&[rex, 0x83, 0xE0 | dst_mod, imm as u8]); + buf.extend([rex, 0x83, 0xE0 | dst_mod, imm as u8]); } /// `CMOVL r64,r/m64` -> Move if less (SF≠ OF). @@ -1694,7 +1694,7 @@ fn cmovl_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Ge let rex = add_rm_extension(src, rex); let dst_mod = (dst as u8 % 8) << 3; let src_mod = src as u8 % 8; - buf.extend(&[rex, 0x0F, 0x4C, 0xC0 | dst_mod | src_mod]); + buf.extend([rex, 0x0F, 0x4C, 0xC0 | dst_mod | src_mod]); } /// `CMP r/m64,i32` -> Compare i32 to r/m64. @@ -1703,8 +1703,8 @@ fn cmp_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) { let rex = add_rm_extension(dst, REX_W); let dst_mod = dst as u8 % 8; buf.reserve(7); - buf.extend(&[rex, 0x81, 0xF8 | dst_mod]); - buf.extend(&imm.to_le_bytes()); + buf.extend([rex, 0x81, 0xF8 | dst_mod]); + buf.extend(imm.to_le_bytes()); } /// `CMP r/m64,r64` -> Compare r64 to r/m64. @@ -1738,7 +1738,7 @@ fn mul_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) { rex |= REX_PREFIX_B; } - buf.extend(&[rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]); + buf.extend([rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]); } /// `IDIV r/m64` -> Signed divide RDX:RAX by r/m64, with result stored in RAX ← Quotient, RDX ← Remainder. @@ -1756,9 +1756,9 @@ fn idiv_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) { // // The CQO instruction (available in 64-bit mode only) copies the sign (bit 63) // of the value in the RAX register into every bit position in the RDX register - buf.extend(&[0x48, 0x99]); + buf.extend([0x48, 0x99]); - buf.extend(&[rex, 0xF7, 0b1111_1000 | (src as u8 % 8)]); + buf.extend([rex, 0xF7, 0b1111_1000 | (src as u8 % 8)]); } /// `DIV r/m64` -> Unsigned divide RDX:RAX by r/m64, with result stored in RAX ← Quotient, RDX ← Remainder. @@ -1776,10 +1776,10 @@ fn udiv_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) { // // The CQO instruction (available in 64-bit mode only) copies the sign (bit 63) // of the value in the RAX register into every bit position in the RDX register - buf.extend(&[0x48, 0x99]); + buf.extend([0x48, 0x99]); // adds a cqo (convert doubleword to quadword) - buf.extend(&[rex, 0xF7, 0b1111_0000 | (src as u8 % 8)]); + buf.extend([rex, 0xF7, 0b1111_0000 | (src as u8 % 8)]); } /// Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits. @@ -1787,7 +1787,7 @@ fn udiv_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) { fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) { buf.reserve(5); buf.push(0xE9); - buf.extend(&imm.to_le_bytes()); + buf.extend(imm.to_le_bytes()); } /// Jump near if not equal (ZF=0). @@ -1796,7 +1796,7 @@ fn jne_imm32(buf: &mut Vec<'_, u8>, imm: i32) { buf.reserve(6); buf.push(0x0F); buf.push(0x85); - buf.extend(&imm.to_le_bytes()); + buf.extend(imm.to_le_bytes()); } /// `MOV r/m64, imm32` -> Move imm32 sign extended to 64-bits to r/m64. @@ -1805,8 +1805,8 @@ fn mov_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) { let rex = add_rm_extension(dst, REX_W); let dst_mod = dst as u8 % 8; buf.reserve(7); - buf.extend(&[rex, 0xC7, 0xC0 | dst_mod]); - buf.extend(&imm.to_le_bytes()); + buf.extend([rex, 0xC7, 0xC0 | dst_mod]); + buf.extend(imm.to_le_bytes()); } /// `MOV r64, imm64` -> Move imm64 to r64. @@ -1818,8 +1818,8 @@ fn mov_reg64_imm64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i64) { let rex = add_opcode_extension(dst, REX_W); let dst_mod = dst as u8 % 8; buf.reserve(10); - buf.extend(&[rex, 0xB8 | dst_mod]); - buf.extend(&imm.to_le_bytes()); + buf.extend([rex, 0xB8 | dst_mod]); + buf.extend(imm.to_le_bytes()); } } @@ -1854,12 +1854,12 @@ fn mov_base64_offset32_reg64( let src_mod = (src as u8 % 8) << 3; let base_mod = base as u8 % 8; buf.reserve(8); - buf.extend(&[rex, 0x89, 0x80 | src_mod | base_mod]); + buf.extend([rex, 0x89, 0x80 | src_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `MOV r64,r/m64` -> Move r/m64 to r64, where m64 references a base + offset. @@ -1875,12 +1875,12 @@ fn mov_reg64_base64_offset32( let dst_mod = (dst as u8 % 8) << 3; let base_mod = base as u8 % 8; buf.reserve(8); - buf.extend(&[rex, 0x8B, 0x80 | dst_mod | base_mod]); + buf.extend([rex, 0x8B, 0x80 | dst_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `MOVZX r64,r/m8` -> Move r/m8 with zero extention to r64, where m8 references a base + offset. @@ -1896,12 +1896,12 @@ fn movzx_reg64_base8_offset32( let dst_mod = (dst as u8 % 8) << 3; let base_mod = base as u8 % 8; buf.reserve(9); - buf.extend(&[rex, 0x0F, 0xB6, 0x80 | dst_mod | base_mod]); + buf.extend([rex, 0x0F, 0xB6, 0x80 | dst_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register. @@ -1922,7 +1922,7 @@ fn raw_movsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_ let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF2, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1930,7 +1930,7 @@ fn raw_movsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_ 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF2, 0x0F, 0x10, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF2, 0x0F, 0x10, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1952,7 +1952,7 @@ fn raw_movss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_ let src_high = src as u8 > 7; let src_mod = src as u8 % 8; if dst_high || src_high { - buf.extend(&[ + buf.extend([ 0xF3, 0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x0F, @@ -1960,7 +1960,7 @@ fn raw_movss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_ 0xC0 | (dst_mod << 3) | (src_mod), ]) } else { - buf.extend(&[0xF3, 0x0F, 0x10, 0xC0 | (dst_mod << 3) | (src_mod)]) + buf.extend([0xF3, 0x0F, 0x10, 0xC0 | (dst_mod << 3) | (src_mod)]) } } @@ -1970,12 +1970,12 @@ fn movss_freg32_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: let dst_mod = dst as u8 % 8; if dst as u8 > 7 { buf.reserve(9); - buf.extend(&[0xF3, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); + buf.extend([0xF3, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); } else { buf.reserve(8); - buf.extend(&[0xF3, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); + buf.extend([0xF3, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } // `MOVSD xmm, m64` -> Load scalar double-precision floating-point value from m64 to xmm register. @@ -1984,12 +1984,12 @@ fn movsd_freg64_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: let dst_mod = dst as u8 % 8; if dst as u8 > 7 { buf.reserve(9); - buf.extend(&[0xF2, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); + buf.extend([0xF2, 0x44, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); } else { buf.reserve(8); - buf.extend(&[0xF2, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); + buf.extend([0xF2, 0x0F, 0x10, 0x05 | (dst_mod << 3)]); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `MOVSD r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pointer. @@ -2009,12 +2009,12 @@ fn movsd_base64_offset32_freg64( if src as u8 > 7 || base as u8 > 7 { buf.push(rex); } - buf.extend(&[0x0F, 0x11, 0x80 | src_mod | base_mod]); + buf.extend([0x0F, 0x11, 0x80 | src_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `MOVSD xmm1,r/m64` -> Move r/m64 to xmm1. where m64 references the base pointer. @@ -2034,12 +2034,12 @@ fn movsd_freg64_base64_offset32( if dst as u8 > 7 || base as u8 > 7 { buf.push(rex); } - buf.extend(&[0x0F, 0x10, 0x80 | dst_mod | base_mod]); + buf.extend([0x0F, 0x10, 0x80 | dst_mod | base_mod]); // Using RSP or R12 requires a secondary index byte. if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 { buf.push(0x24); } - buf.extend(&offset.to_le_bytes()); + buf.extend(offset.to_le_bytes()); } /// `NEG r/m64` -> Two's complement negate r/m64. @@ -2047,7 +2047,7 @@ fn movsd_freg64_base64_offset32( fn neg_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { let rex = add_rm_extension(reg, REX_W); let reg_mod = reg as u8 % 8; - buf.extend(&[rex, 0xF7, 0xD8 | reg_mod]); + buf.extend([rex, 0xF7, 0xD8 | reg_mod]); } // helper function for `set*` instructions @@ -2060,10 +2060,10 @@ fn set_reg64_help(op_code: u8, buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { let reg_mod = reg as u8 % 8; use X86_64GeneralReg::*; match reg { - RAX | RCX | RDX | RBX => buf.extend(&[0x0F, op_code, 0xC0 | reg_mod]), - RSP | RBP | RSI | RDI => buf.extend(&[REX, 0x0F, op_code, 0xC0 | reg_mod]), + RAX | RCX | RDX | RBX => buf.extend([0x0F, op_code, 0xC0 | reg_mod]), + RSP | RBP | RSI | RDI => buf.extend([REX, 0x0F, op_code, 0xC0 | reg_mod]), R8 | R9 | R10 | R11 | R12 | R13 | R14 | R15 => { - buf.extend(&[REX | 1, 0x0F, op_code, 0xC0 | reg_mod]) + buf.extend([REX | 1, 0x0F, op_code, 0xC0 | reg_mod]) } } @@ -2085,7 +2085,7 @@ fn cvtsi2_help( let mod1 = (dst.value() % 8) << 3; let mod2 = src.value() % 8; - buf.extend(&[op_code1, rex, 0x0F, op_code2, 0xC0 | mod1 | mod2]) + buf.extend([op_code1, rex, 0x0F, op_code2, 0xC0 | mod1 | mod2]) } #[inline(always)] @@ -2099,7 +2099,7 @@ fn cvtsx2_help( let mod1 = (dst.value() % 8) << 3; let mod2 = src.value() % 8; - buf.extend(&[op_code1, 0x0F, op_code2, 0xC0 | mod1 | mod2]) + buf.extend([op_code1, 0x0F, op_code2, 0xC0 | mod1 | mod2]) } /// `SETE r/m64` -> Set Byte on Condition - zero/equal (ZF=1) @@ -2183,8 +2183,8 @@ fn sub_reg64_imm32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i32) { let rex = add_rm_extension(dst, REX_W); let dst_mod = dst as u8 % 8; buf.reserve(7); - buf.extend(&[rex, 0x81, 0xE8 | dst_mod]); - buf.extend(&imm.to_le_bytes()); + buf.extend([rex, 0x81, 0xE8 | dst_mod]); + buf.extend(imm.to_le_bytes()); } /// `SUB r/m64,r64` -> Sub r64 to r/m64. @@ -2199,7 +2199,7 @@ fn pop_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { let reg_mod = reg as u8 % 8; if reg as u8 > 7 { let rex = add_opcode_extension(reg, REX); - buf.extend(&[rex, 0x58 | reg_mod]); + buf.extend([rex, 0x58 | reg_mod]); } else { buf.push(0x58 | reg_mod); } @@ -2211,7 +2211,7 @@ fn push_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) { let reg_mod = reg as u8 % 8; if reg as u8 > 7 { let rex = add_opcode_extension(reg, REX); - buf.extend(&[rex, 0x50 | reg_mod]); + buf.extend([rex, 0x50 | reg_mod]); } else { buf.push(0x50 | reg_mod); } diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index bcdcf51862..128d689ea5 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -156,7 +156,7 @@ trait Backend<'a> { let module_id = env.module_id; let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap(); - rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, *following) + rc_proc_gen.expand_refcount_stmt(ident_ids, layout, modify, following) }; for spec in new_specializations.into_iter() { diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 85cb925361..51b90f7735 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -421,7 +421,7 @@ fn build_proc<'a, B: Backend<'a>>( } Relocation::LinkedFunction { offset, name } => { // If the symbol is an undefined roc function, we need to add it here. - if output.symbol_id(name.as_bytes()) == None && name.starts_with("roc_") { + if output.symbol_id(name.as_bytes()).is_none() && name.starts_with("roc_") { let builtin_symbol = Symbol { name: name.as_bytes().to_vec(), value: 0, @@ -435,7 +435,7 @@ fn build_proc<'a, B: Backend<'a>>( output.add_symbol(builtin_symbol); } // If the symbol is an undefined reference counting procedure, we need to add it here. - if output.symbol_id(name.as_bytes()) == None { + if output.symbol_id(name.as_bytes()).is_none() { for (sym, rc_name) in rc_proc_names.iter() { if name == rc_name { let section_id = output.add_section( diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index 686fa688bf..21183dae66 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -411,16 +411,16 @@ fn build_rc_wrapper<'a, 'ctx, 'env>( match rc_operation { Mode::Inc => { let n = 1; - increment_refcount_layout(env, function_value, layout_ids, n, value, layout); + increment_refcount_layout(env, layout_ids, n, value, layout); } Mode::IncN => { let n = it.next().unwrap().into_int_value(); n.set_name(Symbol::ARG_2.as_str(&env.interns)); - increment_n_refcount_layout(env, function_value, layout_ids, n, value, layout); + increment_n_refcount_layout(env, layout_ids, n, value, layout); } Mode::Dec => { - decrement_refcount_layout(env, function_value, layout_ids, value, layout); + decrement_refcount_layout(env, layout_ids, value, layout); } } diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 73126c90b1..9a6ffcfc3c 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -126,7 +126,7 @@ impl<'ctx> Iterator for FunctionIterator<'ctx> { } } -#[derive(Default, Debug, Clone, PartialEq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Scope<'a, 'ctx> { symbols: ImMap, BasicValueEnum<'ctx>)>, pub top_level_thunks: ImMap, FunctionValue<'ctx>)>, @@ -973,7 +973,7 @@ pub fn build_exp_literal<'a, 'ctx, 'env>( _ => unreachable!("incorrect small_str_bytes"), } } else { - let ptr = define_global_str_literal_ptr(env, *str_literal); + let ptr = define_global_str_literal_ptr(env, str_literal); let number_of_elements = env.ptr_int().const_int(str_literal.len() as u64, false); let alloca = @@ -2723,14 +2723,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( let layout = *layout; if layout.contains_refcounted(env.layout_interner) { - increment_refcount_layout( - env, - parent, - layout_ids, - *inc_amount, - value, - &layout, - ); + increment_refcount_layout(env, layout_ids, *inc_amount, value, &layout); } build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont) @@ -2739,7 +2732,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>( let (value, layout) = load_symbol_and_layout(scope, symbol); if layout.contains_refcounted(env.layout_interner) { - decrement_refcount_layout(env, parent, layout_ids, value, layout); + decrement_refcount_layout(env, layout_ids, value, layout); } build_exp_stmt(env, layout_ids, func_spec_solutions, scope, parent, cont) @@ -3847,12 +3840,9 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>( arg_type.into_pointer_type().get_element_type(), ); // C return pointer goes at the beginning of params, and we must skip it if it exists. - let param_index = (i - + (if matches!(cc_return, CCReturn::ByPointer) { - 1 - } else { - 0 - })) as u32; + let returns_pointer = matches!(cc_return, CCReturn::ByPointer); + let param_index = i as u32 + returns_pointer as u32; + c_function.add_attribute(AttributeLoc::Param(param_index), byval); c_function.add_attribute(AttributeLoc::Param(param_index), nonnull); } @@ -4554,7 +4544,7 @@ fn build_procedures_help<'a, 'ctx, 'env>( fn_val.print_to_stderr(); if let Some(app_ll_file) = debug_output_file { - env.module.print_to_file(&app_ll_file).unwrap(); + env.module.print_to_file(app_ll_file).unwrap(); panic!( r"😱 LLVM errors when defining function {:?}; I wrote the full LLVM IR to {:?}", @@ -6209,14 +6199,7 @@ fn run_low_level<'a, 'ctx, 'env>( let element_layout = list_element_layout!(list_layout); - list_get_unsafe( - env, - layout_ids, - parent, - element_layout, - elem_index, - wrapper_struct, - ) + list_get_unsafe(env, layout_ids, element_layout, elem_index, wrapper_struct) } ListReplaceUnsafe => { let list = load_symbol(scope, &args[0]); diff --git a/crates/compiler/gen_llvm/src/llvm/build_list.rs b/crates/compiler/gen_llvm/src/llvm/build_list.rs index e2dccc7593..c8fd1f425a 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_list.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_list.rs @@ -121,7 +121,6 @@ pub(crate) fn list_with_capacity<'a, 'ctx, 'env>( pub(crate) fn list_get_unsafe<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, - parent: FunctionValue<'ctx>, element_layout: &Layout<'a>, elem_index: IntValue<'ctx>, wrapper_struct: StructValue<'ctx>, @@ -140,7 +139,7 @@ pub(crate) fn list_get_unsafe<'a, 'ctx, 'env>( let result = load_roc_value(env, *element_layout, elem_ptr, "list_get_load_element"); - increment_refcount_layout(env, parent, layout_ids, 1, result, element_layout); + increment_refcount_layout(env, layout_ids, 1, result, element_layout); result } diff --git a/crates/compiler/gen_llvm/src/llvm/refcounting.rs b/crates/compiler/gen_llvm/src/llvm/refcounting.rs index 25cbea9e1c..467110004b 100644 --- a/crates/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/crates/compiler/gen_llvm/src/llvm/refcounting.rs @@ -327,8 +327,6 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>( arg_val.set_name(arg_symbol.as_str(&env.interns)); - let parent = fn_val; - let wrapper_struct = arg_val.into_struct_value(); for (i, field_layout) in layouts.iter().enumerate() { @@ -347,7 +345,6 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>( modify_refcount_layout_help( env, - parent, layout_ids, mode.to_call_mode(fn_val), when_recursive, @@ -362,42 +359,32 @@ fn modify_refcount_struct_help<'a, 'ctx, 'env>( pub fn increment_refcount_layout<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, inc_amount: u64, value: BasicValueEnum<'ctx>, layout: &Layout<'a>, ) { let amount = env.ptr_int().const_int(inc_amount, false); - increment_n_refcount_layout(env, parent, layout_ids, amount, value, layout); + increment_n_refcount_layout(env, layout_ids, amount, value, layout); } pub fn increment_n_refcount_layout<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, amount: IntValue<'ctx>, value: BasicValueEnum<'ctx>, layout: &Layout<'a>, ) { - modify_refcount_layout( - env, - parent, - layout_ids, - CallMode::Inc(amount), - value, - layout, - ); + modify_refcount_layout(env, layout_ids, CallMode::Inc(amount), value, layout); } pub fn decrement_refcount_layout<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, value: BasicValueEnum<'ctx>, layout: &Layout<'a>, ) { - modify_refcount_layout(env, parent, layout_ids, CallMode::Dec, value, layout); + modify_refcount_layout(env, layout_ids, CallMode::Dec, value, layout); } fn modify_refcount_builtin<'a, 'ctx, 'env>( @@ -435,7 +422,6 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>( fn modify_refcount_layout<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, call_mode: CallMode<'ctx>, value: BasicValueEnum<'ctx>, @@ -443,7 +429,6 @@ fn modify_refcount_layout<'a, 'ctx, 'env>( ) { modify_refcount_layout_help( env, - parent, layout_ids, call_mode, &WhenRecursive::Unreachable, @@ -460,7 +445,6 @@ enum WhenRecursive<'a> { fn modify_refcount_layout_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, call_mode: CallMode<'ctx>, when_recursive: &WhenRecursive<'a>, @@ -474,7 +458,6 @@ fn modify_refcount_layout_help<'a, 'ctx, 'env>( let function = match modify_refcount_layout_build_function( env, - parent, layout_ids, mode, when_recursive, @@ -538,7 +521,6 @@ fn call_help<'a, 'ctx, 'env>( fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - parent: FunctionValue<'ctx>, layout_ids: &mut LayoutIds<'a>, mode: Mode, when_recursive: &WhenRecursive<'a>, @@ -603,7 +585,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( let function = modify_refcount_layout_build_function( env, - parent, layout_ids, mode, when_recursive, @@ -615,7 +596,6 @@ fn modify_refcount_layout_build_function<'a, 'ctx, 'env>( }, LambdaSet(lambda_set) => modify_refcount_layout_build_function( env, - parent, layout_ids, mode, when_recursive, @@ -731,7 +711,6 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>( let loop_fn = |_index, element| { modify_refcount_layout_help( env, - parent, layout_ids, mode.to_call_mode(fn_val), when_recursive, @@ -1302,7 +1281,6 @@ fn build_rec_union_recursive_decrement<'a, 'ctx, 'env>( for (field, field_layout) in deferred_nonrec { modify_refcount_layout_help( env, - parent, layout_ids, mode.to_call_mode(decrement_fn), when_recursive, @@ -1366,7 +1344,7 @@ fn union_layout_tags<'a>( match union_layout { NullableWrapped { other_tags: tags, .. - } => *tags, + } => tags, NullableUnwrapped { other_fields, .. } => arena.alloc([*other_fields]), NonNullableUnwrapped(fields) => arena.alloc([*fields]), Recursive(tags) => tags, @@ -1687,7 +1665,6 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( modify_refcount_layout_help( env, - parent, layout_ids, mode.to_call_mode(fn_val), when_recursive, @@ -1709,7 +1686,6 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>( modify_refcount_layout_help( env, - parent, layout_ids, mode.to_call_mode(fn_val), when_recursive, diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index 1b8b26f3d7..53f6a48981 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -1237,7 +1237,7 @@ impl<'a> WasmBackend<'a> { } CallType::HigherOrder(higher_order_lowlevel) => { - call_higher_order_lowlevel(self, ret_sym, ret_layout, *higher_order_lowlevel) + call_higher_order_lowlevel(self, ret_sym, ret_layout, higher_order_lowlevel) } CallType::Foreign { diff --git a/crates/compiler/gen_wasm/src/wasm_module/code_builder.rs b/crates/compiler/gen_wasm/src/wasm_module/code_builder.rs index 5adb833a60..d4b1ef3c36 100644 --- a/crates/compiler/gen_wasm/src/wasm_module/code_builder.rs +++ b/crates/compiler/gen_wasm/src/wasm_module/code_builder.rs @@ -69,7 +69,7 @@ impl std::fmt::Debug for VmBlock<'_> { /// Rust representation matches Wasm encoding. /// It's an error to specify alignment higher than the "natural" alignment of the instruction #[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)] pub enum Align { Bytes1 = 0, Bytes2 = 1, @@ -111,7 +111,7 @@ impl From for Align { } } -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy)] pub enum VmSymbolState { /// Value doesn't exist yet NotYetPushed, diff --git a/crates/compiler/gen_wasm/src/wasm_module/sections.rs b/crates/compiler/gen_wasm/src/wasm_module/sections.rs index dfb69bc7dd..705fd1442d 100644 --- a/crates/compiler/gen_wasm/src/wasm_module/sections.rs +++ b/crates/compiler/gen_wasm/src/wasm_module/sections.rs @@ -303,7 +303,7 @@ impl<'a> Serialize for TypeSection<'a> { * *******************************************************************/ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum ImportDesc { Func { signature_index: u32 }, Table { ty: TableType }, @@ -359,7 +359,7 @@ impl Serialize for ImportDesc { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct Import<'a> { pub module: &'a str, pub name: &'a str, @@ -551,7 +551,7 @@ impl Parse<()> for RefType { } } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct TableType { pub ref_type: RefType, pub limits: Limits, @@ -659,7 +659,7 @@ impl Serialize for TableSection { * *******************************************************************/ -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub enum Limits { Min(u32), MinMax(u32, u32), @@ -749,7 +749,7 @@ section_impl!(MemorySection, SectionId::Memory); * *******************************************************************/ -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct GlobalType { pub value_type: ValueType, pub is_mutable: bool, diff --git a/crates/compiler/load/build.rs b/crates/compiler/load/build.rs index 204f6af893..e13232efc8 100644 --- a/crates/compiler/load/build.rs +++ b/crates/compiler/load/build.rs @@ -44,7 +44,7 @@ fn write_subs_for_module(module_id: ModuleId, filename: &str) { println!("cargo:rerun-if-changed={}", filepath.to_str().unwrap()); let mut output_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - output_path.extend(&[filename]); + output_path.extend([filename]); output_path.set_extension("dat"); #[cfg(not(windows))] @@ -64,7 +64,7 @@ fn write_subs_for_module(module_id: ModuleId, filename: &str) { fn write_types_for_module_dummy(output_path: &Path) { // write out a dummy file - std::fs::write(output_path, &[]).unwrap(); + std::fs::write(output_path, []).unwrap(); } #[cfg(not(windows))] @@ -107,7 +107,7 @@ fn write_types_for_module_real(module_id: ModuleId, filename: &str, output_path: let abilities = module.abilities_store; let solved_implementations = module.resolved_implementations; - let mut file = std::fs::File::create(&output_path).unwrap(); + let mut file = std::fs::File::create(output_path).unwrap(); let type_state = TypeState { subs, diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 24b9d62281..9bbf5c37d1 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -6,7 +6,7 @@ use crossbeam::thread; use parking_lot::Mutex; use roc_builtins::roc::module_source; use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl}; -use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; +use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar}; use roc_can::expr::PendingDerives; use roc_can::expr::{Declarations, ExpectLookup}; use roc_can::module::{ @@ -3100,7 +3100,7 @@ fn load_platform_module<'a>( ) -> Result, LoadingProblem<'a>> { let module_start_time = Instant::now(); let file_io_start = Instant::now(); - let file = fs::read(&filename); + let file = fs::read(filename); let file_io_duration = file_io_start.elapsed(); match file { @@ -4262,10 +4262,11 @@ fn synth_list_len_type(subs: &mut Subs) -> Variable { pub fn add_imports( my_module: ModuleId, + constraints: &mut Constraints, subs: &mut Subs, mut pending_abilities: PendingAbilitiesStore, exposed_for_module: &ExposedForModule, - def_types: &mut Vec<(Symbol, Loc)>, + def_types: &mut Vec<(Symbol, Loc)>, rigid_vars: &mut Vec, ) -> (Vec, AbilitiesStore) { use roc_types::types::Type; @@ -4294,10 +4295,11 @@ pub fn add_imports( }; let copied_import = exposed_types.storage_subs.export_variable_to($subs, variable); + let copied_import_index = constraints.push_type(Type::Variable(copied_import.variable)); def_types.push(( $symbol, - Loc::at_zero(Type::Variable(copied_import.variable)), + Loc::at_zero(copied_import_index), )); // not a typo; rigids are turned into flex during type inference, but when imported we must @@ -4327,12 +4329,10 @@ pub fn add_imports( // Patch used symbols from circular dependencies. if my_module == ModuleId::NUM { // Num needs List.len, but List imports Num. - let list_len_type = synth_list_len_type(subs); - def_types.push(( - Symbol::LIST_LEN, - Loc::at_zero(Type::Variable(list_len_type)), - )); - import_variables.push(list_len_type); + let list_len_type_var = synth_list_len_type(subs); + let list_len_type_index = constraints.push_type(Type::Variable(list_len_type_var)); + def_types.push((Symbol::LIST_LEN, Loc::at_zero(list_len_type_index))); + import_variables.push(list_len_type_var); } // Fill in the implementation information of the abilities from the modules we import, which we @@ -4418,12 +4418,13 @@ fn run_solve_solve( } = module; let mut rigid_vars: Vec = Vec::new(); - let mut def_types: Vec<(Symbol, Loc)> = Vec::new(); + let mut def_types: Vec<(Symbol, Loc)> = Vec::new(); let mut subs = Subs::new_from_varstore(var_store); let (import_variables, abilities_store) = add_imports( module.module_id, + &mut constraints, &mut subs, pending_abilities, &exposed_for_module, diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index bc314e0eec..17017a1917 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -70,7 +70,7 @@ pub enum ArgSide { Right, } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Associativity { /// left-associative operators: /// diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index f01026f0e3..e373557750 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -44,7 +44,7 @@ const SYMBOL_HAS_NICHE: () = // register_debug_idents calls (which should be made in debug mode). // Set it to false if you want to see the raw ModuleId and IdentId ints, // but please set it back to true before checking in the result! -#[cfg(debug_assertions)] +#[cfg(any(debug_assertions, feature = "debug-symbols"))] const PRETTY_PRINT_DEBUG_SYMBOLS: bool = true; pub const DERIVABLE_ABILITIES: &[(Symbol, &[Symbol])] = &[ @@ -183,7 +183,7 @@ impl Symbol { /// /// `Foo.bar` impl fmt::Debug for Symbol { - #[cfg(debug_assertions)] + #[cfg(any(debug_assertions, feature = "debug-symbols"))] #[allow(clippy::print_in_format_impl)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if PRETTY_PRINT_DEBUG_SYMBOLS { @@ -216,7 +216,7 @@ impl fmt::Debug for Symbol { } } - #[cfg(not(debug_assertions))] + #[cfg(not(any(debug_assertions, feature = "debug-symbols")))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fallback_debug_fmt(*self, f) } @@ -256,7 +256,7 @@ fn fallback_debug_fmt(symbol: Symbol, f: &mut fmt::Formatter) -> fmt::Result { // end up using it in release builds anyway. Right? ...Right? lazy_static! {} -#[cfg(debug_assertions)] +#[cfg(any(debug_assertions, feature = "debug-symbols"))] lazy_static! { /// This is used in Debug builds only, to let us have a Debug instance /// which displays not only the Module ID, but also the Module Name which @@ -399,7 +399,7 @@ impl fmt::Debug for ModuleId { /// needs a global mutex, so we don't do this in release builds. This means /// the Debug impl in release builds only shows the number, not the name (which /// it does not have available, due to having never stored it in the mutexed intern table.) - #[cfg(debug_assertions)] + #[cfg(any(debug_assertions, feature = "debug-symbols"))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Originally, this printed both name and numeric ID, but the numeric ID // didn't seem to add anything useful. Feel free to temporarily re-add it @@ -425,7 +425,7 @@ impl fmt::Debug for ModuleId { } /// In release builds, all we have access to is the number, so only display that. - #[cfg(not(debug_assertions))] + #[cfg(not(any(debug_assertions, feature = "debug-symbols")))] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) } @@ -470,7 +470,7 @@ impl<'a> PackageModuleIds<'a> { // didn't find it, so we'll add it let module_id = ModuleId::from_zero_indexed(self.by_id.len()); self.by_id.push(module_name.clone()); - if cfg!(debug_assertions) { + if cfg!(any(debug_assertions, feature = "debug-symbols")) { Self::insert_debug_name(module_id, module_name); } @@ -487,7 +487,7 @@ impl<'a> PackageModuleIds<'a> { ModuleIds { by_id } } - #[cfg(debug_assertions)] + #[cfg(any(debug_assertions, feature = "debug-symbols"))] fn insert_debug_name(module_id: ModuleId, module_name: &PQModuleName) { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); @@ -503,7 +503,7 @@ impl<'a> PackageModuleIds<'a> { } } - #[cfg(not(debug_assertions))] + #[cfg(not(any(debug_assertions, feature = "debug-symbols")))] fn insert_debug_name(_module_id: ModuleId, _module_name: &PQModuleName) { // By design, this is a no-op in release builds! } @@ -557,14 +557,14 @@ impl ModuleIds { // didn't find it, so we'll add it let module_id = ModuleId::from_zero_indexed(self.by_id.len()); self.by_id.push(module_name.clone()); - if cfg!(debug_assertions) { + if cfg!(any(debug_assertions, feature = "debug-symbols")) { Self::insert_debug_name(module_id, module_name); } module_id } - #[cfg(debug_assertions)] + #[cfg(any(debug_assertions, feature = "debug-symbols"))] fn insert_debug_name(module_id: ModuleId, module_name: &ModuleName) { let mut names = DEBUG_MODULE_ID_NAMES.lock().expect("Failed to acquire lock for Debug interning into DEBUG_MODULE_ID_NAMES, presumably because a thread panicked."); @@ -574,7 +574,7 @@ impl ModuleIds { } } - #[cfg(not(debug_assertions))] + #[cfg(not(any(debug_assertions, feature = "debug-symbols")))] fn insert_debug_name(_module_id: ModuleId, _module_name: &ModuleName) { // By design, this is a no-op in release builds! } @@ -868,7 +868,7 @@ macro_rules! define_builtins { IdentIds{ interner } }; - if cfg!(debug_assertions) { + if cfg!(any(debug_assertions, feature = "debug-symbols")) { let name = PQModuleName::Unqualified($module_name.into()); PackageModuleIds::insert_debug_name(module_id, &name); module_id.register_debug_idents(&ident_ids); @@ -910,7 +910,7 @@ macro_rules! define_builtins { let mut insert_both = |id: ModuleId, name_str: &'static str| { let name: ModuleName = name_str.into(); - if cfg!(debug_assertions) { + if cfg!(any(debug_assertions, feature = "debug-symbols")) { Self::insert_debug_name(id, &name); } @@ -936,7 +936,7 @@ macro_rules! define_builtins { let raw_name: IdentStr = name_str.into(); let name = PQModuleName::Unqualified(raw_name.into()); - if cfg!(debug_assertions) { + if cfg!(any(debug_assertions, feature = "debug-symbols")) { Self::insert_debug_name(id, &name); } diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index a9532aed88..07a27df060 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -471,7 +471,7 @@ impl<'a> CodeGenHelp<'a> { ) -> (bool, Vec<'a, Option>) { use UnionLayout::*; match union { - NonRecursive(_) => return (false, bumpalo::vec![in self.arena]), + NonRecursive(_) => (false, bumpalo::vec![in self.arena]), Recursive(tags) => self.union_tail_recursion_fields_help(tags), diff --git a/crates/compiler/mono/src/decision_tree.rs b/crates/compiler/mono/src/decision_tree.rs index 17a0746c11..f8d038a29a 100644 --- a/crates/compiler/mono/src/decision_tree.rs +++ b/crates/compiler/mono/src/decision_tree.rs @@ -1,11 +1,12 @@ use crate::ir::{ - BranchInfo, DestructType, Env, Expr, JoinPointId, Literal, Param, Pattern, Procs, Stmt, + build_list_index_probe, BranchInfo, Call, CallType, DestructType, Env, Expr, JoinPointId, + ListIndex, Literal, Param, Pattern, Procs, Stmt, }; use crate::layout::{Builtin, Layout, LayoutCache, TagIdIntType, UnionLayout}; use roc_builtins::bitcode::{FloatWidth, IntWidth}; use roc_collections::all::{MutMap, MutSet}; use roc_error_macros::internal_error; -use roc_exhaustive::{Ctor, CtorName, RenderAs, TagId, Union}; +use roc_exhaustive::{Ctor, CtorName, ListArity, RenderAs, TagId, Union}; use roc_module::ident::TagName; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; @@ -77,6 +78,12 @@ enum GuardedTest<'a> { Placeholder, } +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +enum ListLenBound { + Exact, + AtLeast, +} + #[derive(Clone, Debug, PartialEq)] #[allow(clippy::enum_variant_names)] enum Test<'a> { @@ -95,6 +102,10 @@ enum Test<'a> { tag_id: TagIdIntType, num_alts: usize, }, + IsListLen { + bound: ListLenBound, + len: u64, + }, } impl<'a> Test<'a> { @@ -110,6 +121,10 @@ impl<'a> Test<'a> { Test::IsStr(_) => false, Test::IsBit(_) => true, Test::IsByte { .. } => true, + Test::IsListLen { bound, .. } => match bound { + ListLenBound::Exact => true, + ListLenBound::AtLeast => false, + }, } } } @@ -149,10 +164,13 @@ impl<'a> Hash for Test<'a> { num_alts.hash(state); } IsDecimal(v) => { - // TODO: Is this okay? state.write_u8(6); v.hash(state); } + IsListLen { len, bound } => { + state.write_u8(7); + (len, bound).hash(state); + } } } } @@ -331,6 +349,11 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool { Test::IsFloat(_, _) => false, Test::IsDecimal(_) => false, Test::IsStr(_) => false, + Test::IsListLen { + bound: ListLenBound::AtLeast, + len: 0, + } => true, // [..] test + Test::IsListLen { .. } => false, } } @@ -578,6 +601,18 @@ fn test_at_path<'a>( arguments: arguments.to_vec(), }, + List { + arity, + element_layout: _, + elements: _, + } => IsListLen { + bound: match arity { + ListArity::Exact(_) => ListLenBound::Exact, + ListArity::Slice(_, _) => ListLenBound::AtLeast, + }, + len: arity.min_len() as _, + }, + Voided { .. } => internal_error!("unreachable"), OpaqueUnwrap { opaque, argument } => { @@ -755,6 +790,37 @@ fn to_relevant_branch_help<'a>( _ => None, }, + List { + arity: my_arity, + elements, + element_layout: _, + } => match test { + IsListLen { bound: _, len } if my_arity.covers_length(*len as _) => { + let sub_positions = elements.into_iter().enumerate().map(|(index, elem_pat)| { + let mut new_path = path.to_vec(); + + let probe_index = ListIndex::from_pattern_index(index, my_arity); + + let next_instr = PathInstruction::ListIndex { + index: probe_index as _, + }; + new_path.push(next_instr); + + (new_path, elem_pat) + }); + start.extend(sub_positions); + start.extend(end); + + Some(Branch { + goal: branch.goal, + guard: branch.guard.clone(), + patterns: start, + }) + } + + _ => None, + }, + NewtypeDestructure { tag_name, arguments, @@ -1021,7 +1087,8 @@ fn needs_tests(pattern: &Pattern) -> bool { | IntLiteral(_, _) | FloatLiteral(_, _) | DecimalLiteral(_) - | StrLiteral(_) => true, + | StrLiteral(_) + | List { .. } => true, Voided { .. } => internal_error!("unreachable"), } @@ -1268,6 +1335,7 @@ pub fn optimize_when<'a>( enum PathInstruction { NewType, TagIndex { index: u64, tag_id: TagIdIntType }, + ListIndex { index: ListIndex }, } fn path_to_expr_help<'a>( @@ -1337,19 +1405,46 @@ fn path_to_expr_help<'a>( } } } + + PathInstruction::ListIndex { index } => { + let list_sym = symbol; + + match layout { + Layout::Builtin(Builtin::List(elem_layout)) => { + let (index_sym, new_stores) = build_list_index_probe(env, list_sym, index); + + stores.extend(new_stores); + + let load_sym = env.unique_symbol(); + let load_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::ListGetUnsafe, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([list_sym, index_sym]), + }); + + stores.push((load_sym, *elem_layout, load_expr)); + + layout = *elem_layout; + symbol = load_sym; + } + _ => internal_error!("not a list"), + } + } } } (symbol, stores, layout) } -fn test_to_equality<'a>( +fn test_to_comparison<'a>( env: &mut Env<'a, '_>, cond_symbol: Symbol, cond_layout: &Layout<'a>, path: &[PathInstruction], test: Test<'a>, -) -> (StoresVec<'a>, Symbol, Symbol, Option>) { +) -> (StoresVec<'a>, Comparison, Option>) { let (rhs_symbol, mut stores, test_layout) = path_to_expr_help(env, cond_symbol, path, *cond_layout); @@ -1379,8 +1474,7 @@ fn test_to_equality<'a>( ( stores, - lhs_symbol, - rhs_symbol, + (lhs_symbol, Comparator::Eq, rhs_symbol), Some(ConstructorKnown::OnlyPass { scrutinee: path_symbol, layout: *cond_layout, @@ -1397,7 +1491,7 @@ fn test_to_equality<'a>( let lhs_symbol = env.unique_symbol(); stores.push((lhs_symbol, Layout::int_width(precision), lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) } Test::IsFloat(test_int, precision) => { @@ -1407,7 +1501,7 @@ fn test_to_equality<'a>( let lhs_symbol = env.unique_symbol(); stores.push((lhs_symbol, Layout::float_width(precision), lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) } Test::IsDecimal(test_dec) => { @@ -1415,7 +1509,7 @@ fn test_to_equality<'a>( let lhs_symbol = env.unique_symbol(); stores.push((lhs_symbol, *cond_layout, lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) } Test::IsByte { @@ -1427,7 +1521,7 @@ fn test_to_equality<'a>( let lhs_symbol = env.unique_symbol(); stores.push((lhs_symbol, Layout::u8(), lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) } Test::IsBit(test_bit) => { @@ -1435,7 +1529,7 @@ fn test_to_equality<'a>( let lhs_symbol = env.unique_symbol(); stores.push((lhs_symbol, Layout::Builtin(Builtin::Bool), lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) } Test::IsStr(test_str) => { @@ -1444,15 +1538,58 @@ fn test_to_equality<'a>( stores.push((lhs_symbol, Layout::Builtin(Builtin::Str), lhs)); - (stores, lhs_symbol, rhs_symbol, None) + (stores, (lhs_symbol, Comparator::Eq, rhs_symbol), None) + } + + Test::IsListLen { bound, len } => { + let list_layout = test_layout; + let list_sym = rhs_symbol; + + match list_layout { + Layout::Builtin(Builtin::List(_elem_layout)) => { + let real_len_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::ListLen, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([list_sym]), + }); + let test_len_expr = Expr::Literal(Literal::Int((len as i128).to_ne_bytes())); + + let real_len = env.unique_symbol(); + let test_len = env.unique_symbol(); + + let usize_layout = Layout::usize(env.target_info); + + stores.push((real_len, usize_layout, real_len_expr)); + stores.push((test_len, usize_layout, test_len_expr)); + + let comparison = match bound { + ListLenBound::Exact => (real_len, Comparator::Eq, test_len), + ListLenBound::AtLeast => (real_len, Comparator::Geq, test_len), + }; + + (stores, comparison, None) + } + _ => internal_error!( + "test path is not a list: {:#?}", + (cond_layout, test_layout, path) + ), + } } } } +enum Comparator { + Eq, + Geq, +} + +type Comparison = (Symbol, Comparator, Symbol); + type Tests<'a> = std::vec::Vec<( bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>, - Symbol, - Symbol, + Comparison, Option>, )>; @@ -1466,17 +1603,25 @@ fn stores_and_condition<'a>( // Assumption: there is at most 1 guard, and it is the outer layer. for (path, test) in test_chain { - tests.push(test_to_equality(env, cond_symbol, cond_layout, &path, test)) + tests.push(test_to_comparison( + env, + cond_symbol, + cond_layout, + &path, + test, + )) } tests } +#[allow(clippy::too_many_arguments)] fn compile_test<'a>( env: &mut Env<'a, '_>, ret_layout: Layout<'a>, stores: bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>, lhs: Symbol, + cmp: Comparator, rhs: Symbol, fail: &'a Stmt<'a>, cond: Stmt<'a>, @@ -1487,6 +1632,7 @@ fn compile_test<'a>( ret_layout, stores, lhs, + cmp, rhs, fail, cond, @@ -1500,6 +1646,7 @@ fn compile_test_help<'a>( ret_layout: Layout<'a>, stores: bumpalo::collections::Vec<'a, (Symbol, Layout<'a>, Expr<'a>)>, lhs: Symbol, + cmp: Comparator, rhs: Symbol, fail: &'a Stmt<'a>, mut cond: Stmt<'a>, @@ -1560,7 +1707,10 @@ fn compile_test_help<'a>( default_branch, }; - let op = LowLevel::Eq; + let op = match cmp { + Comparator::Eq => LowLevel::Eq, + Comparator::Geq => LowLevel::NumGte, + }; let test = Expr::Call(crate::ir::Call { call_type: crate::ir::CallType::LowLevel { op, @@ -1592,13 +1742,15 @@ fn compile_tests<'a>( fail: &'a Stmt<'a>, mut cond: Stmt<'a>, ) -> Stmt<'a> { - for (new_stores, lhs, rhs, opt_constructor_info) in tests.into_iter() { + for (new_stores, (lhs, cmp, rhs), opt_constructor_info) in tests.into_iter() { match opt_constructor_info { None => { - cond = compile_test(env, ret_layout, new_stores, lhs, rhs, fail, cond); + cond = compile_test(env, ret_layout, new_stores, lhs, cmp, rhs, fail, cond); } Some(cinfo) => { - cond = compile_test_help(env, cinfo, ret_layout, new_stores, lhs, rhs, fail, cond); + cond = compile_test_help( + env, cinfo, ret_layout, new_stores, lhs, cmp, rhs, fail, cond, + ); } } } @@ -1781,7 +1933,7 @@ fn decide_to_branching<'a>( if number_of_tests == 1 { // if there is just one test, compile to a simple if-then-else - let (new_stores, lhs, rhs, _cinfo) = tests.into_iter().next().unwrap(); + let (new_stores, (lhs, cmp, rhs), _cinfo) = tests.into_iter().next().unwrap(); compile_test_help( env, @@ -1789,6 +1941,7 @@ fn decide_to_branching<'a>( ret_layout, new_stores, lhs, + cmp, rhs, fail, pass_expr, @@ -1854,6 +2007,12 @@ fn decide_to_branching<'a>( Test::IsBit(v) => v as u64, Test::IsByte { tag_id, .. } => tag_id as u64, Test::IsCtor { tag_id, .. } => tag_id as u64, + Test::IsListLen { len, bound } => match bound { + ListLenBound::Exact => len as _, + ListLenBound::AtLeast => { + unreachable!("at-least bounds cannot be switched on") + } + }, Test::IsDecimal(_) => unreachable!("decimals cannot be switched on"), Test::IsStr(_) => unreachable!("strings cannot be switched on"), }; @@ -1911,6 +2070,31 @@ fn decide_to_branching<'a>( union_layout.tag_id_layout(), env.arena.alloc(temp), ) + } else if let Layout::Builtin(Builtin::List(_)) = inner_cond_layout { + let len_symbol = env.unique_symbol(); + + let switch = Stmt::Switch { + cond_layout: Layout::usize(env.target_info), + cond_symbol: len_symbol, + branches: branches.into_bump_slice(), + default_branch: (default_branch_info, env.arena.alloc(default_branch)), + ret_layout, + }; + + let len_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::ListLen, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([inner_cond_symbol]), + }); + + Stmt::Let( + len_symbol, + len_expr, + Layout::usize(env.target_info), + env.arena.alloc(switch), + ) } else { Stmt::Switch { cond_layout: inner_cond_layout, diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index af61e7f816..98face2034 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -21,7 +21,7 @@ use roc_debug_flags::{ }; use roc_derive::SharedDerivedModule; use roc_error_macros::{internal_error, todo_abilities}; -use roc_exhaustive::{Ctor, CtorName, RenderAs, TagId}; +use roc_exhaustive::{Ctor, CtorName, ListArity, RenderAs, TagId}; use roc_intern::Interner; use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapshot}; use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed}; @@ -284,7 +284,7 @@ impl AbilityAliases { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CapturedSymbols<'a> { None, Captured(&'a [(Symbol, Variable)]), @@ -317,7 +317,7 @@ pub struct Proc<'a> { pub host_exposed_layouts: HostExposedLayouts<'a>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum HostExposedLayouts<'a> { NotHostExposed, HostExposed { @@ -326,13 +326,13 @@ pub enum HostExposedLayouts<'a> { }, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum SelfRecursive { NotSelfRecursive, SelfRecursive(JoinPointId), } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Parens { NotNeeded, InTypeParam, @@ -847,6 +847,38 @@ struct SpecializationMark<'a> { function_mark: Option>, } +/// The deepest closure in the current stack of procedures under specialization a symbol specialization +/// was used in. +/// +/// This is necessary to understand what symbol specializations are used in what capture sets. For +/// example, consider +/// +/// main = +/// x = 1 +/// +/// y = \{} -> 1u8 + x +/// z = \{} -> 1u16 + x +/// +/// Here, we have a two specializations of `x` to U8 and U16 with deepest uses of +/// (2, y) and (2, z), respectively. This tells us that both of those specializations must be +/// preserved by `main` (which is at depth 1), but that `y` and `z` respectively only need to +/// capture one particular specialization of `x` each. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct UseDepth { + depth: usize, + symbol: Symbol, +} + +impl UseDepth { + fn is_nested_use_in(&self, outer: &Self) -> bool { + if self.symbol == outer.symbol { + debug_assert!(self.depth == outer.depth); + return true; + } + self.depth > outer.depth + } +} + /// When walking a function body, we may encounter specialized usages of polymorphic symbols. For /// example /// @@ -865,73 +897,10 @@ struct SymbolSpecializations<'a>( // 2. the number of specializations of a symbol in a def is even smaller (almost always only one) // So, a linear VecMap is preferrable. Use a two-layered one to make (1) extraction of defs easy // and (2) reads of a certain symbol be determined by its first occurrence, not its last. - VecMap, (Variable, Symbol)>>, + VecMap, (Variable, Symbol, UseDepth)>>, ); impl<'a> SymbolSpecializations<'a> { - /// Gets a specialization for a symbol, or creates a new one. - #[inline(always)] - fn get_or_insert( - &mut self, - env: &mut Env<'a, '_>, - layout_cache: &mut LayoutCache<'a>, - symbol: Symbol, - specialization_var: Variable, - ) -> Symbol { - let arena = env.arena; - let subs: &Subs = env.subs; - - let layout = match layout_cache.from_var(arena, specialization_var, subs) { - Ok(layout) => layout, - // This can happen when the def symbol has a type error. In such cases just use the - // def symbol, which is erroring. - Err(_) => return symbol, - }; - - let is_closure = matches!( - subs.get_content_without_compacting(specialization_var), - Content::Structure(FlatType::Func(..)) - ); - let function_mark = if is_closure { - let fn_layout = match layout_cache.raw_from_var(arena, specialization_var, subs) { - Ok(layout) => layout, - // This can happen when the def symbol has a type error. In such cases just use the - // def symbol, which is erroring. - Err(_) => return symbol, - }; - Some(fn_layout) - } else { - None - }; - - let specialization_mark = SpecializationMark { - layout, - function_mark, - }; - - let symbol_specializations = self.0.get_or_insert(symbol, Default::default); - - // For the first specialization, always reuse the current symbol. The vast majority of defs - // only have one instance type, so this preserves readability of the IR. - // TODO: turn me off and see what breaks. - let needs_fresh_symbol = !symbol_specializations.is_empty(); - - let mut make_specialized_symbol = || { - if needs_fresh_symbol { - env.unique_symbol() - } else { - symbol - } - }; - - let (_var, specialized_symbol) = symbol_specializations - .get_or_insert(specialization_mark, || { - (specialization_var, make_specialized_symbol()) - }); - - *specialized_symbol - } - /// Inserts a known specialization for a symbol. Returns the overwritten specialization, if any. pub fn get_or_insert_known( &mut self, @@ -939,17 +908,19 @@ impl<'a> SymbolSpecializations<'a> { mark: SpecializationMark<'a>, specialization_var: Variable, specialization_symbol: Symbol, - ) -> Option<(Variable, Symbol)> { - self.0 - .get_or_insert(symbol, Default::default) - .insert(mark, (specialization_var, specialization_symbol)) + deepest_use: UseDepth, + ) -> Option<(Variable, Symbol, UseDepth)> { + self.0.get_or_insert(symbol, Default::default).insert( + mark, + (specialization_var, specialization_symbol, deepest_use), + ) } /// Removes all specializations for a symbol, returning the type and symbol of each specialization. pub fn remove( &mut self, symbol: Symbol, - ) -> impl ExactSizeIterator, (Variable, Symbol))> { + ) -> impl ExactSizeIterator, (Variable, Symbol, UseDepth))> { self.0 .remove(&symbol) .map(|(_, specializations)| specializations) @@ -969,7 +940,7 @@ impl<'a> SymbolSpecializations<'a> { symbol ); - specializations.next().map(|(_, (_, symbol))| symbol) + specializations.next().map(|(_, (_, symbol, _))| symbol) } pub fn is_empty(&self) -> bool { @@ -987,6 +958,20 @@ pub struct ProcsBase<'a> { pub imported_module_thunks: &'a [Symbol], } +/// The current set of functions under specialization. They form a stack where the latest +/// specialization to be seen is at the head of the stack. +#[derive(Clone, Debug)] +struct SpecializationStack<'a>(Vec<'a, Symbol>); + +impl<'a> SpecializationStack<'a> { + fn current_use_depth(&self) -> UseDepth { + UseDepth { + depth: self.0.len(), + symbol: *self.0.last().unwrap(), + } + } +} + #[derive(Clone, Debug)] pub struct Procs<'a> { pub partial_procs: PartialProcs<'a>, @@ -998,8 +983,7 @@ pub struct Procs<'a> { pub runtime_errors: BumpMap, pub externals_we_need: BumpMap>, symbol_specializations: SymbolSpecializations<'a>, - /// The current set of functions under specialization. - pub specialization_stack: Vec<'a, Symbol>, + specialization_stack: SpecializationStack<'a>, } impl<'a> Procs<'a> { @@ -1014,17 +998,18 @@ impl<'a> Procs<'a> { runtime_errors: BumpMap::new_in(arena), externals_we_need: BumpMap::new_in(arena), symbol_specializations: Default::default(), - specialization_stack: Vec::with_capacity_in(16, arena), + specialization_stack: SpecializationStack(Vec::with_capacity_in(16, arena)), } } fn push_active_specialization(&mut self, specialization: Symbol) { - self.specialization_stack.push(specialization); + self.specialization_stack.0.push(specialization); } fn pop_active_specialization(&mut self, specialization: Symbol) { let popped = self .specialization_stack + .0 .pop() .expect("specialization stack is empty"); debug_assert_eq!( @@ -1049,7 +1034,7 @@ impl<'a> Procs<'a> { /// specialize both `foo : Str False -> Str` and `foo : {} False -> Str` at the same time, so /// the latter specialization must be deferred. fn symbol_needs_suspended_specialization(&self, specialization: Symbol) -> bool { - self.specialization_stack.contains(&specialization) + self.specialization_stack.0.contains(&specialization) } } @@ -1368,6 +1353,94 @@ impl<'a> Procs<'a> { } } } + + /// Gets a specialization for a symbol, or creates a new one. + #[inline(always)] + fn get_or_insert_symbol_specialization( + &mut self, + env: &mut Env<'a, '_>, + layout_cache: &mut LayoutCache<'a>, + symbol: Symbol, + specialization_var: Variable, + ) -> Symbol { + let arena = env.arena; + let subs: &Subs = env.subs; + + let layout = match layout_cache.from_var(arena, specialization_var, subs) { + Ok(layout) => layout, + // This can happen when the def symbol has a type error. In such cases just use the + // def symbol, which is erroring. + Err(_) => return symbol, + }; + + let is_closure = matches!( + subs.get_content_without_compacting(specialization_var), + Content::Structure(FlatType::Func(..)) + ); + let function_mark = if is_closure { + let fn_layout = match layout_cache.raw_from_var(arena, specialization_var, subs) { + Ok(layout) => layout, + // This can happen when the def symbol has a type error. In such cases just use the + // def symbol, which is erroring. + Err(_) => return symbol, + }; + Some(fn_layout) + } else { + None + }; + + let specialization_mark = SpecializationMark { + layout, + function_mark, + }; + + let symbol_specializations = self + .symbol_specializations + .0 + .get_or_insert(symbol, Default::default); + + // For the first specialization, always reuse the current symbol. The vast majority of defs + // only have one instance type, so this preserves readability of the IR. + // TODO: turn me off and see what breaks. + let needs_fresh_symbol = !symbol_specializations.is_empty(); + + let mut make_specialized_symbol = || { + if needs_fresh_symbol { + env.unique_symbol() + } else { + symbol + } + }; + + let current_use = self.specialization_stack.current_use_depth(); + let (_var, specialized_symbol, deepest_use) = symbol_specializations + .get_or_insert(specialization_mark, || { + (specialization_var, make_specialized_symbol(), current_use) + }); + + if deepest_use.is_nested_use_in(¤t_use) { + *deepest_use = current_use; + } + + *specialized_symbol + } + + /// Get the symbol specializations used in the active specialization's body. + pub fn get_symbol_specializations_used_in_body( + &self, + symbol: Symbol, + ) -> Option + '_> { + let this_use = self.specialization_stack.current_use_depth(); + self.symbol_specializations.0.get(&symbol).map(move |l| { + l.iter().filter_map(move |(_, (var, sym, deepest_use))| { + if deepest_use.is_nested_use_in(&this_use) { + Some((*var, *sym)) + } else { + None + } + }) + }) + } } #[derive(Default)] @@ -1502,7 +1575,7 @@ impl<'a, 'i> Env<'a, 'i> { #[derive(Clone, Debug, PartialEq, Copy, Eq, Hash)] pub struct JoinPointId(pub Symbol); -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Param<'a> { pub symbol: Symbol, pub borrow: bool, @@ -1586,7 +1659,7 @@ pub enum Stmt<'a> { } /// in the block below, symbol `scrutinee` is assumed be be of shape `tag_id` -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum BranchInfo<'a> { None, Constructor { @@ -1629,7 +1702,7 @@ impl<'a> BranchInfo<'a> { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ModifyRc { /// Increment a reference count Inc(Symbol, u64), @@ -1775,7 +1848,7 @@ impl<'a> Call<'a> { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CallSpecId { id: u32, } @@ -1790,7 +1863,7 @@ impl CallSpecId { pub const BACKEND_DUMMY: Self = Self { id: 0 }; } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UpdateModeId { id: u32, } @@ -1805,7 +1878,7 @@ impl UpdateModeId { pub const BACKEND_DUMMY: Self = Self { id: 0 }; } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UpdateModeIds { next: u32, } @@ -1822,7 +1895,7 @@ impl UpdateModeIds { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum CallType<'a> { ByName { name: LambdaName<'a>, @@ -1841,7 +1914,7 @@ pub enum CallType<'a> { HigherOrder(&'a HigherOrderLowLevel<'a>), } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct PassedFunction<'a> { /// name of the top-level function that is passed as an argument /// e.g. in `List.map xs Num.abs` this would be `Num.abs` @@ -1858,7 +1931,7 @@ pub struct PassedFunction<'a> { pub owns_captured_environment: bool, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct HigherOrderLowLevel<'a> { pub op: crate::low_level::HigherOrder, @@ -2503,7 +2576,7 @@ fn from_can_let<'a>( // We do need specializations 1 => { - let (_specialization_mark, (var, specialized_symbol)) = + let (_specialization_mark, (var, specialized_symbol, _deepest_use)) = needed_specializations.next().unwrap(); // Make sure rigid variables in the annotation are converted to flex variables. @@ -2534,7 +2607,7 @@ fn from_can_let<'a>( // Need to eat the cost and create a specialized version of the body for // each specialization. - for (_specialization_mark, (var, specialized_symbol)) in + for (_specialization_mark, (var, specialized_symbol, _deepest_use)) in needed_specializations { use roc_can::copy::deep_copy_type_vars_into_expr; @@ -2703,7 +2776,7 @@ fn pattern_to_when<'a>( ) -> (Symbol, Loc) { use roc_can::expr::Expr::*; use roc_can::expr::{WhenBranch, WhenBranchPattern}; - use roc_can::pattern::Pattern::*; + use roc_can::pattern::Pattern::{self, *}; match &pattern.value { Identifier(symbol) => (*symbol, body), @@ -2766,6 +2839,8 @@ fn pattern_to_when<'a>( (symbol, Loc::at_zero(wrapped_body)) } + Pattern::List { .. } => todo!(), + IntLiteral(..) | NumLiteral(..) | FloatLiteral(..) @@ -3388,11 +3463,23 @@ fn specialize_proc_help<'a>( // An argument from the closure list may have taken on a specialized symbol // name during the evaluation of the def body. If this is the case, load the // specialized name rather than the original captured name! - let mut get_specialized_name = |symbol| { - procs - .symbol_specializations - .remove_single(symbol) - .unwrap_or(symbol) + let get_specialized_name = |symbol| { + let specs_used_in_body = + procs.get_symbol_specializations_used_in_body(symbol); + + match specs_used_in_body { + Some(mut specs) => { + let spec_symbol = + specs.next().map(|(_, sym)| sym).unwrap_or(symbol); + if specs.next().is_some() { + internal_error!( + "polymorphic symbol captures not supported yet" + ); + } + spec_symbol + } + None => symbol, + } }; match closure_layout @@ -4083,9 +4170,7 @@ pub fn with_hole<'a>( variable, ) { let real_symbol = - procs - .symbol_specializations - .get_or_insert(env, layout_cache, symbol, variable); + procs.get_or_insert_symbol_specialization(env, layout_cache, symbol, variable); symbol = real_symbol; } @@ -4182,7 +4267,7 @@ pub fn with_hole<'a>( match can_reuse_symbol(env, procs, &loc_arg_expr.value, arg_var) { // Opaques decay to their argument. ReuseSymbol::Value(symbol) => { - let real_name = procs.symbol_specializations.get_or_insert( + let real_name = procs.get_or_insert_symbol_specialization( env, layout_cache, symbol, @@ -4247,7 +4332,7 @@ pub fn with_hole<'a>( can_fields.push(Field::FunctionOrUnspecialized(symbol, variable)); } Value(symbol) => { - let reusable = procs.symbol_specializations.get_or_insert( + let reusable = procs.get_or_insert_symbol_specialization( env, layout_cache, symbol, @@ -4722,6 +4807,7 @@ pub fn with_hole<'a>( find_lambda_name(env, layout_cache, lambda_set, name, &[]); construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -4775,6 +4861,7 @@ pub fn with_hole<'a>( find_lambda_name(env, layout_cache, lambda_set, name, &[]); construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -5023,6 +5110,7 @@ pub fn with_hole<'a>( construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -5169,7 +5257,7 @@ pub fn with_hole<'a>( } } Value(function_symbol) => { - let function_symbol = procs.symbol_specializations.get_or_insert( + let function_symbol = procs.get_or_insert_symbol_specialization( env, layout_cache, function_symbol, @@ -5585,7 +5673,8 @@ where #[allow(clippy::too_many_arguments)] fn construct_closure_data<'a, I>( env: &mut Env<'a, '_>, - layout_cache: &LayoutCache<'a>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>, lambda_set: LambdaSet<'a>, name: LambdaName<'a>, symbols: I, @@ -5670,12 +5759,19 @@ where debug_assert_eq!(symbols.len(), 1); let mut symbols = symbols; - let (captured_symbol, _) = symbols.next().unwrap(); + let (captured_symbol, captured_var) = symbols.next().unwrap(); + + let captured_symbol = procs.get_or_insert_symbol_specialization( + env, + layout_cache, + *captured_symbol, + *captured_var, + ); // The capture set is unwrapped, so just replaced the assigned capture symbol with the // only capture. let mut hole = hole.clone(); - substitute_in_exprs(env.arena, &mut hole, assigned, *captured_symbol); + substitute_in_exprs(env.arena, &mut hole, assigned, captured_symbol); hole } ClosureRepresentation::EnumDispatch(repr) => match repr { @@ -6056,6 +6152,7 @@ fn tag_union_to_function<'a>( debug_assert!(lambda_name.no_captures()); construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -6388,7 +6485,7 @@ pub fn from_can<'a>( stmt = with_hole( env, loc_condition.value, - variable, + Variable::BOOL, procs, layout_cache, cond_symbol, @@ -6444,7 +6541,7 @@ pub fn from_can<'a>( stmt = with_hole( env, loc_condition.value, - variable, + Variable::BOOL, procs, layout_cache, cond_symbol, @@ -6912,7 +7009,7 @@ fn substitute_in_call<'a>( } => substitute(subs, name.name()).map(|new| CallType::ByName { name: name.replace_name(new), arg_layouts, - ret_layout: *ret_layout, + ret_layout, specialization_id: *specialization_id, }), CallType::Foreign { .. } => None, @@ -7061,7 +7158,7 @@ fn substitute_in_expr<'a>( } => match substitute(subs, *structure) { Some(structure) => Some(StructAtIndex { index: *index, - field_layouts: *field_layouts, + field_layouts, structure, }), None => None, @@ -7192,6 +7289,24 @@ fn store_pattern_help<'a>( stmt, ); } + + List { + arity, + element_layout, + elements, + } => { + return store_list_pattern( + env, + procs, + layout_cache, + outer_symbol, + *arity, + *element_layout, + elements, + stmt, + ) + } + Voided { .. } => { return StorePattern::NotProductive(stmt); } @@ -7264,6 +7379,190 @@ fn store_pattern_help<'a>( StorePattern::Productive(stmt) } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct ListIndex( + /// Positive if we should index from the head, negative if we should index from the tail + /// 0 is lst[0] + /// -1 is lst[List.len lst - 1] + i64, +); + +impl ListIndex { + pub fn from_pattern_index(index: usize, arity: ListArity) -> Self { + match arity { + ListArity::Exact(_) => Self(index as _), + ListArity::Slice(head, tail) => { + if index < head { + Self(index as _) + } else { + // Slice(head=2, tail=5) + // + // s t ... w y z x q + // 0 1 2 3 4 5 6 index + // 0 1 2 3 4 (index - head) + // 5 4 3 2 1 (tail - (index - head)) + Self(-((tail - (index - head)) as i64)) + } + } + } + } +} + +pub(crate) type Store<'a> = (Symbol, Layout<'a>, Expr<'a>); + +/// Builds the list index we should index into +#[must_use] +pub(crate) fn build_list_index_probe<'a>( + env: &mut Env<'a, '_>, + list_sym: Symbol, + list_index: &ListIndex, +) -> (Symbol, impl DoubleEndedIterator>) { + let usize_layout = Layout::usize(env.target_info); + + let list_index = list_index.0; + let index_sym = env.unique_symbol(); + + let (opt_len_store, opt_offset_store, index_store) = if list_index >= 0 { + let index_expr = Expr::Literal(Literal::Int((list_index as i128).to_ne_bytes())); + + let index_store = (index_sym, usize_layout, index_expr); + + (None, None, index_store) + } else { + let len_sym = env.unique_symbol(); + let len_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::ListLen, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([list_sym]), + }); + + let offset = list_index.abs(); + let offset_sym = env.unique_symbol(); + let offset_expr = Expr::Literal(Literal::Int((offset as i128).to_ne_bytes())); + + let index_expr = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::NumSub, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([len_sym, offset_sym]), + }); + + let len_store = (len_sym, usize_layout, len_expr); + let offset_store = (offset_sym, usize_layout, offset_expr); + let index_store = (index_sym, usize_layout, index_expr); + + (Some(len_store), Some(offset_store), index_store) + }; + + let stores = (opt_len_store.into_iter()) + .chain(opt_offset_store) + .chain([index_store]); + + (index_sym, stores) +} + +#[allow(clippy::too_many_arguments)] +fn store_list_pattern<'a>( + env: &mut Env<'a, '_>, + procs: &mut Procs<'a>, + layout_cache: &mut LayoutCache<'a>, + list_sym: Symbol, + list_arity: ListArity, + element_layout: Layout<'a>, + elements: &[Pattern<'a>], + mut stmt: Stmt<'a>, +) -> StorePattern<'a> { + use Pattern::*; + + let mut is_productive = false; + + for (index, element) in elements.iter().enumerate().rev() { + let compute_element_load = |env: &mut Env<'a, '_>| { + let list_index = ListIndex::from_pattern_index(index, list_arity); + + let (index_sym, needed_stores) = build_list_index_probe(env, list_sym, &list_index); + + let load = Expr::Call(Call { + call_type: CallType::LowLevel { + op: LowLevel::ListGetUnsafe, + update_mode: env.next_update_mode_id(), + }, + arguments: env.arena.alloc([list_sym, index_sym]), + }); + + (load, needed_stores) + }; + + let (store_loaded, needed_stores) = match element { + Identifier(symbol) => { + let (load, needed_stores) = compute_element_load(env); + + // Pattern can define only one specialization + let symbol = procs + .symbol_specializations + .remove_single(*symbol) + .unwrap_or(*symbol); + + // store immediately in the given symbol + ( + Stmt::Let(symbol, load, element_layout, env.arena.alloc(stmt)), + needed_stores, + ) + } + Underscore + | IntLiteral(_, _) + | FloatLiteral(_, _) + | DecimalLiteral(_) + | EnumLiteral { .. } + | BitLiteral { .. } + | StrLiteral(_) => { + // ignore + continue; + } + _ => { + // store the field in a symbol, and continue matching on it + let symbol = env.unique_symbol(); + + // first recurse, continuing to unpack symbol + match store_pattern_help(env, procs, layout_cache, element, symbol, stmt) { + StorePattern::Productive(new) => { + stmt = new; + let (load, needed_stores) = compute_element_load(env); + + // only if we bind one of its (sub)fields to a used name should we + // extract the field + ( + Stmt::Let(symbol, load, element_layout, env.arena.alloc(stmt)), + needed_stores, + ) + } + StorePattern::NotProductive(new) => { + // do nothing + stmt = new; + continue; + } + } + } + }; + + is_productive = true; + + stmt = store_loaded; + for (sym, lay, expr) in needed_stores.rev() { + stmt = Stmt::Let(sym, expr, lay, env.arena.alloc(stmt)); + } + } + + if is_productive { + StorePattern::Productive(stmt) + } else { + StorePattern::NotProductive(stmt) + } +} + #[allow(clippy::too_many_arguments)] fn store_tag_pattern<'a>( env: &mut Env<'a, '_>, @@ -7586,9 +7885,7 @@ fn possible_reuse_symbol_or_specialize<'a>( ) -> Symbol { match can_reuse_symbol(env, procs, expr, var) { ReuseSymbol::Value(symbol) => { - procs - .symbol_specializations - .get_or_insert(env, layout_cache, symbol, var) + procs.get_or_insert_symbol_specialization(env, layout_cache, symbol, var) } _ => env.unique_symbol(), } @@ -7664,13 +7961,17 @@ where // captured symbols can only ever be specialized outside the closure. // After that is done, remove this hack. .chain(if no_specializations_needed { - [Some((variable, left))] + [Some(( + variable, + left, + procs.specialization_stack.current_use_depth(), + ))] } else { [None] }) .flatten(); - for (variable, left) in needed_specializations_of_left { + for (variable, left, _deepest_use) in needed_specializations_of_left { add_needed_external(procs, env, variable, LambdaName::no_niche(right)); let res_layout = layout_cache.from_var(env.arena, variable, env.subs); @@ -7694,7 +7995,7 @@ where let left_had_specialization_symbols = needed_specializations_of_left.len() > 0; - for (specialization_mark, (specialized_var, specialized_sym)) in + for (specialization_mark, (specialized_var, specialized_sym, deepest_use)) in needed_specializations_of_left { let old_specialized_sym = procs.symbol_specializations.get_or_insert_known( @@ -7702,9 +8003,10 @@ where specialization_mark, specialized_var, specialized_sym, + deepest_use, ); - if let Some((_, old_specialized_sym)) = old_specialized_sym { + if let Some((_, old_specialized_sym, _)) = old_specialized_sym { scratchpad_update_specializations.push((old_specialized_sym, specialized_sym)); } } @@ -7887,6 +8189,7 @@ fn specialize_symbol<'a>( construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -7938,6 +8241,7 @@ fn specialize_symbol<'a>( construct_closure_data( env, + procs, layout_cache, lambda_set, lambda_name, @@ -8343,6 +8647,7 @@ fn call_by_name_help<'a>( construct_closure_data( env, + procs, layout_cache, lambda_set, proc_name, @@ -8739,6 +9044,7 @@ fn call_specialized_proc<'a>( let result = construct_closure_data( env, + procs, layout_cache, lambda_set, proc_name, @@ -8818,6 +9124,11 @@ pub enum Pattern<'a> { opaque: Symbol, argument: Box<(Pattern<'a>, Layout<'a>)>, }, + List { + arity: ListArity, + element_layout: Layout<'a>, + elements: Vec<'a, Pattern<'a>>, + }, } impl<'a> Pattern<'a> { @@ -8854,6 +9165,7 @@ impl<'a> Pattern<'a> { stack.extend(arguments.iter().map(|(t, _)| t)) } Pattern::OpaqueUnwrap { argument, .. } => stack.push(&argument.0), + Pattern::List { elements, .. } => stack.extend(elements), } } @@ -9604,6 +9916,35 @@ fn from_can_pattern_help<'a>( field_layouts.into_bump_slice(), )) } + + List { + list_var: _, + elem_var, + patterns, + } => { + let element_layout = match layout_cache.from_var(env.arena, *elem_var, env.subs) { + Ok(lay) => lay, + Err(LayoutProblem::UnresolvedTypeVar(_)) => { + return Err(RuntimeError::UnresolvedTypeVar) + } + Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType), + }; + + let arity = patterns.arity(); + + let mut mono_patterns = Vec::with_capacity_in(patterns.patterns.len(), env.arena); + for loc_pat in patterns.patterns.iter() { + let mono_pat = + from_can_pattern_help(env, procs, layout_cache, &loc_pat.value, assignments)?; + mono_patterns.push(mono_pat); + } + + Ok(Pattern::List { + arity, + element_layout, + elements: mono_patterns, + }) + } } } diff --git a/crates/compiler/mono/src/low_level.rs b/crates/compiler/mono/src/low_level.rs index 3856e95ba9..ddad554c81 100644 --- a/crates/compiler/mono/src/low_level.rs +++ b/crates/compiler/mono/src/low_level.rs @@ -1,6 +1,6 @@ use roc_module::symbol::Symbol; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HigherOrder { ListMap { xs: Symbol, diff --git a/crates/compiler/mono/src/reset_reuse.rs b/crates/compiler/mono/src/reset_reuse.rs index feec4fe8e4..6b2447c250 100644 --- a/crates/compiler/mono/src/reset_reuse.rs +++ b/crates/compiler/mono/src/reset_reuse.rs @@ -128,7 +128,7 @@ fn function_s<'a, 'i>( remainder, } => { let id = *id; - let body: &Stmt = *body; + let body: &Stmt = body; let new_body = function_s(env, w, c, body); let new_join = if std::ptr::eq(body, new_body) || body == new_body { @@ -179,7 +179,7 @@ fn function_s<'a, 'i>( arena.alloc(new_switch) } Refcounting(op, continuation) => { - let continuation: &Stmt = *continuation; + let continuation: &Stmt = continuation; let new_continuation = function_s(env, w, c, continuation); if std::ptr::eq(continuation, new_continuation) || continuation == new_continuation { @@ -198,7 +198,7 @@ fn function_s<'a, 'i>( layouts, remainder, } => { - let continuation: &Stmt = *remainder; + let continuation: &Stmt = remainder; let new_continuation = function_s(env, w, c, continuation); if std::ptr::eq(continuation, new_continuation) || continuation == new_continuation { @@ -223,7 +223,7 @@ fn function_s<'a, 'i>( layouts, remainder, } => { - let continuation: &Stmt = *remainder; + let continuation: &Stmt = remainder; let new_continuation = function_s(env, w, c, continuation); if std::ptr::eq(continuation, new_continuation) || continuation == new_continuation { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 9a71e44e33..c4cb383606 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -109,7 +109,7 @@ pub enum StrSegment<'a> { Interpolated(Loc<&'a Expr<'a>>), // e.g. (name) in "Hi, \(name)!" } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum EscapedChar { Newline, // \n Tab, // \t @@ -581,7 +581,7 @@ pub enum AssignedField<'a, Val> { Malformed(&'a str), } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CommentOrNewline<'a> { Newline, LineComment(&'a str), @@ -888,7 +888,7 @@ impl<'a, T> Collection<'a, T> { pub fn final_comments(&self) -> &'a [CommentOrNewline<'a>] { if let Some(final_comments) = self.final_comments { - *final_comments + final_comments } else { &[] } diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index e211a6037e..2b1f3e30a7 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -346,6 +346,7 @@ fn parse_expr_start<'a>( loc!(move |a, s, m| parse_expr_operator_chain(m, options, a, s)), fail_expr_start_e() ] + .trace("expr_start") .parse(arena, state, min_indent) } @@ -546,7 +547,7 @@ fn numeric_negate_expression<'a, T>( expr: Loc>, spaces: &'a [CommentOrNewline<'a>], ) -> Loc> { - debug_assert_eq!(state.bytes().get(0), Some(&b'-')); + debug_assert_eq!(state.bytes().first(), Some(&b'-')); // for overflow reasons, we must make the unary minus part of the number literal. let start = state.pos(); let region = Region::new(start, expr.region.end()); @@ -1933,7 +1934,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result Err(()), Expr::Str(string) => Ok(Pattern::StrLiteral(*string)), - Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(*string)), + Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(string)), Expr::MalformedIdent(string, _problem) => Ok(Pattern::Malformed(string)), } } @@ -2105,10 +2106,10 @@ mod when { parser::keyword_e(keyword::IS, EWhen::Is) ) ), - move |arena, state, progress, (case_indent, loc_condition), min_indent| { + move |arena, state, _progress, (case_indent, loc_condition), min_indent| { if case_indent < min_indent { return Err(( - progress, + MadeProgress, // TODO maybe pass case_indent here? EWhen::PatternAlignment(5, state.pos()), state, @@ -2118,15 +2119,18 @@ mod when { // Everything in the branches must be indented at least as much as the case itself. let min_indent = case_indent; - let (p1, branches, state) = branches(options).parse(arena, state, min_indent)?; + let (_p1, branches, state) = branches(options) + .parse(arena, state, min_indent) + .map_err(|(_p, e, s)| (MadeProgress, e, s))?; Ok(( - progress.or(p1), + MadeProgress, Expr::When(arena.alloc(loc_condition), branches.into_bump_slice()), state, )) }, ) + .trace("when") } /// Parsing when with indentation. diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 4fd3266e65..8c09f2daf1 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -52,7 +52,7 @@ pub enum VersionComparison { DisallowsEqual, } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct PackageName<'a>(&'a str); impl<'a> PackageName<'a> { @@ -160,7 +160,7 @@ pub struct HostedHeader<'a> { pub after_with: &'a [CommentOrNewline<'a>], } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum To<'a> { ExistingPackage(&'a str), NewPackage(PackageName<'a>), @@ -262,7 +262,7 @@ pub struct TypedIdent<'a> { pub ann: Loc>, } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct PackageEntry<'a> { pub shorthand: &'a str, pub spaces_after_shorthand: &'a [CommentOrNewline<'a>], diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index bd00602b9a..328d4b3b0a 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -737,6 +737,7 @@ pub trait Parser<'a, Output, Error> { ) -> ParseResult<'a, Output, Error>; #[cfg(not(feature = "parse_debug_trace"))] + #[inline(always)] fn trace(self, _message: &'static str) -> Self where Self: Sized, @@ -789,7 +790,7 @@ impl<'a, O: std::fmt::Debug, E: std::fmt::Debug, P: Parser<'a, O, E>> Parser<'a, where E: 'a, { - fn parse(&self, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, O, E> { + fn parse(&self, arena: &'a Bump, state: State<'a>, min_indent: u32) -> ParseResult<'a, O, E> { use std::cell::RefCell; thread_local! { @@ -803,7 +804,7 @@ where let cur_indent = INDENT.with(|i| *i.borrow()); println!( - "{:>5?}: {}{:<50}", + "{:<5?}: {}{:<50}", state.pos(), &indent_text[..cur_indent * 2], self.message @@ -1379,11 +1380,12 @@ macro_rules! and { macro_rules! one_of { ($p1:expr, $p2:expr) => { move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| { + let original_state = state.clone(); match $p1.parse(arena, state, min_indent) { valid @ Ok(_) => valid, Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), - Err((NoProgress, _, state)) => $p2.parse(arena, state, min_indent), + Err((NoProgress, _, _)) => $p2.parse(arena, original_state, min_indent), } } }; diff --git a/crates/compiler/parse/src/string_literal.rs b/crates/compiler/parse/src/string_literal.rs index a0a957c2c0..dedff7ebc6 100644 --- a/crates/compiler/parse/src/string_literal.rs +++ b/crates/compiler/parse/src/string_literal.rs @@ -122,10 +122,7 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { } } -fn consume_indent<'a>( - mut state: State<'a>, - mut indent: u32, -) -> Result, State<'a>)> { +fn consume_indent(mut state: State, mut indent: u32) -> Result { while indent > 0 { match state.bytes().first() { Some(b' ') => { diff --git a/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.result-ast b/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.result-ast new file mode 100644 index 0000000000..0843d5c707 --- /dev/null +++ b/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.result-ast @@ -0,0 +1 @@ +Expr(When(Arrow(@24), @24), @0) \ No newline at end of file diff --git a/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.roc b/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.roc new file mode 100644 index 0000000000..e44e443eb0 --- /dev/null +++ b/crates/compiler/parse/tests/snapshots/fail/pattern_binds_keyword.expr.roc @@ -0,0 +1,6 @@ +when Just 4 is + Just when -> + 4 + + _ -> + 2 \ No newline at end of file diff --git a/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.result-ast b/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.result-ast new file mode 100644 index 0000000000..bd2fce1211 --- /dev/null +++ b/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.result-ast @@ -0,0 +1 @@ +Expr(When(Arrow(@26), @20), @0) \ No newline at end of file diff --git a/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.roc b/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.roc new file mode 100644 index 0000000000..31fb667aa9 --- /dev/null +++ b/crates/compiler/parse/tests/snapshots/fail/when_missing_arrow.expr.roc @@ -0,0 +1,3 @@ +when 5 is + 1 -> 2 + _ \ No newline at end of file diff --git a/crates/compiler/parse/tests/test_parse.rs b/crates/compiler/parse/tests/test_parse.rs index 86c5123cc1..8b38116a26 100644 --- a/crates/compiler/parse/tests/test_parse.rs +++ b/crates/compiler/parse/tests/test_parse.rs @@ -124,6 +124,8 @@ mod test_parse { fail/lambda_missing_indent.expr, fail/type_argument_no_arrow.expr, fail/type_double_comma.expr, + fail/when_missing_arrow.expr, + fail/pattern_binds_keyword.expr, pass/ability_demand_signature_is_multiline.expr, pass/ability_multi_line.expr, pass/ability_single_line.expr, diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index e16e72524e..5a37dc9890 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -7,19 +7,19 @@ use roc_parse::pattern::PatternType; use roc_region::all::{Loc, Region}; use roc_types::types::AliasKind; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CycleEntry { pub symbol: Symbol, pub symbol_region: Region, pub expr_region: Region, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum BadPattern { Unsupported(PatternType), } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ShadowKind { Variable, Alias(Symbol), @@ -28,7 +28,7 @@ pub enum ShadowKind { } /// Problems that can occur in the course of canonicalization. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Problem { UnusedDef(Symbol, Region), UnusedImport(Symbol, Region), @@ -185,6 +185,9 @@ pub enum Problem { UnnecessaryOutputWildcard { region: Region, }, + MultipleListRestPattern { + region: Region, + }, } impl Problem { @@ -313,6 +316,7 @@ impl Problem { def_pattern: region, .. } + | Problem::MultipleListRestPattern { region } | Problem::UnnecessaryOutputWildcard { region } => Some(*region), Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) | Problem::BadRecursion(cycle_entries) => { @@ -330,13 +334,13 @@ impl Problem { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ExtensionTypeKind { Record, TagUnion, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum PrecedenceProblem { BothNonAssociative(Region, Loc, Loc), } @@ -385,7 +389,7 @@ pub enum FloatErrorKind { IntSuffix, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum RuntimeError { Shadowing { original_region: Region, @@ -508,7 +512,7 @@ impl RuntimeError { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum MalformedPatternProblem { MalformedInt, MalformedFloat, @@ -518,4 +522,5 @@ pub enum MalformedPatternProblem { BadIdent(roc_parse::ident::BadIdent), EmptySingleQuote, MultipleCharsInSingleQuote, + DuplicateListRestPattern, } diff --git a/crates/compiler/region/src/all.rs b/crates/compiler/region/src/all.rs index 3eed5060d5..457810cdf9 100644 --- a/crates/compiler/region/src/all.rs +++ b/crates/compiler/region/src/all.rs @@ -133,7 +133,8 @@ impl Position { impl Debug for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "@{}", self.offset) + write!(f, "@")?; + self.offset.fmt(f) } } diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 130dcf3447..fc85ba7b94 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -31,7 +31,7 @@ pub enum AbilityImplError { } /// Indexes a requested deriving of an ability for an opaque type. -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct RequestedDeriveKey { pub opaque: Symbol, pub ability: Symbol, diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index e5929f6340..d66181fc08 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -947,15 +947,16 @@ fn solve( ); let expectation = &constraints.expectations[expectation_index.index()]; - let expected = type_cell_to_var( + let expected = either_type_index_to_var( + constraints, subs, rank, + pools, problems, abilities_store, obligation_cache, - pools, aliases, - expectation.get_type_ref(), + *expectation.get_type_ref(), ); match unify( @@ -1065,15 +1066,16 @@ fn solve( let actual = deep_copy_var_in(subs, rank, pools, var, arena); let expectation = &constraints.expectations[expectation_index.index()]; - let expected = type_cell_to_var( + let expected = either_type_index_to_var( + constraints, subs, rank, + pools, problems, abilities_store, obligation_cache, - pools, aliases, - expectation.get_type_ref(), + *expectation.get_type_ref(), ); match unify( @@ -1178,15 +1180,16 @@ fn solve( ); let expectation = &constraints.pattern_expectations[expectation_index.index()]; - let expected = type_cell_to_var( + let expected = either_type_index_to_var( + constraints, subs, rank, + pools, problems, abilities_store, obligation_cache, - pools, aliases, - expectation.get_type_ref(), + *expectation.get_type_ref(), ); let mode = match constraint { @@ -1487,15 +1490,16 @@ fn solve( // 4. Condition and branch types aren't "almost equal", this is just a normal type // error. - let (real_var, real_region, expected_type, category_and_expected) = match eq { + let (real_var, real_region, branches_var, category_and_expected) = match eq { Ok(eq) => { let roc_can::constraint::Eq(real_var, expected, category, real_region) = constraints.eq[eq.index()]; let expected = &constraints.expectations[expected.index()]; + ( real_var, real_region, - expected.get_type_ref(), + *expected.get_type_ref(), Ok((category, expected)), ) } @@ -1507,10 +1511,11 @@ fn solve( real_region, ) = constraints.pattern_eq[peq.index()]; let expected = &constraints.pattern_expectations[expected.index()]; + ( real_var, real_region, - expected.get_type_ref(), + *expected.get_type_ref(), Err((category, expected)), ) } @@ -1528,15 +1533,16 @@ fn solve( real_var, ); - let branches_var = type_cell_to_var( + let branches_var = either_type_index_to_var( + constraints, subs, rank, + pools, problems, abilities_store, obligation_cache, - pools, aliases, - expected_type, + branches_var, ); let cond_source_is_likely_positive_value = category_and_expected.is_ok(); @@ -1736,24 +1742,28 @@ fn solve( close_pattern_matched_tag_unions(subs, real_var); } - let ExhaustiveSummary { + if let Ok(ExhaustiveSummary { errors, exhaustive, redundancies, - } = check(subs, real_var, sketched_rows, context); + }) = check(subs, real_var, sketched_rows, context) + { + // Store information about whether the "when" is exhaustive, and + // which (if any) of its branches are redundant. Codegen may use + // this for branch-fixing and redundant elimination. + if !exhaustive { + exhaustive_mark.set_non_exhaustive(subs); + } + for redundant_mark in redundancies { + redundant_mark.set_redundant(subs); + } - // Store information about whether the "when" is exhaustive, and - // which (if any) of its branches are redundant. Codegen may use - // this for branch-fixing and redundant elimination. - if !exhaustive { - exhaustive_mark.set_non_exhaustive(subs); + // Store the errors. + problems.extend(errors.into_iter().map(TypeError::Exhaustive)); + } else { + // Otherwise there were type errors deeper in the pattern; we will have + // already reported them. } - for redundant_mark in redundancies { - redundant_mark.set_redundant(subs); - } - - // Store the errors. - problems.extend(errors.into_iter().map(TypeError::Exhaustive)); } state @@ -1892,6 +1902,11 @@ fn open_tag_union(subs: &mut Subs, var: Variable) { stack.extend(subs.get_subs_slice(fields.variables())); } + Structure(Apply(Symbol::LIST_LIST, args)) => { + // Open up nested tag unions. + stack.extend(subs.get_subs_slice(args)); + } + _ => { // Everything else is not a structural type that can be opened // (i.e. cannot be matched in a pattern-match) @@ -1952,10 +1967,15 @@ fn close_pattern_matched_tag_unions(subs: &mut Subs, var: Variable) { } Structure(Record(fields, _)) => { - // Open up all nested tag unions. + // Close up all nested tag unions. stack.extend(subs.get_subs_slice(fields.variables())); } + Structure(Apply(Symbol::LIST_LIST, args)) => { + // Close up nested tag unions. + stack.extend(subs.get_subs_slice(args)); + } + Alias(_, _, real_var, _) => { stack.push(real_var); } @@ -2233,21 +2253,22 @@ impl LocalDefVarsVec<(Symbol, Loc)> { subs: &mut Subs, def_types_slice: roc_can::constraint::DefTypes, ) -> Self { - let types_slice = &constraints.types[def_types_slice.types.indices()]; + let type_indices_slice = &constraints.type_slices[def_types_slice.types.indices()]; let loc_symbols_slice = &constraints.loc_symbols[def_types_slice.loc_symbols.indices()]; - let mut local_def_vars = Self::with_length(types_slice.len()); + let mut local_def_vars = Self::with_length(type_indices_slice.len()); - for (&(symbol, region), typ_cell) in (loc_symbols_slice.iter()).zip(types_slice) { - let var = type_cell_to_var( + for (&(symbol, region), typ_index) in (loc_symbols_slice.iter()).zip(type_indices_slice) { + let var = either_type_index_to_var( + constraints, subs, rank, + pools, problems, abilities_store, obligation_cache, - pools, aliases, - typ_cell, + *typ_index, ); local_def_vars.push((symbol, Loc { value: var, region })); diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 9c9583465c..7f6795d5c2 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -4590,7 +4590,7 @@ mod solve_expr { removeHelp 1i64 Empty "# ), - "RBTree I64 I64", + "RBTree (Key (Integer Signed64)) I64", ); } diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index e1de64944d..0d91cbfce5 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -31,7 +31,7 @@ pub enum TypeError { }, } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum Unfulfilled { /// No claimed implementation of an ability for an opaque type. OpaqueDoesNotImplement { typ: Symbol, ability: Symbol }, @@ -51,7 +51,7 @@ pub enum Unfulfilled { }, } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum UnderivableReason { NotABuiltin, /// The surface type is not derivable @@ -60,7 +60,7 @@ pub enum UnderivableReason { NestedNotDerivable(ErrorType, NotDerivableContext), } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum NotDerivableContext { NoContext, Function, @@ -70,12 +70,12 @@ pub enum NotDerivableContext { Eq(NotDerivableEq), } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum NotDerivableDecode { OptionalRecordField(Lowercase), } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum NotDerivableEq { FloatingPoint, } diff --git a/crates/compiler/test_derive/src/pretty_print.rs b/crates/compiler/test_derive/src/pretty_print.rs index 0eaa70ce72..5c072c84ac 100644 --- a/crates/compiler/test_derive/src/pretty_print.rs +++ b/crates/compiler/test_derive/src/pretty_print.rs @@ -387,6 +387,7 @@ fn pattern<'a>( ) .append(f.text("}")) .group(), + List { .. } => todo!(), NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => { f.text(&**n) } diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 621a94dc65..00547f4bad 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -375,6 +375,7 @@ fn check_derived_typechecks_and_golden( let mut rigid_vars = Default::default(); let (import_variables, abilities_store) = add_imports( test_module, + &mut constraints, &mut test_subs, pending_abilities, &exposed_for_module, diff --git a/crates/compiler/test_gen/Cargo.toml b/crates/compiler/test_gen/Cargo.toml index 4d16e8f947..db12829684 100644 --- a/crates/compiler/test_gen/Cargo.toml +++ b/crates/compiler/test_gen/Cargo.toml @@ -11,6 +11,7 @@ path = "src/tests.rs" [build-dependencies] roc_builtins = { path = "../builtins" } +roc_utils = { path = "../../utils" } wasi_libc_sys = { path = "../../wasi-libc-sys" } [dev-dependencies] @@ -25,6 +26,7 @@ roc_types = { path = "../types" } roc_builtins = { path = "../builtins" } roc_constrain = { path = "../constrain" } roc_unify = { path = "../unify" } +roc_utils = { path = "../../utils" } roc_solve = { path = "../solve" } roc_mono = { path = "../mono" } roc_reporting = { path = "../../reporting" } diff --git a/crates/compiler/test_gen/build.rs b/crates/compiler/test_gen/build.rs index 94e1577d15..74aca5c32f 100644 --- a/crates/compiler/test_gen/build.rs +++ b/crates/compiler/test_gen/build.rs @@ -1,9 +1,9 @@ use roc_builtins::bitcode; +use roc_utils::zig; use std::env; use std::fs; use std::path::Path; use std::path::PathBuf; -use std::process::Command; use wasi_libc_sys::{WASI_COMPILER_RT_PATH, WASI_LIBC_PATH}; @@ -113,13 +113,6 @@ fn build_wasm_test_host() { ]); } -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn build_wasm_platform(out_dir: &str, source_path: &str) -> PathBuf { let mut outfile = PathBuf::from(out_dir).join(PLATFORM_FILENAME); outfile.set_extension("o"); @@ -146,16 +139,25 @@ fn feature_is_enabled(feature_name: &str) -> bool { // Run cargo with -vv to see commands printed out fn run_zig(args: &[&str]) { - let zig = zig_executable(); - println!("{} {}", zig, args.join(" ")); - let output = Command::new(&zig).args(args).output().unwrap(); + let mut zig_cmd = zig(); - if !output.status.success() { - eprintln!("stdout:\n{}", String::from_utf8_lossy(&output.stdout)); - eprintln!("stderr:\n{}", String::from_utf8_lossy(&output.stderr)); - panic!("zig call failed with status {:?}", output.status); + let full_zig_cmd = zig_cmd.args(args); + println!("{:?}", full_zig_cmd); + + let zig_cmd_output = full_zig_cmd.output().unwrap(); + + if !zig_cmd_output.status.success() { + eprintln!( + "stdout:\n{}", + String::from_utf8_lossy(&zig_cmd_output.stdout) + ); + eprintln!( + "stderr:\n{}", + String::from_utf8_lossy(&zig_cmd_output.stderr) + ); + panic!("zig call failed with status {:?}", zig_cmd_output.status); } - assert!(output.stdout.is_empty(), "{:#?}", output); - assert!(output.stderr.is_empty(), "{:#?}", output); + assert!(zig_cmd_output.stdout.is_empty(), "{:#?}", zig_cmd_output); + assert!(zig_cmd_output.stderr.is_empty(), "{:#?}", zig_cmd_output); } diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index 5c619b0cae..55c7a4cf38 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -3578,3 +3578,135 @@ fn list_walk_from_even_prefix_sum() { i64 ); } + +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +mod pattern_match { + #[cfg(feature = "gen-llvm")] + use crate::helpers::llvm::assert_evals_to; + + #[cfg(feature = "gen-wasm")] + use crate::helpers::wasm::assert_evals_to; + + use super::RocList; + + #[test] + fn unary_exact_size_match() { + assert_evals_to!( + r#" + helper = \l -> when l is + [] -> 1u8 + _ -> 2u8 + + [ helper [], helper [{}] ] + "#, + RocList::from_slice(&[1, 2]), + RocList + ) + } + + #[test] + fn many_exact_size_match() { + assert_evals_to!( + r#" + helper = \l -> when l is + [] -> 1u8 + [_] -> 2u8 + [_, _] -> 3u8 + [_, _, _] -> 4u8 + _ -> 5u8 + + [ helper [], helper [{}], helper [{}, {}], helper [{}, {}, {}], helper [{}, {}, {}, {}] ] + "#, + RocList::from_slice(&[1, 2, 3, 4, 5]), + RocList + ) + } + + #[test] + fn ranged_matches_head() { + assert_evals_to!( + r#" + helper = \l -> when l is + [] -> 1u8 + [A] -> 2u8 + [A, A, ..] -> 3u8 + [A, B, ..] -> 4u8 + [B, ..] -> 5u8 + + [ + helper [], + helper [A], + helper [A, A], helper [A, A, A], helper [A, A, B], helper [A, A, B, A], + helper [A, B], helper [A, B, A], helper [A, B, B], helper [A, B, A, B], + helper [B], helper [B, A], helper [B, B], helper [B, A, B, B], + ] + "#, + RocList::from_slice(&[ + 1, // + 2, // + 3, 3, 3, 3, // + 4, 4, 4, 4, // + 5, 5, 5, 5, // + ]), + RocList + ) + } + + #[test] + fn ranged_matches_tail() { + assert_evals_to!( + r#" + helper = \l -> when l is + [] -> 1u8 + [A] -> 2u8 + [.., A, A] -> 3u8 + [.., B, A] -> 4u8 + [.., B] -> 5u8 + + [ + helper [], + helper [A], + helper [A, A], helper [A, A, A], helper [B, A, A], helper [A, B, A, A], + helper [B, A], helper [A, B, A], helper [B, B, A], helper [B, A, B, A], + helper [B], helper [A, B], helper [B, B], helper [B, A, B, B], + ] + "#, + RocList::from_slice(&[ + 1, // + 2, // + 3, 3, 3, 3, // + 4, 4, 4, 4, // + 5, 5, 5, 5, // + ]), + RocList + ) + } + + #[test] + fn bind_variables() { + assert_evals_to!( + r#" + helper : List U16 -> U16 + helper = \l -> when l is + [] -> 1 + [x] -> x + [.., w, x, y, z] -> w * x * y * z + [x, y, ..] -> x * y + + [ + helper [], + helper [5], + helper [3, 5], helper [3, 5, 7], + helper [2, 3, 5, 7], helper [11, 2, 3, 5, 7], helper [13, 11, 2, 3, 5, 7], + ] + "#, + RocList::from_slice(&[ + 1, // + 5, // + 15, 15, // + 210, 210, 210, // + ]), + RocList + ) + } +} diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 870950ec2f..5d453fcca3 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -4104,3 +4104,25 @@ fn issue_4348() { RocStr ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn issue_4349() { + assert_evals_to!( + indoc!( + r#" + ir = Ok "" + res = + Result.try ir \_ -> + when ir is + Ok "" -> Ok "" + _ -> Err Bad + when res is + Ok _ -> "okay" + _ -> "FAIL" + "# + ), + RocStr::from("okay"), + RocStr + ); +} diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 707584fc47..70856acc73 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -12,6 +12,7 @@ use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading}; use roc_mono::ir::OptLevel; use roc_region::all::LineInfo; use roc_reporting::report::RenderTarget; +use roc_utils::zig; use target_lexicon::Triple; #[cfg(feature = "gen-llvm-wasm")] @@ -340,11 +341,11 @@ fn annotate_with_debug_info<'ctx>( let app_bc_file = "/tmp/roc-debugir.bc"; // write the ll code to a file, so we can modify it - module.print_to_file(&app_ll_file).unwrap(); + module.print_to_file(app_ll_file).unwrap(); // run the debugir https://github.com/vaivaswatha/debugir tool match Command::new("debugir") - .args(&["-instnamer", app_ll_file]) + .args(["-instnamer", app_ll_file]) .output() { Ok(_) => {} @@ -360,11 +361,11 @@ fn annotate_with_debug_info<'ctx>( } Command::new("llvm-as") - .args(&[app_dbg_ll_file, "-o", app_bc_file]) + .args([app_dbg_ll_file, "-o", app_bc_file]) .output() .unwrap(); - inkwell::module::Module::parse_bitcode_from_path(&app_bc_file, context).unwrap() + inkwell::module::Module::parse_bitcode_from_path(app_bc_file, context).unwrap() } #[allow(dead_code)] @@ -456,11 +457,9 @@ fn llvm_module_to_wasm_file( .write_to_file(llvm_module, file_type, &test_a_path) .unwrap(); - use std::process::Command; - - let output = Command::new(&crate::helpers::zig_executable()) + let output = zig() .current_dir(dir_path) - .args(&[ + .args([ "wasm-ld", concat!(env!("OUT_DIR"), "/wasm_test_platform.wasm"), test_a_path.to_str().unwrap(), diff --git a/crates/compiler/test_gen/src/helpers/mod.rs b/crates/compiler/test_gen/src/helpers/mod.rs index 577cf25d01..ed49fa04ed 100644 --- a/crates/compiler/test_gen/src/helpers/mod.rs +++ b/crates/compiler/test_gen/src/helpers/mod.rs @@ -10,14 +10,6 @@ pub mod llvm; #[cfg(any(feature = "gen-wasm", feature = "gen-llvm-wasm"))] pub mod wasm; -#[allow(dead_code)] -pub fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - #[allow(dead_code)] pub(crate) fn src_hash(src: &str) -> u64 { use std::collections::hash_map::DefaultHasher; diff --git a/crates/compiler/test_mono/Cargo.toml b/crates/compiler/test_mono/Cargo.toml index 98fc318cee..9f3ef1305e 100644 --- a/crates/compiler/test_mono/Cargo.toml +++ b/crates/compiler/test_mono/Cargo.toml @@ -11,7 +11,7 @@ path = "src/tests.rs" [dev-dependencies] roc_collections = { path = "../collections" } -roc_module = { path = "../module" } +roc_module = { path = "../module", features = ["debug-symbols"] } roc_builtins = { path = "../builtins" } roc_load = { path = "../load" } roc_can = { path = "../can" } diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt index d6a64cf200..5e5af921e0 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt @@ -1,7 +1,7 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.380 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; + let List.409 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; decref #Attr.2; - ret List.380; + ret List.409; procedure Test.2 (Test.3): let Test.7 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt index 2b758f2f1e..947e114ed7 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt @@ -1,7 +1,7 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.380 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; + let List.409 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; decref #Attr.2; - ret List.380; + ret List.409; procedure Test.2 (Test.3): let Test.7 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/choose_i128_layout.txt b/crates/compiler/test_mono/generated/choose_i128_layout.txt index a8df0f6aeb..811ac35b19 100644 --- a/crates/compiler/test_mono/generated/choose_i128_layout.txt +++ b/crates/compiler/test_mono/generated/choose_i128_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.258 : I128 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I128 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.257; procedure Test.0 (): let Test.6 : I128 = 18446744073709551616i64; diff --git a/crates/compiler/test_mono/generated/choose_u128_layout.txt b/crates/compiler/test_mono/generated/choose_u128_layout.txt index cbed3ede38..acf76c6677 100644 --- a/crates/compiler/test_mono/generated/choose_u128_layout.txt +++ b/crates/compiler/test_mono/generated/choose_u128_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : U128 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U128 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.2 : U128 = 170141183460469231731687303715884105728u128; diff --git a/crates/compiler/test_mono/generated/choose_u64_layout.txt b/crates/compiler/test_mono/generated/choose_u64_layout.txt index 8f967decc8..7a88a19504 100644 --- a/crates/compiler/test_mono/generated/choose_u64_layout.txt +++ b/crates/compiler/test_mono/generated/choose_u64_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.2 : U64 = 9999999999999999999i64; diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index 6741c034f9..a5d2cfc8c8 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,6 +1,6 @@ procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index 8f4ea5f8b6..560247d7f4 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -1,14 +1,14 @@ procedure Dict.1 (): - let Dict.100 : List {[], []} = Array []; - ret Dict.100; + let Dict.318 : List {[], []} = Array []; + ret Dict.318; -procedure Dict.7 (Dict.94): - let Dict.99 : U64 = CallByName List.6 Dict.94; - ret Dict.99; +procedure Dict.7 (Dict.312): + let Dict.317 : U64 = CallByName List.6 Dict.312; + ret Dict.317; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure Test.0 (): let Test.2 : List {[], []} = CallByName Dict.1; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index 8b9698c414..50831ffd48 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -1,30 +1,30 @@ procedure Bool.1 (): - let Bool.11 : Int1 = false; - ret Bool.11; + let Bool.23 : Int1 = false; + ret Bool.23; -procedure List.2 (List.90, List.91): - let List.386 : U64 = CallByName List.6 List.90; - let List.382 : Int1 = CallByName Num.22 List.91 List.386; - if List.382 then - let List.384 : {} = CallByName List.66 List.90 List.91; - let List.383 : [C {}, C {}] = TagId(1) List.384; - ret List.383; +procedure List.2 (List.94, List.95): + let List.415 : U64 = CallByName List.6 List.94; + let List.411 : Int1 = CallByName Num.22 List.95 List.415; + if List.411 then + let List.413 : {} = CallByName List.66 List.94 List.95; + let List.412 : [C {}, C {}] = TagId(1) List.413; + ret List.412; else - let List.381 : {} = Struct {}; - let List.380 : [C {}, C {}] = TagId(0) List.381; - ret List.380; + let List.410 : {} = Struct {}; + let List.409 : [C {}, C {}] = TagId(0) List.410; + ret List.409; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; procedure List.66 (#Attr.2, #Attr.3): - let List.385 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.385; + let List.414 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; procedure Test.2 (Test.5): let Test.17 : Str = "bar"; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index 5a72793ccf..d2c1a0d87a 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.101, List.102): - let List.383 : U64 = 1i64; - let List.381 : List U8 = CallByName List.70 List.101 List.383; - let List.380 : List U8 = CallByName List.71 List.381 List.102; - ret List.380; +procedure List.4 (List.105, List.106): + let List.412 : U64 = 1i64; + let List.410 : List U8 = CallByName List.70 List.105 List.412; + let List.409 : List U8 = CallByName List.71 List.410 List.106; + ret List.409; procedure List.70 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.384; + let List.413 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.413; procedure List.71 (#Attr.2, #Attr.3): - let List.382 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.382; + let List.411 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.411; procedure Test.23 (Test.24, Test.35, Test.22): let Test.37 : List U8 = CallByName List.4 Test.24 Test.22; diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 6fa4d2ee16..069b904448 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -44,7 +44,7 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.106; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.113 Encode.94 Encode.96 Encode.102; + let Encode.113 : List U8 = CallByName Json.112 Encode.94 Encode.96 Encode.102; ret Encode.113; procedure Encode.23 (Encode.94, Encode.102, Encode.96): @@ -52,11 +52,11 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.115; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.125 : List U8 = CallByName Json.113 Encode.94 Encode.96 Encode.102; + let Encode.125 : List U8 = CallByName Json.112 Encode.94 Encode.96 Encode.102; ret Encode.125; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.128 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.128 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.128; procedure Encode.25 (Encode.100, Encode.101): @@ -66,284 +66,284 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.113 (Json.114, Json.399, Json.112): - let Json.432 : I32 = 123i64; - let Json.431 : U8 = CallByName Num.123 Json.432; - let Json.116 : List U8 = CallByName List.4 Json.114 Json.431; - let Json.430 : U64 = CallByName List.6 Json.112; - let Json.407 : {List U8, U64} = Struct {Json.116, Json.430}; - let Json.408 : {} = Struct {}; - let Json.406 : {List U8, U64} = CallByName List.18 Json.112 Json.407 Json.408; - dec Json.112; - let Json.118 : List U8 = StructAtIndex 0 Json.406; - inc Json.118; - dec Json.406; - let Json.405 : I32 = 125i64; - let Json.404 : U8 = CallByName Num.123 Json.405; - let Json.403 : List U8 = CallByName List.4 Json.118 Json.404; - ret Json.403; +procedure Json.112 (Json.113, Json.397, Json.111): + let Json.430 : I64 = 123i64; + let Json.429 : U8 = CallByName Num.125 Json.430; + let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; + let Json.428 : U64 = CallByName List.6 Json.111; + let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; + let Json.406 : {} = Struct {}; + let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; + dec Json.111; + let Json.117 : List U8 = StructAtIndex 0 Json.404; + inc Json.117; + dec Json.404; + let Json.403 : I64 = 125i64; + let Json.402 : U8 = CallByName Num.125 Json.403; + let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; + ret Json.401; -procedure Json.113 (Json.114, Json.399, Json.112): - let Json.472 : I32 = 123i64; - let Json.471 : U8 = CallByName Num.123 Json.472; - let Json.116 : List U8 = CallByName List.4 Json.114 Json.471; - let Json.470 : U64 = CallByName List.6 Json.112; - let Json.447 : {List U8, U64} = Struct {Json.116, Json.470}; - let Json.448 : {} = Struct {}; - let Json.446 : {List U8, U64} = CallByName List.18 Json.112 Json.447 Json.448; - dec Json.112; - let Json.118 : List U8 = StructAtIndex 0 Json.446; - inc Json.118; - dec Json.446; - let Json.445 : I32 = 125i64; - let Json.444 : U8 = CallByName Num.123 Json.445; - let Json.443 : List U8 = CallByName List.4 Json.118 Json.444; - ret Json.443; +procedure Json.112 (Json.113, Json.397, Json.111): + let Json.470 : I64 = 123i64; + let Json.469 : U8 = CallByName Num.125 Json.470; + let Json.115 : List U8 = CallByName List.4 Json.113 Json.469; + let Json.468 : U64 = CallByName List.6 Json.111; + let Json.445 : {List U8, U64} = Struct {Json.115, Json.468}; + let Json.446 : {} = Struct {}; + let Json.444 : {List U8, U64} = CallByName List.18 Json.111 Json.445 Json.446; + dec Json.111; + let Json.117 : List U8 = StructAtIndex 0 Json.444; + inc Json.117; + dec Json.444; + let Json.443 : I64 = 125i64; + let Json.442 : U8 = CallByName Num.125 Json.443; + let Json.441 : List U8 = CallByName List.4 Json.117 Json.442; + ret Json.441; -procedure Json.115 (Json.401, Json.402): - let Json.121 : Str = StructAtIndex 0 Json.402; +procedure Json.114 (Json.399, Json.400): + let Json.120 : Str = StructAtIndex 0 Json.400; + inc Json.120; + let Json.121 : Str = StructAtIndex 1 Json.400; inc Json.121; - let Json.122 : Str = StructAtIndex 1 Json.402; - inc Json.122; - dec Json.402; - let Json.119 : List U8 = StructAtIndex 0 Json.401; - inc Json.119; - let Json.120 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.429 : I32 = 34i64; - let Json.428 : U8 = CallByName Num.123 Json.429; - let Json.426 : List U8 = CallByName List.4 Json.119 Json.428; - let Json.427 : List U8 = CallByName Str.12 Json.121; - let Json.423 : List U8 = CallByName List.8 Json.426 Json.427; - let Json.425 : I32 = 34i64; - let Json.424 : U8 = CallByName Num.123 Json.425; - let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; - let Json.422 : I32 = 58i64; - let Json.421 : U8 = CallByName Num.123 Json.422; - let Json.418 : List U8 = CallByName List.4 Json.420 Json.421; - let Json.419 : {} = Struct {}; - let Json.123 : List U8 = CallByName Encode.23 Json.418 Json.122 Json.419; - joinpoint Json.413 Json.124: - let Json.411 : U64 = 1i64; - let Json.410 : U64 = CallByName Num.20 Json.120 Json.411; - let Json.409 : {List U8, U64} = Struct {Json.124, Json.410}; - ret Json.409; + dec Json.400; + let Json.118 : List U8 = StructAtIndex 0 Json.399; + inc Json.118; + let Json.119 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.427 : I64 = 34i64; + let Json.426 : U8 = CallByName Num.125 Json.427; + let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; + let Json.425 : List U8 = CallByName Str.12 Json.120; + let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; + let Json.423 : I64 = 34i64; + let Json.422 : U8 = CallByName Num.125 Json.423; + let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; + let Json.420 : I64 = 58i64; + let Json.419 : U8 = CallByName Num.125 Json.420; + let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; + let Json.417 : {} = Struct {}; + let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; + joinpoint Json.411 Json.123: + let Json.409 : U64 = 1i64; + let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; + let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; + ret Json.407; in - let Json.417 : U64 = 1i64; - let Json.414 : Int1 = CallByName Num.24 Json.120 Json.417; - if Json.414 then - let Json.416 : I32 = 44i64; - let Json.415 : U8 = CallByName Num.123 Json.416; - let Json.412 : List U8 = CallByName List.4 Json.123 Json.415; - jump Json.413 Json.412; + let Json.415 : U64 = 1i64; + let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; + if Json.412 then + let Json.414 : I64 = 44i64; + let Json.413 : U8 = CallByName Num.125 Json.414; + let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; + jump Json.411 Json.410; else - jump Json.413 Json.123; + jump Json.411 Json.122; -procedure Json.115 (Json.401, Json.402): - let Json.121 : Str = StructAtIndex 0 Json.402; +procedure Json.114 (Json.399, Json.400): + let Json.120 : Str = StructAtIndex 0 Json.400; + inc Json.120; + let Json.121 : Str = StructAtIndex 1 Json.400; inc Json.121; - let Json.122 : Str = StructAtIndex 1 Json.402; - inc Json.122; - dec Json.402; - let Json.119 : List U8 = StructAtIndex 0 Json.401; - inc Json.119; - let Json.120 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.469 : I32 = 34i64; - let Json.468 : U8 = CallByName Num.123 Json.469; - let Json.466 : List U8 = CallByName List.4 Json.119 Json.468; - let Json.467 : List U8 = CallByName Str.12 Json.121; - let Json.463 : List U8 = CallByName List.8 Json.466 Json.467; - let Json.465 : I32 = 34i64; - let Json.464 : U8 = CallByName Num.123 Json.465; - let Json.460 : List U8 = CallByName List.4 Json.463 Json.464; - let Json.462 : I32 = 58i64; - let Json.461 : U8 = CallByName Num.123 Json.462; - let Json.458 : List U8 = CallByName List.4 Json.460 Json.461; - let Json.459 : {} = Struct {}; - let Json.123 : List U8 = CallByName Encode.23 Json.458 Json.122 Json.459; - joinpoint Json.453 Json.124: - let Json.451 : U64 = 1i64; - let Json.450 : U64 = CallByName Num.20 Json.120 Json.451; - let Json.449 : {List U8, U64} = Struct {Json.124, Json.450}; - ret Json.449; + dec Json.400; + let Json.118 : List U8 = StructAtIndex 0 Json.399; + inc Json.118; + let Json.119 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.467 : I64 = 34i64; + let Json.466 : U8 = CallByName Num.125 Json.467; + let Json.464 : List U8 = CallByName List.4 Json.118 Json.466; + let Json.465 : List U8 = CallByName Str.12 Json.120; + let Json.461 : List U8 = CallByName List.8 Json.464 Json.465; + let Json.463 : I64 = 34i64; + let Json.462 : U8 = CallByName Num.125 Json.463; + let Json.458 : List U8 = CallByName List.4 Json.461 Json.462; + let Json.460 : I64 = 58i64; + let Json.459 : U8 = CallByName Num.125 Json.460; + let Json.456 : List U8 = CallByName List.4 Json.458 Json.459; + let Json.457 : {} = Struct {}; + let Json.122 : List U8 = CallByName Encode.23 Json.456 Json.121 Json.457; + joinpoint Json.451 Json.123: + let Json.449 : U64 = 1i64; + let Json.448 : U64 = CallByName Num.20 Json.119 Json.449; + let Json.447 : {List U8, U64} = Struct {Json.123, Json.448}; + ret Json.447; in - let Json.457 : U64 = 1i64; - let Json.454 : Int1 = CallByName Num.24 Json.120 Json.457; - if Json.454 then - let Json.456 : I32 = 44i64; - let Json.455 : U8 = CallByName Num.123 Json.456; - let Json.452 : List U8 = CallByName List.4 Json.123 Json.455; - jump Json.453 Json.452; + let Json.455 : U64 = 1i64; + let Json.452 : Int1 = CallByName Num.24 Json.119 Json.455; + if Json.452 then + let Json.454 : I64 = 44i64; + let Json.453 : U8 = CallByName Num.125 Json.454; + let Json.450 : List U8 = CallByName List.4 Json.122 Json.453; + jump Json.451 Json.450; else - jump Json.453 Json.123; + jump Json.451 Json.122; -procedure Json.18 (Json.96): - let Json.473 : Str = CallByName Encode.22 Json.96; - ret Json.473; +procedure Json.18 (Json.95): + let Json.471 : Str = CallByName Encode.22 Json.95; + ret Json.471; -procedure Json.20 (Json.112): - let Json.397 : List {Str, Str} = CallByName Encode.22 Json.112; - ret Json.397; +procedure Json.20 (Json.111): + let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; + ret Json.395; -procedure Json.20 (Json.112): - let Json.439 : List {Str, Str} = CallByName Encode.22 Json.112; - ret Json.439; +procedure Json.20 (Json.111): + let Json.437 : List {Str, Str} = CallByName Encode.22 Json.111; + ret Json.437; -procedure Json.97 (Json.98, Json.475, Json.96): - let Json.484 : I32 = 34i64; - let Json.483 : U8 = CallByName Num.123 Json.484; - let Json.481 : List U8 = CallByName List.4 Json.98 Json.483; - let Json.482 : List U8 = CallByName Str.12 Json.96; - let Json.478 : List U8 = CallByName List.8 Json.481 Json.482; - let Json.480 : I32 = 34i64; - let Json.479 : U8 = CallByName Num.123 Json.480; - let Json.477 : List U8 = CallByName List.4 Json.478 Json.479; - ret Json.477; +procedure Json.96 (Json.97, Json.473, Json.95): + let Json.482 : I64 = 34i64; + let Json.481 : U8 = CallByName Num.125 Json.482; + let Json.479 : List U8 = CallByName List.4 Json.97 Json.481; + let Json.480 : List U8 = CallByName Str.12 Json.95; + let Json.476 : List U8 = CallByName List.8 Json.479 Json.480; + let Json.478 : I64 = 34i64; + let Json.477 : U8 = CallByName Num.125 Json.478; + let Json.475 : List U8 = CallByName List.4 Json.476 Json.477; + ret Json.475; -procedure List.133 (List.134, List.135, List.132): - let List.420 : {List U8, U64} = CallByName Json.115 List.134 List.135; - ret List.420; +procedure List.137 (List.138, List.139, List.136): + let List.449 : {List U8, U64} = CallByName Json.114 List.138 List.139; + ret List.449; -procedure List.133 (List.134, List.135, List.132): - let List.492 : {List U8, U64} = CallByName Json.115 List.134 List.135; - ret List.492; +procedure List.137 (List.138, List.139, List.136): + let List.521 : {List U8, U64} = CallByName Json.114 List.138 List.139; + ret List.521; -procedure List.18 (List.130, List.131, List.132): - let List.402 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.402; +procedure List.18 (List.134, List.135, List.136): + let List.431 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.431; -procedure List.18 (List.130, List.131, List.132): - let List.474 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.474; +procedure List.18 (List.134, List.135, List.136): + let List.503 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.503; -procedure List.4 (List.101, List.102): - let List.473 : U64 = 1i64; - let List.472 : List U8 = CallByName List.70 List.101 List.473; - let List.471 : List U8 = CallByName List.71 List.472 List.102; - ret List.471; +procedure List.4 (List.105, List.106): + let List.502 : U64 = 1i64; + let List.501 : List U8 = CallByName List.70 List.105 List.502; + let List.500 : List U8 = CallByName List.71 List.501 List.106; + ret List.500; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.422 : U64 = lowlevel ListLen #Attr.2; - ret List.422; + let List.451 : U64 = lowlevel ListLen #Attr.2; + ret List.451; procedure List.6 (#Attr.2): - let List.495 : U64 = lowlevel ListLen #Attr.2; - ret List.495; + let List.524 : U64 = lowlevel ListLen #Attr.2; + ret List.524; procedure List.66 (#Attr.2, #Attr.3): - let List.417 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.417; + let List.446 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.446; procedure List.66 (#Attr.2, #Attr.3): - let List.489 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.489; + let List.518 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.518; procedure List.70 (#Attr.2, #Attr.3): - let List.452 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.452; + let List.481 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.481; procedure List.71 (#Attr.2, #Attr.3): - let List.450 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.450; - -procedure List.75 (List.356, List.357, List.358): - let List.406 : U64 = 0i64; - let List.407 : U64 = CallByName List.6 List.356; - let List.405 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.406 List.407; - ret List.405; - -procedure List.75 (List.356, List.357, List.358): - let List.478 : U64 = 0i64; - let List.479 : U64 = CallByName List.6 List.356; - let List.477 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.478 List.479; - ret List.477; + let List.479 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.479; procedure List.8 (#Attr.2, #Attr.3): - let List.494 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.494; + let List.523 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.523; -procedure List.86 (List.432, List.433, List.434, List.435, List.436): - joinpoint List.408 List.359 List.360 List.361 List.362 List.363: - let List.410 : Int1 = CallByName Num.22 List.362 List.363; - if List.410 then - let List.416 : {Str, Str} = CallByName List.66 List.359 List.362; - let List.411 : {List U8, U64} = CallByName List.133 List.360 List.416 List.361; - let List.414 : U64 = 1i64; - let List.413 : U64 = CallByName Num.19 List.362 List.414; - jump List.408 List.359 List.411 List.361 List.413 List.363; +procedure List.89 (List.385, List.386, List.387): + let List.435 : U64 = 0i64; + let List.436 : U64 = CallByName List.6 List.385; + let List.434 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.435 List.436; + ret List.434; + +procedure List.89 (List.385, List.386, List.387): + let List.507 : U64 = 0i64; + let List.508 : U64 = CallByName List.6 List.385; + let List.506 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.507 List.508; + ret List.506; + +procedure List.90 (List.461, List.462, List.463, List.464, List.465): + joinpoint List.437 List.388 List.389 List.390 List.391 List.392: + let List.439 : Int1 = CallByName Num.22 List.391 List.392; + if List.439 then + let List.445 : {Str, Str} = CallByName List.66 List.388 List.391; + let List.440 : {List U8, U64} = CallByName List.137 List.389 List.445 List.390; + let List.443 : U64 = 1i64; + let List.442 : U64 = CallByName Num.19 List.391 List.443; + jump List.437 List.388 List.440 List.390 List.442 List.392; else - ret List.360; + ret List.389; in - jump List.408 List.432 List.433 List.434 List.435 List.436; + jump List.437 List.461 List.462 List.463 List.464 List.465; -procedure List.86 (List.505, List.506, List.507, List.508, List.509): - joinpoint List.480 List.359 List.360 List.361 List.362 List.363: - let List.482 : Int1 = CallByName Num.22 List.362 List.363; - if List.482 then - let List.488 : {Str, Str} = CallByName List.66 List.359 List.362; - let List.483 : {List U8, U64} = CallByName List.133 List.360 List.488 List.361; - let List.486 : U64 = 1i64; - let List.485 : U64 = CallByName Num.19 List.362 List.486; - jump List.480 List.359 List.483 List.361 List.485 List.363; +procedure List.90 (List.534, List.535, List.536, List.537, List.538): + joinpoint List.509 List.388 List.389 List.390 List.391 List.392: + let List.511 : Int1 = CallByName Num.22 List.391 List.392; + if List.511 then + let List.517 : {Str, Str} = CallByName List.66 List.388 List.391; + let List.512 : {List U8, U64} = CallByName List.137 List.389 List.517 List.390; + let List.515 : U64 = 1i64; + let List.514 : U64 = CallByName Num.19 List.391 List.515; + jump List.509 List.388 List.512 List.390 List.514 List.392; else - ret List.360; + ret List.389; in - jump List.480 List.505 List.506 List.507 List.508 List.509; + jump List.509 List.534 List.535 List.536 List.537 List.538; -procedure Num.123 (#Attr.2): - let Num.283 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.283; +procedure Num.125 (#Attr.2): + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.19 (#Attr.2, #Attr.3): - let Num.286 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.286; - -procedure Num.20 (#Attr.2, #Attr.3): - let Num.284 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.284; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.287 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.287; - -procedure Num.24 (#Attr.2, #Attr.3): - let Num.285 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + let Num.285 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.285; +procedure Num.20 (#Attr.2, #Attr.3): + let Num.283 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.283; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.286 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.286; + +procedure Num.24 (#Attr.2, #Attr.3): + let Num.284 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.284; + procedure Str.12 (#Attr.2): - let Str.268 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.268; + let Str.282 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.282; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.12 : Str = "bar"; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt index be6b0abf1b..9b81c3f70e 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -25,11 +25,11 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.106; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.113 Encode.94 Encode.96 Encode.102; + let Encode.113 : List U8 = CallByName Json.112 Encode.94 Encode.96 Encode.102; ret Encode.113; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.116 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.116 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.116; procedure Encode.25 (Encode.100, Encode.101): @@ -39,188 +39,188 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.113 (Json.114, Json.399, Json.112): - let Json.432 : I32 = 123i64; - let Json.431 : U8 = CallByName Num.123 Json.432; - let Json.116 : List U8 = CallByName List.4 Json.114 Json.431; - let Json.430 : U64 = CallByName List.6 Json.112; - let Json.407 : {List U8, U64} = Struct {Json.116, Json.430}; - let Json.408 : {} = Struct {}; - let Json.406 : {List U8, U64} = CallByName List.18 Json.112 Json.407 Json.408; - dec Json.112; - let Json.118 : List U8 = StructAtIndex 0 Json.406; - inc Json.118; - dec Json.406; - let Json.405 : I32 = 125i64; - let Json.404 : U8 = CallByName Num.123 Json.405; - let Json.403 : List U8 = CallByName List.4 Json.118 Json.404; - ret Json.403; +procedure Json.112 (Json.113, Json.397, Json.111): + let Json.430 : I64 = 123i64; + let Json.429 : U8 = CallByName Num.125 Json.430; + let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; + let Json.428 : U64 = CallByName List.6 Json.111; + let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; + let Json.406 : {} = Struct {}; + let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; + dec Json.111; + let Json.117 : List U8 = StructAtIndex 0 Json.404; + inc Json.117; + dec Json.404; + let Json.403 : I64 = 125i64; + let Json.402 : U8 = CallByName Num.125 Json.403; + let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; + ret Json.401; -procedure Json.115 (Json.401, Json.402): - let Json.121 : Str = StructAtIndex 0 Json.402; +procedure Json.114 (Json.399, Json.400): + let Json.120 : Str = StructAtIndex 0 Json.400; + inc Json.120; + let Json.121 : Str = StructAtIndex 1 Json.400; inc Json.121; - let Json.122 : Str = StructAtIndex 1 Json.402; - inc Json.122; - dec Json.402; - let Json.119 : List U8 = StructAtIndex 0 Json.401; - inc Json.119; - let Json.120 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.429 : I32 = 34i64; - let Json.428 : U8 = CallByName Num.123 Json.429; - let Json.426 : List U8 = CallByName List.4 Json.119 Json.428; - let Json.427 : List U8 = CallByName Str.12 Json.121; - let Json.423 : List U8 = CallByName List.8 Json.426 Json.427; - let Json.425 : I32 = 34i64; - let Json.424 : U8 = CallByName Num.123 Json.425; - let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; - let Json.422 : I32 = 58i64; - let Json.421 : U8 = CallByName Num.123 Json.422; - let Json.418 : List U8 = CallByName List.4 Json.420 Json.421; - let Json.419 : {} = Struct {}; - let Json.123 : List U8 = CallByName Encode.23 Json.418 Json.122 Json.419; - joinpoint Json.413 Json.124: - let Json.411 : U64 = 1i64; - let Json.410 : U64 = CallByName Num.20 Json.120 Json.411; - let Json.409 : {List U8, U64} = Struct {Json.124, Json.410}; - ret Json.409; + dec Json.400; + let Json.118 : List U8 = StructAtIndex 0 Json.399; + inc Json.118; + let Json.119 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.427 : I64 = 34i64; + let Json.426 : U8 = CallByName Num.125 Json.427; + let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; + let Json.425 : List U8 = CallByName Str.12 Json.120; + let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; + let Json.423 : I64 = 34i64; + let Json.422 : U8 = CallByName Num.125 Json.423; + let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; + let Json.420 : I64 = 58i64; + let Json.419 : U8 = CallByName Num.125 Json.420; + let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; + let Json.417 : {} = Struct {}; + let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; + joinpoint Json.411 Json.123: + let Json.409 : U64 = 1i64; + let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; + let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; + ret Json.407; in - let Json.417 : U64 = 1i64; - let Json.414 : Int1 = CallByName Num.24 Json.120 Json.417; - if Json.414 then - let Json.416 : I32 = 44i64; - let Json.415 : U8 = CallByName Num.123 Json.416; - let Json.412 : List U8 = CallByName List.4 Json.123 Json.415; - jump Json.413 Json.412; + let Json.415 : U64 = 1i64; + let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; + if Json.412 then + let Json.414 : I64 = 44i64; + let Json.413 : U8 = CallByName Num.125 Json.414; + let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; + jump Json.411 Json.410; else - jump Json.413 Json.123; + jump Json.411 Json.122; -procedure Json.18 (Json.96): - let Json.433 : Str = CallByName Encode.22 Json.96; - ret Json.433; +procedure Json.18 (Json.95): + let Json.431 : Str = CallByName Encode.22 Json.95; + ret Json.431; -procedure Json.20 (Json.112): - let Json.397 : List {Str, Str} = CallByName Encode.22 Json.112; - ret Json.397; +procedure Json.20 (Json.111): + let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; + ret Json.395; -procedure Json.97 (Json.98, Json.435, Json.96): - let Json.444 : I32 = 34i64; - let Json.443 : U8 = CallByName Num.123 Json.444; - let Json.441 : List U8 = CallByName List.4 Json.98 Json.443; - let Json.442 : List U8 = CallByName Str.12 Json.96; - let Json.438 : List U8 = CallByName List.8 Json.441 Json.442; - let Json.440 : I32 = 34i64; - let Json.439 : U8 = CallByName Num.123 Json.440; - let Json.437 : List U8 = CallByName List.4 Json.438 Json.439; - ret Json.437; +procedure Json.96 (Json.97, Json.433, Json.95): + let Json.442 : I64 = 34i64; + let Json.441 : U8 = CallByName Num.125 Json.442; + let Json.439 : List U8 = CallByName List.4 Json.97 Json.441; + let Json.440 : List U8 = CallByName Str.12 Json.95; + let Json.436 : List U8 = CallByName List.8 Json.439 Json.440; + let Json.438 : I64 = 34i64; + let Json.437 : U8 = CallByName Num.125 Json.438; + let Json.435 : List U8 = CallByName List.4 Json.436 Json.437; + ret Json.435; -procedure List.133 (List.134, List.135, List.132): - let List.426 : {List U8, U64} = CallByName Json.115 List.134 List.135; - ret List.426; +procedure List.137 (List.138, List.139, List.136): + let List.455 : {List U8, U64} = CallByName Json.114 List.138 List.139; + ret List.455; -procedure List.18 (List.130, List.131, List.132): - let List.408 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.408; +procedure List.18 (List.134, List.135, List.136): + let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.437; -procedure List.4 (List.101, List.102): - let List.407 : U64 = 1i64; - let List.406 : List U8 = CallByName List.70 List.101 List.407; - let List.405 : List U8 = CallByName List.71 List.406 List.102; - ret List.405; +procedure List.4 (List.105, List.106): + let List.436 : U64 = 1i64; + let List.435 : List U8 = CallByName List.70 List.105 List.436; + let List.434 : List U8 = CallByName List.71 List.435 List.106; + ret List.434; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.429 : U64 = lowlevel ListLen #Attr.2; - ret List.429; + let List.458 : U64 = lowlevel ListLen #Attr.2; + ret List.458; procedure List.66 (#Attr.2, #Attr.3): - let List.423 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.423; + let List.452 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.452; procedure List.70 (#Attr.2, #Attr.3): - let List.386 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.386; + let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.415; procedure List.71 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.384; - -procedure List.75 (List.356, List.357, List.358): - let List.412 : U64 = 0i64; - let List.413 : U64 = CallByName List.6 List.356; - let List.411 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.412 List.413; - ret List.411; + let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.413; procedure List.8 (#Attr.2, #Attr.3): - let List.428 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.428; + let List.457 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.457; -procedure List.86 (List.439, List.440, List.441, List.442, List.443): - joinpoint List.414 List.359 List.360 List.361 List.362 List.363: - let List.416 : Int1 = CallByName Num.22 List.362 List.363; - if List.416 then - let List.422 : {Str, Str} = CallByName List.66 List.359 List.362; - let List.417 : {List U8, U64} = CallByName List.133 List.360 List.422 List.361; - let List.420 : U64 = 1i64; - let List.419 : U64 = CallByName Num.19 List.362 List.420; - jump List.414 List.359 List.417 List.361 List.419 List.363; +procedure List.89 (List.385, List.386, List.387): + let List.441 : U64 = 0i64; + let List.442 : U64 = CallByName List.6 List.385; + let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442; + ret List.440; + +procedure List.90 (List.468, List.469, List.470, List.471, List.472): + joinpoint List.443 List.388 List.389 List.390 List.391 List.392: + let List.445 : Int1 = CallByName Num.22 List.391 List.392; + if List.445 then + let List.451 : {Str, Str} = CallByName List.66 List.388 List.391; + let List.446 : {List U8, U64} = CallByName List.137 List.389 List.451 List.390; + let List.449 : U64 = 1i64; + let List.448 : U64 = CallByName Num.19 List.391 List.449; + jump List.443 List.388 List.446 List.390 List.448 List.392; else - ret List.360; + ret List.389; in - jump List.414 List.439 List.440 List.441 List.442 List.443; + jump List.443 List.468 List.469 List.470 List.471 List.472; -procedure Num.123 (#Attr.2): - let Num.264 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.264; +procedure Num.125 (#Attr.2): + let Num.263 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.263; procedure Num.19 (#Attr.2, #Attr.3): - let Num.267 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.267; - -procedure Num.20 (#Attr.2, #Attr.3): - let Num.265 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.265; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.268 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.268; - -procedure Num.24 (#Attr.2, #Attr.3): - let Num.266 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + let Num.266 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.266; +procedure Num.20 (#Attr.2, #Attr.3): + let Num.264 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.264; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.267 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.267; + +procedure Num.24 (#Attr.2, #Attr.3): + let Num.265 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.265; + procedure Str.12 (#Attr.2): - let Str.266 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.266; + let Str.280 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.280; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.11 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt index c40b1c595e..faa8c35dcf 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -33,11 +33,11 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.106; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.113 Encode.94 Encode.96 Encode.102; + let Encode.113 : List U8 = CallByName Json.112 Encode.94 Encode.96 Encode.102; ret Encode.113; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.117 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.117 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.117; procedure Encode.25 (Encode.100, Encode.101): @@ -47,188 +47,188 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.113 (Json.114, Json.399, Json.112): - let Json.432 : I32 = 123i64; - let Json.431 : U8 = CallByName Num.123 Json.432; - let Json.116 : List U8 = CallByName List.4 Json.114 Json.431; - let Json.430 : U64 = CallByName List.6 Json.112; - let Json.407 : {List U8, U64} = Struct {Json.116, Json.430}; - let Json.408 : {} = Struct {}; - let Json.406 : {List U8, U64} = CallByName List.18 Json.112 Json.407 Json.408; - dec Json.112; - let Json.118 : List U8 = StructAtIndex 0 Json.406; - inc Json.118; - dec Json.406; - let Json.405 : I32 = 125i64; - let Json.404 : U8 = CallByName Num.123 Json.405; - let Json.403 : List U8 = CallByName List.4 Json.118 Json.404; - ret Json.403; +procedure Json.112 (Json.113, Json.397, Json.111): + let Json.430 : I64 = 123i64; + let Json.429 : U8 = CallByName Num.125 Json.430; + let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; + let Json.428 : U64 = CallByName List.6 Json.111; + let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; + let Json.406 : {} = Struct {}; + let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; + dec Json.111; + let Json.117 : List U8 = StructAtIndex 0 Json.404; + inc Json.117; + dec Json.404; + let Json.403 : I64 = 125i64; + let Json.402 : U8 = CallByName Num.125 Json.403; + let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; + ret Json.401; -procedure Json.115 (Json.401, Json.402): - let Json.121 : Str = StructAtIndex 0 Json.402; +procedure Json.114 (Json.399, Json.400): + let Json.120 : Str = StructAtIndex 0 Json.400; + inc Json.120; + let Json.121 : Str = StructAtIndex 1 Json.400; inc Json.121; - let Json.122 : Str = StructAtIndex 1 Json.402; - inc Json.122; - dec Json.402; - let Json.119 : List U8 = StructAtIndex 0 Json.401; - inc Json.119; - let Json.120 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.429 : I32 = 34i64; - let Json.428 : U8 = CallByName Num.123 Json.429; - let Json.426 : List U8 = CallByName List.4 Json.119 Json.428; - let Json.427 : List U8 = CallByName Str.12 Json.121; - let Json.423 : List U8 = CallByName List.8 Json.426 Json.427; - let Json.425 : I32 = 34i64; - let Json.424 : U8 = CallByName Num.123 Json.425; - let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; - let Json.422 : I32 = 58i64; - let Json.421 : U8 = CallByName Num.123 Json.422; - let Json.418 : List U8 = CallByName List.4 Json.420 Json.421; - let Json.419 : {} = Struct {}; - let Json.123 : List U8 = CallByName Encode.23 Json.418 Json.122 Json.419; - joinpoint Json.413 Json.124: - let Json.411 : U64 = 1i64; - let Json.410 : U64 = CallByName Num.20 Json.120 Json.411; - let Json.409 : {List U8, U64} = Struct {Json.124, Json.410}; - ret Json.409; + dec Json.400; + let Json.118 : List U8 = StructAtIndex 0 Json.399; + inc Json.118; + let Json.119 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.427 : I64 = 34i64; + let Json.426 : U8 = CallByName Num.125 Json.427; + let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; + let Json.425 : List U8 = CallByName Str.12 Json.120; + let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; + let Json.423 : I64 = 34i64; + let Json.422 : U8 = CallByName Num.125 Json.423; + let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; + let Json.420 : I64 = 58i64; + let Json.419 : U8 = CallByName Num.125 Json.420; + let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; + let Json.417 : {} = Struct {}; + let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; + joinpoint Json.411 Json.123: + let Json.409 : U64 = 1i64; + let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; + let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; + ret Json.407; in - let Json.417 : U64 = 1i64; - let Json.414 : Int1 = CallByName Num.24 Json.120 Json.417; - if Json.414 then - let Json.416 : I32 = 44i64; - let Json.415 : U8 = CallByName Num.123 Json.416; - let Json.412 : List U8 = CallByName List.4 Json.123 Json.415; - jump Json.413 Json.412; + let Json.415 : U64 = 1i64; + let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; + if Json.412 then + let Json.414 : I64 = 44i64; + let Json.413 : U8 = CallByName Num.125 Json.414; + let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; + jump Json.411 Json.410; else - jump Json.413 Json.123; + jump Json.411 Json.122; -procedure Json.18 (Json.96): - let Json.445 : Str = CallByName Encode.22 Json.96; - ret Json.445; +procedure Json.18 (Json.95): + let Json.443 : Str = CallByName Encode.22 Json.95; + ret Json.443; -procedure Json.20 (Json.112): - let Json.397 : List {Str, Str} = CallByName Encode.22 Json.112; - ret Json.397; +procedure Json.20 (Json.111): + let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; + ret Json.395; -procedure Json.97 (Json.98, Json.435, Json.96): - let Json.444 : I32 = 34i64; - let Json.443 : U8 = CallByName Num.123 Json.444; - let Json.441 : List U8 = CallByName List.4 Json.98 Json.443; - let Json.442 : List U8 = CallByName Str.12 Json.96; - let Json.438 : List U8 = CallByName List.8 Json.441 Json.442; - let Json.440 : I32 = 34i64; - let Json.439 : U8 = CallByName Num.123 Json.440; - let Json.437 : List U8 = CallByName List.4 Json.438 Json.439; - ret Json.437; +procedure Json.96 (Json.97, Json.433, Json.95): + let Json.442 : I64 = 34i64; + let Json.441 : U8 = CallByName Num.125 Json.442; + let Json.439 : List U8 = CallByName List.4 Json.97 Json.441; + let Json.440 : List U8 = CallByName Str.12 Json.95; + let Json.436 : List U8 = CallByName List.8 Json.439 Json.440; + let Json.438 : I64 = 34i64; + let Json.437 : U8 = CallByName Num.125 Json.438; + let Json.435 : List U8 = CallByName List.4 Json.436 Json.437; + ret Json.435; -procedure List.133 (List.134, List.135, List.132): - let List.426 : {List U8, U64} = CallByName Json.115 List.134 List.135; - ret List.426; +procedure List.137 (List.138, List.139, List.136): + let List.455 : {List U8, U64} = CallByName Json.114 List.138 List.139; + ret List.455; -procedure List.18 (List.130, List.131, List.132): - let List.408 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.408; +procedure List.18 (List.134, List.135, List.136): + let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.437; -procedure List.4 (List.101, List.102): - let List.407 : U64 = 1i64; - let List.406 : List U8 = CallByName List.70 List.101 List.407; - let List.405 : List U8 = CallByName List.71 List.406 List.102; - ret List.405; +procedure List.4 (List.105, List.106): + let List.436 : U64 = 1i64; + let List.435 : List U8 = CallByName List.70 List.105 List.436; + let List.434 : List U8 = CallByName List.71 List.435 List.106; + ret List.434; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.429 : U64 = lowlevel ListLen #Attr.2; - ret List.429; + let List.458 : U64 = lowlevel ListLen #Attr.2; + ret List.458; procedure List.66 (#Attr.2, #Attr.3): - let List.423 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.423; + let List.452 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.452; procedure List.70 (#Attr.2, #Attr.3): - let List.386 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.386; + let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.415; procedure List.71 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.384; - -procedure List.75 (List.356, List.357, List.358): - let List.412 : U64 = 0i64; - let List.413 : U64 = CallByName List.6 List.356; - let List.411 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.412 List.413; - ret List.411; + let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.413; procedure List.8 (#Attr.2, #Attr.3): - let List.428 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.428; + let List.457 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.457; -procedure List.86 (List.439, List.440, List.441, List.442, List.443): - joinpoint List.414 List.359 List.360 List.361 List.362 List.363: - let List.416 : Int1 = CallByName Num.22 List.362 List.363; - if List.416 then - let List.422 : {Str, Str} = CallByName List.66 List.359 List.362; - let List.417 : {List U8, U64} = CallByName List.133 List.360 List.422 List.361; - let List.420 : U64 = 1i64; - let List.419 : U64 = CallByName Num.19 List.362 List.420; - jump List.414 List.359 List.417 List.361 List.419 List.363; +procedure List.89 (List.385, List.386, List.387): + let List.441 : U64 = 0i64; + let List.442 : U64 = CallByName List.6 List.385; + let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442; + ret List.440; + +procedure List.90 (List.468, List.469, List.470, List.471, List.472): + joinpoint List.443 List.388 List.389 List.390 List.391 List.392: + let List.445 : Int1 = CallByName Num.22 List.391 List.392; + if List.445 then + let List.451 : {Str, Str} = CallByName List.66 List.388 List.391; + let List.446 : {List U8, U64} = CallByName List.137 List.389 List.451 List.390; + let List.449 : U64 = 1i64; + let List.448 : U64 = CallByName Num.19 List.391 List.449; + jump List.443 List.388 List.446 List.390 List.448 List.392; else - ret List.360; + ret List.389; in - jump List.414 List.439 List.440 List.441 List.442 List.443; + jump List.443 List.468 List.469 List.470 List.471 List.472; -procedure Num.123 (#Attr.2): - let Num.264 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.264; +procedure Num.125 (#Attr.2): + let Num.263 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.263; procedure Num.19 (#Attr.2, #Attr.3): - let Num.267 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.267; - -procedure Num.20 (#Attr.2, #Attr.3): - let Num.265 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.265; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.268 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.268; - -procedure Num.24 (#Attr.2, #Attr.3): - let Num.266 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + let Num.266 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.266; +procedure Num.20 (#Attr.2, #Attr.3): + let Num.264 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.264; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.267 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.267; + +procedure Num.24 (#Attr.2, #Attr.3): + let Num.265 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.265; + procedure Str.12 (#Attr.2): - let Str.266 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.266; + let Str.280 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.280; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.11 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index 445fb2415a..922ec7bc09 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -2,7 +2,7 @@ procedure Encode.22 (Encode.93): ret Encode.93; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.106 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.106 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.106; procedure Encode.25 (Encode.100, Encode.101): @@ -12,76 +12,76 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.18 (Json.96): - let Json.397 : Str = CallByName Encode.22 Json.96; - ret Json.397; +procedure Json.18 (Json.95): + let Json.395 : Str = CallByName Encode.22 Json.95; + ret Json.395; -procedure Json.97 (Json.98, Json.399, Json.96): - let Json.408 : I32 = 34i64; - let Json.407 : U8 = CallByName Num.123 Json.408; - let Json.405 : List U8 = CallByName List.4 Json.98 Json.407; - let Json.406 : List U8 = CallByName Str.12 Json.96; - let Json.402 : List U8 = CallByName List.8 Json.405 Json.406; - let Json.404 : I32 = 34i64; - let Json.403 : U8 = CallByName Num.123 Json.404; - let Json.401 : List U8 = CallByName List.4 Json.402 Json.403; - ret Json.401; +procedure Json.96 (Json.97, Json.397, Json.95): + let Json.406 : I64 = 34i64; + let Json.405 : U8 = CallByName Num.125 Json.406; + let Json.403 : List U8 = CallByName List.4 Json.97 Json.405; + let Json.404 : List U8 = CallByName Str.12 Json.95; + let Json.400 : List U8 = CallByName List.8 Json.403 Json.404; + let Json.402 : I64 = 34i64; + let Json.401 : U8 = CallByName Num.125 Json.402; + let Json.399 : List U8 = CallByName List.4 Json.400 Json.401; + ret Json.399; -procedure List.4 (List.101, List.102): - let List.389 : U64 = 1i64; - let List.388 : List U8 = CallByName List.70 List.101 List.389; - let List.387 : List U8 = CallByName List.71 List.388 List.102; - ret List.387; +procedure List.4 (List.105, List.106): + let List.418 : U64 = 1i64; + let List.417 : List U8 = CallByName List.70 List.105 List.418; + let List.416 : List U8 = CallByName List.71 List.417 List.106; + ret List.416; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.70 (#Attr.2, #Attr.3): - let List.386 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.386; + let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.415; procedure List.71 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.384; + let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.413; procedure List.8 (#Attr.2, #Attr.3): - let List.390 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.390; + let List.419 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.419; -procedure Num.123 (#Attr.2): - let Num.258 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.258; +procedure Num.125 (#Attr.2): + let Num.257 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.257; procedure Str.12 (#Attr.2): - let Str.265 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.265; + let Str.279 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.279; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.9 : Str = "abc"; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index fe61a284f4..d5e22ddb0f 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -27,11 +27,11 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.106; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.127 Encode.94 Encode.96 Encode.102; + let Encode.113 : List U8 = CallByName Json.126 Encode.94 Encode.96 Encode.102; ret Encode.113; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.116 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.116 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.116; procedure Encode.25 (Encode.100, Encode.101): @@ -41,195 +41,195 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.127 (Json.128, Json.399, #Attr.12): - let Json.126 : List Str = StructAtIndex 1 #Attr.12; - inc Json.126; - let Json.125 : Str = StructAtIndex 0 #Attr.12; +procedure Json.126 (Json.127, Json.397, #Attr.12): + let Json.125 : List Str = StructAtIndex 1 #Attr.12; inc Json.125; + let Json.124 : Str = StructAtIndex 0 #Attr.12; + inc Json.124; dec #Attr.12; - let Json.437 : I32 = 123i64; - let Json.436 : U8 = CallByName Num.123 Json.437; - let Json.433 : List U8 = CallByName List.4 Json.128 Json.436; - let Json.435 : I32 = 34i64; - let Json.434 : U8 = CallByName Num.123 Json.435; - let Json.431 : List U8 = CallByName List.4 Json.433 Json.434; - let Json.432 : List U8 = CallByName Str.12 Json.125; - let Json.428 : List U8 = CallByName List.8 Json.431 Json.432; - let Json.430 : I32 = 34i64; - let Json.429 : U8 = CallByName Num.123 Json.430; - let Json.425 : List U8 = CallByName List.4 Json.428 Json.429; - let Json.427 : I32 = 58i64; - let Json.426 : U8 = CallByName Num.123 Json.427; - let Json.422 : List U8 = CallByName List.4 Json.425 Json.426; - let Json.424 : I32 = 91i64; - let Json.423 : U8 = CallByName Num.123 Json.424; - let Json.130 : List U8 = CallByName List.4 Json.422 Json.423; - let Json.421 : U64 = CallByName List.6 Json.126; - let Json.409 : {List U8, U64} = Struct {Json.130, Json.421}; - let Json.410 : {} = Struct {}; - let Json.408 : {List U8, U64} = CallByName List.18 Json.126 Json.409 Json.410; - dec Json.126; - let Json.132 : List U8 = StructAtIndex 0 Json.408; + let Json.435 : I64 = 123i64; + let Json.434 : U8 = CallByName Num.125 Json.435; + let Json.431 : List U8 = CallByName List.4 Json.127 Json.434; + let Json.433 : I64 = 34i64; + let Json.432 : U8 = CallByName Num.125 Json.433; + let Json.429 : List U8 = CallByName List.4 Json.431 Json.432; + let Json.430 : List U8 = CallByName Str.12 Json.124; + let Json.426 : List U8 = CallByName List.8 Json.429 Json.430; + let Json.428 : I64 = 34i64; + let Json.427 : U8 = CallByName Num.125 Json.428; + let Json.423 : List U8 = CallByName List.4 Json.426 Json.427; + let Json.425 : I64 = 58i64; + let Json.424 : U8 = CallByName Num.125 Json.425; + let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; + let Json.422 : I64 = 91i64; + let Json.421 : U8 = CallByName Num.125 Json.422; + let Json.129 : List U8 = CallByName List.4 Json.420 Json.421; + let Json.419 : U64 = CallByName List.6 Json.125; + let Json.407 : {List U8, U64} = Struct {Json.129, Json.419}; + let Json.408 : {} = Struct {}; + let Json.406 : {List U8, U64} = CallByName List.18 Json.125 Json.407 Json.408; + dec Json.125; + let Json.131 : List U8 = StructAtIndex 0 Json.406; + inc Json.131; + dec Json.406; + let Json.405 : I64 = 93i64; + let Json.404 : U8 = CallByName Num.125 Json.405; + let Json.401 : List U8 = CallByName List.4 Json.131 Json.404; + let Json.403 : I64 = 125i64; + let Json.402 : U8 = CallByName Num.125 Json.403; + let Json.400 : List U8 = CallByName List.4 Json.401 Json.402; + ret Json.400; + +procedure Json.128 (Json.399, Json.134): + let Json.132 : List U8 = StructAtIndex 0 Json.399; inc Json.132; - dec Json.408; - let Json.407 : I32 = 93i64; - let Json.406 : U8 = CallByName Num.123 Json.407; - let Json.403 : List U8 = CallByName List.4 Json.132 Json.406; - let Json.405 : I32 = 125i64; - let Json.404 : U8 = CallByName Num.123 Json.405; - let Json.402 : List U8 = CallByName List.4 Json.403 Json.404; - ret Json.402; - -procedure Json.129 (Json.401, Json.135): - let Json.133 : List U8 = StructAtIndex 0 Json.401; - inc Json.133; - let Json.134 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.420 : {} = Struct {}; - let Json.136 : List U8 = CallByName Encode.23 Json.133 Json.135 Json.420; - joinpoint Json.415 Json.137: - let Json.413 : U64 = 1i64; - let Json.412 : U64 = CallByName Num.20 Json.134 Json.413; - let Json.411 : {List U8, U64} = Struct {Json.137, Json.412}; - ret Json.411; + let Json.133 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.418 : {} = Struct {}; + let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.418; + joinpoint Json.413 Json.136: + let Json.411 : U64 = 1i64; + let Json.410 : U64 = CallByName Num.20 Json.133 Json.411; + let Json.409 : {List U8, U64} = Struct {Json.136, Json.410}; + ret Json.409; in - let Json.419 : U64 = 1i64; - let Json.416 : Int1 = CallByName Num.24 Json.134 Json.419; - if Json.416 then - let Json.418 : I32 = 44i64; - let Json.417 : U8 = CallByName Num.123 Json.418; - let Json.414 : List U8 = CallByName List.4 Json.136 Json.417; - jump Json.415 Json.414; + let Json.417 : U64 = 1i64; + let Json.414 : Int1 = CallByName Num.24 Json.133 Json.417; + if Json.414 then + let Json.416 : I64 = 44i64; + let Json.415 : U8 = CallByName Num.125 Json.416; + let Json.412 : List U8 = CallByName List.4 Json.135 Json.415; + jump Json.413 Json.412; else - jump Json.415 Json.136; + jump Json.413 Json.135; -procedure Json.18 (Json.96): - let Json.438 : Str = CallByName Encode.22 Json.96; - ret Json.438; +procedure Json.18 (Json.95): + let Json.436 : Str = CallByName Encode.22 Json.95; + ret Json.436; -procedure Json.21 (Json.125, Json.126): - let Json.398 : {Str, List Str} = Struct {Json.125, Json.126}; - let Json.397 : {Str, List Str} = CallByName Encode.22 Json.398; - ret Json.397; +procedure Json.21 (Json.124, Json.125): + let Json.396 : {Str, List Str} = Struct {Json.124, Json.125}; + let Json.395 : {Str, List Str} = CallByName Encode.22 Json.396; + ret Json.395; -procedure Json.97 (Json.98, Json.440, Json.96): - let Json.449 : I32 = 34i64; - let Json.448 : U8 = CallByName Num.123 Json.449; - let Json.446 : List U8 = CallByName List.4 Json.98 Json.448; - let Json.447 : List U8 = CallByName Str.12 Json.96; - let Json.443 : List U8 = CallByName List.8 Json.446 Json.447; - let Json.445 : I32 = 34i64; - let Json.444 : U8 = CallByName Num.123 Json.445; - let Json.442 : List U8 = CallByName List.4 Json.443 Json.444; - ret Json.442; +procedure Json.96 (Json.97, Json.438, Json.95): + let Json.447 : I64 = 34i64; + let Json.446 : U8 = CallByName Num.125 Json.447; + let Json.444 : List U8 = CallByName List.4 Json.97 Json.446; + let Json.445 : List U8 = CallByName Str.12 Json.95; + let Json.441 : List U8 = CallByName List.8 Json.444 Json.445; + let Json.443 : I64 = 34i64; + let Json.442 : U8 = CallByName Num.125 Json.443; + let Json.440 : List U8 = CallByName List.4 Json.441 Json.442; + ret Json.440; -procedure List.133 (List.134, List.135, List.132): - let List.432 : {List U8, U64} = CallByName Json.129 List.134 List.135; - ret List.432; +procedure List.137 (List.138, List.139, List.136): + let List.461 : {List U8, U64} = CallByName Json.128 List.138 List.139; + ret List.461; -procedure List.18 (List.130, List.131, List.132): - let List.414 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.414; +procedure List.18 (List.134, List.135, List.136): + let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.443; -procedure List.4 (List.101, List.102): - let List.413 : U64 = 1i64; - let List.412 : List U8 = CallByName List.70 List.101 List.413; - let List.411 : List U8 = CallByName List.71 List.412 List.102; - ret List.411; +procedure List.4 (List.105, List.106): + let List.442 : U64 = 1i64; + let List.441 : List U8 = CallByName List.70 List.105 List.442; + let List.440 : List U8 = CallByName List.71 List.441 List.106; + ret List.440; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.433 : U64 = lowlevel ListLen #Attr.2; - ret List.433; + let List.462 : U64 = lowlevel ListLen #Attr.2; + ret List.462; procedure List.66 (#Attr.2, #Attr.3): - let List.429 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.429; + let List.458 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.458; procedure List.70 (#Attr.2, #Attr.3): - let List.386 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.386; + let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.415; procedure List.71 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.384; - -procedure List.75 (List.356, List.357, List.358): - let List.418 : U64 = 0i64; - let List.419 : U64 = CallByName List.6 List.356; - let List.417 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.418 List.419; - ret List.417; + let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.413; procedure List.8 (#Attr.2, #Attr.3): - let List.435 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.435; + let List.464 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.464; -procedure List.86 (List.445, List.446, List.447, List.448, List.449): - joinpoint List.420 List.359 List.360 List.361 List.362 List.363: - let List.422 : Int1 = CallByName Num.22 List.362 List.363; - if List.422 then - let List.428 : Str = CallByName List.66 List.359 List.362; - let List.423 : {List U8, U64} = CallByName List.133 List.360 List.428 List.361; - let List.426 : U64 = 1i64; - let List.425 : U64 = CallByName Num.19 List.362 List.426; - jump List.420 List.359 List.423 List.361 List.425 List.363; +procedure List.89 (List.385, List.386, List.387): + let List.447 : U64 = 0i64; + let List.448 : U64 = CallByName List.6 List.385; + let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448; + ret List.446; + +procedure List.90 (List.474, List.475, List.476, List.477, List.478): + joinpoint List.449 List.388 List.389 List.390 List.391 List.392: + let List.451 : Int1 = CallByName Num.22 List.391 List.392; + if List.451 then + let List.457 : Str = CallByName List.66 List.388 List.391; + let List.452 : {List U8, U64} = CallByName List.137 List.389 List.457 List.390; + let List.455 : U64 = 1i64; + let List.454 : U64 = CallByName Num.19 List.391 List.455; + jump List.449 List.388 List.452 List.390 List.454 List.392; else - ret List.360; + ret List.389; in - jump List.420 List.445 List.446 List.447 List.448 List.449; + jump List.449 List.474 List.475 List.476 List.477 List.478; -procedure Num.123 (#Attr.2): - let Num.266 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.266; +procedure Num.125 (#Attr.2): + let Num.265 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.265; procedure Num.19 (#Attr.2, #Attr.3): - let Num.269 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.269; - -procedure Num.20 (#Attr.2, #Attr.3): - let Num.267 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.267; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.270 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.270; - -procedure Num.24 (#Attr.2, #Attr.3): - let Num.268 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + let Num.268 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.268; +procedure Num.20 (#Attr.2, #Attr.3): + let Num.266 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.266; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.269 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.269; + +procedure Num.24 (#Attr.2, #Attr.3): + let Num.267 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.267; + procedure Str.12 (#Attr.2): - let Str.266 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.266; + let Str.280 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.280; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.12 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index 0fba32c529..39d9458331 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -33,11 +33,11 @@ procedure Encode.23 (Encode.94, Encode.102, Encode.96): ret Encode.106; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.113 : List U8 = CallByName Json.127 Encode.94 Encode.96 Encode.102; + let Encode.113 : List U8 = CallByName Json.126 Encode.94 Encode.96 Encode.102; ret Encode.113; procedure Encode.23 (Encode.94, Encode.102, Encode.96): - let Encode.117 : List U8 = CallByName Json.97 Encode.94 Encode.96 Encode.102; + let Encode.117 : List U8 = CallByName Json.96 Encode.94 Encode.96 Encode.102; ret Encode.117; procedure Encode.25 (Encode.100, Encode.101): @@ -47,195 +47,195 @@ procedure Encode.25 (Encode.100, Encode.101): ret Encode.103; procedure Json.1 (): - let Json.396 : {} = Struct {}; - ret Json.396; + let Json.394 : {} = Struct {}; + ret Json.394; -procedure Json.127 (Json.128, Json.399, #Attr.12): - let Json.126 : List Str = StructAtIndex 1 #Attr.12; - inc Json.126; - let Json.125 : Str = StructAtIndex 0 #Attr.12; +procedure Json.126 (Json.127, Json.397, #Attr.12): + let Json.125 : List Str = StructAtIndex 1 #Attr.12; inc Json.125; + let Json.124 : Str = StructAtIndex 0 #Attr.12; + inc Json.124; dec #Attr.12; - let Json.437 : I32 = 123i64; - let Json.436 : U8 = CallByName Num.123 Json.437; - let Json.433 : List U8 = CallByName List.4 Json.128 Json.436; - let Json.435 : I32 = 34i64; - let Json.434 : U8 = CallByName Num.123 Json.435; - let Json.431 : List U8 = CallByName List.4 Json.433 Json.434; - let Json.432 : List U8 = CallByName Str.12 Json.125; - let Json.428 : List U8 = CallByName List.8 Json.431 Json.432; - let Json.430 : I32 = 34i64; - let Json.429 : U8 = CallByName Num.123 Json.430; - let Json.425 : List U8 = CallByName List.4 Json.428 Json.429; - let Json.427 : I32 = 58i64; - let Json.426 : U8 = CallByName Num.123 Json.427; - let Json.422 : List U8 = CallByName List.4 Json.425 Json.426; - let Json.424 : I32 = 91i64; - let Json.423 : U8 = CallByName Num.123 Json.424; - let Json.130 : List U8 = CallByName List.4 Json.422 Json.423; - let Json.421 : U64 = CallByName List.6 Json.126; - let Json.409 : {List U8, U64} = Struct {Json.130, Json.421}; - let Json.410 : {} = Struct {}; - let Json.408 : {List U8, U64} = CallByName List.18 Json.126 Json.409 Json.410; - dec Json.126; - let Json.132 : List U8 = StructAtIndex 0 Json.408; + let Json.435 : I64 = 123i64; + let Json.434 : U8 = CallByName Num.125 Json.435; + let Json.431 : List U8 = CallByName List.4 Json.127 Json.434; + let Json.433 : I64 = 34i64; + let Json.432 : U8 = CallByName Num.125 Json.433; + let Json.429 : List U8 = CallByName List.4 Json.431 Json.432; + let Json.430 : List U8 = CallByName Str.12 Json.124; + let Json.426 : List U8 = CallByName List.8 Json.429 Json.430; + let Json.428 : I64 = 34i64; + let Json.427 : U8 = CallByName Num.125 Json.428; + let Json.423 : List U8 = CallByName List.4 Json.426 Json.427; + let Json.425 : I64 = 58i64; + let Json.424 : U8 = CallByName Num.125 Json.425; + let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; + let Json.422 : I64 = 91i64; + let Json.421 : U8 = CallByName Num.125 Json.422; + let Json.129 : List U8 = CallByName List.4 Json.420 Json.421; + let Json.419 : U64 = CallByName List.6 Json.125; + let Json.407 : {List U8, U64} = Struct {Json.129, Json.419}; + let Json.408 : {} = Struct {}; + let Json.406 : {List U8, U64} = CallByName List.18 Json.125 Json.407 Json.408; + dec Json.125; + let Json.131 : List U8 = StructAtIndex 0 Json.406; + inc Json.131; + dec Json.406; + let Json.405 : I64 = 93i64; + let Json.404 : U8 = CallByName Num.125 Json.405; + let Json.401 : List U8 = CallByName List.4 Json.131 Json.404; + let Json.403 : I64 = 125i64; + let Json.402 : U8 = CallByName Num.125 Json.403; + let Json.400 : List U8 = CallByName List.4 Json.401 Json.402; + ret Json.400; + +procedure Json.128 (Json.399, Json.134): + let Json.132 : List U8 = StructAtIndex 0 Json.399; inc Json.132; - dec Json.408; - let Json.407 : I32 = 93i64; - let Json.406 : U8 = CallByName Num.123 Json.407; - let Json.403 : List U8 = CallByName List.4 Json.132 Json.406; - let Json.405 : I32 = 125i64; - let Json.404 : U8 = CallByName Num.123 Json.405; - let Json.402 : List U8 = CallByName List.4 Json.403 Json.404; - ret Json.402; - -procedure Json.129 (Json.401, Json.135): - let Json.133 : List U8 = StructAtIndex 0 Json.401; - inc Json.133; - let Json.134 : U64 = StructAtIndex 1 Json.401; - dec Json.401; - let Json.420 : {} = Struct {}; - let Json.136 : List U8 = CallByName Encode.23 Json.133 Json.135 Json.420; - joinpoint Json.415 Json.137: - let Json.413 : U64 = 1i64; - let Json.412 : U64 = CallByName Num.20 Json.134 Json.413; - let Json.411 : {List U8, U64} = Struct {Json.137, Json.412}; - ret Json.411; + let Json.133 : U64 = StructAtIndex 1 Json.399; + dec Json.399; + let Json.418 : {} = Struct {}; + let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.418; + joinpoint Json.413 Json.136: + let Json.411 : U64 = 1i64; + let Json.410 : U64 = CallByName Num.20 Json.133 Json.411; + let Json.409 : {List U8, U64} = Struct {Json.136, Json.410}; + ret Json.409; in - let Json.419 : U64 = 1i64; - let Json.416 : Int1 = CallByName Num.24 Json.134 Json.419; - if Json.416 then - let Json.418 : I32 = 44i64; - let Json.417 : U8 = CallByName Num.123 Json.418; - let Json.414 : List U8 = CallByName List.4 Json.136 Json.417; - jump Json.415 Json.414; + let Json.417 : U64 = 1i64; + let Json.414 : Int1 = CallByName Num.24 Json.133 Json.417; + if Json.414 then + let Json.416 : I64 = 44i64; + let Json.415 : U8 = CallByName Num.125 Json.416; + let Json.412 : List U8 = CallByName List.4 Json.135 Json.415; + jump Json.413 Json.412; else - jump Json.415 Json.136; + jump Json.413 Json.135; -procedure Json.18 (Json.96): - let Json.450 : Str = CallByName Encode.22 Json.96; - ret Json.450; +procedure Json.18 (Json.95): + let Json.448 : Str = CallByName Encode.22 Json.95; + ret Json.448; -procedure Json.21 (Json.125, Json.126): - let Json.398 : {Str, List Str} = Struct {Json.125, Json.126}; - let Json.397 : {Str, List Str} = CallByName Encode.22 Json.398; - ret Json.397; +procedure Json.21 (Json.124, Json.125): + let Json.396 : {Str, List Str} = Struct {Json.124, Json.125}; + let Json.395 : {Str, List Str} = CallByName Encode.22 Json.396; + ret Json.395; -procedure Json.97 (Json.98, Json.440, Json.96): - let Json.449 : I32 = 34i64; - let Json.448 : U8 = CallByName Num.123 Json.449; - let Json.446 : List U8 = CallByName List.4 Json.98 Json.448; - let Json.447 : List U8 = CallByName Str.12 Json.96; - let Json.443 : List U8 = CallByName List.8 Json.446 Json.447; - let Json.445 : I32 = 34i64; - let Json.444 : U8 = CallByName Num.123 Json.445; - let Json.442 : List U8 = CallByName List.4 Json.443 Json.444; - ret Json.442; +procedure Json.96 (Json.97, Json.438, Json.95): + let Json.447 : I64 = 34i64; + let Json.446 : U8 = CallByName Num.125 Json.447; + let Json.444 : List U8 = CallByName List.4 Json.97 Json.446; + let Json.445 : List U8 = CallByName Str.12 Json.95; + let Json.441 : List U8 = CallByName List.8 Json.444 Json.445; + let Json.443 : I64 = 34i64; + let Json.442 : U8 = CallByName Num.125 Json.443; + let Json.440 : List U8 = CallByName List.4 Json.441 Json.442; + ret Json.440; -procedure List.133 (List.134, List.135, List.132): - let List.432 : {List U8, U64} = CallByName Json.129 List.134 List.135; - ret List.432; +procedure List.137 (List.138, List.139, List.136): + let List.461 : {List U8, U64} = CallByName Json.128 List.138 List.139; + ret List.461; -procedure List.18 (List.130, List.131, List.132): - let List.414 : {List U8, U64} = CallByName List.75 List.130 List.131 List.132; - ret List.414; +procedure List.18 (List.134, List.135, List.136): + let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; + ret List.443; -procedure List.4 (List.101, List.102): - let List.413 : U64 = 1i64; - let List.412 : List U8 = CallByName List.70 List.101 List.413; - let List.411 : List U8 = CallByName List.71 List.412 List.102; - ret List.411; +procedure List.4 (List.105, List.106): + let List.442 : U64 = 1i64; + let List.441 : List U8 = CallByName List.70 List.105 List.442; + let List.440 : List U8 = CallByName List.71 List.441 List.106; + ret List.440; procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.433 : U64 = lowlevel ListLen #Attr.2; - ret List.433; + let List.462 : U64 = lowlevel ListLen #Attr.2; + ret List.462; procedure List.66 (#Attr.2, #Attr.3): - let List.429 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.429; + let List.458 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.458; procedure List.70 (#Attr.2, #Attr.3): - let List.386 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.386; + let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.415; procedure List.71 (#Attr.2, #Attr.3): - let List.384 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.384; - -procedure List.75 (List.356, List.357, List.358): - let List.418 : U64 = 0i64; - let List.419 : U64 = CallByName List.6 List.356; - let List.417 : {List U8, U64} = CallByName List.86 List.356 List.357 List.358 List.418 List.419; - ret List.417; + let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.413; procedure List.8 (#Attr.2, #Attr.3): - let List.435 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.435; + let List.464 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.464; -procedure List.86 (List.445, List.446, List.447, List.448, List.449): - joinpoint List.420 List.359 List.360 List.361 List.362 List.363: - let List.422 : Int1 = CallByName Num.22 List.362 List.363; - if List.422 then - let List.428 : Str = CallByName List.66 List.359 List.362; - let List.423 : {List U8, U64} = CallByName List.133 List.360 List.428 List.361; - let List.426 : U64 = 1i64; - let List.425 : U64 = CallByName Num.19 List.362 List.426; - jump List.420 List.359 List.423 List.361 List.425 List.363; +procedure List.89 (List.385, List.386, List.387): + let List.447 : U64 = 0i64; + let List.448 : U64 = CallByName List.6 List.385; + let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448; + ret List.446; + +procedure List.90 (List.474, List.475, List.476, List.477, List.478): + joinpoint List.449 List.388 List.389 List.390 List.391 List.392: + let List.451 : Int1 = CallByName Num.22 List.391 List.392; + if List.451 then + let List.457 : Str = CallByName List.66 List.388 List.391; + let List.452 : {List U8, U64} = CallByName List.137 List.389 List.457 List.390; + let List.455 : U64 = 1i64; + let List.454 : U64 = CallByName Num.19 List.391 List.455; + jump List.449 List.388 List.452 List.390 List.454 List.392; else - ret List.360; + ret List.389; in - jump List.420 List.445 List.446 List.447 List.448 List.449; + jump List.449 List.474 List.475 List.476 List.477 List.478; -procedure Num.123 (#Attr.2): - let Num.266 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.266; +procedure Num.125 (#Attr.2): + let Num.265 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.265; procedure Num.19 (#Attr.2, #Attr.3): - let Num.269 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.269; - -procedure Num.20 (#Attr.2, #Attr.3): - let Num.267 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.267; - -procedure Num.22 (#Attr.2, #Attr.3): - let Num.270 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.270; - -procedure Num.24 (#Attr.2, #Attr.3): - let Num.268 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + let Num.268 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; ret Num.268; +procedure Num.20 (#Attr.2, #Attr.3): + let Num.266 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.266; + +procedure Num.22 (#Attr.2, #Attr.3): + let Num.269 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.269; + +procedure Num.24 (#Attr.2, #Attr.3): + let Num.267 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.267; + procedure Str.12 (#Attr.2): - let Str.266 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.266; + let Str.280 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.280; procedure Str.48 (#Attr.2, #Attr.3, #Attr.4): - let Str.260 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; - ret Str.260; + let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4; + ret Str.274; -procedure Str.9 (Str.73): - let Str.258 : U64 = 0i64; - let Str.259 : U64 = CallByName List.6 Str.73; - let Str.74 : {U64, Str, Int1, U8} = CallByName Str.48 Str.73 Str.258 Str.259; - let Str.255 : Int1 = StructAtIndex 2 Str.74; - if Str.255 then - let Str.257 : Str = StructAtIndex 1 Str.74; - inc Str.257; - dec Str.74; - let Str.256 : [C {U64, U8}, C Str] = TagId(1) Str.257; - ret Str.256; +procedure Str.9 (Str.76): + let Str.272 : U64 = 0i64; + let Str.273 : U64 = CallByName List.6 Str.76; + let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273; + let Str.269 : Int1 = StructAtIndex 2 Str.77; + if Str.269 then + let Str.271 : Str = StructAtIndex 1 Str.77; + inc Str.271; + dec Str.77; + let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271; + ret Str.270; else - let Str.253 : U8 = StructAtIndex 3 Str.74; - let Str.254 : U64 = StructAtIndex 0 Str.74; - dec Str.74; - let Str.252 : {U64, U8} = Struct {Str.254, Str.253}; - let Str.251 : [C {U64, U8}, C Str] = TagId(0) Str.252; - ret Str.251; + let Str.267 : U8 = StructAtIndex 3 Str.77; + let Str.268 : U64 = StructAtIndex 0 Str.77; + dec Str.77; + let Str.266 : {U64, U8} = Struct {Str.268, Str.267}; + let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266; + ret Str.265; procedure Test.0 (): let Test.13 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/factorial.txt b/crates/compiler/test_mono/generated/factorial.txt index 9aee646f72..b0319aa20a 100644 --- a/crates/compiler/test_mono/generated/factorial.txt +++ b/crates/compiler/test_mono/generated/factorial.txt @@ -1,10 +1,10 @@ procedure Num.20 (#Attr.2, #Attr.3): - let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.257; procedure Num.21 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.15, Test.16): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/guard_pattern_true.txt b/crates/compiler/test_mono/generated/guard_pattern_true.txt index f535579130..dfded1801e 100644 --- a/crates/compiler/test_mono/generated/guard_pattern_true.txt +++ b/crates/compiler/test_mono/generated/guard_pattern_true.txt @@ -1,6 +1,6 @@ procedure Bool.1 (): - let Bool.11 : Int1 = false; - ret Bool.11; + let Bool.23 : Int1 = false; + ret Bool.23; procedure Test.1 (Test.2): let Test.5 : I64 = 2i64; diff --git a/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt b/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt index 1565f25f88..b8a0dde808 100644 --- a/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt +++ b/crates/compiler/test_mono/generated/if_guard_bind_variable_false.txt @@ -1,6 +1,6 @@ -procedure Bool.7 (#Attr.2, #Attr.3): - let Bool.11 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.11; +procedure Bool.11 (#Attr.2, #Attr.3): + let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.23; procedure Test.1 (Test.3): let Test.6 : I64 = 10i64; @@ -13,7 +13,7 @@ procedure Test.1 (Test.3): ret Test.11; in let Test.10 : I64 = 5i64; - let Test.9 : Int1 = CallByName Bool.7 Test.6 Test.10; + let Test.9 : Int1 = CallByName Bool.11 Test.6 Test.10; jump Test.8 Test.9; procedure Test.0 (): diff --git a/crates/compiler/test_mono/generated/if_multi_branch.txt b/crates/compiler/test_mono/generated/if_multi_branch.txt index 36dddc2fe9..3b9a81546f 100644 --- a/crates/compiler/test_mono/generated/if_multi_branch.txt +++ b/crates/compiler/test_mono/generated/if_multi_branch.txt @@ -1,10 +1,10 @@ procedure Bool.1 (): - let Bool.11 : Int1 = false; - ret Bool.11; + let Bool.23 : Int1 = false; + ret Bool.23; procedure Bool.2 (): - let Bool.12 : Int1 = true; - ret Bool.12; + let Bool.24 : Int1 = true; + ret Bool.24; procedure Test.0 (): let Test.4 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index d8abd2d336..ca23069c99 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,10 +1,10 @@ procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure Num.19 (#Attr.2, #Attr.3): - let Num.259 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.258; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64]; diff --git a/crates/compiler/test_mono/generated/ir_plus.txt b/crates/compiler/test_mono/generated/ir_plus.txt index 2bbd4d95d2..2994403a5a 100644 --- a/crates/compiler/test_mono/generated/ir_plus.txt +++ b/crates/compiler/test_mono/generated/ir_plus.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.2 : I64 = 1i64; diff --git a/crates/compiler/test_mono/generated/ir_round.txt b/crates/compiler/test_mono/generated/ir_round.txt index 69029d87c2..0ba65b4515 100644 --- a/crates/compiler/test_mono/generated/ir_round.txt +++ b/crates/compiler/test_mono/generated/ir_round.txt @@ -1,6 +1,6 @@ procedure Num.45 (#Attr.2): - let Num.257 : I64 = lowlevel NumRound #Attr.2; - ret Num.257; + let Num.256 : I64 = lowlevel NumRound #Attr.2; + ret Num.256; procedure Test.0 (): let Test.2 : Float64 = 3.6f64; diff --git a/crates/compiler/test_mono/generated/ir_two_defs.txt b/crates/compiler/test_mono/generated/ir_two_defs.txt index f54ffd118b..aa2e631fbb 100644 --- a/crates/compiler/test_mono/generated/ir_two_defs.txt +++ b/crates/compiler/test_mono/generated/ir_two_defs.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.1 : I64 = 3i64; diff --git a/crates/compiler/test_mono/generated/ir_when_idiv.txt b/crates/compiler/test_mono/generated/ir_when_idiv.txt index 6ab0065bc7..5bd2abf771 100644 --- a/crates/compiler/test_mono/generated/ir_when_idiv.txt +++ b/crates/compiler/test_mono/generated/ir_when_idiv.txt @@ -1,22 +1,22 @@ -procedure Bool.7 (#Attr.2, #Attr.3): - let Bool.11 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.11; +procedure Num.30 (#Attr.2): + let Num.263 : I64 = 0i64; + let Num.262 : Int1 = lowlevel Eq #Attr.2 Num.263; + ret Num.262; procedure Num.39 (#Attr.2, #Attr.3): - let Num.259 : I64 = lowlevel NumDivTruncUnchecked #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : I64 = lowlevel NumDivTruncUnchecked #Attr.2 #Attr.3; + ret Num.258; -procedure Num.40 (Num.229, Num.230): - let Num.263 : I64 = 0i64; - let Num.260 : Int1 = CallByName Bool.7 Num.230 Num.263; - if Num.260 then - let Num.262 : {} = Struct {}; - let Num.261 : [C {}, C I64] = TagId(0) Num.262; - ret Num.261; +procedure Num.40 (Num.228, Num.229): + let Num.259 : Int1 = CallByName Num.30 Num.229; + if Num.259 then + let Num.261 : {} = Struct {}; + let Num.260 : [C {}, C I64] = TagId(0) Num.261; + ret Num.260; else - let Num.258 : I64 = CallByName Num.39 Num.229 Num.230; - let Num.257 : [C {}, C I64] = TagId(1) Num.258; - ret Num.257; + let Num.257 : I64 = CallByName Num.39 Num.228 Num.229; + let Num.256 : [C {}, C I64] = TagId(1) Num.257; + ret Num.256; procedure Test.0 (): let Test.8 : I64 = 1000i64; diff --git a/crates/compiler/test_mono/generated/ir_when_just.txt b/crates/compiler/test_mono/generated/ir_when_just.txt index 8ceef4e3e4..95413986b7 100644 --- a/crates/compiler/test_mono/generated/ir_when_just.txt +++ b/crates/compiler/test_mono/generated/ir_when_just.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.10 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/is_nil.txt b/crates/compiler/test_mono/generated/is_nil.txt index aaea58f73a..e130f31ad5 100644 --- a/crates/compiler/test_mono/generated/is_nil.txt +++ b/crates/compiler/test_mono/generated/is_nil.txt @@ -1,10 +1,10 @@ procedure Bool.1 (): - let Bool.12 : Int1 = false; - ret Bool.12; + let Bool.24 : Int1 = false; + ret Bool.24; procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; procedure Test.2 (Test.4): let Test.11 : U8 = 1i64; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index 4dcb37efbc..132b5e6fa0 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -1,71 +1,71 @@ +procedure Bool.11 (#Attr.2, #Attr.3): + let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.24; + procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; -procedure Bool.7 (#Attr.2, #Attr.3): - let Bool.12 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.12; - -procedure List.2 (List.90, List.91): - let List.394 : U64 = CallByName List.6 List.90; - let List.390 : Int1 = CallByName Num.22 List.91 List.394; - if List.390 then - let List.392 : I64 = CallByName List.66 List.90 List.91; - let List.391 : [C {}, C I64] = TagId(1) List.392; - ret List.391; +procedure List.2 (List.94, List.95): + let List.423 : U64 = CallByName List.6 List.94; + let List.419 : Int1 = CallByName Num.22 List.95 List.423; + if List.419 then + let List.421 : I64 = CallByName List.66 List.94 List.95; + let List.420 : [C {}, C I64] = TagId(1) List.421; + ret List.420; else - let List.389 : {} = Struct {}; - let List.388 : [C {}, C I64] = TagId(0) List.389; - ret List.388; + let List.418 : {} = Struct {}; + let List.417 : [C {}, C I64] = TagId(0) List.418; + ret List.417; procedure List.6 (#Attr.2): - let List.395 : U64 = lowlevel ListLen #Attr.2; - ret List.395; + let List.424 : U64 = lowlevel ListLen #Attr.2; + ret List.424; procedure List.66 (#Attr.2, #Attr.3): - let List.393 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.393; + let List.422 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.422; -procedure List.9 (List.213): - let List.387 : U64 = 0i64; - let List.380 : [C {}, C I64] = CallByName List.2 List.213 List.387; - let List.384 : U8 = 1i64; - let List.385 : U8 = GetTagId List.380; - let List.386 : Int1 = lowlevel Eq List.384 List.385; - if List.386 then - let List.214 : I64 = UnionAtIndex (Id 1) (Index 0) List.380; - let List.381 : [C Int1, C I64] = TagId(1) List.214; - ret List.381; +procedure List.9 (List.242): + let List.416 : U64 = 0i64; + let List.409 : [C {}, C I64] = CallByName List.2 List.242 List.416; + let List.413 : U8 = 1i64; + let List.414 : U8 = GetTagId List.409; + let List.415 : Int1 = lowlevel Eq List.413 List.414; + if List.415 then + let List.243 : I64 = UnionAtIndex (Id 1) (Index 0) List.409; + let List.410 : [C Int1, C I64] = TagId(1) List.243; + ret List.410; else - let List.383 : Int1 = true; - let List.382 : [C Int1, C I64] = TagId(0) List.383; - ret List.382; + let List.412 : Int1 = true; + let List.411 : [C Int1, C I64] = TagId(0) List.412; + ret List.411; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; -procedure Str.27 (Str.93): - let Str.251 : [C Int1, C I64] = CallByName Str.66 Str.93; - ret Str.251; +procedure Str.27 (Str.96): + let Str.265 : [C Int1, C I64] = CallByName Str.69 Str.96; + ret Str.265; procedure Str.47 (#Attr.2): - let Str.259 : {I64, U8} = lowlevel StrToNum #Attr.2; - ret Str.259; + let Str.273 : {I64, U8} = lowlevel StrToNum #Attr.2; + ret Str.273; -procedure Str.66 (Str.222): - let Str.223 : {I64, U8} = CallByName Str.47 Str.222; - let Str.257 : U8 = StructAtIndex 1 Str.223; - let Str.258 : U8 = 0i64; - let Str.254 : Int1 = CallByName Bool.7 Str.257 Str.258; - if Str.254 then - let Str.256 : I64 = StructAtIndex 0 Str.223; - let Str.255 : [C Int1, C I64] = TagId(1) Str.256; - ret Str.255; +procedure Str.69 (Str.231): + let Str.232 : {I64, U8} = CallByName Str.47 Str.231; + let Str.271 : U8 = StructAtIndex 1 Str.232; + let Str.272 : U8 = 0i64; + let Str.268 : Int1 = CallByName Bool.11 Str.271 Str.272; + if Str.268 then + let Str.270 : I64 = StructAtIndex 0 Str.232; + let Str.269 : [C Int1, C I64] = TagId(1) Str.270; + ret Str.269; else - let Str.253 : Int1 = false; - let Str.252 : [C Int1, C I64] = TagId(0) Str.253; - ret Str.252; + let Str.267 : Int1 = false; + let Str.266 : [C Int1, C I64] = TagId(0) Str.267; + ret Str.266; procedure Test.0 (): let Test.3 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/issue_3669.txt b/crates/compiler/test_mono/generated/issue_3669.txt index 11d9485149..3f50a60607 100644 --- a/crates/compiler/test_mono/generated/issue_3669.txt +++ b/crates/compiler/test_mono/generated/issue_3669.txt @@ -1,6 +1,6 @@ -procedure Bool.7 (#Attr.2, #Attr.3): - let Bool.11 : Int1 = lowlevel Eq #Attr.2 #Attr.3; - ret Bool.11; +procedure Bool.11 (#Attr.2, #Attr.3): + let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; + ret Bool.23; procedure Test.2 (Test.19): joinpoint Test.13 Test.7: @@ -21,6 +21,6 @@ procedure Test.0 (): let Test.10 : {} = CallByName Test.2 Test.12; dec Test.12; let Test.11 : {} = Struct {}; - let Test.8 : Int1 = CallByName Bool.7 Test.10 Test.11; + let Test.8 : Int1 = CallByName Bool.11 Test.10 Test.11; let Test.9 : Str = ""; ret Test.9; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt index b9c8c3a9f4..351d5255f1 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt @@ -1,11 +1,11 @@ +procedure Num.94 (#Attr.2): + let Num.256 : Str = lowlevel NumToStr #Attr.2; + ret Num.256; + procedure Num.94 (#Attr.2): let Num.257 : Str = lowlevel NumToStr #Attr.2; ret Num.257; -procedure Num.94 (#Attr.2): - let Num.258 : Str = lowlevel NumToStr #Attr.2; - ret Num.258; - procedure Test.1 (Test.4): let Test.16 : [C U8, C U64] = TagId(1) Test.4; ret Test.16; diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index 4ec9e800f3..224cea70dd 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.101, List.102): - let List.383 : U64 = 1i64; - let List.381 : List I64 = CallByName List.70 List.101 List.383; - let List.380 : List I64 = CallByName List.71 List.381 List.102; - ret List.380; +procedure List.4 (List.105, List.106): + let List.412 : U64 = 1i64; + let List.410 : List I64 = CallByName List.70 List.105 List.412; + let List.409 : List I64 = CallByName List.71 List.410 List.106; + ret List.409; procedure List.70 (#Attr.2, #Attr.3): - let List.384 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.384; + let List.413 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.413; procedure List.71 (#Attr.2, #Attr.3): - let List.382 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.382; + let List.411 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.411; procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index 66396fabc0..2f02f8b4e4 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.101, List.102): - let List.383 : U64 = 1i64; - let List.381 : List I64 = CallByName List.70 List.101 List.383; - let List.380 : List I64 = CallByName List.71 List.381 List.102; - ret List.380; +procedure List.4 (List.105, List.106): + let List.412 : U64 = 1i64; + let List.410 : List I64 = CallByName List.70 List.105 List.412; + let List.409 : List I64 = CallByName List.71 List.410 List.106; + ret List.409; procedure List.70 (#Attr.2, #Attr.3): - let List.384 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.384; + let List.413 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.413; procedure List.71 (#Attr.2, #Attr.3): - let List.382 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.382; + let List.411 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.411; procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index d8f76d3ba2..638d364d74 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,35 +1,35 @@ -procedure List.3 (List.98, List.99, List.100): - let List.383 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100; - let List.382 : List I64 = StructAtIndex 0 List.383; - inc List.382; - dec List.383; - ret List.382; +procedure List.3 (List.102, List.103, List.104): + let List.412 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; + let List.411 : List I64 = StructAtIndex 0 List.412; + inc List.411; + dec List.412; + ret List.411; procedure List.6 (#Attr.2): - let List.381 : U64 = lowlevel ListLen #Attr.2; - ret List.381; + let List.410 : U64 = lowlevel ListLen #Attr.2; + ret List.410; -procedure List.64 (List.95, List.96, List.97): - let List.388 : U64 = CallByName List.6 List.95; - let List.385 : Int1 = CallByName Num.22 List.96 List.388; - if List.385 then - let List.386 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97; - ret List.386; +procedure List.64 (List.99, List.100, List.101): + let List.417 : U64 = CallByName List.6 List.99; + let List.414 : Int1 = CallByName Num.22 List.100 List.417; + if List.414 then + let List.415 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; + ret List.415; else - let List.384 : {List I64, I64} = Struct {List.95, List.97}; - ret List.384; + let List.413 : {List I64, I64} = Struct {List.99, List.101}; + ret List.413; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.387 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.387; + let List.416 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.416; procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Num.22 (#Attr.2, #Attr.3): - let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.257; procedure Test.1 (): let Test.8 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index db3e811cb3..53734e6aa0 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -1,26 +1,26 @@ -procedure List.2 (List.90, List.91): - let List.386 : U64 = CallByName List.6 List.90; - let List.382 : Int1 = CallByName Num.22 List.91 List.386; - if List.382 then - let List.384 : I64 = CallByName List.66 List.90 List.91; - let List.383 : [C {}, C I64] = TagId(1) List.384; - ret List.383; +procedure List.2 (List.94, List.95): + let List.415 : U64 = CallByName List.6 List.94; + let List.411 : Int1 = CallByName Num.22 List.95 List.415; + if List.411 then + let List.413 : I64 = CallByName List.66 List.94 List.95; + let List.412 : [C {}, C I64] = TagId(1) List.413; + ret List.412; else - let List.381 : {} = Struct {}; - let List.380 : [C {}, C I64] = TagId(0) List.381; - ret List.380; + let List.410 : {} = Struct {}; + let List.409 : [C {}, C I64] = TagId(0) List.410; + ret List.409; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; procedure List.66 (#Attr.2, #Attr.3): - let List.385 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.385; + let List.414 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.2): let Test.6 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index 75148df945..ba25fbc1f1 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,14 +1,14 @@ procedure List.6 (#Attr.2): - let List.380 : U64 = lowlevel ListLen #Attr.2; - ret List.380; + let List.409 : U64 = lowlevel ListLen #Attr.2; + ret List.409; procedure List.6 (#Attr.2): - let List.381 : U64 = lowlevel ListLen #Attr.2; - ret List.381; + let List.410 : U64 = lowlevel ListLen #Attr.2; + ret List.410; procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 102f75b8cb..ca1cabd824 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,38 +1,38 @@ -procedure List.2 (List.90, List.91): - let List.386 : U64 = CallByName List.6 List.90; - let List.382 : Int1 = CallByName Num.22 List.91 List.386; - if List.382 then - let List.384 : Str = CallByName List.66 List.90 List.91; - let List.383 : [C {}, C Str] = TagId(1) List.384; - ret List.383; +procedure List.2 (List.94, List.95): + let List.415 : U64 = CallByName List.6 List.94; + let List.411 : Int1 = CallByName Num.22 List.95 List.415; + if List.411 then + let List.413 : Str = CallByName List.66 List.94 List.95; + let List.412 : [C {}, C Str] = TagId(1) List.413; + ret List.412; else - let List.381 : {} = Struct {}; - let List.380 : [C {}, C Str] = TagId(0) List.381; - ret List.380; + let List.410 : {} = Struct {}; + let List.409 : [C {}, C Str] = TagId(0) List.410; + ret List.409; procedure List.5 (#Attr.2, #Attr.3): - let List.388 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; - ret List.388; + let List.417 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + ret List.417; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; procedure List.66 (#Attr.2, #Attr.3): - let List.385 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.385; + let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; procedure Str.16 (#Attr.2, #Attr.3): - let Str.251 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; - ret Str.251; + let Str.265 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; + ret Str.265; procedure Str.3 (#Attr.2, #Attr.3): - let Str.252 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.252; + let Str.266 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.266; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index 85c4902acb..dd79d46b04 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,36 +1,35 @@ -procedure List.2 (List.90, List.91): - let List.386 : U64 = CallByName List.6 List.90; - let List.382 : Int1 = CallByName Num.22 List.91 List.386; - if List.382 then - let List.384 : Str = CallByName List.66 List.90 List.91; - let List.383 : [C {}, C Str] = TagId(1) List.384; - ret List.383; +procedure List.2 (List.94, List.95): + let List.415 : U64 = CallByName List.6 List.94; + let List.411 : Int1 = CallByName Num.22 List.95 List.415; + if List.411 then + let List.413 : Str = CallByName List.66 List.94 List.95; + let List.412 : [C {}, C Str] = TagId(1) List.413; + ret List.412; else - let List.381 : {} = Struct {}; - let List.380 : [C {}, C Str] = TagId(0) List.381; - ret List.380; + let List.410 : {} = Struct {}; + let List.409 : [C {}, C Str] = TagId(0) List.410; + ret List.409; procedure List.5 (#Attr.2, #Attr.3): - inc #Attr.2; - let List.388 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + let List.417 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; decref #Attr.2; - ret List.388; + ret List.417; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; procedure List.66 (#Attr.2, #Attr.3): - let List.385 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.385; + let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; procedure Str.3 (#Attr.2, #Attr.3): - let Str.252 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.252; + let Str.266 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.266; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; @@ -44,7 +43,6 @@ procedure Test.2 (): let Test.15 : List Str = CallByName Test.1; let Test.16 : {} = Struct {}; let Test.14 : List Str = CallByName List.5 Test.15 Test.16; - dec Test.15; ret Test.14; procedure Test.3 (Test.4): diff --git a/crates/compiler/test_mono/generated/list_pass_to_function.txt b/crates/compiler/test_mono/generated/list_pass_to_function.txt index 97947c1243..4bdc8393cf 100644 --- a/crates/compiler/test_mono/generated/list_pass_to_function.txt +++ b/crates/compiler/test_mono/generated/list_pass_to_function.txt @@ -1,31 +1,31 @@ -procedure List.3 (List.98, List.99, List.100): - let List.381 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100; - let List.380 : List I64 = StructAtIndex 0 List.381; - inc List.380; - dec List.381; - ret List.380; +procedure List.3 (List.102, List.103, List.104): + let List.410 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; + let List.409 : List I64 = StructAtIndex 0 List.410; + inc List.409; + dec List.410; + ret List.409; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; -procedure List.64 (List.95, List.96, List.97): - let List.386 : U64 = CallByName List.6 List.95; - let List.383 : Int1 = CallByName Num.22 List.96 List.386; - if List.383 then - let List.384 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97; - ret List.384; +procedure List.64 (List.99, List.100, List.101): + let List.415 : U64 = CallByName List.6 List.99; + let List.412 : Int1 = CallByName Num.22 List.100 List.415; + if List.412 then + let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; + ret List.413; else - let List.382 : {List I64, I64} = Struct {List.95, List.97}; - ret List.382; + let List.411 : {List I64, I64} = Struct {List.99, List.101}; + ret List.411; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.385 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.385; + let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.256; procedure Test.2 (Test.3): let Test.6 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index e3bee9c60c..a375c93ae5 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,20 +1,20 @@ procedure List.28 (#Attr.2, #Attr.3): - let List.382 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; + let List.411 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2; if #Derived_gen.0 then - ret List.382; + ret List.411; else decref #Attr.2; - ret List.382; + ret List.411; -procedure List.59 (List.208): - let List.381 : {} = Struct {}; - let List.380 : List I64 = CallByName List.28 List.208 List.381; - ret List.380; +procedure List.59 (List.237): + let List.410 : {} = Struct {}; + let List.409 : List I64 = CallByName List.28 List.237 List.410; + ret List.409; procedure Num.46 (#Attr.2, #Attr.3): - let Num.257 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64]; diff --git a/crates/compiler/test_mono/generated/match_list.txt b/crates/compiler/test_mono/generated/match_list.txt new file mode 100644 index 0000000000..3d5dd6dc6c --- /dev/null +++ b/crates/compiler/test_mono/generated/match_list.txt @@ -0,0 +1,67 @@ +procedure Test.0 (): + let Test.36 : Int1 = false; + let Test.37 : Int1 = true; + let Test.1 : List Int1 = Array [Test.36, Test.37]; + joinpoint Test.10: + let Test.8 : Str = "E"; + ret Test.8; + in + joinpoint Test.9: + let Test.5 : Str = "B"; + ret Test.5; + in + let Test.33 : U64 = lowlevel ListLen Test.1; + let Test.34 : U64 = 0i64; + let Test.35 : Int1 = lowlevel Eq Test.33 Test.34; + if Test.35 then + dec Test.1; + let Test.4 : Str = "A"; + ret Test.4; + else + let Test.30 : U64 = lowlevel ListLen Test.1; + let Test.31 : U64 = 1i64; + let Test.32 : Int1 = lowlevel Eq Test.30 Test.31; + if Test.32 then + let Test.11 : U64 = 0i64; + let Test.12 : Int1 = lowlevel ListGetUnsafe Test.1 Test.11; + dec Test.1; + let Test.13 : Int1 = false; + let Test.14 : Int1 = lowlevel Eq Test.13 Test.12; + if Test.14 then + jump Test.9; + else + jump Test.10; + else + let Test.27 : U64 = lowlevel ListLen Test.1; + let Test.28 : U64 = 2i64; + let Test.29 : Int1 = lowlevel NumGte Test.27 Test.28; + if Test.29 then + let Test.19 : U64 = 0i64; + let Test.20 : Int1 = lowlevel ListGetUnsafe Test.1 Test.19; + let Test.21 : Int1 = false; + let Test.22 : Int1 = lowlevel Eq Test.21 Test.20; + if Test.22 then + let Test.15 : U64 = 1i64; + let Test.16 : Int1 = lowlevel ListGetUnsafe Test.1 Test.15; + dec Test.1; + let Test.17 : Int1 = false; + let Test.18 : Int1 = lowlevel Eq Test.17 Test.16; + if Test.18 then + let Test.6 : Str = "C"; + ret Test.6; + else + let Test.7 : Str = "D"; + ret Test.7; + else + dec Test.1; + jump Test.10; + else + let Test.23 : U64 = 0i64; + let Test.24 : Int1 = lowlevel ListGetUnsafe Test.1 Test.23; + dec Test.1; + let Test.25 : Int1 = false; + let Test.26 : Int1 = lowlevel Eq Test.25 Test.24; + if Test.26 then + jump Test.9; + else + jump Test.10; diff --git a/crates/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt b/crates/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt index fe89b5144c..490a3c5643 100644 --- a/crates/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt +++ b/crates/compiler/test_mono/generated/monomorphized_tag_with_aliased_args.txt @@ -1,6 +1,6 @@ procedure Bool.1 (): - let Bool.12 : Int1 = false; - ret Bool.12; + let Bool.24 : Int1 = false; + ret Bool.24; procedure Test.4 (Test.6): let Test.8 : U64 = 1i64; diff --git a/crates/compiler/test_mono/generated/nested_pattern_match.txt b/crates/compiler/test_mono/generated/nested_pattern_match.txt index e8e714e59e..4b2af8c406 100644 --- a/crates/compiler/test_mono/generated/nested_pattern_match.txt +++ b/crates/compiler/test_mono/generated/nested_pattern_match.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt b/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt index 1925eaca63..e5c7df8fe3 100644 --- a/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt +++ b/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt @@ -1,6 +1,6 @@ procedure Num.37 (#Attr.2, #Attr.3): - let Num.257 : Float64 = lowlevel NumDivFrac #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : Float64 = lowlevel NumDivFrac #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.2 : Float64 = 1f64; diff --git a/crates/compiler/test_mono/generated/optional_when.txt b/crates/compiler/test_mono/generated/optional_when.txt index 1da4dd989c..28667ba1d6 100644 --- a/crates/compiler/test_mono/generated/optional_when.txt +++ b/crates/compiler/test_mono/generated/optional_when.txt @@ -1,6 +1,6 @@ procedure Num.21 (#Attr.2, #Attr.3): - let Num.259 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.258; procedure Test.1 (Test.6): let Test.21 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/quicksort_help.txt b/crates/compiler/test_mono/generated/quicksort_help.txt index 884a9adbad..b8a6ea2e5d 100644 --- a/crates/compiler/test_mono/generated/quicksort_help.txt +++ b/crates/compiler/test_mono/generated/quicksort_help.txt @@ -1,14 +1,14 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Num.20 (#Attr.2, #Attr.3): - let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.257; procedure Num.22 (#Attr.2, #Attr.3): - let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.258; procedure Test.1 (Test.24, Test.25, Test.26): joinpoint Test.12 Test.2 Test.3 Test.4: diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index ee01276f67..7714860bc7 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,47 +1,47 @@ -procedure List.2 (List.90, List.91): - let List.402 : U64 = CallByName List.6 List.90; - let List.399 : Int1 = CallByName Num.22 List.91 List.402; - if List.399 then - let List.401 : I64 = CallByName List.66 List.90 List.91; - let List.400 : [C {}, C I64] = TagId(1) List.401; - ret List.400; +procedure List.2 (List.94, List.95): + let List.431 : U64 = CallByName List.6 List.94; + let List.428 : Int1 = CallByName Num.22 List.95 List.431; + if List.428 then + let List.430 : I64 = CallByName List.66 List.94 List.95; + let List.429 : [C {}, C I64] = TagId(1) List.430; + ret List.429; else - let List.398 : {} = Struct {}; - let List.397 : [C {}, C I64] = TagId(0) List.398; - ret List.397; + let List.427 : {} = Struct {}; + let List.426 : [C {}, C I64] = TagId(0) List.427; + ret List.426; -procedure List.3 (List.98, List.99, List.100): - let List.389 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100; - let List.388 : List I64 = StructAtIndex 0 List.389; - inc List.388; - dec List.389; - ret List.388; +procedure List.3 (List.102, List.103, List.104): + let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; + let List.417 : List I64 = StructAtIndex 0 List.418; + inc List.417; + dec List.418; + ret List.417; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; -procedure List.64 (List.95, List.96, List.97): - let List.386 : U64 = CallByName List.6 List.95; - let List.383 : Int1 = CallByName Num.22 List.96 List.386; - if List.383 then - let List.384 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97; - ret List.384; +procedure List.64 (List.99, List.100, List.101): + let List.415 : U64 = CallByName List.6 List.99; + let List.412 : Int1 = CallByName Num.22 List.100 List.415; + if List.412 then + let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; + ret List.413; else - let List.382 : {List I64, I64} = Struct {List.95, List.97}; - ret List.382; + let List.411 : {List I64, I64} = Struct {List.99, List.101}; + ret List.411; procedure List.66 (#Attr.2, #Attr.3): - let List.395 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.395; + let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.424; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.385 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.385; + let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.258; procedure Test.1 (Test.2): let Test.28 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt index 988b0cf432..2f7c2c8b22 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.4): let Test.2 : I64 = StructAtIndex 0 Test.4; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt index 356f58f136..49267ae4b1 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.4): let Test.2 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt index 2abaf868ba..21ebd109c8 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.2): let Test.3 : I64 = StructAtIndex 0 Test.2; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt index b70eba153b..c960fd1fb2 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.2): let Test.3 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt index 1d0e02c71f..b969b12528 100644 --- a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt +++ b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt @@ -1,10 +1,10 @@ procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : U32 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : U32 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.2): let Test.8 : U32 = 0i64; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index 3026b305c0..71b3ba4340 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,47 +1,47 @@ -procedure List.2 (List.90, List.91): - let List.402 : U64 = CallByName List.6 List.90; - let List.399 : Int1 = CallByName Num.22 List.91 List.402; - if List.399 then - let List.401 : I64 = CallByName List.66 List.90 List.91; - let List.400 : [C {}, C I64] = TagId(1) List.401; - ret List.400; +procedure List.2 (List.94, List.95): + let List.431 : U64 = CallByName List.6 List.94; + let List.428 : Int1 = CallByName Num.22 List.95 List.431; + if List.428 then + let List.430 : I64 = CallByName List.66 List.94 List.95; + let List.429 : [C {}, C I64] = TagId(1) List.430; + ret List.429; else - let List.398 : {} = Struct {}; - let List.397 : [C {}, C I64] = TagId(0) List.398; - ret List.397; + let List.427 : {} = Struct {}; + let List.426 : [C {}, C I64] = TagId(0) List.427; + ret List.426; -procedure List.3 (List.98, List.99, List.100): - let List.389 : {List I64, I64} = CallByName List.64 List.98 List.99 List.100; - let List.388 : List I64 = StructAtIndex 0 List.389; - inc List.388; - dec List.389; - ret List.388; +procedure List.3 (List.102, List.103, List.104): + let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; + let List.417 : List I64 = StructAtIndex 0 List.418; + inc List.417; + dec List.418; + ret List.417; procedure List.6 (#Attr.2): - let List.387 : U64 = lowlevel ListLen #Attr.2; - ret List.387; + let List.416 : U64 = lowlevel ListLen #Attr.2; + ret List.416; -procedure List.64 (List.95, List.96, List.97): - let List.386 : U64 = CallByName List.6 List.95; - let List.383 : Int1 = CallByName Num.22 List.96 List.386; - if List.383 then - let List.384 : {List I64, I64} = CallByName List.67 List.95 List.96 List.97; - ret List.384; +procedure List.64 (List.99, List.100, List.101): + let List.415 : U64 = CallByName List.6 List.99; + let List.412 : Int1 = CallByName Num.22 List.100 List.415; + if List.412 then + let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; + ret List.413; else - let List.382 : {List I64, I64} = Struct {List.95, List.97}; - ret List.382; + let List.411 : {List I64, I64} = Struct {List.99, List.101}; + ret List.411; procedure List.66 (#Attr.2, #Attr.3): - let List.395 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.395; + let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.424; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.385 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.385; + let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.414; procedure Num.22 (#Attr.2, #Attr.3): - let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.259; + let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.258; procedure Test.1 (Test.2, Test.3, Test.4): let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3; diff --git a/crates/compiler/test_mono/generated/simple_if.txt b/crates/compiler/test_mono/generated/simple_if.txt index 0ffc888c09..42fbbf320f 100644 --- a/crates/compiler/test_mono/generated/simple_if.txt +++ b/crates/compiler/test_mono/generated/simple_if.txt @@ -1,6 +1,6 @@ procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; procedure Test.0 (): let Test.2 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/specialize_closures.txt b/crates/compiler/test_mono/generated/specialize_closures.txt index 4dacbd022f..844180ae10 100644 --- a/crates/compiler/test_mono/generated/specialize_closures.txt +++ b/crates/compiler/test_mono/generated/specialize_closures.txt @@ -1,14 +1,14 @@ procedure Bool.2 (): - let Bool.12 : Int1 = true; - ret Bool.12; + let Bool.24 : Int1 = true; + ret Bool.24; procedure Num.19 (#Attr.2, #Attr.3): - let Num.258 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.257; procedure Num.21 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.256; procedure Test.1 (Test.2, Test.3): let Test.15 : U8 = GetTagId Test.2; diff --git a/crates/compiler/test_mono/generated/specialize_lowlevel.txt b/crates/compiler/test_mono/generated/specialize_lowlevel.txt index 017db92ce7..cb54585dd7 100644 --- a/crates/compiler/test_mono/generated/specialize_lowlevel.txt +++ b/crates/compiler/test_mono/generated/specialize_lowlevel.txt @@ -1,14 +1,14 @@ procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; procedure Num.19 (#Attr.2, #Attr.3): - let Num.258 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.257; procedure Num.21 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.256; procedure Test.6 (Test.8, #Attr.12): let Test.4 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/tail_call_elimination.txt b/crates/compiler/test_mono/generated/tail_call_elimination.txt index 996ca5866c..8d5dd76e08 100644 --- a/crates/compiler/test_mono/generated/tail_call_elimination.txt +++ b/crates/compiler/test_mono/generated/tail_call_elimination.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Num.20 (#Attr.2, #Attr.3): - let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.258; + let Num.257 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.257; procedure Test.1 (Test.15, Test.16): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/unreachable_void_constructor.txt b/crates/compiler/test_mono/generated/unreachable_void_constructor.txt index 16b7d9807e..017064eed0 100644 --- a/crates/compiler/test_mono/generated/unreachable_void_constructor.txt +++ b/crates/compiler/test_mono/generated/unreachable_void_constructor.txt @@ -1,6 +1,6 @@ procedure Bool.2 (): - let Bool.11 : Int1 = true; - ret Bool.11; + let Bool.23 : Int1 = true; + ret Bool.23; procedure Test.0 (): let Test.6 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/when_nested_maybe.txt b/crates/compiler/test_mono/generated/when_nested_maybe.txt index e8e714e59e..4b2af8c406 100644 --- a/crates/compiler/test_mono/generated/when_nested_maybe.txt +++ b/crates/compiler/test_mono/generated/when_nested_maybe.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/when_on_record.txt b/crates/compiler/test_mono/generated/when_on_record.txt index 18381ca043..83aabd4e7f 100644 --- a/crates/compiler/test_mono/generated/when_on_record.txt +++ b/crates/compiler/test_mono/generated/when_on_record.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.5 : I64 = 2i64; diff --git a/crates/compiler/test_mono/generated/when_on_two_values.txt b/crates/compiler/test_mono/generated/when_on_two_values.txt index 76a1f73339..14788ffbaf 100644 --- a/crates/compiler/test_mono/generated/when_on_two_values.txt +++ b/crates/compiler/test_mono/generated/when_on_two_values.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.257; + let Num.256 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.256; procedure Test.0 (): let Test.15 : I64 = 3i64; diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 0ecb7cd27e..c66e9d5ca1 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -143,7 +143,6 @@ fn compiles_to_ir(test_name: &str, src: &str) { verify_procedures(test_name, layout_interner, procedures, main_fn_symbol); } -#[cfg(debug_assertions)] fn verify_procedures<'a>( test_name: &str, interner: STLayoutInterner<'a>, @@ -174,7 +173,7 @@ fn verify_procedures<'a>( use std::process::Command; let is_tracked = Command::new("git") - .args(&["ls-files", "--error-unmatch", &path]) + .args(["ls-files", "--error-unmatch", &path]) .output() .unwrap(); @@ -186,7 +185,7 @@ fn verify_procedures<'a>( } let has_changes = Command::new("git") - .args(&["diff", "--color=always", &path]) + .args(["diff", "--color=always", &path]) .output() .unwrap(); @@ -201,19 +200,6 @@ fn verify_procedures<'a>( } } -// NOTE because the Show instance of module names is different in --release mode, -// these tests would all fail. In the future, when we do interesting optimizations, -// we'll likely want some tests for --release too. -#[cfg(not(debug_assertions))] -fn verify_procedures( - _expected: &str, - _interner: STLayoutInterner<'_>, - _procedures: MutMap<(Symbol, ProcLayout<'_>), Proc<'_>>, - _main_fn_symbol: Symbol, -) { - // Do nothing -} - #[mono_test] fn ir_int_literal() { r#" @@ -2000,3 +1986,19 @@ fn unreachable_branch_is_eliminated_but_produces_lambda_specializations() { "# ) } + +#[mono_test] +fn match_list() { + indoc!( + r#" + l = [A, B] + + when l is + [] -> "A" + [A] -> "B" + [A, A, ..] -> "C" + [A, B, ..] -> "D" + [B, ..] -> "E" + "# + ) +} diff --git a/crates/compiler/types/src/pretty_print.rs b/crates/compiler/types/src/pretty_print.rs index 67ff778cd6..bfe2126d98 100644 --- a/crates/compiler/types/src/pretty_print.rs +++ b/crates/compiler/types/src/pretty_print.rs @@ -30,7 +30,7 @@ static EMPTY_TAG_UNION: &str = "[]"; /// List (List I64) /// /// Otherwise, parens are unnecessary. -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Parens { InFn, InTypeParam, diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 7f983dfcef..943ca81a98 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -8,7 +8,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName}; use roc_module::low_level::LowLevel; -use roc_module::symbol::{Interns, ModuleId, Symbol}; +use roc_module::symbol::{Interns, Symbol}; use roc_region::all::{Loc, Region}; use std::fmt; use std::fmt::Write; @@ -2160,6 +2160,7 @@ pub enum PReason { tag_name: TagName, index: HumanIndex, }, + ListElem, PatternGuard, OptionalField, } @@ -2249,7 +2250,7 @@ pub enum Reason { }, } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum Category { Lookup(Symbol), CallResult(Option, CalledVia), @@ -2296,6 +2297,7 @@ pub enum Category { #[derive(Debug, Clone, PartialEq, Eq)] pub enum PatternCategory { Record, + List, EmptyRecord, PatternGuard, PatternDefault, @@ -2332,7 +2334,7 @@ impl AliasKind { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct AliasVar { pub name: Lowercase, pub var: Variable, @@ -2516,15 +2518,14 @@ impl ErrorType { } } -pub fn write_error_type(home: ModuleId, interns: &Interns, error_type: ErrorType) -> String { +pub fn write_error_type(interns: &Interns, error_type: ErrorType) -> String { let mut buf = String::new(); - write_error_type_help(home, interns, error_type, &mut buf, Parens::Unnecessary); + write_error_type_help(interns, error_type, &mut buf, Parens::Unnecessary); buf } fn write_error_type_help( - home: ModuleId, interns: &Interns, error_type: ErrorType, buf: &mut String, @@ -2548,7 +2549,7 @@ fn write_error_type_help( for arg in arguments { buf.push(' '); - write_error_type_help(home, interns, arg, buf, Parens::InTypeParam); + write_error_type_help(interns, arg, buf, Parens::InTypeParam); } if write_parens { @@ -2574,7 +2575,7 @@ fn write_error_type_help( buf.push('('); } buf.push_str("Num "); - write_error_type_help(home, interns, other, buf, Parens::InTypeParam); + write_error_type_help(interns, other, buf, Parens::InTypeParam); if write_parens { buf.push(')'); @@ -2592,7 +2593,7 @@ fn write_error_type_help( let mut it = arguments.into_iter().peekable(); while let Some(arg) = it.next() { - write_error_type_help(home, interns, arg, buf, Parens::InFn); + write_error_type_help(interns, arg, buf, Parens::InFn); if it.peek().is_some() { buf.push_str(", "); } @@ -2600,7 +2601,7 @@ fn write_error_type_help( buf.push_str(" -> "); - write_error_type_help(home, interns, *result, buf, Parens::InFn); + write_error_type_help(interns, *result, buf, Parens::InFn); if write_parens { buf.push(')'); @@ -2625,7 +2626,7 @@ fn write_error_type_help( } }; - write_error_type_help(home, interns, content, buf, Parens::Unnecessary); + write_error_type_help(interns, content, buf, Parens::Unnecessary); } buf.push('}'); diff --git a/crates/compiler/unify/src/unify.rs b/crates/compiler/unify/src/unify.rs index c6b232e4e7..a309fc697a 100644 --- a/crates/compiler/unify/src/unify.rs +++ b/crates/compiler/unify/src/unify.rs @@ -2788,7 +2788,7 @@ fn unify_flat_type( } (Apply(l_symbol, l_args), Apply(r_symbol, r_args)) if l_symbol == r_symbol => { - let mut outcome = unify_zip_slices(env, pool, *l_args, *r_args); + let mut outcome = unify_zip_slices(env, pool, *l_args, *r_args, ctx.mode); if outcome.mismatches.is_empty() { outcome.union(merge(env, ctx, Structure(Apply(*r_symbol, *r_args)))); @@ -2799,7 +2799,7 @@ fn unify_flat_type( (Func(l_args, l_closure, l_ret), Func(r_args, r_closure, r_ret)) if l_args.len() == r_args.len() => { - let arg_outcome = unify_zip_slices(env, pool, *l_args, *r_args); + let arg_outcome = unify_zip_slices(env, pool, *l_args, *r_args, ctx.mode); let ret_outcome = unify_pool(env, pool, *l_ret, *r_ret, ctx.mode); let closure_outcome = unify_pool(env, pool, *l_closure, *r_closure, ctx.mode); @@ -2926,6 +2926,7 @@ fn unify_zip_slices( pool: &mut Pool, left: SubsSlice, right: SubsSlice, + mode: Mode, ) -> Outcome { let mut outcome = Outcome::default(); @@ -2935,7 +2936,7 @@ fn unify_zip_slices( let l_var = env.subs[l_index]; let r_var = env.subs[r_index]; - outcome.union(unify_pool(env, pool, l_var, r_var, Mode::EQ)); + outcome.union(unify_pool(env, pool, l_var, r_var, mode)); } outcome diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index d4777885be..32cc2376fc 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -30,6 +30,7 @@ roc_problem = { path = "../compiler/problem" } roc_types = { path = "../compiler/types" } roc_unify = { path = "../compiler/unify" } roc_reporting = { path = "../reporting" } +roc_utils = { path = "../utils" } roc_solve = { path = "../compiler/solve" } ven_graph = { path = "../vendor/pathfinding" } bumpalo = { version = "3.11.0", features = ["collections"] } diff --git a/crates/editor/src/editor/main.rs b/crates/editor/src/editor/main.rs index b345815db5..1095bcec47 100644 --- a/crates/editor/src/editor/main.rs +++ b/crates/editor/src/editor/main.rs @@ -507,7 +507,7 @@ fn read_main_roc_file(project_dir_path_opt: Option<&Path>) -> (PathBuf, String) if let Some(&roc_file_name) = roc_file_names.first() { let full_roc_file_path = project_dir_path.join(roc_file_name); - let file_as_str = std::fs::read_to_string(&Path::new(&full_roc_file_path)) + let file_as_str = std::fs::read_to_string(Path::new(&full_roc_file_path)) .unwrap_or_else(|err| panic!("In the provided project {:?}, I found the roc file {:?}, but I failed to read it: {}", &project_dir_path, full_roc_file_path, err)); (full_roc_file_path, file_as_str) diff --git a/crates/editor/src/editor/mvc/ed_update.rs b/crates/editor/src/editor/mvc/ed_update.rs index 6bae9d3a07..6e4fc667b3 100644 --- a/crates/editor/src/editor/mvc/ed_update.rs +++ b/crates/editor/src/editor/mvc/ed_update.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use std::process::Command; use std::process::Stdio; use crate::editor::code_lines::CodeLines; @@ -63,6 +62,7 @@ use roc_solve::module::Solved; use roc_types::pretty_print::name_and_print_var; use roc_types::pretty_print::DebugPrint; use roc_types::subs::{Subs, VarStore, Variable}; +use roc_utils::cargo; use snafu::OptionExt; use threadpool::ThreadPool; use winit::event::VirtualKeyCode; @@ -622,7 +622,7 @@ impl<'a> EdModel<'a> { let roc_file_str = path_to_string(self.file_path); - let cmd_out = Command::new("cargo") + let cmd_out = cargo() .arg("run") .arg("check") .arg(roc_file_str) @@ -641,7 +641,7 @@ impl<'a> EdModel<'a> { let roc_file_str = path_to_string(self.file_path); - Command::new("cargo") + cargo() .arg("run") .arg(roc_file_str) .stdout(Stdio::inherit()) diff --git a/crates/editor/src/graphics/lowlevel/buffer.rs b/crates/editor/src/graphics/lowlevel/buffer.rs index 76f26e0480..593309460e 100644 --- a/crates/editor/src/graphics/lowlevel/buffer.rs +++ b/crates/editor/src/graphics/lowlevel/buffer.rs @@ -42,7 +42,7 @@ impl QuadBufferBuilder { max_y: f32, color: [f32; 4], ) -> Self { - self.vertex_data.extend(&[ + self.vertex_data.extend([ Vertex { position: (min_x, min_y).into(), color, @@ -60,7 +60,7 @@ impl QuadBufferBuilder { color, }, ]); - self.index_data.extend(&[ + self.index_data.extend([ self.current_quad * 4, self.current_quad * 4 + 1, self.current_quad * 4 + 2, diff --git a/crates/highlight/src/highlight_parser.rs b/crates/highlight/src/highlight_parser.rs index f096cf7cec..ff1efc1e79 100644 --- a/crates/highlight/src/highlight_parser.rs +++ b/crates/highlight/src/highlight_parser.rs @@ -233,7 +233,7 @@ pub mod highlight_tests { node_to_string_w_children( *highlight_defs("a = 0", &mut mark_node_pool) .unwrap() - .get(0) + .first() .unwrap(), &mut str_buffer, &mark_node_pool, diff --git a/crates/linker/src/elf.rs b/crates/linker/src/elf.rs index 29ac545944..3b59d08bd0 100644 --- a/crates/linker/src/elf.rs +++ b/crates/linker/src/elf.rs @@ -1597,7 +1597,7 @@ mod tests { // we need to compile the app first let output = std::process::Command::new(&zig) .current_dir(dir) - .args(&[ + .args([ "build-obj", "app.zig", "-fPIC", @@ -1637,7 +1637,7 @@ mod tests { // now we can compile the host (it uses libapp.so, hence the order here) let output = std::process::Command::new(&zig) .current_dir(dir) - .args(&[ + .args([ "build-exe", "libapp.so", "host.zig", @@ -1672,7 +1672,7 @@ mod tests { std::fs::copy(&dir.join("preprocessedhost"), &dir.join("final")).unwrap(); surgery_elf( - &*roc_app, + &roc_app, &dir.join("metadata"), &dir.join("final"), false, diff --git a/crates/linker/src/generate_dylib/macho.rs b/crates/linker/src/generate_dylib/macho.rs index 4c80fb06eb..f1c6ba5342 100644 --- a/crates/linker/src/generate_dylib/macho.rs +++ b/crates/linker/src/generate_dylib/macho.rs @@ -47,7 +47,7 @@ pub fn create_dylib_macho( } std::fs::write( - &dummy_obj_file, + dummy_obj_file, out_object.write().expect("failed to build output object"), ) .expect("failed to write object to file"); @@ -67,7 +67,7 @@ pub fn create_dylib_macho( let output = Command::new("ld") .args(ld_prefix_args) - .args(&[ + .args([ ld_flag_soname, dummy_lib_file.file_name().unwrap().to_str().unwrap(), dummy_obj_file.to_str().unwrap(), diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index b731035ad1..6f96d5aade 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -226,7 +226,7 @@ fn generate_import_library(stub_lib_path: &Path, custom_names: &[String]) { // > https://github.com/messense/implib-rs let output = std::process::Command::new(&zig) .current_dir(stub_lib_path.parent().unwrap()) - .args(&[ + .args([ "dlltool", "-d", def_filename.to_str().unwrap(), diff --git a/crates/linker/src/metadata.rs b/crates/linker/src/metadata.rs index aa665988a0..9085ea15e1 100644 --- a/crates/linker/src/metadata.rs +++ b/crates/linker/src/metadata.rs @@ -8,13 +8,13 @@ use roc_collections::all::MutMap; use roc_error_macros::internal_error; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub enum VirtualOffset { Absolute, Relative(u64), } -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct SurgeryEntry { pub file_offset: u64, pub virtual_offset: VirtualOffset, @@ -25,7 +25,7 @@ pub struct SurgeryEntry { // I think a number of them can be combined to reduce string duplication. // Also I think a few of them aren't need. // For example, I think preprocessing can deal with all shifting and remove the need for added_byte_count. -#[derive(Default, Serialize, Deserialize, PartialEq, Debug)] +#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug)] pub struct Metadata { pub app_functions: Vec, // offset followed by address. diff --git a/crates/linker/src/pe.rs b/crates/linker/src/pe.rs index 24613441e4..8a0846d8a2 100644 --- a/crates/linker/src/pe.rs +++ b/crates/linker/src/pe.rs @@ -1,6 +1,5 @@ use std::{ io::{BufReader, BufWriter}, - ops::Range, path::Path, }; @@ -253,6 +252,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b let md = PeMetadata::read_from_file(metadata_path); let app_obj_sections = AppSections::from_data(roc_app_bytes); + let mut symbols = app_obj_sections.roc_symbols; let image_base: u64 = md.image_base; @@ -262,7 +262,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b let app_sections_size: usize = app_obj_sections .sections .iter() - .map(|s| next_multiple_of(s.file_range.end - s.file_range.start, file_alignment)) + .map(|s| next_multiple_of(s.bytes.len(), file_alignment)) .sum(); let executable = &mut open_mmap_mut(executable_path, md.dynhost_file_size + app_sections_size); @@ -300,7 +300,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b .sections .iter() .filter(|s| s.kind == kind) - .map(|s| s.file_range.end - s.file_range.start) + .map(|s| s.bytes.len()) .sum(); // offset_in_section now becomes a proper virtual address @@ -348,7 +348,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b let mut offset = section_file_offset; let it = app_obj_sections.sections.iter().filter(|s| s.kind == kind); for section in it { - let slice = &roc_app_bytes[section.file_range.start..section.file_range.end]; + let slice = section.bytes; executable[offset..][..slice.len()].copy_from_slice(slice); let it = section @@ -392,14 +392,14 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b } else if name == "___chkstk_ms" { // this is a stack probe that is inserted when a function uses more than 2 // pages of stack space. The source of this function is not linked in, so we - // have to get a bit creative: we just jump to a `ret` instruction, so this - // function call becomes a no-op. + // have to do it ourselves. We patch in the bytes as a separate section, and + // here just need to jump to those bytes - // the last byte of the section should be a `ret` instruction - assert_eq!(slice.last(), Some(0xc3).as_ref()); + // This relies on the ___CHKSTK_MS section being the last text section in the list of sections + let destination = length - ___CHKSTK_MS.len(); let delta = - (slice.len() - 1) as i64 - *offset_in_section as i64 + relocation.addend(); + destination as i64 - *offset_in_section as i64 + relocation.addend(); executable[offset + *offset_in_section as usize..][..4] .copy_from_slice(&(delta as i32).to_le_bytes()); @@ -994,9 +994,8 @@ struct AppRelocation { } #[derive(Debug)] -struct Section { - /// File range of the section (in the app object) - file_range: Range, +struct Section<'a> { + bytes: &'a [u8], kind: SectionKind, relocations: MutMap>, app_section_index: SectionIndex, @@ -1010,8 +1009,8 @@ struct AppSymbol { } #[derive(Debug, Default)] -struct AppSections { - sections: Vec
, +struct AppSections<'a> { + sections: Vec>, roc_symbols: Vec, other_symbols: Vec<(SectionIndex, AppSymbol)>, } @@ -1043,7 +1042,7 @@ fn process_internal_relocations( let length: usize = sections .iter() .filter(|s| s.kind == kind) - .map(|s| s.file_range.end - s.file_range.start) + .map(|s| s.bytes.len()) .sum(); host_section_virtual_address += next_multiple_of(length, section_alignment) as u32; @@ -1052,8 +1051,8 @@ fn process_internal_relocations( result } -impl AppSections { - fn from_data(data: &[u8]) -> Self { +impl<'a> AppSections<'a> { + fn from_data(data: &'a [u8]) -> Self { use object::ObjectSection; let file = object::File::parse(data).unwrap(); @@ -1098,7 +1097,7 @@ impl AppSections { } let (start, length) = section.file_range().unwrap(); - let file_range = start as usize..(start + length) as usize; + let file_range = &data[start as usize..][..length as usize]; // sections are one-indexed... let index = SectionIndex(i + 1); @@ -1116,7 +1115,7 @@ impl AppSections { let section = Section { app_section_index: index, - file_range, + bytes: file_range, kind, relocations, }; @@ -1124,6 +1123,16 @@ impl AppSections { sections.push(section); } + // add a fake section that contains code for a stack probe that some app functions need + let stack_check_section = Section { + bytes: &___CHKSTK_MS, + kind: SectionKind::Text, + relocations: Default::default(), + app_section_index: object::SectionIndex(0), + }; + + sections.push(stack_check_section); + let mut roc_symbols = Vec::new(); let mut other_symbols = Vec::new(); @@ -1276,6 +1285,40 @@ pub(crate) fn redirect_libc_functions(name: &str) -> Option<&str> { } } +// 0000000000000000 <.text>: +// 0: 51 push rcx +// 1: 50 push rax +// 2: 48 3d 00 10 00 00 cmp rax,0x1000 +// 8: 48 8d 4c 24 18 lea rcx,[rsp+0x18] +// d: 72 18 jb 27 <.text+0x27> +// f: 48 81 e9 00 10 00 00 sub rcx,0x1000 +// 16: 48 85 09 test QWORD PTR [rcx],rcx +// 19: 48 2d 00 10 00 00 sub rax,0x1000 +// 1f: 48 3d 00 10 00 00 cmp rax,0x1000 +// 25: 77 e8 ja f <.text+0xf> +// 27: 48 29 c1 sub rcx,rax +// 2a: 48 85 09 test QWORD PTR [rcx],rcx +// 2d: 58 pop rax +// 2e: 59 pop rcx +// 2f: c3 ret +const ___CHKSTK_MS: [u8; 48] = [ + 0x51, // push rcx + 0x50, // push rax + 0x48, 0x3d, 0x00, 0x10, 0x00, 0x00, // cmp rax,0x0x1000 + 0x48, 0x8d, 0x4c, 0x24, 0x18, // lea rcx,0x[rsp+0x18] + 0x72, 0x18, // jb 0x27 + 0x48, 0x81, 0xe9, 0x00, 0x10, 0x00, 0x00, // sub rcx,0x0x1000 + 0x48, 0x85, 0x09, // test QWORD PTR [rcx],0xrcx + 0x48, 0x2d, 0x00, 0x10, 0x00, 0x00, // sub rax,0x0x1000 + 0x48, 0x3d, 0x00, 0x10, 0x00, 0x00, // cmp rax,0x0x1000 + 0x77, 0xe8, // ja 0xf + 0x48, 0x29, 0xc1, // sub rcx,0xrax + 0x48, 0x85, 0x09, // test QWORD PTR [rcx],rcx + 0x58, // pop rax + 0x59, // pop rcx + 0xc3, // ret +]; + #[cfg(test)] mod test { const PE_DYNHOST: &[u8] = include_bytes!("../dynhost_benchmarks_windows.exe") as &[_]; @@ -1574,7 +1617,7 @@ mod test { // we need to compile the app first let output = std::process::Command::new(&zig) .current_dir(dir) - .args(&[ + .args([ "build-obj", "app.zig", "-target", @@ -1599,7 +1642,7 @@ mod test { let file = std::fs::File::open(dir.join("app.obj")).unwrap(); let roc_app = unsafe { memmap2::Mmap::map(&file) }.unwrap(); - let roc_app_sections = AppSections::from_data(&*roc_app); + let roc_app_sections = AppSections::from_data(&roc_app); let symbols = roc_app_sections.roc_symbols; // make the dummy dylib based on the app object @@ -1610,7 +1653,7 @@ mod test { // now we can compile the host (it uses libapp.dll, hence the order here) let output = std::process::Command::new(&zig) .current_dir(dir) - .args(&[ + .args([ "build-exe", "libapp.dll", "host.zig", @@ -1646,7 +1689,7 @@ mod test { std::fs::copy(&dir.join("preprocessedhost"), &dir.join("app.exe")).unwrap(); - surgery_pe(&dir.join("app.exe"), &dir.join("metadata"), &*roc_app); + surgery_pe(&dir.join("app.exe"), &dir.join("metadata"), &roc_app); } #[allow(dead_code)] @@ -1688,7 +1731,7 @@ mod test { let output = std::process::Command::new("wine") .current_dir(dir) - .args(&["app.exe"]) + .args(["app.exe"]) .output() .unwrap(); @@ -1826,7 +1869,7 @@ mod test { let output = std::process::Command::new(&zig) .current_dir(dir) - .args(&[ + .args([ "build-exe", "host.zig", "-lc", diff --git a/crates/repl_eval/src/eval.rs b/crates/repl_eval/src/eval.rs index 41f4d27688..e2f7019757 100644 --- a/crates/repl_eval/src/eval.rs +++ b/crates/repl_eval/src/eval.rs @@ -353,13 +353,8 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( let expr = match layout { Layout::Builtin(Builtin::Bool) => { - app.call_function(main_fn_name, |mem: &A::Memory, num: bool| { - bool_to_ast( - env, - mem, - num, - env.subs.get_content_without_compacting(raw_var), - ) + app.call_function(main_fn_name, |_mem: &A::Memory, num: bool| { + bool_to_ast(env, num, env.subs.get_content_without_compacting(raw_var)) }) } Layout::Builtin(Builtin::Int(int_width)) => { @@ -370,13 +365,8 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>( (Some(Alias(Symbol::NUM_UNSIGNED8, ..)), U8) => num_helper!(u8), (_, U8) => { // This is not a number, it's a tag union or something else - app.call_function(main_fn_name, |mem: &A::Memory, num: u8| { - byte_to_ast( - env, - mem, - num, - env.subs.get_content_without_compacting(raw_var), - ) + app.call_function(main_fn_name, |_mem: &A::Memory, num: u8| { + byte_to_ast(env, num, env.subs.get_content_without_compacting(raw_var)) }) } // The rest are numbers... for now @@ -569,7 +559,7 @@ fn addr_to_ast<'a, M: ReplAppMemory>( // num is always false at the moment. let num: bool = mem.deref_bool(addr); - bool_to_ast(env, mem, num, raw_content) + bool_to_ast(env, num, raw_content) } (_, Layout::Builtin(Builtin::Int(int_width))) => { use IntWidth::*; @@ -1128,12 +1118,7 @@ fn unpack_two_element_tag_union( (tag_name1, payload_vars1, tag_name2, payload_vars2) } -fn bool_to_ast<'a, M: ReplAppMemory>( - env: &Env<'a, '_>, - mem: &M, - value: bool, - content: &Content, -) -> Expr<'a> { +fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a> { use Content::*; let arena = env.arena; @@ -1163,7 +1148,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>( let content = env.subs.get_content_without_compacting(var); let loc_payload = &*arena.alloc(Loc { - value: bool_to_ast(env, mem, value, content), + value: bool_to_ast(env, value, content), region: Region::zero(), }); @@ -1212,7 +1197,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>( Alias(_, _, var, _) => { let content = env.subs.get_content_without_compacting(*var); - bool_to_ast(env, mem, value, content) + bool_to_ast(env, value, content) } other => { unreachable!("Unexpected FlatType {:?} in bool_to_ast", other); @@ -1220,12 +1205,7 @@ fn bool_to_ast<'a, M: ReplAppMemory>( } } -fn byte_to_ast<'a, M: ReplAppMemory>( - env: &mut Env<'a, '_>, - mem: &M, - value: u8, - content: &Content, -) -> Expr<'a> { +fn byte_to_ast<'a>(env: &mut Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> { use Content::*; let arena = env.arena; @@ -1255,7 +1235,7 @@ fn byte_to_ast<'a, M: ReplAppMemory>( let content = env.subs.get_content_without_compacting(var); let loc_payload = &*arena.alloc(Loc { - value: byte_to_ast(env, mem, value, content), + value: byte_to_ast(env, value, content), region: Region::zero(), }); @@ -1332,7 +1312,7 @@ fn byte_to_ast<'a, M: ReplAppMemory>( Alias(_, _, var, _) => { let content = env.subs.get_content_without_compacting(*var); - byte_to_ast(env, mem, value, content) + byte_to_ast(env, value, content) } other => { unreachable!("Unexpected FlatType {:?} in byte_to_ast", other); diff --git a/crates/repl_expect/src/run.rs b/crates/repl_expect/src/run.rs index 0e74c9c93d..0f20deeba3 100644 --- a/crates/repl_expect/src/run.rs +++ b/crates/repl_expect/src/run.rs @@ -239,7 +239,7 @@ fn run_expect_fx<'a, W: std::io::Write>( ) -> std::io::Result { use signal_hook::{consts::signal::SIGCHLD, consts::signal::SIGUSR1, iterator::Signals}; - let mut signals = Signals::new(&[SIGCHLD, SIGUSR1]).unwrap(); + let mut signals = Signals::new([SIGCHLD, SIGUSR1]).unwrap(); match unsafe { libc::fork() } { 0 => unsafe { diff --git a/crates/repl_test/test_wasm.sh b/crates/repl_test/test_wasm.sh index 08a49a8d31..506d98501c 100755 --- a/crates/repl_test/test_wasm.sh +++ b/crates/repl_test/test_wasm.sh @@ -1,4 +1,5 @@ -set -eux +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +set -euxo pipefail # Build the compiler for WebAssembly target # We *could* write a build.rs to do this but we'd have nested cargo processes with different targets. diff --git a/crates/repl_wasm/Cargo.toml b/crates/repl_wasm/Cargo.toml index 469c95acde..560a314f21 100644 --- a/crates/repl_wasm/Cargo.toml +++ b/crates/repl_wasm/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] [build-dependencies] roc_builtins = {path = "../compiler/builtins"} +roc_utils = {path = "../utils"} wasi_libc_sys = { path = "../wasi-libc-sys" } [dependencies] diff --git a/crates/repl_wasm/build-www.sh b/crates/repl_wasm/build-www.sh index a2f8afd761..bc439e05aa 100755 --- a/crates/repl_wasm/build-www.sh +++ b/crates/repl_wasm/build-www.sh @@ -8,6 +8,7 @@ # Our website deployment script downloads that zipfile and copies the files into www/build/repl/ # We use this two-step process because Netlify times out if we try to build the Web REPL there. +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail if ! which wasm-pack diff --git a/crates/repl_wasm/build.rs b/crates/repl_wasm/build.rs index 93f869ef43..f7397754d7 100644 --- a/crates/repl_wasm/build.rs +++ b/crates/repl_wasm/build.rs @@ -20,7 +20,7 @@ fn main() { let platform_obj = build_wasm_platform(&out_dir, &source_path); let mut pre_linked_binary_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - pre_linked_binary_path.extend(&["pre_linked_binary"]); + pre_linked_binary_path.extend(["pre_linked_binary"]); pre_linked_binary_path.set_extension("o"); let output = Command::new(&zig_executable()) diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 429d3fd71d..4664d4eec4 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1024,6 +1024,19 @@ pub fn can_problem<'b>( title = "UNNECESSARY WILDCARD".to_string(); severity = Severity::Warning; } + Problem::MultipleListRestPattern { region } => { + doc = alloc.stack([ + alloc.reflow("This list pattern match has multiple rest patterns:"), + alloc.region(lines.convert_region(region)), + alloc.concat([ + alloc.reflow("I only support compiling list patterns with one "), + alloc.parser_suggestion(".."), + alloc.reflow(" pattern! Can you remove this additional one?"), + ]), + ]); + title = "MULTIPLE LIST REST PATTERNS".to_string(); + severity = Severity::RuntimeError; + } }; Report { @@ -1521,6 +1534,7 @@ fn pretty_runtime_error<'b>( QualifiedIdentifier => " qualified ", EmptySingleQuote => " empty character literal ", MultipleCharsInSingleQuote => " overfull literal ", + DuplicateListRestPattern => " second rest pattern ", }; let tip = match problem { @@ -1533,6 +1547,9 @@ fn pretty_runtime_error<'b>( QualifiedIdentifier => alloc .tip() .append(alloc.reflow("In patterns, only tags can be qualified")), + DuplicateListRestPattern => alloc + .tip() + .append(alloc.reflow("List patterns can only have one rest pattern")), }; doc = alloc.stack([ diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index adc7901361..3fd01de7da 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -4,7 +4,7 @@ use roc_can::expected::{Expected, PExpected}; use roc_collections::all::{HumanIndex, MutSet, SendMap}; use roc_collections::VecMap; use roc_error_macros::internal_error; -use roc_exhaustive::CtorName; +use roc_exhaustive::{CtorName, ListArity}; use roc_module::called_via::{BinOp, CalledVia}; use roc_module::ident::{Ident, IdentStr, Lowercase, TagName}; use roc_module::symbol::Symbol; @@ -1945,8 +1945,35 @@ fn to_pattern_report<'b>( severity: Severity::RuntimeError, } } + PReason::ListElem => { + let doc = alloc.stack([ + alloc.concat([alloc.reflow("This list element doesn't match the types of other elements in the pattern:")]), + alloc.region(lines.convert_region(region)), + pattern_type_comparison( + alloc, + found, + expected_type, + add_pattern_category( + alloc, + alloc.text("It matches"), + &category, + ), + alloc.concat([ + alloc.text("But the other elements in this list pattern match") + ]), + vec![], + ), + ]); + + Report { + filename, + title: "TYPE MISMATCH".to_string(), + doc, + severity: Severity::RuntimeError, + } + } PReason::TagArg { .. } | PReason::PatternGuard => { - unreachable!("I didn't think this could trigger. Please tell Folkert about it!") + internal_error!("We didn't think this could trigger. Please tell us about it on Zulip if it does!") } }, } @@ -1993,6 +2020,7 @@ fn add_pattern_category<'b>( PatternDefault => alloc.reflow(" an optional field of type:"), Set => alloc.reflow(" sets of type:"), Map => alloc.reflow(" maps of type:"), + List => alloc.reflow(" lists of type:"), Ctor(tag_name) => alloc.concat([ alloc.reflow(" a "), alloc.tag_name(tag_name.clone()), @@ -4352,6 +4380,37 @@ fn pattern_to_doc_help<'b>( Decimal(d) => alloc.text(RocDec::from_ne_bytes(d).to_string()), Str(s) => alloc.string(s.into()), }, + List(arity, patterns) => { + let inner = match arity { + ListArity::Exact(_) => alloc.intersperse( + patterns + .into_iter() + .map(|p| pattern_to_doc_help(alloc, p, false)), + alloc.text(",").append(alloc.space()), + ), + ListArity::Slice(num_before, num_after) => { + let mut all_patterns = patterns + .into_iter() + .map(|p| pattern_to_doc_help(alloc, p, in_type_param)); + + let spread = alloc.text(".."); + let comma_space = alloc.text(",").append(alloc.space()); + + let mut list = alloc.intersperse( + all_patterns.by_ref().take(num_before).chain([spread]), + comma_space.clone(), + ); + + if num_after > 0 { + let after = all_patterns; + list = alloc.intersperse([list].into_iter().chain(after), comma_space); + } + + list + } + }; + alloc.concat([alloc.text("["), inner, alloc.text("]")]) + } Ctor(union, tag_id, args) => { match union.render_as { RenderAs::Guard => { @@ -4377,7 +4436,7 @@ fn pattern_to_doc_help<'b>( Anything => { arg_docs.push(alloc.text(label.to_string())); } - Literal(_) | Ctor(_, _, _) => { + Literal(_) | Ctor(_, _, _) | List(..) => { arg_docs.push( alloc .text(label.to_string()) diff --git a/crates/reporting/tests/helpers/mod.rs b/crates/reporting/tests/helpers/mod.rs index 7f0a6ea06c..22bd02d54e 100644 --- a/crates/reporting/tests/helpers/mod.rs +++ b/crates/reporting/tests/helpers/mod.rs @@ -140,9 +140,12 @@ pub fn can_expr_with<'a>( } }; + let mut constraints = Constraints::new(); + let mut var_store = VarStore::default(); let var = var_store.fresh(); - let expected = Expected::NoExpectation(Type::Variable(var)); + let var_index = constraints.push_type(Type::Variable(var)); + let expected = Expected::NoExpectation(var_index); let mut module_ids = ModuleIds::default(); // ensure the Test module is accessible in our tests @@ -169,7 +172,6 @@ pub fn can_expr_with<'a>( &loc_expr.value, ); - let mut constraints = Constraints::new(); let constraint = constrain_expr( &mut constraints, &mut roc_constrain::expr::Env { diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index ccd134d44b..997d714d84 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -404,6 +404,16 @@ mod test_reporting { } } + macro_rules! test_no_problem { + ($(#[$meta:meta])* $test_name: ident, $program:expr) => { + #[test] + $(#[$meta])* + fn $test_name() { + __new_report_problem_as(std::stringify!($test_name), $program, |golden| pretty_assertions::assert_eq!(golden, "")) + } + } + } + fn human_readable(str: &str) -> String { str.replace(ANSI_STYLE_CODES.red, "") .replace(ANSI_STYLE_CODES.white, "") @@ -4837,14 +4847,27 @@ mod test_reporting { "# ), @r###" - ── MISSING EXPRESSION ───────────────────── tmp/pattern_binds_keyword/Test.roc ─ + ── MISSING ARROW ────────────────────────── tmp/pattern_binds_keyword/Test.roc ─ - I am partway through parsing a `when` expression, but I got stuck here: + I am partway through parsing a `when` expression, but got stuck here: 5│ Just when -> - ^ + ^ - I was expecting to see an expression like 42 or "hello". + I was expecting to see an arrow next. + + Note: Sometimes I get confused by indentation, so try to make your `when` + look something like this: + + when List.first plants is + Ok n -> + n + + Err _ -> + 200 + + Notice the indentation. All patterns are aligned, and each branch is + indented a bit more than the corresponding pattern. That is important! "### ); @@ -11714,7 +11737,7 @@ All branches in an `if` must have the same type! list_pattern_weird_rest_pattern, indoc!( r#" - when [] is + when [] is [...] -> "" "# ), @@ -11753,4 +11776,638 @@ All branches in an `if` must have the same type! safely. "### ); + + test_report!( + multiple_list_patterns_start_and_end, + indoc!( + r#" + when [] is + [.., A, ..] -> "" + "# + ), + @r###" + ── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─ + + This list pattern match has multiple rest patterns: + + 5│ [.., A, ..] -> "" + ^^ + + I only support compiling list patterns with one .. pattern! Can you + remove this additional one? + + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 4│> when [] is + 5│> [.., A, ..] -> "" + + Other possibilities include: + + _ + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + multiple_list_patterns_in_a_row, + indoc!( + r#" + when [] is + [A, .., .., B] -> "" + "# + ), + @r###" + ── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─ + + This list pattern match has multiple rest patterns: + + 5│ [A, .., .., B] -> "" + ^^ + + I only support compiling list patterns with one .. pattern! Can you + remove this additional one? + + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 4│> when [] is + 5│> [A, .., .., B] -> "" + + Other possibilities include: + + _ + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + mismatch_within_list_pattern, + indoc!( + r#" + when [] is + [A, 1u8] -> "" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + This list element doesn't match the types of other elements in the + pattern: + + 5│ [A, 1u8] -> "" + ^^^ + + It matches integers: + + U8 + + But the other elements in this list pattern match + + [A] + "### + ); + + test_report!( + mismatch_list_pattern_vs_condition, + indoc!( + r#" + when [A, B] is + ["foo", "bar"] -> "" + "# + ), + @r###" + ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ + + The branches of this `when` expression don't match the condition: + + 4│> when [A, B] is + 5│ ["foo", "bar"] -> "" + + The `when` condition is a list of type: + + List [A, B] + + But the branch patterns have type: + + List Str + + The branches must be cases of the `when` condition's type! + "### + ); + + test_report!( + list_match_non_exhaustive_only_empty, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + + Other possibilities include: + + [_, ..] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_no_problem!( + list_match_spread_exhaustive, + indoc!( + r#" + l : List [A] + + when l is + [..] -> "" + "# + ) + ); + + test_report!( + list_match_non_exhaustive_infinite, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + [A] -> "" + [A, A] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + 8│> [A] -> "" + 9│> [A, A] -> "" + + Other possibilities include: + + [_, _, _, ..] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_no_problem!( + list_match_exhaustive_empty_and_rest_with_unary_head, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + [_, ..] -> "" + "# + ) + ); + + test_no_problem!( + list_match_exhaustive_empty_and_rest_with_exhausted_head, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [A, ..] -> "" + [B, ..] -> "" + "# + ) + ); + + test_report!( + list_match_exhaustive_empty_and_rest_with_nonexhaustive_head, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [A, ..] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + 8│> [A, ..] -> "" + + Other possibilities include: + + [B, ..] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + list_match_no_small_sizes_and_non_exhaustive_head, + indoc!( + r#" + l : List [A, B] + + when l is + [A, B, ..] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [A, B, ..] -> "" + + Other possibilities include: + + [] + [_] + [_, A, ..] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_no_problem!( + list_match_exhaustive_empty_and_rest_with_exhausted_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [.., A] -> "" + [.., B] -> "" + "# + ) + ); + + test_report!( + list_match_exhaustive_empty_and_rest_with_nonexhaustive_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [.., A] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + 8│> [.., A] -> "" + + Other possibilities include: + + [.., B] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + list_match_no_small_sizes_and_non_exhaustive_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [.., B, A] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [.., B, A] -> "" + + Other possibilities include: + + [] + [_] + [.., _, B] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_no_problem!( + list_match_exhaustive_empty_and_rest_with_exhausted_head_and_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [A] -> "" + [B] -> "" + [A, .., A] -> "" + [A, .., B] -> "" + [B, .., A] -> "" + [B, .., B] -> "" + "# + ) + ); + + test_report!( + list_match_exhaustive_empty_and_rest_with_nonexhaustive_head_and_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [] -> "" + [_] -> "" + [A, .., B] -> "" + [B, .., A] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + 8│> [_] -> "" + 9│> [A, .., B] -> "" + 10│> [B, .., A] -> "" + + Other possibilities include: + + [_, .., _] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + list_match_no_small_sizes_and_non_exhaustive_head_and_tail, + indoc!( + r#" + l : List [A, B] + + when l is + [A, .., B] -> "" + [B, .., A] -> "" + [B, .., B] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [A, .., B] -> "" + 8│> [B, .., A] -> "" + 9│> [B, .., B] -> "" + + Other possibilities include: + + [] + [_] + [A, .., A] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + list_match_exhaustive_big_sizes_but_not_small_sizes, + indoc!( + r#" + l : List [A] + + when l is + [A, A, A, .., A, A, A] -> "" + [A, A, A, .., A, A] -> "" + [A, A, .., A, A] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [A, A, A, .., A, A, A] -> "" + 8│> [A, A, A, .., A, A] -> "" + 9│> [A, A, .., A, A] -> "" + + Other possibilities include: + + [] + [_] + [_, _] + [_, _, _] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_no_problem!( + list_match_nested_list_exhaustive, + indoc!( + r#" + l : List (List [A]) + + when l is + [] -> "" + [[]] -> "" + [[A, ..]] -> "" + [[..], .., [..]] -> "" + "# + ) + ); + + test_report!( + list_match_nested_list_not_exhaustive, + indoc!( + r#" + l : List (List [A, B]) + + when l is + [] -> "" + [[]] -> "" + [[A, ..]] -> "" + [[..], .., [.., B]] -> "" + "# + ), + @r###" + ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ + + This `when` does not cover all the possibilities: + + 6│> when l is + 7│> [] -> "" + 8│> [[]] -> "" + 9│> [[A, ..]] -> "" + 10│> [[..], .., [.., B]] -> "" + + Other possibilities include: + + [[B, ..]] + [_, .., []] + [_, .., [.., A]] + + I would have to crash if I saw one of those! Add branches for them! + "### + ); + + test_report!( + list_match_redundant_exact_size, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + [_] -> "" + [_] -> "" + [..] -> "" + "# + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ + + The 3rd pattern is redundant: + + 6│ when l is + 7│ [] -> "" + 8│ [_] -> "" + 9│> [_] -> "" + 10│ [..] -> "" + + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); + + test_report!( + list_match_redundant_any_slice, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + [_, ..] -> "" + [..] -> "" + "# + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ + + The 3rd pattern is redundant: + + 6│ when l is + 7│ [] -> "" + 8│ [_, ..] -> "" + 9│ [..] -> "" + ^^^^ + + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); + + test_report!( + list_match_redundant_suffix_slice_with_sized_prefix, + indoc!( + r#" + l : List [A] + + when l is + [] -> "" + [_, ..] -> "" + [.., _] -> "" + "# + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ + + The 3rd pattern is redundant: + + 6│ when l is + 7│ [] -> "" + 8│ [_, ..] -> "" + 9│ [.., _] -> "" + ^^^^^^^ + + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); + + test_report!( + list_match_redundant_based_on_ctors, + indoc!( + r#" + l : List {} + + when l is + [{}, .., _] -> "" + [_, .., {}] -> "" + [..] -> "" + "# + ), + @r###" + ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ + + The 2nd pattern is redundant: + + 6│ when l is + 7│ [{}, .., _] -> "" + 8│> [_, .., {}] -> "" + 9│ [..] -> "" + + Any value of this shape will be handled by a previous pattern, so this + one should be removed. + "### + ); + + test_no_problem!( + list_match_with_guard, + indoc!( + r#" + l : List [A] + + when l is + [ A, .. ] if Bool.true -> "" + [ A, .. ] -> "" + _ -> "" + "# + ) + ); } diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 78619e5e3c..ec90be9814 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; #[doc(hidden)] pub use pretty_assertions::assert_eq as _pretty_assert_eq; -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub struct DebugAsDisplay(pub T); impl std::fmt::Debug for DebugAsDisplay { @@ -31,7 +31,7 @@ impl TmpDir { let path = std::path::Path::new(dir); // ensure_empty_dir will fail if the dir doesn't already exist std::fs::create_dir_all(path).unwrap(); - remove_dir_all::ensure_empty_dir(&path).unwrap(); + remove_dir_all::ensure_empty_dir(path).unwrap(); let mut pathbuf = std::path::PathBuf::new(); pathbuf.push(path); diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index cdc5d3fb0e..3182885cec 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,5 +1,7 @@ use snafu::OptionExt; -use std::{collections::HashMap, path::PathBuf, slice::SliceIndex}; +use std::{ + collections::HashMap, env::VarError, path::PathBuf, process::Command, slice::SliceIndex, +}; use util_error::{IndexOfFailedSnafu, KeyNotFoundSnafu, OutOfBoundsSnafu, UtilResult}; pub mod util_error; @@ -120,3 +122,76 @@ pub fn get_lib_path() -> Option { None } + +/// Gives a friendly error if cargo is not installed. +/// Also makes it easy to track where we use cargo in the codebase. +pub fn cargo() -> Command { + let command_str = "cargo"; + + if check_command_available(command_str) { + Command::new(command_str) + } else { + panic!("I could not find the cargo command.\nVisit https://rustup.rs/ to install cargo.",) + } +} + +/// Gives a friendly error if clang is not installed. +/// Also makes it easy to track where we use clang in the codebase. +pub fn clang() -> Command { + let command_str = "clang"; + + if check_command_available(command_str) { + Command::new(command_str) + } else { + panic!("I could not find the clang command.\nPlease install clang.",) + //TODO detect OS and provide detailed install instructions + } +} + +/// Gives a friendly error if zig is not installed. +/// Also makes it easy to track where we use zig in the codebase. +pub fn zig() -> Command { + let command_str = match std::env::var("ROC_ZIG") { + Ok(path) => path, + Err(_) => "zig".into(), + }; + + if check_command_available(&command_str) { + Command::new(command_str) + } else { + panic!("I could not find the zig command.\nPlease install zig, see instructions at https://ziglang.org/learn/getting-started/.",) + } +} + +fn check_command_available(command_name: &str) -> bool { + if cfg!(target_family = "unix") { + let unparsed_path = match std::env::var("PATH") { + Ok(var) => var, + Err(VarError::NotPresent) => return false, + Err(VarError::NotUnicode(_)) => { + panic!("found PATH, but it included invalid unicode data!") + } + }; + + std::env::split_paths(&unparsed_path).any(|dir| dir.join(command_name).exists()) + } else if cfg!(target = "windows") { + let mut cmd = Command::new("Get-Command"); + + cmd.args([command_name]); + + let cmd_str = format!("{:?}", cmd); + + let cmd_out = cmd.output().unwrap_or_else(|err| { + panic!( + "Failed to execute `{}` to check if {} is available:\n {}", + cmd_str, command_name, err + ) + }); + + cmd_out.status.success() + } else { + // We're in uncharted waters, best not to panic if + // things may end up working out down the line. + true + } +} diff --git a/crates/vendor/morphic_lib/src/analyze.rs b/crates/vendor/morphic_lib/src/analyze.rs index 498a2c279b..44f58da4ad 100644 --- a/crates/vendor/morphic_lib/src/analyze.rs +++ b/crates/vendor/morphic_lib/src/analyze.rs @@ -1562,11 +1562,11 @@ impl Query { } hasher.update((self.arg_slots_touched.len() as u64).to_le_bytes()); for &arg_touched in &self.arg_slots_touched { - hasher.update(&[arg_touched as u8]); + hasher.update([arg_touched as u8]); } hasher.update((self.ret_slots_touched.len() as u64).to_le_bytes()); for &ret_touched in &self.ret_slots_touched { - hasher.update(&[ret_touched as u8]); + hasher.update([ret_touched as u8]); } api::FuncSpec(hasher.finalize().into()) } @@ -1775,7 +1775,7 @@ pub(crate) fn analyze(tc: TypeCache, program: &ir::Program) -> ProgramSolutions fn hash_func_id_trivial(func_id: FuncId) -> api::FuncSpec { use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); - hasher.update(&func_id.0.to_le_bytes()); + hasher.update(func_id.0.to_le_bytes()); api::FuncSpec(hasher.finalize().into()) } diff --git a/crates/vendor/morphic_lib/src/api.rs b/crates/vendor/morphic_lib/src/api.rs index b93180764c..70ec8efa19 100644 --- a/crates/vendor/morphic_lib/src/api.rs +++ b/crates/vendor/morphic_lib/src/api.rs @@ -1654,7 +1654,7 @@ pub fn solve_trivial(api_program: Program) -> Result { // solutions fn hash_bstr(hasher: &mut Sha256, bstr: &[u8]) { let header = (bstr.len() as u64).to_le_bytes(); - hasher.update(&header); + hasher.update(header); hasher.update(bstr); } diff --git a/crates/wasi-libc-sys/Cargo.toml b/crates/wasi-libc-sys/Cargo.toml index 1406303e29..f7faff97fd 100644 --- a/crates/wasi-libc-sys/Cargo.toml +++ b/crates/wasi-libc-sys/Cargo.toml @@ -6,3 +6,6 @@ license = "UPL-1.0" name = "wasi_libc_sys" repository = "https://github.com/roc-lang/roc" version = "0.0.1" + +[build-dependencies] +roc_utils = {path = "../utils"} \ No newline at end of file diff --git a/crates/wasi-libc-sys/build.rs b/crates/wasi-libc-sys/build.rs index 4474290304..2aca3eadd2 100644 --- a/crates/wasi-libc-sys/build.rs +++ b/crates/wasi-libc-sys/build.rs @@ -1,8 +1,8 @@ +use roc_utils::zig; use std::env; use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -13,7 +13,7 @@ fn main() { let out_file = PathBuf::from(&out_dir).join("wasi-libc.a"); // Compile a dummy C program with Zig, with our own private cache directory - Command::new(&zig_executable()) + zig() .args([ "build-exe", "-target", @@ -51,13 +51,6 @@ fn main() { ); } -fn zig_executable() -> String { - match std::env::var("ROC_ZIG") { - Ok(path) => path, - Err(_) => "zig".into(), - } -} - fn find(dir: &Path, filename: &OsString) -> std::io::Result> { for entry in fs::read_dir(dir)? { let entry = entry?; diff --git a/default.nix b/default.nix index 863d911a4a..cf520d7a7d 100644 --- a/default.nix +++ b/default.nix @@ -3,7 +3,7 @@ nixpkgsSource ? builtins.fetchTarball { url = "https://github.com/nixos/nixpkgs/tarball/${rev}"; sha256 = "sha256-5DGKX81wIPAAiLwUmUYECpA3vop94AHHR7WmGXSsQok="; }, pkgs ? import nixpkgsSource { } -, cargoSha256 ? "sha256-F6UOJZ5oDOZ+80z70A21VzDR0YtmgD0dnEcjPgpicpo=", }: +, cargoSha256 ? "sha256-Qmriwe+xSL5/pU8oqqj5Qw6H179KYqOljWl0rpPD6MY=", }: # we only this file to release a nix package, use flake.nix for development let rustPlatform = pkgs.rustPlatform; diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index ca432d7864..befff45bed 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -198,6 +198,7 @@ fn roc_fx_getInt_help() !i64 { const stdin = std.io.getStdIn().reader(); var buf: [40]u8 = undefined; + // 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); diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index 1a60543206..7c11041617 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -264,6 +264,7 @@ fn roc_fx_getInt_help() !i64 { const stdin = std.io.getStdIn().reader(); var buf: [40]u8 = undefined; + // 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); diff --git a/examples/swiftui/run.sh b/examples/swiftui/run.sh index 9a3eb2f430..d20a18d314 100755 --- a/examples/swiftui/run.sh +++ b/examples/swiftui/run.sh @@ -1,5 +1,8 @@ #! /usr/bin/env bash +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +set -euxo pipefail + cargo run -- build mkdir -p SwiftUIDemo.app/Contents/MacOS/ mv swiftui SwiftUIDemo.app/Contents/MacOS/SwiftUIDemo diff --git a/flake.nix b/flake.nix index 82fa0aa3f5..2b1cfc3820 100644 --- a/flake.nix +++ b/flake.nix @@ -105,6 +105,7 @@ debugir rust rust-bindgen + cargo-criterion ]); in { diff --git a/www/build.sh b/www/build.sh index 7828b1c0a5..f8cd18e5d8 100755 --- a/www/build.sh +++ b/www/build.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail # cd into the directory where this script lives. diff --git a/www/netlify.sh b/www/netlify.sh index 43aa6c01b6..165a70bceb 100644 --- a/www/netlify.sh +++ b/www/netlify.sh @@ -2,6 +2,7 @@ # Runs on every Netlify build, to set up the Netlify server. +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ set -euxo pipefail rustup update