mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-28 11:59:41 +00:00
Merge branch 'main' into basic-cli-5-release
Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
This commit is contained in:
commit
63d2f8e852
283 changed files with 5966 additions and 4923 deletions
22
.github/workflows/basic_cli_test_arm64.yml
vendored
Normal file
22
.github/workflows/basic_cli_test_arm64.yml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
# this cancels workflows currently in progress if you start a new one
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-basic-cli-release-arm64:
|
||||
runs-on: [self-hosted, Linux, ARM64]
|
||||
steps:
|
||||
- name: clone basic-cli repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: roc-lang/basic-cli
|
||||
ref: main
|
||||
|
||||
- name: Run all tests with latest roc nightly + latest basic-cli release
|
||||
run: |
|
||||
sed -i 's/x86_64/arm64/g' ./ci/test_latest_release.sh
|
||||
./ci/test_latest_release.sh
|
||||
57
.github/workflows/devtools_test.yml
vendored
Normal file
57
.github/workflows/devtools_test.yml
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
on:
|
||||
pull_request:
|
||||
|
||||
name: Test the devtools nix files
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
devtools-test:
|
||||
name: devtools-test
|
||||
runs-on: [ubuntu-20.04]
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Only run all steps if flake.lock changed
|
||||
id: checklock
|
||||
run: |
|
||||
if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep 'flake.lock'; then
|
||||
echo "Flake.lock was changed. Testing devtools nix files..."
|
||||
echo "::set-output name=changed::true"
|
||||
else
|
||||
echo "Flake.lock was not changed. No need to run tests."
|
||||
echo "::set-output name=changed::false"
|
||||
fi
|
||||
|
||||
- uses: cachix/install-nix-action@v23
|
||||
if: steps.checklock.outputs.changed == 'true'
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: test devtools/flake.nix
|
||||
if: steps.checklock.outputs.changed == 'true'
|
||||
id: devtools_test_step
|
||||
run: |
|
||||
sed -i "s|/home/username/gitrepos/roc|$(realpath .)|g" devtools/flake.nix
|
||||
cat devtools/flake.nix
|
||||
mkdir -p ../temp
|
||||
cp devtools/flake.nix ../temp
|
||||
cp devtools/flake.lock ../temp
|
||||
cd ../temp
|
||||
git init
|
||||
git add flake.nix flake.lock
|
||||
nix develop
|
||||
|
||||
- name: Print tip on fail
|
||||
if: steps.devtools_test_step.outcome == 'failure'
|
||||
run: |
|
||||
echo "The devtools test failed, this can likely be fixed by"
|
||||
echo "locally deleting devtools/flake.lock and following the"
|
||||
echo "instructions in devtools/README.md. This will create a"
|
||||
echo "new flake.lock you should use to replace the old devtools/flake.lock"
|
||||
|
||||
|
||||
|
||||
6
.github/workflows/nix_linux_x86_64.yml
vendored
6
.github/workflows/nix_linux_x86_64.yml
vendored
|
|
@ -31,3 +31,9 @@ jobs:
|
|||
|
||||
- name: test the dev backend # these tests require an explicit feature flag
|
||||
run: nix develop -c cargo test --locked --release --package test_gen --no-default-features --features gen-dev
|
||||
|
||||
- name: wasm repl tests
|
||||
run: nix develop -c crates/repl_test/test_wasm.sh
|
||||
|
||||
- name: test building wasm repl
|
||||
run: nix develop -c ./ci/www-repl.sh
|
||||
|
|
|
|||
13
.github/workflows/nix_macos_apple_silicon.yml
vendored
13
.github/workflows/nix_macos_apple_silicon.yml
vendored
|
|
@ -39,11 +39,18 @@ jobs:
|
|||
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked
|
||||
|
||||
- name: test launching the editor
|
||||
run: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs
|
||||
run: nix develop -c cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs
|
||||
|
||||
# we run the llvm wasm tests only on this machine because it is fast and wasm should be cross-target
|
||||
- name: execute llvm wasm tests with --release
|
||||
run: nix develop -c cargo test-gen-llvm-wasm --locked --release
|
||||
|
||||
- name: test website build script
|
||||
run: REPL_DEBUG=1 bash www/build.sh
|
||||
- name: set env var and test website build script
|
||||
run: |
|
||||
nix develop -c bash www/build.sh
|
||||
|
||||
- name: wasm repl tests
|
||||
run: nix develop -c crates/repl_test/test_wasm.sh
|
||||
|
||||
- name: test building wasm repl
|
||||
run: nix develop -c ./ci/www-repl.sh
|
||||
|
|
|
|||
6
.github/workflows/test_nightly_many_os.yml
vendored
6
.github/workflows/test_nightly_many_os.yml
vendored
|
|
@ -2,15 +2,15 @@ on:
|
|||
#pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Test latest nightly release for macOS, ubu 20.04, ubu 22.04 x86_64
|
||||
name: Test latest nightly releases for macOS and Linux x86_64
|
||||
|
||||
jobs:
|
||||
test-nightly:
|
||||
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
|
||||
name: test nightly macos 11/12/13, ubuntu 20.04/22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ macos-11, macos-12, ubuntu-20.04, ubuntu-22.04 ]
|
||||
os: [ macos-11, macos-12, macos-13, ubuntu-20.04, ubuntu-22.04 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
|
|
|
|||
2
.github/workflows/ubuntu_x86_64.yml
vendored
2
.github/workflows/ubuntu_x86_64.yml
vendored
|
|
@ -65,4 +65,4 @@ jobs:
|
|||
#TODO verify-no-git-changes
|
||||
|
||||
- name: test website build script
|
||||
run: REPL_DEBUG=1 bash www/build.sh
|
||||
run: bash www/build.sh
|
||||
|
|
|
|||
2
.github/workflows/windows_tests.yml
vendored
2
.github/workflows/windows_tests.yml
vendored
|
|
@ -1,5 +1,5 @@
|
|||
on:
|
||||
#pull_request:
|
||||
#pull_request:
|
||||
|
||||
name: windows - subset of tests
|
||||
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -4099,6 +4099,7 @@ dependencies = [
|
|||
"roc_debug_flags",
|
||||
"roc_error_macros",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_region",
|
||||
"roc_serialize",
|
||||
"static_assertions",
|
||||
|
|
@ -4784,7 +4785,6 @@ dependencies = [
|
|||
"criterion",
|
||||
"indoc",
|
||||
"inkwell",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libloading",
|
||||
"roc_bitcode",
|
||||
|
|
|
|||
278
FAQ.md
278
FAQ.md
|
|
@ -31,61 +31,6 @@ fantastical, and it has incredible potential for puns. Here are some different w
|
|||
|
||||
Fun fact: "roc" translates to 鹏 in Chinese, [which means](https://www.mdbg.net/chinese/dictionary?page=worddict&wdrst=0&wdqb=%E9%B9%8F) "a large fabulous bird."
|
||||
|
||||
## Why make a new editor instead of making an LSP plugin for VSCode, Vim or Emacs?
|
||||
|
||||
The Roc editor is one of the key areas where we want to innovate. Constraining ourselves to a plugin for existing editors would severely limit our possibilities for innovation.
|
||||
|
||||
A key part of our editor will be the use of plugins that are shipped with libraries. Think of a regex visualizer, parser debugger, or color picker. For library authors, it would be most convenient to write these plugins in Roc. Trying to dynamically load library plugins (written in Roc) in for example VSCode seems very difficult.
|
||||
|
||||
## Is there syntax highlighting for Vim/Emacs/VS Code or a LSP?
|
||||
|
||||
Not currently. Although they will presumably exist someday, while Roc is in the early days there's actually a conscious
|
||||
effort to focus on the Roc Editor _instead of_ adding Roc support to other editors - specifically in order to give the Roc
|
||||
Editor the best possible chance at kickstarting a virtuous cycle of plugin authorship.
|
||||
|
||||
This is an unusual approach, but there are more details in [this 2021 interview](https://youtu.be/ITrDd6-PbvY?t=212).
|
||||
|
||||
In the meantime, using CoffeeScript syntax highlighting for .roc files turns out to work surprisingly well!
|
||||
|
||||
## Why won't the editor be able to edit non-roc files like .md, .gitignore, .yml, ... ?
|
||||
|
||||
The downside of having the Roc editor support files other than .roc is that it seems extremely difficult to avoid scope creep if we allow it. For example, it starts with just editing json as plaintext but then it's annoying that there's no syntax highlighting, so maybe we add the capability to do syntax highlighting for json but of course then some people want it for toml, .md, etc, so we need to add a way to specify custom syntax highlighting rules for all of those.
|
||||
|
||||
Then of course people don't want to be copy/pasting syntax highlighting rules from online, so maybe someone develops a third party "plugin manager" for the editor to distribute these syntax highlighting definitions.
|
||||
So maybe we add sharing syntax highlighting as a first-class thing, so people don't have to download a separate tool to use their editor normally but then some people who are using it for .json and .yaml start using it for .css too. Syntax highlighting is okay but it's annoying that they don't get error reporting when they mess up syntax or type an invalid selector or import and pretty soon there's demand for the Roc editor to do all the hardest parts of VS code.
|
||||
|
||||
We have to draw the line somewhere in there...but where to draw it?
|
||||
It seems like drawing a bright line at .roc files is the most straightforward. It means the roc editor is the absolute best at editing .roc files and it isn't a weak editor for anything else because it doesn't try to be an editor for anything else and it means the scope is very clear.
|
||||
|
||||
## Why is there no way to specify "import everything this module exposes" in `imports`?
|
||||
|
||||
In [Elm](https://elm-lang.org), it's possible to import a module in a way that brings everything that module
|
||||
exposes into scope. It can be convenient, but like all programming language features, it has downsides.
|
||||
|
||||
A minor reason Roc doesn't have this feature is that exposing everything can make it more difficult
|
||||
outside the editor (e.g. on a website) to tell where something comes from, especially if multiple imports are
|
||||
using this. ("I don't see `blah` defined in this module, so it must be coming from an import...but which of
|
||||
these several import-exposing-everything modules could it be? I'll have to check all of them, or
|
||||
download this code base and open it up in the editor so I can jump to definition!")
|
||||
|
||||
The main reason for this design, though, is compiler performance.
|
||||
|
||||
Currently, the name resolution step in compilation can be parallelized across modules, because it's possible to
|
||||
tell if there's a naming error within a module using only the contents of that module. If "expose everything" is
|
||||
allowed, then it's no longer clear whether anything is a naming error or not, until all the "expose everything"
|
||||
modules have been processed, so we know exactly which names they expose. Because that feature doesn't exist in Roc,
|
||||
all modules can do name resolution in parallel.
|
||||
|
||||
Of note, allowing this feature would only slow down modules that used it; modules that didn't use it would still be
|
||||
parallelizable. However, when people find out ways to speed up their builds (in any language), advice starts to
|
||||
circulate about how to unlock those speed boosts. If Roc had this feature, it's predictable that a commonly-accepted
|
||||
piece of advice would eventually circulate: "don't use this feature because it slows down your builds."
|
||||
|
||||
If a feature exists in a language, but the common recommendation is never to use it, that's cause for reconsidering
|
||||
whether the feature should be in the language at all. In the case of this feature, I think it's simpler if the
|
||||
language doesn't have it; that way nobody has to learn (or spend time spreading the word) about the
|
||||
performance-boosting advice not to use it.
|
||||
|
||||
## Why can't functions be compared for equality using the `==` operator?
|
||||
|
||||
Function equality has been proven to be undecidable in the general case because of the [halting problem](https://en.wikipedia.org/wiki/Halting_problem).
|
||||
|
|
@ -126,6 +71,35 @@ The first of these problems could be addressed by having function equality alway
|
|||
Each of these designs makes Roc a language that's some combination of more error-prone, more confusing, and more
|
||||
brittle to change. Disallowing function equality at compile time eliminates all of these drawbacks.
|
||||
|
||||
## Why is there no way to specify "import everything this module exposes" in `imports`?
|
||||
|
||||
In [Elm](https://elm-lang.org), it's possible to import a module in a way that brings everything that module
|
||||
exposes into scope. It can be convenient, but like all programming language features, it has downsides.
|
||||
|
||||
A minor reason Roc doesn't have this feature is that exposing everything can make it more difficult
|
||||
outside the editor (e.g. on a website) to tell where something comes from, especially if multiple imports are
|
||||
using this. ("I don't see `blah` defined in this module, so it must be coming from an import...but which of
|
||||
these several import-exposing-everything modules could it be? I'll have to check all of them, or
|
||||
download this code base and open it up in the editor so I can jump to definition!")
|
||||
|
||||
The main reason for this design, though, is compiler performance.
|
||||
|
||||
Currently, the name resolution step in compilation can be parallelized across modules, because it's possible to
|
||||
tell if there's a naming error within a module using only the contents of that module. If "expose everything" is
|
||||
allowed, then it's no longer clear whether anything is a naming error or not, until all the "expose everything"
|
||||
modules have been processed, so we know exactly which names they expose. Because that feature doesn't exist in Roc,
|
||||
all modules can do name resolution in parallel.
|
||||
|
||||
Of note, allowing this feature would only slow down modules that used it; modules that didn't use it would still be
|
||||
parallelizable. However, when people find out ways to speed up their builds (in any language), advice starts to
|
||||
circulate about how to unlock those speed boosts. If Roc had this feature, it's predictable that a commonly-accepted
|
||||
piece of advice would eventually circulate: "don't use this feature because it slows down your builds."
|
||||
|
||||
If a feature exists in a language, but the common recommendation is never to use it, that's cause for reconsidering
|
||||
whether the feature should be in the language at all. In the case of this feature, it's simpler if the
|
||||
language doesn't have it; that way nobody has to learn (or spend time spreading the word) about the
|
||||
performance-boosting advice not to use it.
|
||||
|
||||
## Why doesn't Roc have a `Maybe` or `Option` or `Optional` type, or `null` or `nil` or `undefined`?
|
||||
|
||||
It's common for programming languages to have a [null reference](https://en.wikipedia.org/wiki/Null_pointer)
|
||||
|
|
@ -174,7 +148,7 @@ On a historical note, `Maybe` may have been thought of as a substitute for null
|
|||
|
||||
## Why doesn't Roc have higher-kinded polymorphism or arbitrary-rank types?
|
||||
|
||||
_Since this is a FAQ answer, I'm going to assume familiarity with higher-kinded types and higher-rank types instead of including a primer on them._
|
||||
_Since this is a FAQ answer, it assumes familiarity with higher-kinded types and higher-rank types instead of including a primer on them._
|
||||
|
||||
A valuable aspect of Roc's type system is that it has decidable [principal](https://en.wikipedia.org/wiki/Principal_type)
|
||||
type inference. This means that:
|
||||
|
|
@ -188,34 +162,33 @@ would be unable to infer a type—and you'd have to write a type annotation. Thi
|
|||
situations where the editor would not be able to reliably tell you the type of part of your program, unlike today
|
||||
where it can accurately tell you the type of anything, even if you have no type annotations in your entire code base.
|
||||
|
||||
assuming that's right, here is a proposed new FAQ entry:
|
||||
This is one factor that higher-rank and higher-kinded types have in common. There are other factors which are specific
|
||||
to each.
|
||||
|
||||
### Higher-rank types
|
||||
|
||||
Roc uses a Rank-1 type system. Other languages, like Haskell, support Rank-2 or even arbitrary-rank (aka "Rank-N") types. Supporting higher-rank types in Roc has been discussed before, but it has several important downsides:
|
||||
|
||||
- It would remove principal decidable type inference. (Only Rank-1 types are compatible with principal decidable type inference; Rank-2 types are decidable but the inferred types are not principal, and Rank 3+ types are not even fully decidable.)
|
||||
Supporting higher-rank types in Roc has been discussed before, but it has several important downsides:
|
||||
|
||||
- It would increase the complexity of the language.
|
||||
- It would make some compiler error messages more confusing (e.g. they might mention `forall` because that was the most general type that could be inferred, even if that wasn't helpful or related to the actual problem).
|
||||
- It would substantially increase the complexity of the type checker, which would necessarily slow it down.
|
||||
- Most significantly, it would make the runtime slower, because Roc compiles programs by fully specializing all function calls to their type instances (this is sometimes called monomorphization). It's unclear how we could fully specialize programs containing Rank-2 types, which means compiling programs that included Rank-2 types (or higher) would require losing specialization in general—which would substantially degrade runtime performance.
|
||||
- It would make some Roc programs run significantly more slowly. Roc compiles programs by [monomorphizing](https://en.wikipedia.org/wiki/Monomorphization), and it's unclear how we could fully monomorphize programs containing Rank-2 types. This means compiling programs which include Rank-2 types (or higher) would require sacrificing monomorphization, which would substantially degrade runtime performance.
|
||||
|
||||
As such, the plan is for Roc to stick with Rank-1 types indefinitely.
|
||||
|
||||
### Higher-kinded polymorphism
|
||||
|
||||
I want to be really clear about this one: the explicit plan is that Roc will never support higher-kinded polymorphism.
|
||||
The explicit plan is that Roc will never support higher-kinded polymorphism.
|
||||
|
||||
On the technical side, the reasons for this are ordinary: I understand the practical benefits and
|
||||
drawbacks of HKP, and I think the drawbacks outweigh the benefits when it comes to Roc. (Those who come to a
|
||||
different conclusion may think HKP's drawbacks would be less of a big a deal in Roc than I do. That's reasonable;
|
||||
we programmers often weigh the same trade-offs differently.) To be clear, I think this in the specific context of
|
||||
Roc; there are plenty of other languages where HKP seems like a great fit. For example, it's hard to imagine Haskell
|
||||
without it. Similarly, I think lifetime annotations are a great fit for Rust, but don't think they'd be right
|
||||
for Roc either.
|
||||
On the technical side, the reasons for this are ordinary: like any language feature, HKP has both benefits and drawbacks,
|
||||
and in the context of Roc, the drawbacks seem to outweigh the benefits. (Those who come to a different conclusion may
|
||||
think HKP's drawbacks would be less of a big a deal in Roc. That's reasonable; we programmers often weigh the same
|
||||
trade-offs differently.) To be clear, this analysis of HKP is in the specific context of Roc; there are plenty of
|
||||
other languages where HKP seems like a great fit. For example, it's hard to imagine Haskell without it. Similarly,
|
||||
lifetime annotations might be a natural fit for Rust, but they wouldn't be a good fit for Roc either.
|
||||
|
||||
I also think it's important to consider the cultural implications of deciding whether or not to support HKP.
|
||||
To illustrate what I mean, imagine this conversation:
|
||||
It's also important to consider the cultural implications of deciding whether or not to support HKP.
|
||||
To illustrate these implications, imagine this conversation:
|
||||
|
||||
**Programmer 1:** "How do you feel about higher-kinded polymorphism?"
|
||||
|
||||
|
|
@ -225,9 +198,9 @@ To illustrate what I mean, imagine this conversation:
|
|||
|
||||
**Programmer 2:** "OH NO."
|
||||
|
||||
I've had several variations of this conversation: I'm talking about higher-kinded types,
|
||||
another programmer asks what that means, I give monads as an example, and their reaction is strongly negative.
|
||||
I've also had plenty of conversations with programmers who love HKP and vigorously advocate for its addition
|
||||
For some, this conversation does not require imagining, because it's so familiar: higher-kinded types come up in
|
||||
conversation, another programmer asks what that means, monads are given as an example, and their reaction is
|
||||
strongly negative. On the flip side, plenty of programmers love HKP and vigorously advocate for its addition
|
||||
to languages they use which don't have it. Feelings about HKP seem strongly divided, maybe more so
|
||||
than any other type system feature besides static and dynamic types.
|
||||
|
||||
|
|
@ -237,67 +210,28 @@ language will inevitably follow. If the language does support HKP, one or more a
|
|||
around monads will inevitably follow, along with corresponding cultural changes. (See Scala for example.)
|
||||
Culturally, to support HKP is to take a side, and to decline to support it is also to take a side.
|
||||
|
||||
Given this, language designers have three options:
|
||||
Given this, languages have three options:
|
||||
|
||||
- Have HKP and have Monad in the standard library. Embrace them and build a culture and ecosystem around them.
|
||||
- Have HKP and don't have Monad in the standard library. An alternate standard library built around monads will inevitably emerge, and both the community and ecosystem will divide themselves along pro-monad and anti-monad lines.
|
||||
- Don't have HKP; build a culture and ecosystem around other things.
|
||||
|
||||
Considering that these are the only three options, I think the best choice for Roc—not only on a technical
|
||||
level, but on a cultural level as well—is to make it clear that the plan is for Roc never to support HKP.
|
||||
I hope this clarity can save a lot of community members' time that would otherwise be spent on advocacy or
|
||||
arguing between the two sides of the divide. Again, I think it's completely reasonable for anyone to have a
|
||||
different preference, but given that language designers can only choose one of these options, I'm confident
|
||||
I've made the right choice for Roc by designing it never to have higher-kinded polymorphism.
|
||||
|
||||
## Why do Roc's syntax and standard library differ from Elm's?
|
||||
|
||||
Roc is a direct descendant of [Elm](https://elm-lang.org/). However, there are some differences between the two languages.
|
||||
|
||||
Syntactic differences are among these. This is a feature, not a bug; if Roc had identical syntax to Elm, then it's
|
||||
predictable that people would write code that was designed to work in both languages - and would then rely on
|
||||
that being true, for example by making a package which advertised "Works in both Elm and Roc!" This in turn
|
||||
would mean that later if either language were to change its syntax in a way that didn't make sense for the other,
|
||||
the result would be broken code and sadness.
|
||||
|
||||
So why does Roc have the specific syntax changes it does? Here are some brief explanations:
|
||||
|
||||
- `#` instead of `--` for comments - this allows [hashbang](https://senthilnayagan.medium.com/shebang-hashbang-10966b8f28a8)s to work without needing special syntax. That isn't a use case Elm supports, but it is one Roc is designed to support.
|
||||
- `{}` instead of `()` for the unit type - Elm has both, and they can both be used as a unit type. Since `{}` has other uses in the type system, but `()` doesn't, I consider it redundant and took it out.
|
||||
- `when`...`is` instead of `case`...`of` - I predict it will be easier for beginners to pick up, because usually the way I explain `case`...`of` to beginners is by saying the words "when" and "is" out loud - e.g. "when `color` is `Red`, it runs this first branch; when `color` is `Blue`, it runs this other branch..."
|
||||
- `:` instead of `=` for record field definitions (e.g. `{ foo: bar }` where Elm syntax would be `{ foo = bar }`): I like `=` being reserved for definitions, and `:` is the most popular alternative.
|
||||
- Backpassing syntax - since Roc is designed to be used for use cases like command-line apps, shell scripts, and servers, I expect chained effects to come up a lot more often than they do in Elm. I think backpassing is nice for those use cases, similarly to how `do` notation is nice for them in Haskell.
|
||||
- Tag unions instead of Elm's custom types (aka algebraic data types). This isn't just a syntactic change; tag unions are mainly in Roc because they can facilitate errors being accumulated across chained effects, which (as noted a moment ago) I expect to be a lot more common in Roc than in Elm. If you have tag unions, you don't really need a separate language feature for algebraic data types, since closed tag unions essentially work the same way - aside from not giving you a way to selectively expose variants or define phantom types. Roc's opaque types language feature covers those use cases instead.
|
||||
- No `::` operator, or `::` pattern matching for lists. Both of these are for the same reason: an Elm `List` is a linked list, so both prepending to it and removing an element from the front are very cheap operations. In contrast, a Roc `List` is a flat array, so both prepending to it and removing an element from the front are among the most expensive operations you can possibly do with it! To get good performance, this usage pattern should be encouraged in Elm and discouraged in Roc. Since having special syntax would encourage it, it would not be good for Roc to have that syntax!
|
||||
- No `<|` operator. In Elm, I almost exclusively found myself wanting to use this in conjunction with anonymous functions (e.g. `foo <| \bar -> ...`) or conditionals (e.g. `foo <| if bar then ...`). In Roc you can do both of these without the `<|`. That means the main remaining use for `<|` is to reduce parentheses, but I tend to think `|>` is better at that (or else the parens are fine), so after the other syntactic changes, I considered `<|` an unnecessary stylistic alternative to `|>` or parens.
|
||||
- The `|>` operator passes the expression before the `|>` as the _first_ argument to the function after the `|>` instead of as the last argument. See the section on currying for details on why this works this way.
|
||||
- `:` instead of `type alias` - I like to avoid reserved keywords for terms that are desirable in userspace, so that people don't have to name things `typ` because `type` is a reserved keyword, or `clazz` because `class` is reserved. (I couldn't think of satisfactory alternatives for `as`, `when`, `is`, or `if` other than different reserved keywords. I could see an argument for `then`—and maybe even `is`—being replaced with a `->` or `=>` or something, but I don't anticipate missing either of those words much in userspace. `then` is used in JavaScript promises, but I think there are several better names for that function.)
|
||||
- No underscores in variable names - I've seen Elm beginners reflexively use `snake_case` over `camelCase` and then need to un-learn the habit after the compiler accepted it. I'd rather have the compiler give feedback that this isn't the way to do it in Roc, and suggest a camelCase alternative. I've also seen underscores used for lazy naming, e.g. `foo` and then `foo_`. If lazy naming is the goal, `foo2` is just as concise as `foo_`, but `foo3` is more concise than `foo__`. So in a way, removing `_` is a forcing function for improved laziness. (Of course, more descriptive naming would be even better.) Acronyms also use camelCase despite being capitalized in English, eg. `xmlHttpRequest` for a variable and `XmlHttpRequest` for a type. Each word starts with a capital letter, so if acronyms are only capitals it's harder to see where the words start. eg. `XMLHTTPRequest` is less clear than `XmlHttpRequest`, unless you already know the acronyms.
|
||||
- Trailing commas - I've seen people walk away (in some cases physically!) from Elm as soon as they saw the leading commas in collection literals. While I think they've made a mistake by not pushing past this aesthetic preference to give the language a chance, I also would prefer not put them in a position to make such a mistake in the first place. Secondarily, while I'm personally fine with either style, between the two I prefer the look of trailing commas.
|
||||
- The `!` unary prefix operator. I didn't want to have a `Basics` module (more on that in a moment), and without `Basics`, this would either need to be called fully-qualified (`Bool.not`) or else a module import of `Bool.{ not }` would be necessary. Both seemed less nice than supporting the `!` prefix that's common to so many widely-used languages, especially when we already have a unary prefix operator of `-` for negation (e.g. `-x`).
|
||||
- `!=` for the inequality operator (instead of Elm's `/=`) - this one pairs more naturally with the `!` prefix operator and is also very common in other languages.
|
||||
|
||||
Roc also has a different standard library from Elm. Some of the differences come down to platforms and applications (e.g. having `Task` in Roc's standard library wouldn't make sense), but others do not. Here are some brief explanations:
|
||||
|
||||
- No `Basics` module. I wanted to have a simple rule of "all modules in the standard library are imported by default, and so are their exposed types," and that's it. Given that I wanted the comparison operators (e.g. `<`) to work only on numbers, it ended up that having `Num` and `Bool` modules meant that almost nothing would be left for a `Basics` equivalent in Roc except `identity` and `Never`. The Roc type `[]` (empty tag union) is equivalent to `Never`, so that wasn't necessary, and I generally think that `identity` is a good concept but a sign of an incomplete API whenever its use comes up in practice. For example, instead of calling `|> List.filterMap identity` I'd rather have access to a more self-descriptive function like `|> List.dropNothings`. With `Num` and `Bool`, and without `identity` and `Never`, there was nothing left in `Basics`.
|
||||
- `Str` instead of `String` - after using the `str` type in Rust, I realized I had no issue whatsoever with the more concise name, especially since it was used in so many places (similar to `Msg` and `Cmd` in Elm) - so I decided to save a couple of letters.
|
||||
- No function composition operators - I stopped using these in Elm so long ago, at one point I forgot they were in the language! See the FAQ entry on currying for details about why.
|
||||
- No `Char`. What most people think of as a "character" is a rendered glyph. However, rendered glyphs are comprised of [grapheme clusters](https://stackoverflow.com/a/27331885), which are a variable number of Unicode code points - and there's no upper bound on how many code points there can be in a single cluster. In a world of emoji, I think this makes `Char` error-prone and it's better to have `Str` be the only first-class unit. For convenience when working with unicode code points (e.g. for performance-critical tasks like parsing), the single-quote syntax is sugar for the corresponding `U32` code point - for example, writing `'鹏'` is exactly the same as writing `40527`. Like Rust, you get a compiler error if you put something in single quotes that's not a valid [Unicode scalar value](http://www.unicode.org/glossary/#unicode_scalar_value).
|
||||
- No `Maybe`. See the "Why doesn't Roc have a `Maybe`/`Option`/`Optional` type" FAQ question
|
||||
Considering that these are the only three options, an early decision in Roc's design—not only on a technical
|
||||
level, but on a cultural level as well—was to make it clear that the plan is for Roc never to support HKP.
|
||||
The hope is that this clarity can save a lot of community members' time that would otherwise be spent on advocacy or
|
||||
arguing between the two sides of the divide. Again, it's completely reasonable for anyone to have a different preference,
|
||||
but given that languages can only choose one of these options, it seems clear that the right choice for Roc
|
||||
is for it to never have higher-kinded polymorphism.
|
||||
|
||||
## Why aren't Roc functions curried by default?
|
||||
|
||||
Although technically any language with first-class functions makes it possible to curry
|
||||
any function (e.g. I can manually curry a Roc function `\x, y, z ->` by writing `\x -> \y -> \z ->` instead),
|
||||
any function (e.g. anyone can manually curry a Roc function `\x, y, z ->` by writing `\x -> \y -> \z ->` instead),
|
||||
typically what people mean when they say Roc isn't a curried language is that Roc functions aren't curried
|
||||
by default. For the rest of this section, I'll use "currying" as a shorthand for "functions that are curried
|
||||
by default. The rest of this section will use "currying" as a shorthand for "functions that are curried
|
||||
by default" for the sake of brevity.
|
||||
|
||||
As I see it, currying has one major upside and several major downsides. The upside:
|
||||
|
||||
- It makes function calls more concise in some cases.
|
||||
|
||||
The downsides:
|
||||
Currying makes function calls more concise in some cases, but it has several significant downsides:
|
||||
|
||||
- It lowers error message quality, because there can no longer be an error for "function called with too few arguments." (Calling a function with fewer arguments is always valid in curried functions; the error you get instead will unavoidably be some other sort of type mismatch, and it will be up to you to figure out that the real problem was that you forgot an argument.)
|
||||
- It makes the `|>` operator more error-prone in some cases.
|
||||
|
|
@ -306,10 +240,10 @@ The downsides:
|
|||
- It facilitates pointfree function composition. (More on why this is listed as a downside later.)
|
||||
|
||||
There's also a downside that it would make runtime performance of compiled programs worse by default,
|
||||
but I assume it would be possible to optimize that away at the cost of slightly longer compile times.
|
||||
but it would most likely be possible to optimize that away at the cost of slightly longer compile times.
|
||||
|
||||
I consider the one upside (conciseness in some places) extremely minor, and have almost never missed it in Roc.
|
||||
Here are some more details about the downsides as I see them.
|
||||
These downsides seem to outweigh the one upside (conciseness in some places). Here are some more details about each of
|
||||
the downsides.
|
||||
|
||||
### Currying and the `|>` operator
|
||||
|
||||
|
|
@ -324,11 +258,17 @@ Str.concat "Hello, " "World!"
|
|||
|> Str.concat "World!"
|
||||
```
|
||||
|
||||
In curried languages with a `|>` operator, the first expression still returns `"Hello, World!"` but the second one returns `"World!Hello, "`. This is because Roc's `|>` operator uses the expression before the `|>` as the _first_ argument, whereas in curried languages, `|>` uses it as the _last_ argument.
|
||||
It's unsurprising to most beginners that these work the same way; it's common for a beginner who has recently learned
|
||||
how `|>` works to assume that `|> Str.concat "!"` would concatenate `!` onto the end of a string.
|
||||
|
||||
(For example, this is how `|>` works in both [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/#function-symbols-and-operators) and in [Elm](https://package.elm-lang.org/packages/elm/core/1.0.5/Basics#|%3E), both of which are curried languages. In contrast, Roc's `|>` design uses the same argument ordering as [Elixir](https://hexdocs.pm/elixir/1.14.0/Kernel.html#%7C%3E/2) and [Gleam](https://gleam.run/book/tour/functions.html#pipe-operator), neither of which is a curried language.)
|
||||
This is not how it works in curried languages, however. In curried languages with a `|>` operator, the first expression
|
||||
still returns `"Hello, World!"` but the second one returns `"World!Hello, "` instead. This can be an unpleasant surprise
|
||||
for beginners, but even experienced users commonly find that this behavior is less useful than having both of
|
||||
these expressions evaluate to the same thing.
|
||||
|
||||
This comes up in other situations as well. For example, consider subtraction and division:
|
||||
In Roc, both expressions evaluate to the same thing because Roc's `|>` operator uses the expression before the `|>` as the _first_ argument, whereas in curried languages, `|>` uses it as the _last_ argument. For example, this is how `|>` works in both [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/#function-symbols-and-operators) and in [Elm](https://package.elm-lang.org/packages/elm/core/1.0.5/Basics#|%3E), both of which are curried languages. In contrast, Roc's `|>` design uses the same argument ordering as [Elixir](https://hexdocs.pm/elixir/1.14.0/Kernel.html#%7C%3E/2) and [Gleam](https://gleam.run/book/tour/functions.html#pipe-operator), none of which are curried languages.
|
||||
|
||||
This comes up in other situations besides string concatenation. For example, consider subtraction and division:
|
||||
|
||||
```elixir
|
||||
someNumber
|
||||
|
|
@ -340,11 +280,14 @@ someNumber
|
|||
|> Num.sub 1
|
||||
```
|
||||
|
||||
What do you expect these expressions to do?
|
||||
Again, it's reasonable to expect that `|> Num.div 2` will divide a number by 2, and that
|
||||
`|> Num.sub 1` will subtract 1 from a number. In Roc, this is how they work, but in
|
||||
curried languages they work the opposite way: `|> Num.div 2` takes the number 2 and
|
||||
divides it by a number, and `|> Num.sub 1` takes the number 1 and subtracts a number
|
||||
from it. This is once again both more surprising to beginners and less useful to
|
||||
experienced users.
|
||||
|
||||
In Roc, the first divides `someNumber` by 2 and the second one subtracts 1 from `someNumber`. In languages where `|>` uses the other argument ordering, the first example instead takes 2 and divides it by `someNumber`, while the second takes 1 and subtracts `someNumber` from it. This was a pain point I ran into with curried languages, and I was pleasantly surprised that changing the argument ordering in `|>` addressed the pain point.
|
||||
|
||||
This style has a second benefit when it comes to higher-order functions. Consider these two examples:
|
||||
The way `|>` works in Roc has a second benefit when it comes to higher-order functions. Consider these two examples:
|
||||
|
||||
```elixir
|
||||
answer = List.map numbers \num ->
|
||||
|
|
@ -387,29 +330,33 @@ answer =
|
|||
numbers
|
||||
```
|
||||
|
||||
This was also a pain point I'd encountered in curried languages. I prefer the way the former example reads, but that style doesn't work with the argument order that currying encourages for higher-order functions like `List.map`. (Prior to using curried languages, I'd used [CoffeeScript](https://coffeescript.org/) in a functional style with [`_.map`](https://underscorejs.org/#map), and was disappointed to realize that I could no longer use the enjoyable style of `answer = _.map numbers (num) -> …` as I had before. In Roc, this style works.)
|
||||
The Roc version of this is nicer in that it doesn't require parentheses around the function argument. A curried language
|
||||
could theoretically adopt Roc's style of `|>` (where it pipes in the first argument instead of the last argument), but
|
||||
to get this second benefit, the language would also need to have `List.map` take the function as its second argument
|
||||
instead of the first. However, this would work against currying's one upside; it would no longer work to write
|
||||
`(List.map negate)` if the `List.map` arguments were flipped, the way they are in Roc. So currying and `|>` are unavoidably
|
||||
in tension.
|
||||
|
||||
As a historical note, these stylistic benefits (of `|> Num.sub 1` working as expected, and being able to write `List.map numbers \num ->`) were not among the original reasons Roc did not have currying. These benefits were discovered after the decision had already been made that Roc would not be a curried language, and they served to reinforce after the fact that the decision was the right one for Roc given the language's goals.
|
||||
|
||||
### Currying and learning curve
|
||||
|
||||
Prior to designing Roc, I taught a lot of beginner [Elm](https://elm-lang.org/) workshops. Sometimes at
|
||||
conferences, sometimes for [Frontend Masters](https://frontendmasters.com/courses/intro-elm/),
|
||||
sometimes for free at local coding bootcamps or meetup groups.
|
||||
In total I've spent well over 100 hours standing in front of a class, introducing the students to their
|
||||
first pure functional programming language.
|
||||
Currying leads to function signatures that look surprising to beginners. For example, in Roc, the
|
||||
[`Bool.and`](https://www.roc-lang.org/builtins/Bool#and) function has the type `Bool, Bool -> Bool`. If Roc were a
|
||||
curried language, this function would instead have the type `Bool -> Bool -> Bool`. Since no mainstream programming
|
||||
languages today are curried, anyone who knows a mainstream language and is learning their first curried language will
|
||||
require additional explanation about why function types look this way.
|
||||
|
||||
Here was my experience teaching currying:
|
||||
This explanation is nontrivial. It requires explaining partial application, how curried functions facilitate partial
|
||||
application, how function signatures accurately reflect that they're curried, and going through examples for all of these.
|
||||
All of it builds up to the punchline that "technically, all functions in this language have a single argument," which
|
||||
some percentage of learners find interesting, and some percentage still find confusing even after all that explanation.
|
||||
|
||||
- The only way to avoid teaching it is to refuse to explain why multi-argument functions have multiple `->`s in them. (If you don't explain it, at least one student will ask about it - and many if not all of the others will wonder.)
|
||||
- Teaching currying properly takes a solid chunk of time, because it requires explaining partial application, explaining how curried functions facilitate partial application, how function signatures accurately reflect that they're curried, and going through examples for all of these.
|
||||
- Even after doing all this, and iterating on my approach each time to try to explain it more effectively than I had the time before, I'd estimate that under 50% of the class ended up actually understanding currying. I consistently heard that in practice it only "clicked" for most people after spending significantly more time writing code with it.
|
||||
|
||||
This is not the end of the world, especially because it's easy enough to think "okay, I still don't totally get this
|
||||
even after that explanation, but I can remember that function arguments are separated by `->` in this language
|
||||
and maybe I'll understand the rest later." (Which they almost always do, if they stick with the language.)
|
||||
Clearly currying doesn't preclude a language from being easy to learn, because Elm has currying, and Elm's learning
|
||||
curve is famously gentle.
|
||||
It's common for beginners to report that currying only "clicked" for them after spending significant time writing code
|
||||
in a curried language. This is not the end of the world, especially because it's easy enough to think "I still don't
|
||||
totally get this even after that explanation, but I can remember that function arguments are separated by `->` in this
|
||||
language and maybe I'll understand the rest later." Clearly currying doesn't preclude a language from being easy to learn,
|
||||
because Elm has currying, and Elm's learning curve is famously gentle.
|
||||
|
||||
That said, beginners who feel confused while learning the language are less likely to continue with it.
|
||||
And however easy Roc would be to learn if it had currying, the language is certainly easier to learn without it.
|
||||
|
|
@ -428,37 +375,26 @@ compose : (a -> b), (c -> a) -> (c -> b)
|
|||
compose = \f, g, x -> f (g x)
|
||||
```
|
||||
|
||||
Here's how I would instead write this:
|
||||
Here's a way to write it without pointfree function composition:
|
||||
|
||||
```elm
|
||||
reverseSort : List elem -> List elem
|
||||
reverseSort = \list -> List.reverse (List.sort list)
|
||||
```
|
||||
|
||||
I've consistently found that I can more quickly and accurately understand function definitions that use
|
||||
named arguments, even though the code is longer. I suspect this is because I'm faster at reading than I am at
|
||||
eta-expanding ( e.g. converting `List.sort` into `\l -> List.sort l` ). Whenever I read
|
||||
the top version I end up needing to mentally eta-expand it into the bottom version.
|
||||
In more complex examples (this is among the tamest pointfree function composition examples I've seen), I make
|
||||
a mistake in my mental eta-expansion, and misunderstand what the function is doing - which can cause bugs.
|
||||
It's common for programmers to build a mental model of what `compose List.reverse List.sort` does by mentally
|
||||
translating it into `\list -> List.reverse (List.sort list)`. This extra mental translation step makes it take
|
||||
longer to read and to understand despite being technically more concise. In more complex examples (this
|
||||
is among the tamest of pointfree function composition examples), the chances increase of making a mistake in
|
||||
the mental translation step, leading to a misundesrtanding of what the function is doing—which can cause bugs.
|
||||
|
||||
I assumed I would get faster and more accurate at this over time. However, by now it's been about a decade
|
||||
since I first learned about the technique, and I'm still slower and less accurate at reading code that uses
|
||||
pointfree function composition (including if I wrote it - but even moreso if I didn't) than code written with
|
||||
with named arguments. I've asked a lot of other programmers about their experiences with pointfree function
|
||||
composition over the years, and the overwhelming majority of responses have been consistent with my experience.
|
||||
|
||||
As such, my opinion about pointfree function composition has gotten less and less nuanced over time. I've now moved
|
||||
past "it's the right tool for the job, sometimes" to concluding it's best thought of as an antipattern. This is
|
||||
because I realized how much time I was spending evaluating on a case-by-case basis whether it might be the
|
||||
right fit for a given situation. The time spent on this analysis alone vastly outweighed the sum of all the
|
||||
benefits I got in the rare cases where I concluded it was a fit. So I've found the way to get the most out of
|
||||
pointfree function composition is to never even think about using it; every other strategy leads to a worse outcome.
|
||||
|
||||
Currying facilitates the antipattern of pointfree function composition, which I view as a downside of currying.
|
||||
Some languages place such a high value on conciseness that they would consider the conciceness upside to outweigh
|
||||
these downsides, but Roc is not one of those languages. It's considered stylistically better in Roc to write the
|
||||
second version above. Given this, since currying facilitates pointfree function composition, making Roc a curried
|
||||
language would have the downside of facilitating an antipattern in the language.
|
||||
|
||||
Stacking up all these downsides of currying against the one upside of making certain function calls more concise,
|
||||
I concluded that it would be a mistake to have it in Roc.
|
||||
it seems clear that Roc should not be a curried language.
|
||||
|
||||
## Will Roc ever have linear types, dependent types, refinement types, or uniqueness types?
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,26 @@ mv roc_nightly* roc_nightly
|
|||
cd roc_nightly
|
||||
|
||||
# test roc hello world
|
||||
./roc examples/helloWorld.roc
|
||||
./roc examples/helloWorld.roc
|
||||
|
||||
./roc examples/platform-switching/rocLovesRust.roc
|
||||
|
||||
./roc examples/platform-switching/rocLovesZig.roc
|
||||
run_zig_test=true
|
||||
# Detect macOS version
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
macos_version=$(sw_vers -productVersion)
|
||||
major_version=$(echo $macos_version | cut -d. -f1)
|
||||
|
||||
# If macOS 13, then set the flag to skip
|
||||
if [[ $major_version -eq 13 ]]; then
|
||||
echo "Skipping zig test on macOS 13 due to https://github.com/roc-lang/roc/issues/5590..."
|
||||
run_zig_test=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if $run_zig_test ; then
|
||||
./roc examples/platform-switching/rocLovesZig.roc
|
||||
fi
|
||||
|
||||
./roc examples/platform-switching/rocLovesC.roc
|
||||
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ mod cli_run {
|
|||
|
||||
if !actual.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with:\n{}\nbut instead got:\n{}\n stderr was:\n{}",
|
||||
"> expected output to end with:\n{}\n> but instead got:\n{}\n> stderr was:\n{}",
|
||||
expected_ending, actual, out.stderr
|
||||
);
|
||||
}
|
||||
|
|
@ -387,7 +387,7 @@ mod cli_run {
|
|||
let mut custom_flags: Vec<&str> = Vec::new();
|
||||
|
||||
match executable_filename {
|
||||
"form" | "hello-gui" | "breakout" | "libhello" => {
|
||||
"form" | "hello-gui" | "breakout" | "libhello" | "inspect-gui" => {
|
||||
// Since these require things the build system often doesn't have
|
||||
// (e.g. GUIs open a window, Ruby needs ruby installed, WASM needs a browser)
|
||||
// we do `roc build` on them but don't run them.
|
||||
|
|
@ -938,6 +938,30 @@ mod cli_run {
|
|||
test_roc_expect("examples/parser/package", "ParserHttp.roc")
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(windows, ignore)]
|
||||
fn inspect_logging() {
|
||||
test_roc_app_slim(
|
||||
"examples",
|
||||
"inspect-logging.roc",
|
||||
"inspect-logging",
|
||||
r#"{people: [{firstName: "John", lastName: "Smith", age: 27, hasBeard: true, favoriteColor: Blue}, {firstName: "Debby", lastName: "Johnson", age: 47, hasBeard: false, favoriteColor: Green}, {firstName: "Jane", lastName: "Doe", age: 33, hasBeard: false, favoriteColor: (RGB (255, 255, 0))}], friends: [{2}, {2}, {0, 1}]}
|
||||
"#,
|
||||
UseValgrind::Yes,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inspect_gui() {
|
||||
test_roc_app_slim(
|
||||
"examples",
|
||||
"inspect-gui.roc",
|
||||
"inspect-gui",
|
||||
"",
|
||||
UseValgrind::No,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO not sure if this cfg should still be here: #[cfg(not(debug_assertions))]
|
||||
// this is for testing the benchmarks, to perform proper benchmarks see crates/cli/benches/README.md
|
||||
mod test_benchmarks {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ Model position : {
|
|||
openSet : Set position,
|
||||
costs : Dict position F64,
|
||||
cameFrom : Dict position position,
|
||||
} | position has Hash & Eq
|
||||
} where position implements Hash & Eq
|
||||
|
||||
initialModel : position -> Model position | position has Hash & Eq
|
||||
initialModel : position -> Model position where position implements Hash & Eq
|
||||
initialModel = \start -> {
|
||||
evaluated: Set.empty {},
|
||||
openSet: Set.single start,
|
||||
|
|
@ -20,7 +20,7 @@ initialModel = \start -> {
|
|||
cameFrom: Dict.empty {},
|
||||
}
|
||||
|
||||
cheapestOpen : (position -> F64), Model position -> Result position {} | position has Hash & Eq
|
||||
cheapestOpen : (position -> F64), Model position -> Result position {} where position implements Hash & Eq
|
||||
cheapestOpen = \costFn, model ->
|
||||
model.openSet
|
||||
|> Set.toList
|
||||
|
|
@ -35,13 +35,13 @@ cheapestOpen = \costFn, model ->
|
|||
|> Result.map .position
|
||||
|> Result.mapErr (\_ -> {})
|
||||
|
||||
reconstructPath : Dict position position, position -> List position | position has Hash & Eq
|
||||
reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
|
||||
reconstructPath = \cameFrom, goal ->
|
||||
when Dict.get cameFrom goal is
|
||||
Err _ -> []
|
||||
Ok next -> List.append (reconstructPath cameFrom next) goal
|
||||
|
||||
updateCost : position, position, Model position -> Model position | position has Hash & Eq
|
||||
updateCost : position, position, Model position -> Model position where position implements Hash & Eq
|
||||
updateCost = \current, neighbor, model ->
|
||||
newCameFrom =
|
||||
Dict.insert model.cameFrom neighbor current
|
||||
|
|
@ -70,7 +70,7 @@ updateCost = \current, neighbor, model ->
|
|||
else
|
||||
model
|
||||
|
||||
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} | position has Hash & Eq
|
||||
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} where position implements Hash & Eq
|
||||
astar = \costFn, moveFn, goal, model ->
|
||||
when cheapestOpen (\source -> costFn source goal) model is
|
||||
Err {} -> Err {}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ platform "benchmarks"
|
|||
imports [Task.{ Task }]
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost : Task {} []
|
||||
mainForHost = main
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use roc_module::low_level::LowLevel;
|
|||
use roc_module::symbol::Symbol;
|
||||
|
||||
use roc_mono::ir::{
|
||||
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLayouts,
|
||||
Call, CallType, EntryPoint, ErasedField, Expr, HigherOrderLowLevel, HostExposedLambdaSet,
|
||||
ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
|
|
@ -137,15 +137,17 @@ fn bytes_as_ascii(bytes: &[u8]) -> String {
|
|||
buf
|
||||
}
|
||||
|
||||
pub fn spec_program<'a, 'r, I>(
|
||||
pub fn spec_program<'a, 'r, I1, I2>(
|
||||
arena: &'a Bump,
|
||||
interner: &'r STLayoutInterner<'a>,
|
||||
opt_level: OptLevel,
|
||||
entry_point: roc_mono::ir::EntryPoint<'a>,
|
||||
procs: I,
|
||||
procs: I1,
|
||||
hels: I2,
|
||||
) -> Result<morphic_lib::Solutions>
|
||||
where
|
||||
I: Iterator<Item = &'r Proc<'a>>,
|
||||
I1: Iterator<Item = &'r Proc<'a>>,
|
||||
I2: Iterator<Item = &'r HostExposedLambdaSet<'a>>,
|
||||
{
|
||||
let main_module = {
|
||||
let mut m = ModDefBuilder::new();
|
||||
|
|
@ -183,50 +185,36 @@ where
|
|||
let mut host_exposed_functions = Vec::new();
|
||||
let mut erased_functions = Vec::new();
|
||||
|
||||
for hels in hels {
|
||||
match hels.raw_function_layout {
|
||||
RawFunctionLayout::Function(_, _, _) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, it, Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, it, Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes =
|
||||
func_name_bytes_help(hels.symbol, [], Niche::NONE, hels.proc_layout.result);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all other functions
|
||||
for proc in procs {
|
||||
let bytes = func_name_bytes(proc);
|
||||
let func_name = FuncName(&bytes);
|
||||
|
||||
if let HostExposedLayouts::HostExposed { aliases, .. } = &proc.host_exposed_layouts {
|
||||
for (_, hels) in aliases {
|
||||
match hels.raw_function_layout {
|
||||
RawFunctionLayout::Function(_, _, _) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
it,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ErasedFunction(..) => {
|
||||
let it = hels.proc_layout.arguments.iter().copied();
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
it,
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||
let bytes = func_name_bytes_help(
|
||||
hels.symbol,
|
||||
[],
|
||||
Niche::NONE,
|
||||
hels.proc_layout.result,
|
||||
);
|
||||
|
||||
host_exposed_functions.push((bytes, hels.proc_layout.arguments));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debug() {
|
||||
eprintln!(
|
||||
"{:?}: {:?} with {:?} args",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use roc_command_utils::{cargo, clang, rustup, zig};
|
|||
use roc_error_macros::internal_error;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::DirEntry;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -785,11 +786,25 @@ fn get_target_str(target: &Triple) -> &str {
|
|||
}
|
||||
}
|
||||
|
||||
fn nix_path_opt() -> Option<String> {
|
||||
env::var_os("NIX_GLIBC_PATH").map(|path| path.into_string().unwrap())
|
||||
fn nix_paths() -> Vec<String> {
|
||||
let mut paths = vec![];
|
||||
|
||||
if let Some(nix_libgcc_s_path) = env::var_os("NIX_LIBGCC_S_PATH") {
|
||||
paths.push(nix_libgcc_s_path.into_string().unwrap())
|
||||
}
|
||||
|
||||
if let Some(nix_glibc_path) = nix_glibc_path_opt() {
|
||||
paths.push(nix_glibc_path.into_string().unwrap())
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
fn library_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> {
|
||||
fn nix_glibc_path_opt() -> Option<OsString> {
|
||||
env::var_os("NIX_GLIBC_PATH")
|
||||
}
|
||||
|
||||
fn build_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> {
|
||||
let mut guess_path = PathBuf::new();
|
||||
for s in segments {
|
||||
guess_path.push(s);
|
||||
|
|
@ -812,22 +827,21 @@ fn library_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> {
|
|||
/// match will be returned.
|
||||
///
|
||||
/// If there are no matches, [`None`] will be returned.
|
||||
fn look_for_library(lib_dirs: &[&[&str]], lib_filename: &str) -> Option<PathBuf> {
|
||||
fn look_for_library(lib_dirs: &[PathBuf], lib_filename: &str) -> Option<PathBuf> {
|
||||
lib_dirs
|
||||
.iter()
|
||||
.map(|lib_dir| {
|
||||
lib_dir.iter().fold(PathBuf::new(), |mut path, segment| {
|
||||
path.push(segment);
|
||||
path
|
||||
})
|
||||
})
|
||||
.map(|mut path| {
|
||||
path.push(lib_filename);
|
||||
path
|
||||
.map(|path| {
|
||||
let mut path_cl = path.clone();
|
||||
path_cl.push(lib_filename);
|
||||
path_cl
|
||||
})
|
||||
.find(|path| path.exists())
|
||||
}
|
||||
|
||||
fn strs_to_path(strs: &[&str]) -> PathBuf {
|
||||
strs.iter().collect()
|
||||
}
|
||||
|
||||
fn link_linux(
|
||||
target: &Triple,
|
||||
output_path: PathBuf,
|
||||
|
|
@ -862,50 +876,37 @@ fn link_linux(
|
|||
));
|
||||
}
|
||||
|
||||
// Some things we'll need to build a list of dirs to check for libraries
|
||||
let maybe_nix_path = nix_path_opt();
|
||||
let usr_lib_arch = ["/usr", "lib", &architecture];
|
||||
let lib_arch = ["/lib", &architecture];
|
||||
let nix_path_segments;
|
||||
let lib_dirs_if_nix: [&[&str]; 5];
|
||||
let lib_dirs_if_nonix: [&[&str]; 4];
|
||||
let nix_paths_vec_string = nix_paths();
|
||||
let nix_paths_vec: Vec<PathBuf> = nix_paths_vec_string.iter().map(PathBuf::from).collect();
|
||||
let usr_lib_arch_path = strs_to_path(&["/usr", "lib", &architecture]);
|
||||
let lib_arch_path = strs_to_path(&["/lib", &architecture]);
|
||||
|
||||
// Build the aformentioned list
|
||||
let lib_dirs: &[&[&str]] =
|
||||
// give preference to nix_path if it's defined, this prevents bugs
|
||||
if let Some(nix_path) = &maybe_nix_path {
|
||||
nix_path_segments = [nix_path.as_str()];
|
||||
lib_dirs_if_nix = [
|
||||
&nix_path_segments,
|
||||
&usr_lib_arch,
|
||||
&lib_arch,
|
||||
&["/usr", "lib"],
|
||||
&["/usr", "lib64"],
|
||||
];
|
||||
&lib_dirs_if_nix
|
||||
} else {
|
||||
lib_dirs_if_nonix = [
|
||||
&usr_lib_arch,
|
||||
&lib_arch,
|
||||
&["/usr", "lib"],
|
||||
&["/usr", "lib64"],
|
||||
];
|
||||
&lib_dirs_if_nonix
|
||||
};
|
||||
let mut lib_dirs: Vec<PathBuf> = vec![];
|
||||
|
||||
// start with nix paths, this prevents version incompatibility
|
||||
if !nix_paths_vec.is_empty() {
|
||||
lib_dirs.extend(nix_paths_vec)
|
||||
}
|
||||
|
||||
lib_dirs.extend([
|
||||
usr_lib_arch_path,
|
||||
lib_arch_path,
|
||||
strs_to_path(&["/usr", "lib"]),
|
||||
strs_to_path(&["/usr", "lib64"]),
|
||||
]);
|
||||
|
||||
// Look for the libraries we'll need
|
||||
|
||||
let libgcc_name = "libgcc_s.so.1";
|
||||
let libgcc_path = look_for_library(lib_dirs, libgcc_name);
|
||||
let libgcc_path = look_for_library(&lib_dirs, libgcc_name);
|
||||
|
||||
let crti_name = "crti.o";
|
||||
let crti_path = look_for_library(lib_dirs, crti_name);
|
||||
let crti_path = look_for_library(&lib_dirs, crti_name);
|
||||
|
||||
let crtn_name = "crtn.o";
|
||||
let crtn_path = look_for_library(lib_dirs, crtn_name);
|
||||
let crtn_path = look_for_library(&lib_dirs, crtn_name);
|
||||
|
||||
let scrt1_name = "Scrt1.o";
|
||||
let scrt1_path = look_for_library(lib_dirs, scrt1_name);
|
||||
let scrt1_path = look_for_library(&lib_dirs, scrt1_name);
|
||||
|
||||
// Unwrap all the paths at once so we can inform the user of all missing libs at once
|
||||
let (libgcc_path, crti_path, crtn_path, scrt1_path) =
|
||||
|
|
@ -925,7 +926,13 @@ fn link_linux(
|
|||
|
||||
let dirs = lib_dirs
|
||||
.iter()
|
||||
.map(|segments| segments.join("/"))
|
||||
.map(|path_buf| {
|
||||
path_buf
|
||||
.as_path()
|
||||
.to_str()
|
||||
.unwrap_or("FAILED TO CONVERT PATH TO STR")
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
eprintln!("We looked in the following directories:\n{dirs}");
|
||||
|
|
@ -936,13 +943,16 @@ fn link_linux(
|
|||
let ld_linux = match target.architecture {
|
||||
Architecture::X86_64 => {
|
||||
// give preference to nix_path if it's defined, this prevents bugs
|
||||
if let Some(nix_path) = nix_path_opt() {
|
||||
library_path([&nix_path, "ld-linux-x86-64.so.2"])
|
||||
if let Some(nix_glibc_path) = nix_glibc_path_opt() {
|
||||
build_path([
|
||||
&nix_glibc_path.into_string().unwrap(),
|
||||
"ld-linux-x86-64.so.2",
|
||||
])
|
||||
} else {
|
||||
library_path(["/lib64", "ld-linux-x86-64.so.2"])
|
||||
build_path(["/lib64", "ld-linux-x86-64.so.2"])
|
||||
}
|
||||
}
|
||||
Architecture::Aarch64(_) => library_path(["/lib", "ld-linux-aarch64.so.1"]),
|
||||
Architecture::Aarch64(_) => build_path(["/lib", "ld-linux-aarch64.so.1"]),
|
||||
_ => internal_error!(
|
||||
"TODO gracefully handle unsupported linux architecture: {:?}",
|
||||
target.architecture
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ fn gen_from_mono_module_llvm<'a>(
|
|||
&loaded.layout_interner,
|
||||
opt_level,
|
||||
loaded.procedures,
|
||||
loaded.host_exposed_lambda_sets,
|
||||
entry_point,
|
||||
Some(&app_ll_file),
|
||||
&loaded.glue_layouts,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Bool
|
|||
## be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the
|
||||
## [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard
|
||||
## specifies that two `NaN`s are not equal.
|
||||
Eq has
|
||||
Eq implements
|
||||
## Returns `Bool.true` if the input values are equal. This is
|
||||
## equivalent to the logic
|
||||
## [XNOR](https://en.wikipedia.org/wiki/Logical_equality) gate. The infix
|
||||
|
|
@ -30,11 +30,11 @@ Eq has
|
|||
## for more detail.
|
||||
## 5. Functions cannot be compared for structural equality, therefore Roc
|
||||
## cannot derive `isEq` for types that contain functions.
|
||||
isEq : a, a -> Bool | a has Eq
|
||||
isEq : a, a -> Bool where a implements Eq
|
||||
|
||||
## Represents the boolean true and false using an opaque type.
|
||||
## `Bool` implements the `Eq` ability.
|
||||
Bool := [True, False] has [Eq { isEq: boolIsEq }]
|
||||
Bool := [True, False] implements [Eq { isEq: boolIsEq }]
|
||||
|
||||
boolIsEq = \@Bool b1, @Bool b2 -> structuralEq b1 b2
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ not : Bool -> Bool
|
|||
## expect (Bool.false != Bool.false) == Bool.false
|
||||
## expect "Apples" != "Oranges"
|
||||
## ```
|
||||
isNotEq : a, a -> Bool | a has Eq
|
||||
isNotEq : a, a -> Bool where a implements Eq
|
||||
isNotEq = \a, b -> structuralNotEq a b
|
||||
|
||||
# INTERNAL COMPILER USE ONLY: used to lower calls to `isEq` to structural
|
||||
|
|
|
|||
|
|
@ -73,30 +73,30 @@ DecodeResult val : { result : Result val DecodeError, rest : List U8 }
|
|||
## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded
|
||||
## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting]
|
||||
## ability
|
||||
Decoder val fmt := List U8, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
Decoder val fmt := List U8, fmt -> DecodeResult val where fmt implements DecoderFormatting
|
||||
|
||||
## Definition of the [Decoding] ability
|
||||
Decoding has
|
||||
decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting
|
||||
Decoding implements
|
||||
decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting
|
||||
|
||||
## Definition of the [DecoderFormatting] ability
|
||||
DecoderFormatting has
|
||||
u8 : Decoder U8 fmt | fmt has DecoderFormatting
|
||||
u16 : Decoder U16 fmt | fmt has DecoderFormatting
|
||||
u32 : Decoder U32 fmt | fmt has DecoderFormatting
|
||||
u64 : Decoder U64 fmt | fmt has DecoderFormatting
|
||||
u128 : Decoder U128 fmt | fmt has DecoderFormatting
|
||||
i8 : Decoder I8 fmt | fmt has DecoderFormatting
|
||||
i16 : Decoder I16 fmt | fmt has DecoderFormatting
|
||||
i32 : Decoder I32 fmt | fmt has DecoderFormatting
|
||||
i64 : Decoder I64 fmt | fmt has DecoderFormatting
|
||||
i128 : Decoder I128 fmt | fmt has DecoderFormatting
|
||||
f32 : Decoder F32 fmt | fmt has DecoderFormatting
|
||||
f64 : Decoder F64 fmt | fmt has DecoderFormatting
|
||||
dec : Decoder Dec fmt | fmt has DecoderFormatting
|
||||
bool : Decoder Bool fmt | fmt has DecoderFormatting
|
||||
string : Decoder Str fmt | fmt has DecoderFormatting
|
||||
list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting
|
||||
DecoderFormatting implements
|
||||
u8 : Decoder U8 fmt where fmt implements DecoderFormatting
|
||||
u16 : Decoder U16 fmt where fmt implements DecoderFormatting
|
||||
u32 : Decoder U32 fmt where fmt implements DecoderFormatting
|
||||
u64 : Decoder U64 fmt where fmt implements DecoderFormatting
|
||||
u128 : Decoder U128 fmt where fmt implements DecoderFormatting
|
||||
i8 : Decoder I8 fmt where fmt implements DecoderFormatting
|
||||
i16 : Decoder I16 fmt where fmt implements DecoderFormatting
|
||||
i32 : Decoder I32 fmt where fmt implements DecoderFormatting
|
||||
i64 : Decoder I64 fmt where fmt implements DecoderFormatting
|
||||
i128 : Decoder I128 fmt where fmt implements DecoderFormatting
|
||||
f32 : Decoder F32 fmt where fmt implements DecoderFormatting
|
||||
f64 : Decoder F64 fmt where fmt implements DecoderFormatting
|
||||
dec : Decoder Dec fmt where fmt implements DecoderFormatting
|
||||
bool : Decoder Bool fmt where fmt implements DecoderFormatting
|
||||
string : Decoder Str fmt where fmt implements DecoderFormatting
|
||||
list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting
|
||||
|
||||
## `record state stepField finalizer` decodes a record field-by-field.
|
||||
##
|
||||
|
|
@ -104,7 +104,7 @@ DecoderFormatting has
|
|||
## `Skip` if the field is not a part of the decoded record.
|
||||
##
|
||||
## `finalizer` should produce the record value from the decoded `state`.
|
||||
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
|
||||
|
||||
## `tuple state stepElem finalizer` decodes a tuple element-by-element.
|
||||
##
|
||||
|
|
@ -113,7 +113,7 @@ DecoderFormatting has
|
|||
## index passed to `stepElem` is 0-indexed.
|
||||
##
|
||||
## `finalizer` should produce the tuple value from the decoded `state`.
|
||||
tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
|
||||
|
||||
## Build a custom [Decoder] function. For example the implementation of
|
||||
## `decodeBool` could be defined as follows;
|
||||
|
|
@ -125,11 +125,11 @@ DecoderFormatting has
|
|||
## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.drop bytes 4 }
|
||||
## _ -> { result: Err TooShort, rest: bytes }
|
||||
## ```
|
||||
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
|
||||
custom = \decode -> @Decoder decode
|
||||
|
||||
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
|
||||
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
|
||||
decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
|
||||
|
||||
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
|
||||
|
|
@ -141,7 +141,7 @@ decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
|
|||
##
|
||||
## actual.result == expected
|
||||
## ```
|
||||
fromBytesPartial : List U8, fmt -> DecodeResult val | val has Decoding, fmt has DecoderFormatting
|
||||
fromBytesPartial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
|
||||
fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
|
||||
|
||||
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
|
||||
|
|
@ -155,7 +155,7 @@ fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
|
|||
##
|
||||
## actual == expected
|
||||
## ```
|
||||
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError | val has Decoding, fmt has DecoderFormatting
|
||||
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
|
||||
fromBytes = \bytes, fmt ->
|
||||
when fromBytesPartial bytes fmt is
|
||||
{ result, rest } ->
|
||||
|
|
|
|||
|
|
@ -95,13 +95,13 @@ Dict k v := {
|
|||
# TODO: As an optimization, we can make all of these lists in one allocation
|
||||
# TODO: Grow data with the rest of the hashmap. This will require creating a list of garbage data.
|
||||
# TODO: Change remove to use tombstones. Store the tombstones in a bitmap.
|
||||
# TODO: define Eq and Hash that are unordered. Only if value has hash/eq?
|
||||
# TODO: define Eq and Hash that are unordered. Only if value implements hash/eq?
|
||||
metadata : List I8,
|
||||
dataIndices : List Nat,
|
||||
data : List (k, v),
|
||||
size : Nat,
|
||||
} | k has Hash & Eq
|
||||
has [
|
||||
} where k implements Hash & Eq
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
|
|
@ -110,7 +110,7 @@ Dict k v := {
|
|||
},
|
||||
]
|
||||
|
||||
isEq : Dict k v, Dict k v -> Bool | k has Hash & Eq, v has Eq
|
||||
isEq : Dict k v, Dict k v -> Bool where k implements Hash & Eq, v implements Eq
|
||||
isEq = \xs, ys ->
|
||||
if len xs != len ys then
|
||||
Bool.false
|
||||
|
|
@ -123,14 +123,14 @@ isEq = \xs, ys ->
|
|||
_ ->
|
||||
Break Bool.false
|
||||
|
||||
hashDict : hasher, Dict k v -> hasher | k has Hash & Eq, v has Hash, hasher has Hasher
|
||||
hashDict : hasher, Dict k v -> hasher where k implements Hash & Eq, v implements Hash, hasher implements Hasher
|
||||
hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
|
||||
|
||||
## Return an empty dictionary.
|
||||
## ```
|
||||
## emptyDict = Dict.empty {}
|
||||
## ```
|
||||
empty : {} -> Dict k v | k has Hash & Eq
|
||||
empty : {} -> Dict * *
|
||||
empty = \{} ->
|
||||
@Dict {
|
||||
metadata: List.repeat emptySlot 8,
|
||||
|
|
@ -151,12 +151,12 @@ capacity : Dict * * -> Nat
|
|||
capacity = \@Dict { dataIndices } ->
|
||||
cap = List.len dataIndices
|
||||
|
||||
cap - Num.shiftRightZfBy cap 3
|
||||
Num.subWrap cap (Num.shiftRightZfBy cap 3)
|
||||
|
||||
## Return a dictionary with space allocated for a number of entries. This
|
||||
## may provide a performance optimization if you know how many entries will be
|
||||
## inserted.
|
||||
withCapacity : Nat -> Dict k v | k has Hash & Eq
|
||||
withCapacity : Nat -> Dict * *
|
||||
withCapacity = \_ ->
|
||||
# TODO: power of 2 * 8 and actual implementation
|
||||
empty {}
|
||||
|
|
@ -167,7 +167,7 @@ withCapacity = \_ ->
|
|||
## Dict.single "A" "B"
|
||||
## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B")
|
||||
## ```
|
||||
single : k, v -> Dict k v | k has Hash & Eq
|
||||
single : k, v -> Dict k v where k implements Hash & Eq
|
||||
single = \k, v ->
|
||||
insert (empty {}) k v
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ single = \k, v ->
|
|||
## |> Dict.insert 4 "Four"
|
||||
## |> Bool.isEq (Dict.fromList [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])
|
||||
## ```
|
||||
fromList : List (k, v) -> Dict k v | k has Hash & Eq
|
||||
fromList : List (k, v) -> Dict k v where k implements Hash & Eq
|
||||
fromList = \data ->
|
||||
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
|
||||
List.walk data (empty {}) (\dict, (k, v) -> insert dict k v)
|
||||
|
|
@ -221,7 +221,7 @@ isEmpty = \@Dict { size } ->
|
|||
##
|
||||
## expect Dict.len clearSongs == 0
|
||||
## ```
|
||||
clear : Dict k v -> Dict k v | k has Hash & Eq
|
||||
clear : Dict k v -> Dict k v where k implements Hash & Eq
|
||||
clear = \@Dict { metadata, dataIndices, data } ->
|
||||
cap = List.len dataIndices
|
||||
|
||||
|
|
@ -241,7 +241,7 @@ clear = \@Dict { metadata, dataIndices, data } ->
|
|||
## Convert each value in the dictionary to something new, by calling a conversion
|
||||
## function on each of them which receives both the key and the old value. Then return a
|
||||
## new dictionary containing the same keys and the converted values.
|
||||
map : Dict k a, (k, a -> b) -> Dict k b | k has Hash & Eq, b has Hash & Eq
|
||||
map : Dict k a, (k, a -> b) -> Dict k b where k implements Hash & Eq, b implements Hash & Eq
|
||||
map = \dict, transform ->
|
||||
init = withCapacity (capacity dict)
|
||||
|
||||
|
|
@ -253,7 +253,7 @@ map = \dict, transform ->
|
|||
## (using [Dict.insertAll]) into one dictionary.
|
||||
##
|
||||
## You may know a similar function named `concatMap` in other languages.
|
||||
joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y | a has Hash & Eq, x has Hash & Eq
|
||||
joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y where a implements Hash & Eq, x implements Hash & Eq
|
||||
joinMap = \dict, transform ->
|
||||
init = withCapacity (capacity dict) # Might be a pessimization
|
||||
|
||||
|
|
@ -271,7 +271,7 @@ joinMap = \dict, transform ->
|
|||
## |> Dict.walk 0 (\count, _, qty -> count + qty)
|
||||
## |> Bool.isEq 36
|
||||
## ```
|
||||
walk : Dict k v, state, (state, k, v -> state) -> state | k has Hash & Eq
|
||||
walk : Dict k v, state, (state, k, v -> state) -> state where k implements Hash & Eq
|
||||
walk = \@Dict { data }, initialState, transform ->
|
||||
List.walk data initialState (\state, (k, v) -> transform state k v)
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ walk = \@Dict { data }, initialState, transform ->
|
|||
##
|
||||
## expect someoneIsAnAdult == Bool.true
|
||||
## ```
|
||||
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state | k has Hash & Eq
|
||||
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state where k implements Hash & Eq
|
||||
walkUntil = \@Dict { data }, initialState, transform ->
|
||||
List.walkUntil data initialState (\state, (k, v) -> transform state k v)
|
||||
|
||||
|
|
@ -318,7 +318,7 @@ walkUntil = \@Dict { data }, initialState, transform ->
|
|||
## expect Dict.get dictionary 1 == Ok "Apple"
|
||||
## expect Dict.get dictionary 2000 == Err KeyNotFound
|
||||
## ```
|
||||
get : Dict k v, k -> Result v [KeyNotFound] | k has Hash & Eq
|
||||
get : Dict k v, k -> Result v [KeyNotFound] where k implements Hash & Eq
|
||||
get = \@Dict { metadata, dataIndices, data }, key ->
|
||||
hashKey =
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|
|
@ -346,7 +346,7 @@ get = \@Dict { metadata, dataIndices, data }, key ->
|
|||
## |> Dict.contains 1234
|
||||
## |> Bool.isEq Bool.true
|
||||
## ```
|
||||
contains : Dict k v, k -> Bool | k has Hash & Eq
|
||||
contains : Dict k v, k -> Bool where k implements Hash & Eq
|
||||
contains = \@Dict { metadata, dataIndices, data }, key ->
|
||||
hashKey =
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|
|
@ -371,7 +371,7 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
|
|||
## |> Dict.get "Apples"
|
||||
## |> Bool.isEq (Ok 12)
|
||||
## ```
|
||||
insert : Dict k v, k, v -> Dict k v | k has Hash & Eq
|
||||
insert : Dict k v, k, v -> Dict k v where k implements Hash & Eq
|
||||
insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
|
||||
hashKey =
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|
|
@ -401,7 +401,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
|
|||
metadata,
|
||||
dataIndices,
|
||||
data,
|
||||
size: size + 1,
|
||||
size: Num.addWrap size 1,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -417,7 +417,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
|
|||
## |> Dict.len
|
||||
## |> Bool.isEq 0
|
||||
## ```
|
||||
remove : Dict k v, k -> Dict k v | k has Hash & Eq
|
||||
remove : Dict k v, k -> Dict k v where k implements Hash & Eq
|
||||
remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
||||
# TODO: change this from swap remove to tombstone and test is performance is still good.
|
||||
hashKey =
|
||||
|
|
@ -430,7 +430,7 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
|||
|
||||
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
|
||||
Ok index ->
|
||||
last = List.len data - 1
|
||||
last = Num.subWrap (List.len data) 1
|
||||
dataIndex = listGetUnsafe dataIndices index
|
||||
|
||||
if dataIndex == last then
|
||||
|
|
@ -438,7 +438,7 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
|||
metadata: List.set metadata index deletedSlot,
|
||||
dataIndices,
|
||||
data: List.dropLast data,
|
||||
size: size - 1,
|
||||
size: Num.subWrap size 1,
|
||||
}
|
||||
else
|
||||
swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data, size }) index last
|
||||
|
|
@ -461,7 +461,7 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
|||
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
|
||||
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
|
||||
## ```
|
||||
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq
|
||||
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v where k implements Hash & Eq
|
||||
update = \dict, key, alter ->
|
||||
# TODO: look into optimizing by merging substeps and reducing lookups.
|
||||
possibleValue =
|
||||
|
|
@ -484,7 +484,7 @@ update = \dict, key, alter ->
|
|||
## |> Dict.toList
|
||||
## |> Bool.isEq [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]
|
||||
## ```
|
||||
toList : Dict k v -> List (k, v) | k has Hash & Eq
|
||||
toList : Dict k v -> List (k, v) where k implements Hash & Eq
|
||||
toList = \@Dict { data } ->
|
||||
data
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ toList = \@Dict { data } ->
|
|||
## |> Dict.keys
|
||||
## |> Bool.isEq [1,2,3,4]
|
||||
## ```
|
||||
keys : Dict k v -> List k | k has Hash & Eq
|
||||
keys : Dict k v -> List k where k implements Hash & Eq
|
||||
keys = \@Dict { data } ->
|
||||
List.map data (\(k, _) -> k)
|
||||
|
||||
|
|
@ -514,7 +514,7 @@ keys = \@Dict { data } ->
|
|||
## |> Dict.values
|
||||
## |> Bool.isEq ["One","Two","Three","Four"]
|
||||
## ```
|
||||
values : Dict k v -> List v | k has Hash & Eq
|
||||
values : Dict k v -> List v where k implements Hash & Eq
|
||||
values = \@Dict { data } ->
|
||||
List.map data (\(_, v) -> v)
|
||||
|
||||
|
|
@ -542,7 +542,7 @@ values = \@Dict { data } ->
|
|||
## expect
|
||||
## Dict.insertAll first second == expected
|
||||
## ```
|
||||
insertAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
|
||||
insertAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
|
||||
insertAll = \xs, ys ->
|
||||
walk ys xs insert
|
||||
|
||||
|
|
@ -564,7 +564,7 @@ insertAll = \xs, ys ->
|
|||
##
|
||||
## expect Dict.keepShared first second == first
|
||||
## ```
|
||||
keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
|
||||
keepShared : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
|
||||
keepShared = \xs, ys ->
|
||||
walk
|
||||
xs
|
||||
|
|
@ -596,11 +596,11 @@ keepShared = \xs, ys ->
|
|||
##
|
||||
## expect Dict.removeAll first second == expected
|
||||
## ```
|
||||
removeAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
|
||||
removeAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
|
||||
removeAll = \xs, ys ->
|
||||
walk ys xs (\state, k, _ -> remove state k)
|
||||
|
||||
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k has Hash & Eq
|
||||
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v where k implements Hash & Eq
|
||||
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
|
||||
(key, _) = listGetUnsafe data lastIndex
|
||||
hashKey =
|
||||
|
|
@ -626,7 +626,7 @@ swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIn
|
|||
# Update index of swaped element.
|
||||
dataIndices: List.set dataIndices index dataIndex,
|
||||
data: nextData,
|
||||
size: size - 1,
|
||||
size: Num.subWrap size 1,
|
||||
}
|
||||
|
||||
Err NotFound ->
|
||||
|
|
@ -664,7 +664,7 @@ nextEmptyOrDeletedHelper = \metadata, probe, offset ->
|
|||
|
||||
# TODO: investigate if this needs to be split into more specific helper functions.
|
||||
# There is a chance that returning specific sub-info like the value would be faster.
|
||||
findIndexHelper : List I8, List Nat, List (k, v), I8, k, Probe, Nat -> Result Nat [NotFound] | k has Hash & Eq
|
||||
findIndexHelper : List I8, List Nat, List (k, v), I8, k, Probe, Nat -> Result Nat [NotFound] where k implements Hash & Eq
|
||||
findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
|
||||
# For finding a value, we must search past all deleted element tombstones.
|
||||
index = Num.addWrap (mul8 probe.slotIndex) offset
|
||||
|
|
@ -696,12 +696,12 @@ findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
|
|||
# This is how we grow the container.
|
||||
# If we aren't to the load factor yet, just ignore this.
|
||||
# The container must have an updated size including any elements about to be inserted.
|
||||
maybeRehash : Dict k v -> Dict k v | k has Hash & Eq
|
||||
maybeRehash : Dict k v -> Dict k v where k implements Hash & Eq
|
||||
maybeRehash = \@Dict { metadata, dataIndices, data, size } ->
|
||||
cap = List.len dataIndices
|
||||
maxLoadCap =
|
||||
# This is 7/8 * capacity, which is the max load factor.
|
||||
cap - Num.shiftRightZfBy cap 3
|
||||
Num.subWrap cap (Num.shiftRightZfBy cap 3)
|
||||
|
||||
if size > maxLoadCap then
|
||||
rehash (@Dict { metadata, dataIndices, data, size })
|
||||
|
|
@ -709,7 +709,7 @@ maybeRehash = \@Dict { metadata, dataIndices, data, size } ->
|
|||
@Dict { metadata, dataIndices, data, size }
|
||||
|
||||
# TODO: switch rehash to iterate data and eventually clear out tombstones as well.
|
||||
rehash : Dict k v -> Dict k v | k has Hash & Eq
|
||||
rehash : Dict k v -> Dict k v where k implements Hash & Eq
|
||||
rehash = \@Dict { metadata, dataIndices, data, size } ->
|
||||
newLen = 2 * List.len dataIndices
|
||||
newDict =
|
||||
|
|
@ -722,7 +722,7 @@ rehash = \@Dict { metadata, dataIndices, data, size } ->
|
|||
|
||||
rehashHelper newDict metadata dataIndices data 0
|
||||
|
||||
rehashHelper : Dict k v, List I8, List Nat, List (k, v), Nat -> Dict k v | k has Hash & Eq
|
||||
rehashHelper : Dict k v, List I8, List Nat, List (k, v), Nat -> Dict k v where k implements Hash & Eq
|
||||
rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
||||
when List.get oldMetadata index is
|
||||
Ok md ->
|
||||
|
|
@ -737,13 +737,13 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
|||
# Empty or deleted data
|
||||
dict
|
||||
|
||||
rehashHelper nextDict oldMetadata oldDataIndices oldData (index + 1)
|
||||
rehashHelper nextDict oldMetadata oldDataIndices oldData (Num.addWrap index 1)
|
||||
|
||||
Err OutOfBounds ->
|
||||
# Walked entire list, complete now.
|
||||
dict
|
||||
|
||||
insertForRehash : Dict k v, k, Nat -> Dict k v | k has Hash & Eq
|
||||
insertForRehash : Dict k v, k, Nat -> Dict k v where k implements Hash & Eq
|
||||
insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex ->
|
||||
hashKey =
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|
|
@ -1010,7 +1010,7 @@ expect
|
|||
# TODO: wyhash is slow for large keys, use something like cityhash if the keys are too long.
|
||||
# TODO: Add a builtin to distinguish big endian systems and change loading orders.
|
||||
# TODO: Switch out Wymum on systems with slow 128bit multiplication.
|
||||
LowLevelHasher := { originalSeed : U64, state : U64 } has [
|
||||
LowLevelHasher := { originalSeed : U64, state : U64 } implements [
|
||||
Hasher {
|
||||
addBytes,
|
||||
addU8,
|
||||
|
|
|
|||
|
|
@ -47,40 +47,40 @@ interface Encode
|
|||
Bool.{ Bool },
|
||||
]
|
||||
|
||||
Encoder fmt := List U8, fmt -> List U8 | fmt has EncoderFormatting
|
||||
Encoder fmt := List U8, fmt -> List U8 where fmt implements EncoderFormatting
|
||||
|
||||
Encoding has
|
||||
toEncoder : val -> Encoder fmt | val has Encoding, fmt has EncoderFormatting
|
||||
Encoding implements
|
||||
toEncoder : val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting
|
||||
|
||||
EncoderFormatting has
|
||||
u8 : U8 -> Encoder fmt | fmt has EncoderFormatting
|
||||
u16 : U16 -> Encoder fmt | fmt has EncoderFormatting
|
||||
u32 : U32 -> Encoder fmt | fmt has EncoderFormatting
|
||||
u64 : U64 -> Encoder fmt | fmt has EncoderFormatting
|
||||
u128 : U128 -> Encoder fmt | fmt has EncoderFormatting
|
||||
i8 : I8 -> Encoder fmt | fmt has EncoderFormatting
|
||||
i16 : I16 -> Encoder fmt | fmt has EncoderFormatting
|
||||
i32 : I32 -> Encoder fmt | fmt has EncoderFormatting
|
||||
i64 : I64 -> Encoder fmt | fmt has EncoderFormatting
|
||||
i128 : I128 -> Encoder fmt | fmt has EncoderFormatting
|
||||
f32 : F32 -> Encoder fmt | fmt has EncoderFormatting
|
||||
f64 : F64 -> Encoder fmt | fmt has EncoderFormatting
|
||||
dec : Dec -> Encoder fmt | fmt has EncoderFormatting
|
||||
bool : Bool -> Encoder fmt | fmt has EncoderFormatting
|
||||
string : Str -> Encoder fmt | fmt has EncoderFormatting
|
||||
list : List elem, (elem -> Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting
|
||||
record : List { key : Str, value : Encoder fmt } -> Encoder fmt | fmt has EncoderFormatting
|
||||
tuple : List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting
|
||||
tag : Str, List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting
|
||||
EncoderFormatting implements
|
||||
u8 : U8 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
u16 : U16 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
u32 : U32 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
u64 : U64 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
u128 : U128 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
i8 : I8 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
i16 : I16 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
i32 : I32 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
i64 : I64 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
i128 : I128 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
f32 : F32 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
f64 : F64 -> Encoder fmt where fmt implements EncoderFormatting
|
||||
dec : Dec -> Encoder fmt where fmt implements EncoderFormatting
|
||||
bool : Bool -> Encoder fmt where fmt implements EncoderFormatting
|
||||
string : Str -> Encoder fmt where fmt implements EncoderFormatting
|
||||
list : List elem, (elem -> Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
record : List { key : Str, value : Encoder fmt } -> Encoder fmt where fmt implements EncoderFormatting
|
||||
tuple : List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
tag : Str, List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
|
||||
custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting
|
||||
custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
custom = \encoder -> @Encoder encoder
|
||||
|
||||
appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has EncoderFormatting
|
||||
appendWith : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting
|
||||
appendWith = \lst, @Encoder doEncoding, fmt -> doEncoding lst fmt
|
||||
|
||||
append : List U8, val, fmt -> List U8 | val has Encoding, fmt has EncoderFormatting
|
||||
append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
|
||||
append = \lst, val, fmt -> appendWith lst (toEncoder val) fmt
|
||||
|
||||
toBytes : val, fmt -> List U8 | val has Encoding, fmt has EncoderFormatting
|
||||
toBytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
|
||||
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
|
||||
|
|
|
|||
|
|
@ -28,40 +28,40 @@ interface Hash
|
|||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, Nat, Dec },
|
||||
]
|
||||
|
||||
## A value that can hashed.
|
||||
Hash has
|
||||
## A value that can be hashed.
|
||||
Hash implements
|
||||
## Hashes a value into a [Hasher].
|
||||
## Note that [hash] does not produce a hash value itself; the hasher must be
|
||||
## [complete]d in order to extract the hash value.
|
||||
hash : hasher, a -> hasher | a has Hash, hasher has Hasher
|
||||
hash : hasher, a -> hasher where a implements Hash, hasher implements Hasher
|
||||
|
||||
## Describes a hashing algorithm that is fed bytes and produces an integer hash.
|
||||
##
|
||||
## The [Hasher] ability describes general-purpose hashers. It only allows
|
||||
## emission of 64-bit unsigned integer hashes. It is not suitable for
|
||||
## cryptographically-secure hashing.
|
||||
Hasher has
|
||||
Hasher implements
|
||||
## Adds a list of bytes to the hasher.
|
||||
addBytes : a, List U8 -> a | a has Hasher
|
||||
addBytes : a, List U8 -> a where a implements Hasher
|
||||
|
||||
## Adds a single U8 to the hasher.
|
||||
addU8 : a, U8 -> a | a has Hasher
|
||||
addU8 : a, U8 -> a where a implements Hasher
|
||||
|
||||
## Adds a single U16 to the hasher.
|
||||
addU16 : a, U16 -> a | a has Hasher
|
||||
addU16 : a, U16 -> a where a implements Hasher
|
||||
|
||||
## Adds a single U32 to the hasher.
|
||||
addU32 : a, U32 -> a | a has Hasher
|
||||
addU32 : a, U32 -> a where a implements Hasher
|
||||
|
||||
## Adds a single U64 to the hasher.
|
||||
addU64 : a, U64 -> a | a has Hasher
|
||||
addU64 : a, U64 -> a where a implements Hasher
|
||||
|
||||
## Adds a single U128 to the hasher.
|
||||
addU128 : a, U128 -> a | a has Hasher
|
||||
addU128 : a, U128 -> a where a implements Hasher
|
||||
|
||||
## Completes the hasher, extracting a hash value from its
|
||||
## accumulated hash state.
|
||||
complete : a -> U64 | a has Hasher
|
||||
complete : a -> U64 where a implements Hasher
|
||||
|
||||
## Adds a string into a [Hasher] by hashing its UTF-8 bytes.
|
||||
hashStrBytes = \hasher, s ->
|
||||
|
|
@ -73,33 +73,33 @@ hashList = \hasher, lst ->
|
|||
hash accumHasher elem
|
||||
|
||||
## Adds a single [Bool] to a hasher.
|
||||
hashBool : a, Bool -> a | a has Hasher
|
||||
hashBool : a, Bool -> a where a implements Hasher
|
||||
hashBool = \hasher, b ->
|
||||
asU8 = if b then 1 else 0
|
||||
addU8 hasher asU8
|
||||
|
||||
## Adds a single I8 to a hasher.
|
||||
hashI8 : a, I8 -> a | a has Hasher
|
||||
hashI8 : a, I8 -> a where a implements Hasher
|
||||
hashI8 = \hasher, n -> addU8 hasher (Num.toU8 n)
|
||||
|
||||
## Adds a single I16 to a hasher.
|
||||
hashI16 : a, I16 -> a | a has Hasher
|
||||
hashI16 : a, I16 -> a where a implements Hasher
|
||||
hashI16 = \hasher, n -> addU16 hasher (Num.toU16 n)
|
||||
|
||||
## Adds a single I32 to a hasher.
|
||||
hashI32 : a, I32 -> a | a has Hasher
|
||||
hashI32 : a, I32 -> a where a implements Hasher
|
||||
hashI32 = \hasher, n -> addU32 hasher (Num.toU32 n)
|
||||
|
||||
## Adds a single I64 to a hasher.
|
||||
hashI64 : a, I64 -> a | a has Hasher
|
||||
hashI64 : a, I64 -> a where a implements Hasher
|
||||
hashI64 = \hasher, n -> addU64 hasher (Num.toU64 n)
|
||||
|
||||
## Adds a single I128 to a hasher.
|
||||
hashI128 : a, I128 -> a | a has Hasher
|
||||
hashI128 : a, I128 -> a where a implements Hasher
|
||||
hashI128 = \hasher, n -> addU128 hasher (Num.toU128 n)
|
||||
|
||||
## Adds a single Nat to a hasher.
|
||||
hashNat : a, Nat -> a | a has Hasher
|
||||
hashNat : a, Nat -> a where a implements Hasher
|
||||
hashNat = \hasher, n ->
|
||||
isPlatform32bit =
|
||||
x : Nat
|
||||
|
|
@ -117,7 +117,7 @@ hashNat = \hasher, n ->
|
|||
i128OfDec : Dec -> I128
|
||||
|
||||
## Adds a single [Dec] to a hasher.
|
||||
hashDec : a, Dec -> a | a has Hasher
|
||||
hashDec : a, Dec -> a where a implements Hasher
|
||||
hashDec = \hasher, n -> hashI128 hasher (i128OfDec n)
|
||||
|
||||
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element.
|
||||
|
|
|
|||
94
crates/compiler/builtins/roc/Inspect.roc
Normal file
94
crates/compiler/builtins/roc/Inspect.roc
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
interface Inspect
|
||||
exposes [
|
||||
Inspect,
|
||||
Inspector,
|
||||
InspectFormatter,
|
||||
ElemWalker,
|
||||
KeyValWalker,
|
||||
inspect,
|
||||
init,
|
||||
list,
|
||||
set,
|
||||
dict,
|
||||
tag,
|
||||
tuple,
|
||||
record,
|
||||
bool,
|
||||
str,
|
||||
opaque,
|
||||
u8,
|
||||
i8,
|
||||
u16,
|
||||
i16,
|
||||
u32,
|
||||
i32,
|
||||
u64,
|
||||
i64,
|
||||
u128,
|
||||
i128,
|
||||
f32,
|
||||
f64,
|
||||
dec,
|
||||
custom,
|
||||
apply,
|
||||
toInspector,
|
||||
]
|
||||
imports [
|
||||
Bool.{ Bool },
|
||||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec },
|
||||
List,
|
||||
Str,
|
||||
]
|
||||
|
||||
KeyValWalker state collection key val : collection, state, (state, key, val -> state) -> state
|
||||
ElemWalker state collection elem : collection, state, (state, elem -> state) -> state
|
||||
|
||||
InspectFormatter implements
|
||||
init : {} -> f where f implements InspectFormatter
|
||||
|
||||
tag : Str, List (Inspector f) -> Inspector f where f implements InspectFormatter
|
||||
tuple : List (Inspector f) -> Inspector f where f implements InspectFormatter
|
||||
record : List { key : Str, value : Inspector f } -> Inspector f where f implements InspectFormatter
|
||||
bool : Bool -> Inspector f where f implements InspectFormatter
|
||||
str : Str -> Inspector f where f implements InspectFormatter
|
||||
|
||||
list : list, ElemWalker state list elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter
|
||||
set : set, ElemWalker state set elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter
|
||||
dict : dict, KeyValWalker state dict key value, (key -> Inspector f), (value -> Inspector f) -> Inspector f where f implements InspectFormatter
|
||||
|
||||
# Note opaque is used for both opaque types and functions.
|
||||
# The auto deriver for functions probably could put the function type.
|
||||
# For regular opaque types, I think we can use the type name, though that may lead to some reflection related issues that still need to be discussed.
|
||||
# As a simple baseline, it can just use the exact words `opaque` and `function` for now.
|
||||
# In text, this would render as `<opaque>`, `<function>`, etc
|
||||
opaque : Str -> Inspector f where f implements InspectFormatter
|
||||
|
||||
u8 : U8 -> Inspector f where f implements InspectFormatter
|
||||
i8 : I8 -> Inspector f where f implements InspectFormatter
|
||||
u16 : U16 -> Inspector f where f implements InspectFormatter
|
||||
i16 : I16 -> Inspector f where f implements InspectFormatter
|
||||
u32 : U32 -> Inspector f where f implements InspectFormatter
|
||||
i32 : I32 -> Inspector f where f implements InspectFormatter
|
||||
u64 : U64 -> Inspector f where f implements InspectFormatter
|
||||
i64 : I64 -> Inspector f where f implements InspectFormatter
|
||||
u128 : U128 -> Inspector f where f implements InspectFormatter
|
||||
i128 : I128 -> Inspector f where f implements InspectFormatter
|
||||
f32 : F32 -> Inspector f where f implements InspectFormatter
|
||||
f64 : F64 -> Inspector f where f implements InspectFormatter
|
||||
dec : Dec -> Inspector f where f implements InspectFormatter
|
||||
|
||||
Inspector f := f -> f where f implements InspectFormatter
|
||||
|
||||
custom : (f -> f) -> Inspector f where f implements InspectFormatter
|
||||
custom = @Inspector
|
||||
|
||||
apply : Inspector f, f -> f where f implements InspectFormatter
|
||||
apply = \@Inspector fn, fmt -> fn fmt
|
||||
|
||||
Inspect implements
|
||||
toInspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter
|
||||
|
||||
inspect : val -> f where val implements Inspect, f implements InspectFormatter
|
||||
inspect = \val ->
|
||||
(@Inspector valFn) = toInspector val
|
||||
valFn (init {})
|
||||
|
|
@ -28,6 +28,7 @@ interface List
|
|||
map2,
|
||||
map3,
|
||||
product,
|
||||
walkWithIndex,
|
||||
walkUntil,
|
||||
walkFrom,
|
||||
walkFromUntil,
|
||||
|
|
@ -391,7 +392,7 @@ repeat = \value, count ->
|
|||
repeatHelp : a, Nat, List a -> List a
|
||||
repeatHelp = \value, count, accum ->
|
||||
if count > 0 then
|
||||
repeatHelp value (count - 1) (List.appendUnsafe accum value)
|
||||
repeatHelp value (Num.subWrap count 1) (List.appendUnsafe accum value)
|
||||
else
|
||||
accum
|
||||
|
||||
|
|
@ -405,7 +406,7 @@ reverse = \list ->
|
|||
|
||||
reverseHelp = \list, left, right ->
|
||||
if left < right then
|
||||
reverseHelp (List.swap list left right) (left + 1) (right - 1)
|
||||
reverseHelp (List.swap list left right) (Num.addWrap left 1) (Num.subWrap right 1)
|
||||
else
|
||||
list
|
||||
|
||||
|
|
@ -418,11 +419,11 @@ reverseHelp = \list, left, right ->
|
|||
join : List (List a) -> List a
|
||||
join = \lists ->
|
||||
totalLength =
|
||||
List.walk lists 0 (\state, list -> state + List.len list)
|
||||
List.walk lists 0 (\state, list -> Num.addWrap state (List.len list))
|
||||
|
||||
List.walk lists (List.withCapacity totalLength) (\state, list -> List.concat state list)
|
||||
List.walk lists (List.withCapacity totalLength) \state, list -> List.concat state list
|
||||
|
||||
contains : List a, a -> Bool | a has Eq
|
||||
contains : List a, a -> Bool where a implements Eq
|
||||
contains = \list, needle ->
|
||||
List.any list (\x -> x == needle)
|
||||
|
||||
|
|
@ -459,12 +460,33 @@ contains = \list, needle ->
|
|||
## Note that in other languages, `walk` is sometimes called `reduce`,
|
||||
## `fold`, `foldLeft`, or `foldl`.
|
||||
walk : List elem, state, (state, elem -> state) -> state
|
||||
walk = \list, state, func ->
|
||||
walkHelp : _, _ -> [Continue _, Break []]
|
||||
walkHelp = \currentState, element -> Continue (func currentState element)
|
||||
walk = \list, init, func ->
|
||||
walkHelp list init func 0 (List.len list)
|
||||
|
||||
when List.iterate list state walkHelp is
|
||||
Continue newState -> newState
|
||||
## internal helper
|
||||
walkHelp : List elem, s, (s, elem -> s), Nat, Nat -> s
|
||||
walkHelp = \list, state, f, index, length ->
|
||||
if index < length then
|
||||
nextState = f state (List.getUnsafe list index)
|
||||
|
||||
walkHelp list nextState f (Num.addWrap index 1) length
|
||||
else
|
||||
state
|
||||
|
||||
## Like [walk], but at each step the function also receives the index of the current element.
|
||||
walkWithIndex : List elem, state, (state, elem, Nat -> state) -> state
|
||||
walkWithIndex = \list, init, func ->
|
||||
walkWithIndexHelp list init func 0 (List.len list)
|
||||
|
||||
## internal helper
|
||||
walkWithIndexHelp : List elem, s, (s, elem, Nat -> s), Nat, Nat -> s
|
||||
walkWithIndexHelp = \list, state, f, index, length ->
|
||||
if index < length then
|
||||
nextState = f state (List.getUnsafe list index) index
|
||||
|
||||
walkWithIndexHelp list nextState f (Num.addWrap index 1) length
|
||||
else
|
||||
state
|
||||
|
||||
## Note that in other languages, `walkBackwards` is sometimes called `reduceRight`,
|
||||
## `fold`, `foldRight`, or `foldr`.
|
||||
|
|
@ -478,7 +500,7 @@ walkBackwardsHelp = \list, state, f, indexPlusOne ->
|
|||
if indexPlusOne == 0 then
|
||||
state
|
||||
else
|
||||
index = indexPlusOne - 1
|
||||
index = Num.subWrap indexPlusOne 1
|
||||
nextState = f state (getUnsafe list index)
|
||||
|
||||
walkBackwardsHelp list nextState f index
|
||||
|
|
@ -510,10 +532,10 @@ walkBackwardsUntil = \list, initial, func ->
|
|||
## Walks to the end of the list from a specified starting index
|
||||
walkFrom : List elem, Nat, state, (state, elem -> state) -> state
|
||||
walkFrom = \list, index, state, func ->
|
||||
walkHelp : _, _ -> [Continue _, Break []]
|
||||
walkHelp = \currentState, element -> Continue (func currentState element)
|
||||
step : _, _ -> [Continue _, Break []]
|
||||
step = \currentState, element -> Continue (func currentState element)
|
||||
|
||||
when List.iterHelp list state walkHelp index (List.len list) is
|
||||
when List.iterHelp list state step index (List.len list) is
|
||||
Continue new -> new
|
||||
|
||||
## A combination of [List.walkFrom] and [List.walkUntil]
|
||||
|
|
@ -590,9 +612,9 @@ keepIfHelp : List a, (a -> Bool), Nat, Nat, Nat -> List a
|
|||
keepIfHelp = \list, predicate, kept, index, length ->
|
||||
if index < length then
|
||||
if predicate (List.getUnsafe list index) then
|
||||
keepIfHelp (List.swap list kept index) predicate (kept + 1) (index + 1) length
|
||||
keepIfHelp (List.swap list kept index) predicate (Num.addWrap kept 1) (Num.addWrap index 1) length
|
||||
else
|
||||
keepIfHelp list predicate kept (index + 1) length
|
||||
keepIfHelp list predicate kept (Num.addWrap index 1) length
|
||||
else
|
||||
List.takeFirst list kept
|
||||
|
||||
|
|
@ -619,7 +641,7 @@ countIf : List a, (a -> Bool) -> Nat
|
|||
countIf = \list, predicate ->
|
||||
walkState = \state, elem ->
|
||||
if predicate elem then
|
||||
state + 1
|
||||
Num.addWrap state 1
|
||||
else
|
||||
state
|
||||
|
||||
|
|
@ -712,7 +734,7 @@ mapWithIndexHelp = \src, dest, func, index, length ->
|
|||
mappedElem = func elem index
|
||||
newDest = List.appendUnsafe dest mappedElem
|
||||
|
||||
mapWithIndexHelp src newDest func (index + 1) length
|
||||
mapWithIndexHelp src newDest func (Num.addWrap index 1) length
|
||||
else
|
||||
dest
|
||||
|
||||
|
|
@ -817,7 +839,7 @@ rangeLengthHelp = \accum, i, remaining, calcNext ->
|
|||
else
|
||||
when i is
|
||||
Ok val ->
|
||||
rangeLengthHelp (List.appendUnsafe accum val) (calcNext val) (remaining - 1) calcNext
|
||||
rangeLengthHelp (List.appendUnsafe accum val) (calcNext val) (Num.subWrap remaining 1) calcNext
|
||||
|
||||
Err _ ->
|
||||
# We went past the end of the numeric range and there is no next.
|
||||
|
|
@ -1036,7 +1058,7 @@ findFirstIndex = \list, matcher ->
|
|||
if matcher elem then
|
||||
Break index
|
||||
else
|
||||
Continue (index + 1)
|
||||
Continue (Num.addWrap index 1)
|
||||
|
||||
when foundIndex is
|
||||
Break index -> Ok index
|
||||
|
|
@ -1048,10 +1070,12 @@ findFirstIndex = \list, matcher ->
|
|||
findLastIndex : List elem, (elem -> Bool) -> Result Nat [NotFound]
|
||||
findLastIndex = \list, matches ->
|
||||
foundIndex = List.iterateBackwards list (List.len list) \prevIndex, elem ->
|
||||
answer = Num.subWrap prevIndex 1
|
||||
|
||||
if matches elem then
|
||||
Break (prevIndex - 1)
|
||||
Break answer
|
||||
else
|
||||
Continue (prevIndex - 1)
|
||||
Continue answer
|
||||
|
||||
when foundIndex is
|
||||
Break index -> Ok index
|
||||
|
|
@ -1104,7 +1128,7 @@ intersperse = \list, sep ->
|
|||
## is considered to "start with" an empty list.
|
||||
##
|
||||
## If the first list is empty, this only returns `Bool.true` if the second list is empty.
|
||||
startsWith : List elem, List elem -> Bool | elem has Eq
|
||||
startsWith : List elem, List elem -> Bool where elem implements Eq
|
||||
startsWith = \list, prefix ->
|
||||
# TODO once we have seamless slices, verify that this wouldn't
|
||||
# have better performance with a function like List.compareSublists
|
||||
|
|
@ -1116,7 +1140,7 @@ startsWith = \list, prefix ->
|
|||
## is considered to "end with" an empty list.
|
||||
##
|
||||
## If the first list is empty, this only returns `Bool.true` if the second list is empty.
|
||||
endsWith : List elem, List elem -> Bool | elem has Eq
|
||||
endsWith : List elem, List elem -> Bool where elem implements Eq
|
||||
endsWith = \list, suffix ->
|
||||
# TODO once we have seamless slices, verify that this wouldn't
|
||||
# have better performance with a function like List.compareSublists
|
||||
|
|
@ -1137,7 +1161,7 @@ split = \elements, userSplitIndex ->
|
|||
length = List.len elements
|
||||
splitIndex = if length > userSplitIndex then userSplitIndex else length
|
||||
before = List.sublist elements { start: 0, len: splitIndex }
|
||||
others = List.sublist elements { start: splitIndex, len: length - splitIndex }
|
||||
others = List.sublist elements { start: splitIndex, len: Num.subWrap length splitIndex }
|
||||
|
||||
{ before, others }
|
||||
|
||||
|
|
@ -1146,12 +1170,12 @@ split = \elements, userSplitIndex ->
|
|||
## ```
|
||||
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Z, Baz] }
|
||||
## ```
|
||||
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
|
||||
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
|
||||
splitFirst = \list, delimiter ->
|
||||
when List.findFirstIndex list (\elem -> elem == delimiter) is
|
||||
Ok index ->
|
||||
before = List.sublist list { start: 0, len: index }
|
||||
after = List.sublist list { start: index + 1, len: List.len list - index - 1 }
|
||||
after = List.sublist list { start: Num.addWrap index 1, len: Num.subWrap (List.len list) index |> Num.subWrap 1 }
|
||||
|
||||
Ok { before, after }
|
||||
|
||||
|
|
@ -1162,12 +1186,12 @@ splitFirst = \list, delimiter ->
|
|||
## ```
|
||||
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Z, Bar], after: [Baz] }
|
||||
## ```
|
||||
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq
|
||||
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
|
||||
splitLast = \list, delimiter ->
|
||||
when List.findLastIndex list (\elem -> elem == delimiter) is
|
||||
Ok index ->
|
||||
before = List.sublist list { start: 0, len: index }
|
||||
after = List.sublist list { start: index + 1, len: List.len list - index - 1 }
|
||||
after = List.sublist list { start: Num.addWrap index 1, len: Num.subWrap (List.len list) index |> Num.subWrap 1 }
|
||||
|
||||
Ok { before, after }
|
||||
|
||||
|
|
@ -1202,7 +1226,7 @@ walkTryHelp : List elem, state, (state, elem -> Result state err), Nat, Nat -> R
|
|||
walkTryHelp = \list, state, f, index, length ->
|
||||
if index < length then
|
||||
when f state (List.getUnsafe list index) is
|
||||
Ok nextState -> walkTryHelp list nextState f (index + 1) length
|
||||
Ok nextState -> walkTryHelp list nextState f (Num.addWrap index 1) length
|
||||
Err b -> Err b
|
||||
else
|
||||
Ok state
|
||||
|
|
@ -1217,7 +1241,7 @@ iterHelp : List elem, s, (s, elem -> [Continue s, Break b]), Nat, Nat -> [Contin
|
|||
iterHelp = \list, state, f, index, length ->
|
||||
if index < length then
|
||||
when f state (List.getUnsafe list index) is
|
||||
Continue nextState -> iterHelp list nextState f (index + 1) length
|
||||
Continue nextState -> iterHelp list nextState f (Num.addWrap index 1) length
|
||||
Break b -> Break b
|
||||
else
|
||||
Continue state
|
||||
|
|
@ -1232,7 +1256,7 @@ iterateBackwards = \list, init, func ->
|
|||
iterBackwardsHelp : List elem, s, (s, elem -> [Continue s, Break b]), Nat -> [Continue s, Break b]
|
||||
iterBackwardsHelp = \list, state, f, prevIndex ->
|
||||
if prevIndex > 0 then
|
||||
index = prevIndex - 1
|
||||
index = Num.subWrap prevIndex 1
|
||||
|
||||
when f state (List.getUnsafe list index) is
|
||||
Continue nextState -> iterBackwardsHelp list nextState f index
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ bytesToU16 = \bytes, index ->
|
|||
# we need at least 1 more byte
|
||||
offset = 1
|
||||
|
||||
if index + offset < List.len bytes then
|
||||
if Num.addSaturated index offset < List.len bytes then
|
||||
Ok (bytesToU16Lowlevel bytes index)
|
||||
else
|
||||
Err OutOfBounds
|
||||
|
|
@ -550,7 +550,7 @@ bytesToU32 = \bytes, index ->
|
|||
# we need at least 3 more bytes
|
||||
offset = 3
|
||||
|
||||
if index + offset < List.len bytes then
|
||||
if Num.addSaturated index offset < List.len bytes then
|
||||
Ok (bytesToU32Lowlevel bytes index)
|
||||
else
|
||||
Err OutOfBounds
|
||||
|
|
@ -560,7 +560,7 @@ bytesToU64 = \bytes, index ->
|
|||
# we need at least 7 more bytes
|
||||
offset = 7
|
||||
|
||||
if index + offset < List.len bytes then
|
||||
if Num.addSaturated index offset < List.len bytes then
|
||||
Ok (bytesToU64Lowlevel bytes index)
|
||||
else
|
||||
Err OutOfBounds
|
||||
|
|
@ -570,7 +570,7 @@ bytesToU128 = \bytes, index ->
|
|||
# we need at least 15 more bytes
|
||||
offset = 15
|
||||
|
||||
if index + offset < List.len bytes then
|
||||
if Num.addSaturated index offset < List.len bytes then
|
||||
Ok (bytesToU128Lowlevel bytes index)
|
||||
else
|
||||
Err OutOfBounds
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ interface Set
|
|||
|
||||
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
|
||||
## type which stores a collection of unique values, without any ordering
|
||||
Set k := Dict.Dict k {} | k has Hash & Eq
|
||||
has [
|
||||
Set k := Dict.Dict k {} where k implements Hash & Eq
|
||||
implements [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
|
|
@ -39,7 +39,7 @@ Set k := Dict.Dict k {} | k has Hash & Eq
|
|||
},
|
||||
]
|
||||
|
||||
isEq : Set k, Set k -> Bool | k has Hash & Eq
|
||||
isEq : Set k, Set k -> Bool where k implements Hash & Eq
|
||||
isEq = \xs, ys ->
|
||||
if len xs != len ys then
|
||||
Bool.false
|
||||
|
|
@ -50,7 +50,7 @@ isEq = \xs, ys ->
|
|||
else
|
||||
Break Bool.false
|
||||
|
||||
hashSet : hasher, Set k -> hasher | k has Hash & Eq, hasher has Hasher
|
||||
hashSet : hasher, Set k -> hasher where k implements Hash & Eq, hasher implements Hasher
|
||||
hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
||||
|
||||
## Creates a new empty `Set`.
|
||||
|
|
@ -60,13 +60,13 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
|||
##
|
||||
## expect countValues == 0
|
||||
## ```
|
||||
empty : {} -> Set k | k has Hash & Eq
|
||||
empty : {} -> Set *
|
||||
empty = \{} -> @Set (Dict.empty {})
|
||||
|
||||
## Return a dictionary with space allocated for a number of entries. This
|
||||
## may provide a performance optimization if you know how many entries will be
|
||||
## inserted.
|
||||
withCapacity : Nat -> Set k | k has Hash & Eq
|
||||
withCapacity : Nat -> Set *
|
||||
withCapacity = \cap ->
|
||||
@Set (Dict.withCapacity cap)
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ withCapacity = \cap ->
|
|||
##
|
||||
## expect countValues == 1
|
||||
## ```
|
||||
single : k -> Set k | k has Hash & Eq
|
||||
single : k -> Set k where k implements Hash & Eq
|
||||
single = \key ->
|
||||
Dict.single key {} |> @Set
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ single = \key ->
|
|||
##
|
||||
## expect countValues == 3
|
||||
## ```
|
||||
insert : Set k, k -> Set k | k has Hash & Eq
|
||||
insert : Set k, k -> Set k where k implements Hash & Eq
|
||||
insert = \@Set dict, key ->
|
||||
Dict.insert dict key {} |> @Set
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ expect
|
|||
## expect has10 == Bool.false
|
||||
## expect has20 == Bool.true
|
||||
## ```
|
||||
remove : Set k, k -> Set k | k has Hash & Eq
|
||||
remove : Set k, k -> Set k where k implements Hash & Eq
|
||||
remove = \@Set dict, key ->
|
||||
Dict.remove dict key |> @Set
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ remove = \@Set dict, key ->
|
|||
## expect hasApple == Bool.true
|
||||
## expect hasBanana == Bool.false
|
||||
## ```
|
||||
contains : Set k, k -> Bool | k has Hash & Eq
|
||||
contains : Set k, k -> Bool where k implements Hash & Eq
|
||||
contains = \@Set dict, key ->
|
||||
Dict.contains dict key
|
||||
|
||||
|
|
@ -210,7 +210,7 @@ contains = \@Set dict, key ->
|
|||
##
|
||||
## expect Set.toList numbers == values
|
||||
## ```
|
||||
toList : Set k -> List k | k has Hash & Eq
|
||||
toList : Set k -> List k where k implements Hash & Eq
|
||||
toList = \@Set dict ->
|
||||
Dict.keys dict
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ toList = \@Set dict ->
|
|||
##
|
||||
## expect Set.fromList [Pear, Apple, Banana] == values
|
||||
## ```
|
||||
fromList : List k -> Set k | k has Hash & Eq
|
||||
fromList : List k -> Set k where k implements Hash & Eq
|
||||
fromList = \list ->
|
||||
initial = @Set (Dict.withCapacity (List.len list))
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ fromList = \list ->
|
|||
##
|
||||
## expect Set.union set1 set2 == Set.fromList [Left, Right]
|
||||
## ```
|
||||
union : Set k, Set k -> Set k | k has Hash & Eq
|
||||
union : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
union = \@Set dict1, @Set dict2 ->
|
||||
Dict.insertAll dict1 dict2 |> @Set
|
||||
|
||||
|
|
@ -253,7 +253,7 @@ union = \@Set dict1, @Set dict2 ->
|
|||
##
|
||||
## expect Set.intersection set1 set2 == Set.single Left
|
||||
## ```
|
||||
intersection : Set k, Set k -> Set k | k has Hash & Eq
|
||||
intersection : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
intersection = \@Set dict1, @Set dict2 ->
|
||||
Dict.keepShared dict1 dict2 |> @Set
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ intersection = \@Set dict1, @Set dict2 ->
|
|||
##
|
||||
## expect Set.difference first second == Set.fromList [Up, Down]
|
||||
## ```
|
||||
difference : Set k, Set k -> Set k | k has Hash & Eq
|
||||
difference : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
difference = \@Set dict1, @Set dict2 ->
|
||||
Dict.removeAll dict1 dict2 |> @Set
|
||||
|
||||
|
|
@ -290,14 +290,14 @@ difference = \@Set dict1, @Set dict2 ->
|
|||
##
|
||||
## expect result == 2
|
||||
## ```
|
||||
walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq
|
||||
walk : Set k, state, (state, k -> state) -> state where k implements Hash & Eq
|
||||
walk = \@Set dict, state, step ->
|
||||
Dict.walk dict state (\s, k, _ -> step s k)
|
||||
|
||||
## Convert each value in the set to something new, by calling a conversion
|
||||
## function on each of them which receives the old value. Then return a
|
||||
## new set containing the converted values.
|
||||
map : Set a, (a -> b) -> Set b | a has Hash & Eq, b has Hash & Eq
|
||||
map : Set a, (a -> b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
map = \set, transform ->
|
||||
init = withCapacity (capacity set)
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ map = \set, transform ->
|
|||
## (using [Set.union]) into one set.
|
||||
##
|
||||
## You may know a similar function named `concatMap` in other languages.
|
||||
joinMap : Set a, (a -> Set b) -> Set b | a has Hash & Eq, b has Hash & Eq
|
||||
joinMap : Set a, (a -> Set b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
joinMap = \set, transform ->
|
||||
init = withCapacity (capacity set) # Might be a pessimization
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ joinMap = \set, transform ->
|
|||
##
|
||||
## expect result == FoundTheAnswer
|
||||
## ```
|
||||
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state | k has Hash & Eq
|
||||
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state where k implements Hash & Eq
|
||||
walkUntil = \@Set dict, state, step ->
|
||||
Dict.walkUntil dict state (\s, k, _ -> step s k)
|
||||
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ expect (Str.fromUtf8 [255]) |> Result.isErr
|
|||
## ```
|
||||
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]
|
||||
fromUtf8Range = \bytes, config ->
|
||||
if config.start + config.count <= List.len bytes then
|
||||
if Num.addSaturated config.start config.count <= List.len bytes then
|
||||
result = fromUtf8RangeLowlevel bytes config.start config.count
|
||||
|
||||
if result.cIsOk then
|
||||
|
|
@ -721,7 +721,7 @@ splitFirst = \haystack, needle ->
|
|||
remaining = Str.countUtf8Bytes haystack - Str.countUtf8Bytes needle - index
|
||||
|
||||
before = Str.substringUnsafe haystack 0 index
|
||||
after = Str.substringUnsafe haystack (index + Str.countUtf8Bytes needle) remaining
|
||||
after = Str.substringUnsafe haystack (Num.addWrap index (Str.countUtf8Bytes needle)) remaining
|
||||
|
||||
Ok { before, after }
|
||||
|
||||
|
|
@ -757,7 +757,7 @@ firstMatchHelp = \haystack, needle, index, lastPossible ->
|
|||
if matchesAt haystack index needle then
|
||||
Some index
|
||||
else
|
||||
firstMatchHelp haystack needle (index + 1) lastPossible
|
||||
firstMatchHelp haystack needle (Num.addWrap index 1) lastPossible
|
||||
else
|
||||
None
|
||||
|
||||
|
|
@ -775,7 +775,7 @@ splitLast = \haystack, needle ->
|
|||
remaining = Str.countUtf8Bytes haystack - Str.countUtf8Bytes needle - index
|
||||
|
||||
before = Str.substringUnsafe haystack 0 index
|
||||
after = Str.substringUnsafe haystack (index + Str.countUtf8Bytes needle) remaining
|
||||
after = Str.substringUnsafe haystack (Num.addWrap index (Str.countUtf8Bytes needle)) remaining
|
||||
|
||||
Ok { before, after }
|
||||
|
||||
|
|
@ -820,7 +820,7 @@ matchesAt : Str, Nat, Str -> Bool
|
|||
matchesAt = \haystack, haystackIndex, needle ->
|
||||
haystackLength = Str.countUtf8Bytes haystack
|
||||
needleLength = Str.countUtf8Bytes needle
|
||||
endIndex = min (haystackIndex + needleLength) haystackLength
|
||||
endIndex = min (Num.addSaturated haystackIndex needleLength) haystackLength
|
||||
|
||||
matchesAtHelp {
|
||||
haystack,
|
||||
|
|
@ -847,8 +847,8 @@ matchesAtHelp = \state ->
|
|||
doesRestMatch =
|
||||
matchesAtHelp
|
||||
{ state &
|
||||
haystackIndex: haystackIndex + 1,
|
||||
needleIndex: needleIndex + 1,
|
||||
haystackIndex: Num.addWrap haystackIndex 1,
|
||||
needleIndex: Num.addWrap needleIndex 1,
|
||||
}
|
||||
|
||||
doesThisMatch && doesRestMatch
|
||||
|
|
@ -871,7 +871,7 @@ walkUtf8WithIndexHelp = \string, state, step, index, length ->
|
|||
byte = Str.getUnsafe string index
|
||||
newState = step state byte index
|
||||
|
||||
walkUtf8WithIndexHelp string newState step (index + 1) length
|
||||
walkUtf8WithIndexHelp string newState step (Num.addWrap index 1) length
|
||||
else
|
||||
state
|
||||
|
||||
|
|
@ -892,7 +892,7 @@ walkUtf8Help = \str, state, step, index, length ->
|
|||
byte = Str.getUnsafe str index
|
||||
newState = step state byte
|
||||
|
||||
walkUtf8Help str newState step (index + 1) length
|
||||
walkUtf8Help str newState step (Num.addWrap index 1) length
|
||||
else
|
||||
state
|
||||
|
||||
|
|
@ -942,7 +942,7 @@ walkScalarsHelp = \string, state, step, index, length ->
|
|||
{ scalar, bytesParsed } = getScalarUnsafe string index
|
||||
newState = step state scalar
|
||||
|
||||
walkScalarsHelp string newState step (index + bytesParsed) length
|
||||
walkScalarsHelp string newState step (Num.addWrap index bytesParsed) length
|
||||
else
|
||||
state
|
||||
|
||||
|
|
@ -970,7 +970,7 @@ walkScalarsUntilHelp = \string, state, step, index, length ->
|
|||
|
||||
when step state scalar is
|
||||
Continue newState ->
|
||||
walkScalarsUntilHelp string newState step (index + bytesParsed) length
|
||||
walkScalarsUntilHelp string newState step (Num.addWrap index bytesParsed) length
|
||||
|
||||
Break newState ->
|
||||
newState
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ interface TotallyNotJson
|
|||
## An opaque type with the `EncoderFormatting` and
|
||||
## `DecoderFormatting` abilities.
|
||||
Json := { fieldNameMapping : FieldNameMapping }
|
||||
has [
|
||||
implements [
|
||||
EncoderFormatting {
|
||||
u8: encodeU8,
|
||||
u16: encodeU16,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ pub fn module_source(module_id: ModuleId) -> &'static str {
|
|||
ModuleId::ENCODE => ENCODE,
|
||||
ModuleId::DECODE => DECODE,
|
||||
ModuleId::HASH => HASH,
|
||||
ModuleId::INSPECT => INSPECT,
|
||||
ModuleId::JSON => JSON,
|
||||
_ => internal_error!(
|
||||
"ModuleId {:?} is not part of the standard library",
|
||||
|
|
@ -34,4 +35,5 @@ const BOOL: &str = include_str!("../roc/Bool.roc");
|
|||
const ENCODE: &str = include_str!("../roc/Encode.roc");
|
||||
const DECODE: &str = include_str!("../roc/Decode.roc");
|
||||
const HASH: &str = include_str!("../roc/Hash.roc");
|
||||
const INSPECT: &str = include_str!("../roc/Inspect.roc");
|
||||
const JSON: &str = include_str!("../roc/TotallyNotJson.roc");
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ impl AbilityMemberData<Resolved> {
|
|||
|
||||
/// Solved lambda sets for an ability member specialization. For example, if we have
|
||||
///
|
||||
/// Default has default : {} -[[] + a:default:1]-> a | a has Default
|
||||
/// Default implements default : {} -[[] + a:default:1]-> a where a implements Default
|
||||
///
|
||||
/// A := {}
|
||||
/// default = \{} -[[closA]]-> @A {}
|
||||
|
|
@ -144,7 +144,7 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
|||
///
|
||||
/// For example, in the program
|
||||
///
|
||||
/// Hash has hash : a -> U64 | a has Hash
|
||||
/// Hash implements hash : a -> U64 where a implements Hash
|
||||
///
|
||||
/// Id := {} implements [Hash {hash: myHash}]
|
||||
/// myHash = \@Id n -> n
|
||||
|
|
@ -155,7 +155,7 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
|
|||
/// Information about all members composing abilities.
|
||||
ability_members: MutMap<Symbol, AbilityMemberData<Phase>>,
|
||||
|
||||
/// Maps a tuple (member, type) specifying that `type` has an implementation of an ability
|
||||
/// Maps a tuple (member, type) specifying that `type` implements an ability
|
||||
/// member `member`, to how that implementation is defined.
|
||||
declared_implementations: MutMap<ImplKey, MemberImpl>,
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
|
|||
}
|
||||
|
||||
/// Finds the implementation key for a symbol specializing the ability member, if it specializes any.
|
||||
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`.
|
||||
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 where a implements Hash`.
|
||||
/// Calling this with `hashId` would retrieve (hash, hashId).
|
||||
pub fn impl_key(&self, specializing_symbol: Symbol) -> Option<&ImplKey> {
|
||||
self.specialization_to_root.get(&specializing_symbol)
|
||||
|
|
@ -392,7 +392,7 @@ pub enum MarkError {
|
|||
impl IAbilitiesStore<Resolved> {
|
||||
/// Finds the symbol name and ability member definition for a symbol specializing the ability
|
||||
/// member, if it specializes any.
|
||||
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`.
|
||||
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 where a implements Hash`.
|
||||
/// Calling this with `hashId` would retrieve the ability member data for `hash`, and what type
|
||||
/// `hashId` is specializing for.
|
||||
pub fn impl_key_and_def(
|
||||
|
|
@ -414,7 +414,7 @@ impl IAbilitiesStore<Resolved> {
|
|||
}
|
||||
|
||||
/// Returns an iterator over pairs ((ability member, type), implementation) specifying that
|
||||
/// the give type has an implementation of an ability member.
|
||||
/// the given type implements an ability member.
|
||||
pub fn iter_declared_implementations(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (ImplKey, &MemberImpl)> + '_ {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ pub struct NamedVariable {
|
|||
pub first_seen: Region,
|
||||
}
|
||||
|
||||
/// A type variable bound to an ability, like "a has Hash".
|
||||
/// A type variable bound to an ability, like "a implements Hash".
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct AbleVariable {
|
||||
pub variable: Variable,
|
||||
|
|
@ -296,7 +296,7 @@ pub(crate) fn canonicalize_annotation(
|
|||
|
||||
let (annotation, region) = match annotation {
|
||||
TypeAnnotation::Where(annotation, clauses) => {
|
||||
// Add each "has" clause. The association of a variable to an ability will be saved on
|
||||
// Add each "implements" clause. The association of a variable to an ability will be saved on
|
||||
// `introduced_variables`, which we'll process later.
|
||||
for clause in clauses.iter() {
|
||||
let opt_err = canonicalize_has_clause(
|
||||
|
|
@ -847,29 +847,17 @@ fn can_annotation_help(
|
|||
let alias = scope.lookup_alias(symbol).unwrap();
|
||||
local_aliases.insert(symbol, alias.clone());
|
||||
|
||||
if vars.is_empty() && env.home == symbol.module_id() {
|
||||
let actual_var = var_store.fresh();
|
||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||
Type::HostExposedAlias {
|
||||
name: symbol,
|
||||
type_arguments: vars,
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
actual_var,
|
||||
}
|
||||
} else {
|
||||
Type::Alias {
|
||||
symbol,
|
||||
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
infer_ext_in_output_types: alias
|
||||
.infer_ext_in_output_variables
|
||||
.iter()
|
||||
.map(|v| Type::Variable(*v))
|
||||
.collect(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
kind: alias.kind,
|
||||
}
|
||||
Type::Alias {
|
||||
symbol,
|
||||
type_arguments: vars.into_iter().map(OptAbleType::unbound).collect(),
|
||||
lambda_set_variables: alias.lambda_set_variables.clone(),
|
||||
infer_ext_in_output_types: alias
|
||||
.infer_ext_in_output_variables
|
||||
.iter()
|
||||
.map(|v| Type::Variable(*v))
|
||||
.collect(),
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
kind: alias.kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1041,8 +1029,8 @@ fn can_annotation_help(
|
|||
Where(_annotation, clauses) => {
|
||||
debug_assert!(!clauses.is_empty());
|
||||
|
||||
// Has clauses are allowed only on the top level of a signature, which we handle elsewhere.
|
||||
env.problem(roc_problem::can::Problem::IllegalHasClause {
|
||||
// Implements clauses are allowed only on the top level of a signature, which we handle elsewhere.
|
||||
env.problem(roc_problem::can::Problem::IllegalImplementsClause {
|
||||
region: Region::across_all(clauses.iter().map(|clause| &clause.region)),
|
||||
});
|
||||
|
||||
|
|
@ -1065,13 +1053,13 @@ fn canonicalize_has_clause(
|
|||
scope: &mut Scope,
|
||||
var_store: &mut VarStore,
|
||||
introduced_variables: &mut IntroducedVariables,
|
||||
clause: &Loc<roc_parse::ast::HasClause<'_>>,
|
||||
clause: &Loc<roc_parse::ast::ImplementsClause<'_>>,
|
||||
pending_abilities_in_scope: &PendingAbilitiesInScope,
|
||||
references: &mut VecSet<Symbol>,
|
||||
) -> Result<(), Type> {
|
||||
let Loc {
|
||||
region,
|
||||
value: roc_parse::ast::HasClause { var, abilities },
|
||||
value: roc_parse::ast::ImplementsClause { var, abilities },
|
||||
} = clause;
|
||||
let region = *region;
|
||||
|
||||
|
|
@ -1097,13 +1085,13 @@ fn canonicalize_has_clause(
|
|||
// or an ability that was imported from elsewhere
|
||||
&& !scope.abilities_store.is_ability(symbol)
|
||||
{
|
||||
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
|
||||
env.problem(roc_problem::can::Problem::ImplementsClauseIsNotAbility { region });
|
||||
return Err(Type::Error);
|
||||
}
|
||||
symbol
|
||||
}
|
||||
_ => {
|
||||
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
|
||||
env.problem(roc_problem::can::Problem::ImplementsClauseIsNotAbility { region });
|
||||
return Err(Type::Error);
|
||||
}
|
||||
};
|
||||
|
|
@ -1112,7 +1100,7 @@ fn canonicalize_has_clause(
|
|||
let already_seen = can_abilities.insert(ability);
|
||||
|
||||
if already_seen {
|
||||
env.problem(roc_problem::can::Problem::DuplicateHasAbility { ability, region });
|
||||
env.problem(roc_problem::can::Problem::DuplicateImplementsAbility { ability, region });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ enum PendingTypeDef<'a> {
|
|||
name: Loc<Symbol>,
|
||||
vars: Vec<Loc<Lowercase>>,
|
||||
ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
derived: Option<&'a Loc<ast::HasAbilities<'a>>>,
|
||||
derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
|
||||
},
|
||||
|
||||
Ability {
|
||||
|
|
@ -497,7 +497,7 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
//
|
||||
// interface F imports [] exposes []
|
||||
//
|
||||
// Hello := {} has [Encoding.{ toEncoder }]
|
||||
// Hello := {} implements [Encoding.{ toEncoder }]
|
||||
//
|
||||
// toEncoder = \@Hello {} -> ...
|
||||
//
|
||||
|
|
@ -509,7 +509,7 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
//
|
||||
// interface F imports [Encoding.{ toEncoder }] exposes []
|
||||
//
|
||||
// Hello := {} has [Encoding.{ toEncoder }]
|
||||
// Hello := {} implements [Encoding.{ toEncoder }]
|
||||
//
|
||||
// toEncoder = \@Hello {} -> ...
|
||||
//
|
||||
|
|
@ -527,9 +527,9 @@ fn canonicalize_claimed_ability_impl<'a>(
|
|||
// definition symbol, for example when the ability is defined in the same
|
||||
// module as an implementer:
|
||||
//
|
||||
// Eq has eq : a, a -> U64 | a has Eq
|
||||
// Eq implements eq : a, a -> U64 where a implements Eq
|
||||
//
|
||||
// A := U8 has [Eq {eq}]
|
||||
// A := U8 implements [Eq {eq}]
|
||||
//
|
||||
// So, do a final check that the implementation symbol is not resolved directly
|
||||
// to the member.
|
||||
|
|
@ -689,7 +689,7 @@ fn canonicalize_opaque<'a>(
|
|||
name_str: &'a str,
|
||||
ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
vars: &[Loc<Lowercase>],
|
||||
has_abilities: Option<&'a Loc<ast::HasAbilities<'a>>>,
|
||||
has_abilities: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
|
||||
) -> Result<CanonicalizedOpaque<'a>, ()> {
|
||||
let alias = canonicalize_alias(
|
||||
env,
|
||||
|
|
@ -712,7 +712,7 @@ fn canonicalize_opaque<'a>(
|
|||
for has_ability in has_abilities.items {
|
||||
let region = has_ability.region;
|
||||
let (ability, opt_impls) = match has_ability.value.extract_spaces().item {
|
||||
ast::HasAbility::HasAbility { ability, impls } => (ability, impls),
|
||||
ast::ImplementsAbility::ImplementsAbility { ability, impls } => (ability, impls),
|
||||
_ => internal_error!("spaces not extracted"),
|
||||
};
|
||||
|
||||
|
|
@ -766,8 +766,8 @@ fn canonicalize_opaque<'a>(
|
|||
// Did the user claim this implementation for a specialization of a different
|
||||
// type? e.g.
|
||||
//
|
||||
// A has [Hash {hash: myHash}]
|
||||
// B has [Hash {hash: myHash}]
|
||||
// A implements [Hash {hash: myHash}]
|
||||
// B implements [Hash {hash: myHash}]
|
||||
//
|
||||
// If so, that's an error and we drop the impl for this opaque type.
|
||||
let member_impl = match scope.abilities_store.impl_key(impl_symbol) {
|
||||
|
|
@ -1198,7 +1198,7 @@ fn canonicalize_type_defs<'a>(
|
|||
Loc<Symbol>,
|
||||
Vec<Loc<Lowercase>>,
|
||||
&'a Loc<ast::TypeAnnotation<'a>>,
|
||||
Option<&'a Loc<ast::HasAbilities<'a>>>,
|
||||
Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
|
||||
),
|
||||
Ability(Loc<Symbol>, Vec<PendingAbilityMember<'a>>),
|
||||
}
|
||||
|
|
@ -1404,7 +1404,7 @@ fn resolve_abilities(
|
|||
[] => {
|
||||
// There are no variables bound to the parent ability - then this member doesn't
|
||||
// need to be a part of the ability.
|
||||
env.problem(Problem::AbilityMemberMissingHasClause {
|
||||
env.problem(Problem::AbilityMemberMissingImplementsClause {
|
||||
member: member_sym,
|
||||
ability,
|
||||
region: member_name_region,
|
||||
|
|
@ -1414,7 +1414,7 @@ fn resolve_abilities(
|
|||
}
|
||||
[..] => {
|
||||
// There is more than one variable bound to the member signature, so something like
|
||||
// Eq has eq : a, b -> Bool | a has Eq, b has Eq
|
||||
// Eq implements eq : a, b -> Bool where a implements Eq, b implements Eq
|
||||
// We have no way of telling what type implements a particular instance of Eq in
|
||||
// this case (a or b?), so disallow it.
|
||||
let span_has_clauses = Region::across_all(
|
||||
|
|
@ -1427,7 +1427,7 @@ fn resolve_abilities(
|
|||
env.problem(Problem::AbilityMemberMultipleBoundVars {
|
||||
member: member_sym,
|
||||
ability,
|
||||
span_has_clauses,
|
||||
span_implements_clauses: span_has_clauses,
|
||||
bound_var_names,
|
||||
});
|
||||
// Pretend the member isn't a part of the ability
|
||||
|
|
@ -2558,7 +2558,7 @@ fn to_pending_alias_or_opaque<'a>(
|
|||
name: &'a Loc<&'a str>,
|
||||
vars: &'a [Loc<ast::Pattern<'a>>],
|
||||
ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
opt_derived: Option<&'a Loc<ast::HasAbilities<'a>>>,
|
||||
opt_derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
|
||||
kind: AliasKind,
|
||||
) -> PendingTypeDef<'a> {
|
||||
let region = Region::span_across(&name.region, &ann.region);
|
||||
|
|
@ -2677,7 +2677,7 @@ fn to_pending_type_def<'a>(
|
|||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
members,
|
||||
loc_has: _,
|
||||
loc_implements: _,
|
||||
} => {
|
||||
let name = match scope
|
||||
.introduce_without_shadow_symbol(&Ident::from(name.value), name.region)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ pub enum Pattern {
|
|||
Underscore,
|
||||
|
||||
/// An identifier that marks a specialization of an ability member.
|
||||
/// For example, given an ability member definition `hash : a -> U64 | a has Hash`,
|
||||
/// For example, given an ability member definition `hash : a -> U64 where a implements Hash`,
|
||||
/// there may be the specialization `hash : Bool -> U64`. In this case we generate a
|
||||
/// new symbol for the specialized "hash" identifier.
|
||||
AbilityMemberSpecialization {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ pub struct Scope {
|
|||
imports: Vec<(Ident, Symbol, Region)>,
|
||||
|
||||
/// Shadows of an ability member, for example a local specialization of `eq` for the ability
|
||||
/// member `Eq has eq : a, a -> Bool | a has Eq` gets a shadow symbol it can use for its
|
||||
/// member `Eq implements eq : a, a -> Bool where a implements Eq` gets a shadow symbol it can use for its
|
||||
/// implementation.
|
||||
///
|
||||
/// Only one shadow of an ability member is permitted per scope.
|
||||
|
|
|
|||
|
|
@ -674,7 +674,7 @@ pub enum FoundSymbol {
|
|||
Symbol(Symbol),
|
||||
}
|
||||
|
||||
/// Given an ability Foo has foo : ..., returns (T, foo1) if the symbol at the given region is a
|
||||
/// Given an ability Foo implements foo : ..., returns (T, foo1) if the symbol at the given region is a
|
||||
/// symbol foo1 that specializes foo for T. Otherwise if the symbol is foo but the specialization
|
||||
/// is unknown, (Foo, foo) is returned. Otherwise [None] is returned.
|
||||
pub fn find_symbol_at(
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ fn wrap_in_decode_custom_decode_with(
|
|||
|
||||
// Decode.decodeWith bytes inner_decoder fmt : DecodeResult val
|
||||
let (decode_with_call, decode_with_result_var) = {
|
||||
// Decode.decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
// Decode.decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
|
||||
let decode_with_type = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH);
|
||||
|
||||
// Decode.decodeWith : bytes, inner_decoder, fmt -> DecoderResult (List val)
|
||||
|
|
@ -80,7 +80,7 @@ fn wrap_in_decode_custom_decode_with(
|
|||
)),
|
||||
);
|
||||
|
||||
// List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting
|
||||
// List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
|
||||
// ~ bytes, Decoder (List elem) fmt, fmt -> DecoderResult (List val)
|
||||
env.unify(decode_with_type, this_decode_with_fn_var);
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ fn wrap_in_decode_custom_decode_with(
|
|||
|
||||
// Decode.custom \bytes, fmt -> Decode.decodeWith bytes inner_decoder fmt
|
||||
let (decode_custom_call, decoder_var) = {
|
||||
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
|
||||
let decode_custom_type = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM);
|
||||
|
||||
// (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt
|
||||
|
|
@ -185,7 +185,7 @@ fn wrap_in_decode_custom_decode_with(
|
|||
)),
|
||||
);
|
||||
|
||||
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting
|
||||
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
|
||||
// ~ (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt
|
||||
env.unify(decode_custom_type, this_decode_custom_fn_var);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::util::Env;
|
|||
pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) {
|
||||
// Build
|
||||
//
|
||||
// def_symbol : Decoder (List elem) fmt | elem has Decoding, fmt has DecoderFormatting
|
||||
// def_symbol : Decoder (List elem) fmt where elem implements Decoding, fmt implements DecoderFormatting
|
||||
// def_symbol = Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt
|
||||
//
|
||||
// NB: reduction to `Decode.list Decode.decoder` is not possible to the HRR.
|
||||
|
|
@ -27,10 +27,10 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
|
|||
// List elem
|
||||
let elem_var = env.subs.fresh_unnamed_flex_var();
|
||||
|
||||
// Decode.decoder : Decoder elem fmt | elem has Decoding, fmt has EncoderFormatting
|
||||
// Decode.decoder : Decoder elem fmt where elem implements Decoding, fmt implements EncoderFormatting
|
||||
let (elem_decoder, elem_decoder_var) = {
|
||||
// build `Decode.decoder : Decoder elem fmt` type
|
||||
// Decoder val fmt | val has Decoding, fmt has EncoderFormatting
|
||||
// Decoder val fmt where val implements Decoding, fmt implements EncoderFormatting
|
||||
let elem_decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER);
|
||||
|
||||
// set val ~ elem
|
||||
|
|
@ -52,7 +52,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
|
|||
};
|
||||
|
||||
// Build `Decode.list Decode.decoder` type
|
||||
// Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting
|
||||
// Decoder val fmt -[uls]-> Decoder (List val) fmt where fmt implements DecoderFormatting
|
||||
let decode_list_fn_var = env.import_builtin_symbol_var(Symbol::DECODE_LIST);
|
||||
|
||||
// Decoder elem fmt -a-> b
|
||||
|
|
@ -68,7 +68,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
|
|||
)),
|
||||
);
|
||||
|
||||
// Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting
|
||||
// Decoder val fmt -[uls]-> Decoder (List val) fmt where fmt implements DecoderFormatting
|
||||
// ~ Decoder elem fmt -a -> b
|
||||
env.unify(decode_list_fn_var, this_decode_list_fn_var);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use super::wrap_in_decode_custom_decode_with;
|
|||
/// we'd like to generate an impl like
|
||||
///
|
||||
/// ```roc
|
||||
/// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting
|
||||
/// decoder : Decoder {first: a, second: b} fmt where a implements Decoding, b implements Decoding, fmt implements DecoderFormatting
|
||||
/// decoder =
|
||||
/// initialState : {f0: Result a [NoField], f1: Result b [NoField]}
|
||||
/// initialState = {f0: Err NoField, f1: Err NoField}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use super::wrap_in_decode_custom_decode_with;
|
|||
/// we'd like to generate an impl like
|
||||
///
|
||||
/// ```roc
|
||||
/// decoder : Decoder (a, b) fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting
|
||||
/// decoder : Decoder (a, b) fmt where a implements Decoding, b implements Decoding, fmt implements DecoderFormatting
|
||||
/// decoder =
|
||||
/// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]}
|
||||
/// initialState = {e0: Err NoElem, e1: Err NoElem}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
|||
);
|
||||
|
||||
// build `toEncoder elem` type
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// elem -[clos]-> t1
|
||||
|
|
@ -136,11 +136,11 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
|||
)),
|
||||
);
|
||||
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ elem -[clos]-> t1
|
||||
env.unify(to_encoder_fn_var, elem_to_encoder_fn_var);
|
||||
|
||||
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, elem_to_encoder_fn_var);
|
||||
let to_encoder_fn = Box::new((
|
||||
to_encoder_fn_var,
|
||||
|
|
@ -201,7 +201,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
|||
});
|
||||
|
||||
// build `Encode.list lst (\elem -> Encode.toEncoder elem)` type
|
||||
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_list_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_LIST);
|
||||
|
||||
// List elem, to_elem_encoder_fn_var -[clos]-> t1
|
||||
|
|
@ -218,11 +218,11 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
|||
)),
|
||||
);
|
||||
|
||||
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ List elem, to_elem_encoder_fn_var -[clos]-> t1
|
||||
env.unify(encode_list_fn_var, this_encode_list_fn_var);
|
||||
|
||||
// Encode.list : List elem, to_elem_encoder_fn_var -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// Encode.list : List elem, to_elem_encoder_fn_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_list = AbilityMember(Symbol::ENCODE_LIST, None, this_encode_list_fn_var);
|
||||
let encode_list_fn = Box::new((
|
||||
this_encode_list_fn_var,
|
||||
|
|
@ -340,7 +340,7 @@ fn to_encoder_record(
|
|||
};
|
||||
|
||||
// build `toEncoder rcd.a` type
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// (typeof rcd.a) -[clos]-> t1
|
||||
|
|
@ -355,11 +355,11 @@ fn to_encoder_record(
|
|||
)),
|
||||
);
|
||||
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ (typeof rcd.a) -[clos]-> t1
|
||||
env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
|
||||
|
||||
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var);
|
||||
let to_encoder_fn = Box::new((
|
||||
to_encoder_fn_var,
|
||||
|
|
@ -420,7 +420,7 @@ fn to_encoder_record(
|
|||
};
|
||||
|
||||
// build `Encode.record [ { key: .., value: ..}, .. ]` type
|
||||
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_record_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_RECORD);
|
||||
|
||||
// fields_list_var -[clos]-> t1
|
||||
|
|
@ -437,11 +437,11 @@ fn to_encoder_record(
|
|||
)),
|
||||
);
|
||||
|
||||
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ fields_list_var -[clos]-> t1
|
||||
env.unify(encode_record_fn_var, this_encode_record_fn_var);
|
||||
|
||||
// Encode.record : fields_list_var -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// Encode.record : fields_list_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_record_var = AbilityMember(Symbol::ENCODE_RECORD, None, encode_record_fn_var);
|
||||
let encode_record_fn = Box::new((
|
||||
encode_record_fn_var,
|
||||
|
|
@ -543,7 +543,7 @@ fn to_encoder_tuple(
|
|||
};
|
||||
|
||||
// build `toEncoder tup.0` type
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
// (typeof tup.0) -[clos]-> t1
|
||||
|
|
@ -558,11 +558,11 @@ fn to_encoder_tuple(
|
|||
)),
|
||||
);
|
||||
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ (typeof tup.0) -[clos]-> t1
|
||||
env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
|
||||
|
||||
// toEncoder : (typeof tup.0) -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// toEncoder : (typeof tup.0) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var);
|
||||
let to_encoder_fn = Box::new((
|
||||
to_encoder_fn_var,
|
||||
|
|
@ -603,7 +603,7 @@ fn to_encoder_tuple(
|
|||
};
|
||||
|
||||
// build `Encode.tuple [ toEncoder tup.0, toEncoder tup.1 ]` type
|
||||
// List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_tuple_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TUPLE);
|
||||
|
||||
// elem_encoders_list_var -[clos]-> t1
|
||||
|
|
@ -620,11 +620,11 @@ fn to_encoder_tuple(
|
|||
)),
|
||||
);
|
||||
|
||||
// List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ elem_encoders_list_var -[clos]-> t1
|
||||
env.unify(encode_tuple_fn_var, this_encode_tuple_fn_var);
|
||||
|
||||
// Encode.tuple : elem_encoders_list_var -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// Encode.tuple : elem_encoders_list_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_tuple_var = AbilityMember(Symbol::ENCODE_TUPLE, None, encode_tuple_fn_var);
|
||||
let encode_tuple_fn = Box::new((
|
||||
encode_tuple_fn_var,
|
||||
|
|
@ -741,7 +741,7 @@ fn to_encoder_tag_union(
|
|||
.zip(payload_vars.iter())
|
||||
.map(|(&sym, &sym_var)| {
|
||||
// build `toEncoder v1` type
|
||||
// expected: val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// expected: val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_fn_var =
|
||||
env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
|
||||
|
||||
|
|
@ -759,11 +759,11 @@ fn to_encoder_tag_union(
|
|||
)),
|
||||
);
|
||||
|
||||
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ t1 -[clos]-> t'
|
||||
env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
|
||||
|
||||
// toEncoder : t1 -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// toEncoder : t1 -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let to_encoder_var =
|
||||
AbilityMember(Symbol::ENCODE_TO_ENCODER, None, this_to_encoder_fn_var);
|
||||
let to_encoder_fn = Box::new((
|
||||
|
|
@ -802,7 +802,7 @@ fn to_encoder_tag_union(
|
|||
};
|
||||
|
||||
// build `Encode.tag "A" [ ... ]` type
|
||||
// expected: Str, List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// expected: Str, List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_tag_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TAG);
|
||||
|
||||
// wanted: Str, List whole_encoders_var -[clos]-> t'
|
||||
|
|
@ -821,11 +821,11 @@ fn to_encoder_tag_union(
|
|||
)),
|
||||
);
|
||||
|
||||
// Str, List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// Str, List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ Str, List whole_encoders_var -[clos]-> t'
|
||||
env.unify(encode_tag_fn_var, this_encode_tag_fn_var);
|
||||
|
||||
// Encode.tag : Str, List whole_encoders_var -[clos]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// Encode.tag : Str, List whole_encoders_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
let encode_tag_var = AbilityMember(Symbol::ENCODE_TAG, None, this_encode_tag_fn_var);
|
||||
let encode_tag_fn = Box::new((
|
||||
this_encode_tag_fn_var,
|
||||
|
|
@ -954,15 +954,15 @@ fn wrap_in_encode_custom(
|
|||
let bytes_sym = env.new_symbol("bytes");
|
||||
let bytes_var = Variable::LIST_U8;
|
||||
|
||||
// fmt: fmt | fmt has EncoderFormatting
|
||||
// fmt: fmt where fmt implements EncoderFormatting
|
||||
let fmt_sym = env.new_symbol("fmt");
|
||||
let fmt_var = env.subs.fresh_unnamed_flex_var();
|
||||
|
||||
// build `Encode.appendWith bytes encoder fmt` type
|
||||
// expected: Encode.appendWith : List U8, Encoder fmt, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting
|
||||
// expected: Encode.appendWith : List U8, Encoder fmt, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
|
||||
let append_with_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_APPEND_WITH);
|
||||
|
||||
// wanted: Encode.appendWith : List U8, encoder_var, fmt -[clos]-> List U8 | fmt has EncoderFormatting
|
||||
// wanted: Encode.appendWith : List U8, encoder_var, fmt -[clos]-> List U8 where fmt implements EncoderFormatting
|
||||
let this_append_with_args_var_slice =
|
||||
VariableSubsSlice::insert_into_subs(env.subs, [Variable::LIST_U8, encoder_var, fmt_var]);
|
||||
let this_append_with_clos_var = env.subs.fresh_unnamed_flex_var(); // -[clos]->
|
||||
|
|
@ -975,11 +975,11 @@ fn wrap_in_encode_custom(
|
|||
)),
|
||||
);
|
||||
|
||||
// List U8, Encoder fmt, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting
|
||||
// ~ List U8, encoder_var, fmt -[clos]-> List U8 | fmt has EncoderFormatting
|
||||
// List U8, Encoder fmt, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
|
||||
// ~ List U8, encoder_var, fmt -[clos]-> List U8 where fmt implements EncoderFormatting
|
||||
env.unify(append_with_fn_var, this_append_with_fn_var);
|
||||
|
||||
// Encode.appendWith : List U8, encoder_var, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting
|
||||
// Encode.appendWith : List U8, encoder_var, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
|
||||
let append_with_fn = Box::new((
|
||||
this_append_with_fn_var,
|
||||
Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH, this_append_with_fn_var)),
|
||||
|
|
@ -1050,7 +1050,7 @@ fn wrap_in_encode_custom(
|
|||
// Build
|
||||
// Encode.custom \bytes, fmt -> Encode.appendWith bytes encoder fmt
|
||||
//
|
||||
// expected: Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting
|
||||
// expected: Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
let custom_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_CUSTOM);
|
||||
|
||||
// wanted: Encode.custom : fn_var -[clos]-> t'
|
||||
|
|
@ -1066,11 +1066,11 @@ fn wrap_in_encode_custom(
|
|||
)),
|
||||
);
|
||||
|
||||
// (List U8, fmt -> List U8) -[..]-> Encoder fmt | fmt has EncoderFormatting
|
||||
// (List U8, fmt -> List U8) -[..]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
// ~ fn_var -[clos]-> t'
|
||||
env.unify(custom_fn_var, this_custom_fn_var);
|
||||
|
||||
// Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting
|
||||
// Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
|
||||
let custom_fn = Box::new((
|
||||
this_custom_fn_var,
|
||||
Loc::at_zero(Var(Symbol::ENCODE_CUSTOM, this_custom_fn_var)),
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec<Lowercase>) -> (V
|
|||
|
||||
// Now, a hasher for this record is
|
||||
//
|
||||
// hash_rcd : hasher, { f1: t1, ..., fn: tn } -> hasher | hasher has Hasher
|
||||
// hash_rcd : hasher, { f1: t1, ..., fn: tn } -> hasher where hasher implements Hasher
|
||||
// hash_rcd = \hasher, rcd ->
|
||||
// Hash.hash (
|
||||
// Hash.hash
|
||||
|
|
@ -144,7 +144,7 @@ fn hash_tuple(env: &mut Env<'_>, fn_name: Symbol, arity: u32) -> (Variable, Expr
|
|||
|
||||
// Now, a hasher for this tuple is
|
||||
//
|
||||
// hash_tup : hasher, (t1, ..., tn) -> hasher | hasher has Hasher
|
||||
// hash_tup : hasher, (t1, ..., tn) -> hasher where hasher implements Hasher
|
||||
// hash_tup = \hasher, tup ->
|
||||
// Hash.hash (
|
||||
// Hash.hash
|
||||
|
|
@ -227,7 +227,7 @@ fn hash_tag_union(
|
|||
|
||||
// Now, a hasher for this tag union is
|
||||
//
|
||||
// hash_union : hasher, [ A t11 .. t1n, ..., Q tq1 .. tqm ] -> hasher | hasher has Hasher
|
||||
// hash_union : hasher, [ A t11 .. t1n, ..., Q tq1 .. tqm ] -> hasher where hasher implements Hasher
|
||||
// hash_union = \hasher, union ->
|
||||
// when union is
|
||||
// A x11 .. x1n -> Hash.hash (... (Hash.hash (Hash.uN hasher 0) x11) ...) x1n
|
||||
|
|
@ -393,7 +393,7 @@ fn hash_newtype_tag_union(
|
|||
|
||||
// Now, a hasher for this tag union is
|
||||
//
|
||||
// hash_union : hasher, [ A t1 .. tn ] -> hasher | hasher has Hasher
|
||||
// hash_union : hasher, [ A t1 .. tn ] -> hasher where hasher implements Hasher
|
||||
// hash_union = \hasher, A x1 .. xn ->
|
||||
// Hash.hash (... (Hash.hash discrHasher x1) ...) xn
|
||||
let hasher_sym = env.new_symbol("hasher");
|
||||
|
|
@ -462,7 +462,7 @@ fn call_hash_ability_member(
|
|||
|
||||
// build `member ...` function type. `member` here is `Hash.hash` or `Hash.addU16`.
|
||||
//
|
||||
// hasher, val -[uls]-> hasher | hasher has Hasher, val has Hash
|
||||
// hasher, val -[uls]-> hasher where hasher implements Hasher, val implements Hash
|
||||
let exposed_hash_fn_var = env.import_builtin_symbol_var(member);
|
||||
|
||||
// (typeof body), (typeof field) -[clos]-> hasher_result
|
||||
|
|
@ -479,11 +479,11 @@ fn call_hash_ability_member(
|
|||
)),
|
||||
);
|
||||
|
||||
// hasher, val -[uls]-> hasher | hasher has Hasher, val has Hash
|
||||
// hasher, val -[uls]-> hasher where hasher implements Hasher, val implements Hash
|
||||
// ~ (typeof body), (typeof field) -[clos]-> hasher_result
|
||||
env.unify(exposed_hash_fn_var, this_hash_fn_var);
|
||||
|
||||
// Hash.hash : hasher, (typeof field) -[clos]-> hasher | hasher has Hasher, (typeof field) has Hash
|
||||
// Hash.hash : hasher, (typeof field) -[clos]-> hasher where hasher implements Hasher, (typeof field) implements Hash
|
||||
let hash_fn_head = Expr::AbilityMember(member, None, this_hash_fn_var);
|
||||
let hash_fn_data = Box::new((
|
||||
this_hash_fn_var,
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ impl Env<'_> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
// Since we're doing `{foo} ~ a | a has Encoding`, we may see "lambda sets to
|
||||
// Since we're doing `{foo} ~ a where a implements Encoding`, we may see "lambda sets to
|
||||
// specialize" for e.g. `{foo}:toEncoder:1`, but these are actually just the
|
||||
// specialization lambda sets, so we don't need to do any extra work!
|
||||
//
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use crate::{
|
|||
Buf,
|
||||
};
|
||||
use roc_parse::ast::{
|
||||
AssignedField, Collection, Expr, ExtractSpaces, HasAbilities, HasAbility, HasClause, HasImpls,
|
||||
RecordBuilderField, Tag, TypeAnnotation, TypeHeader,
|
||||
AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, ImplementsAbilities,
|
||||
ImplementsAbility, ImplementsClause, RecordBuilderField, Tag, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use roc_parse::ident::UppercaseIdent;
|
||||
use roc_region::all::Loc;
|
||||
|
|
@ -350,16 +350,23 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Where(annot, has_clauses) => {
|
||||
Where(annot, implements_clauses) => {
|
||||
annot.format_with_options(buf, parens, newlines, indent);
|
||||
if has_clauses.iter().any(|has| has.is_multiline()) {
|
||||
if implements_clauses
|
||||
.iter()
|
||||
.any(|implements| implements.is_multiline())
|
||||
{
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
}
|
||||
for (i, has) in has_clauses.iter().enumerate() {
|
||||
buf.push(if i == 0 { '|' } else { ',' });
|
||||
for (i, has) in implements_clauses.iter().enumerate() {
|
||||
buf.push_str(if i == 0 {
|
||||
roc_parse::keyword::WHERE
|
||||
} else {
|
||||
","
|
||||
});
|
||||
buf.spaces(1);
|
||||
has.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
|
|
@ -645,16 +652,16 @@ impl<'a> Formattable for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for HasClause<'a> {
|
||||
impl<'a> Formattable for ImplementsClause<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
// No, always put abilities in a "has" clause on one line
|
||||
// No, always put abilities in an "implements" clause on one line
|
||||
false
|
||||
}
|
||||
|
||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||
buf.push_str(self.var.value.extract_spaces().item);
|
||||
buf.spaces(1);
|
||||
buf.push_str("has");
|
||||
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
||||
buf.spaces(1);
|
||||
|
||||
for (i, ab) in self.abilities.iter().enumerate() {
|
||||
|
|
@ -668,30 +675,30 @@ impl<'a> Formattable for HasClause<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for HasImpls<'a> {
|
||||
impl<'a> Formattable for AbilityImpls<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
HasImpls::SpaceBefore(_, _) | HasImpls::SpaceAfter(_, _) => true,
|
||||
HasImpls::HasImpls(impls) => is_collection_multiline(impls),
|
||||
AbilityImpls::SpaceBefore(_, _) | AbilityImpls::SpaceAfter(_, _) => true,
|
||||
AbilityImpls::AbilityImpls(impls) => is_collection_multiline(impls),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||
match self {
|
||||
HasImpls::HasImpls(impls) => {
|
||||
AbilityImpls::AbilityImpls(impls) => {
|
||||
if newlines == Newlines::Yes {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
}
|
||||
fmt_collection(buf, indent, Braces::Curly, *impls, Newlines::No);
|
||||
}
|
||||
HasImpls::SpaceBefore(impls, spaces) => {
|
||||
AbilityImpls::SpaceBefore(impls, spaces) => {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
impls.format_with_options(buf, parens, Newlines::No, indent);
|
||||
}
|
||||
HasImpls::SpaceAfter(impls, spaces) => {
|
||||
AbilityImpls::SpaceAfter(impls, spaces) => {
|
||||
impls.format_with_options(buf, parens, newlines, indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
}
|
||||
|
|
@ -699,11 +706,11 @@ impl<'a> Formattable for HasImpls<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for HasAbility<'a> {
|
||||
impl<'a> Formattable for ImplementsAbility<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
HasAbility::SpaceAfter(..) | HasAbility::SpaceBefore(..) => true,
|
||||
HasAbility::HasAbility { ability, impls } => {
|
||||
ImplementsAbility::SpaceAfter(..) | ImplementsAbility::SpaceBefore(..) => true,
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
ability.is_multiline() || impls.map(|i| i.is_multiline()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -711,7 +718,7 @@ impl<'a> Formattable for HasAbility<'a> {
|
|||
|
||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||
match self {
|
||||
HasAbility::HasAbility { ability, impls } => {
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
if newlines == Newlines::Yes {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
|
|
@ -722,13 +729,13 @@ impl<'a> Formattable for HasAbility<'a> {
|
|||
impls.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
}
|
||||
HasAbility::SpaceBefore(ab, spaces) => {
|
||||
ImplementsAbility::SpaceBefore(ab, spaces) => {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
ab.format_with_options(buf, parens, Newlines::No, indent)
|
||||
}
|
||||
HasAbility::SpaceAfter(ab, spaces) => {
|
||||
ImplementsAbility::SpaceAfter(ab, spaces) => {
|
||||
ab.format_with_options(buf, parens, newlines, indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
}
|
||||
|
|
@ -736,32 +743,34 @@ impl<'a> Formattable for HasAbility<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for HasAbilities<'a> {
|
||||
impl<'a> Formattable for ImplementsAbilities<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
HasAbilities::SpaceAfter(..) | HasAbilities::SpaceBefore(..) => true,
|
||||
HasAbilities::Has(has_abilities) => is_collection_multiline(has_abilities),
|
||||
ImplementsAbilities::SpaceAfter(..) | ImplementsAbilities::SpaceBefore(..) => true,
|
||||
ImplementsAbilities::Implements(has_abilities) => {
|
||||
is_collection_multiline(has_abilities)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||
match self {
|
||||
HasAbilities::Has(has_abilities) => {
|
||||
ImplementsAbilities::Implements(has_abilities) => {
|
||||
if newlines == Newlines::Yes {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
}
|
||||
buf.push_str("has");
|
||||
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
||||
buf.spaces(1);
|
||||
fmt_collection(buf, indent, Braces::Square, *has_abilities, Newlines::No);
|
||||
}
|
||||
HasAbilities::SpaceBefore(has_abilities, spaces) => {
|
||||
ImplementsAbilities::SpaceBefore(has_abilities, spaces) => {
|
||||
buf.newline();
|
||||
buf.indent(indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
has_abilities.format_with_options(buf, parens, Newlines::No, indent)
|
||||
}
|
||||
HasAbilities::SpaceAfter(has_abilities, spaces) => {
|
||||
ImplementsAbilities::SpaceAfter(has_abilities, spaces) => {
|
||||
has_abilities.format_with_options(buf, parens, newlines, indent);
|
||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ impl<'a> Formattable for TypeDef<'a> {
|
|||
}
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has: _,
|
||||
loc_implements: _,
|
||||
members,
|
||||
} => {
|
||||
buf.indent(indent);
|
||||
|
|
@ -133,8 +133,8 @@ impl<'a> Formattable for TypeDef<'a> {
|
|||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||
buf.indent(indent);
|
||||
}
|
||||
|
||||
buf.push_str(" has");
|
||||
buf.spaces(1);
|
||||
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
||||
|
||||
if !self.is_multiline() {
|
||||
debug_assert_eq!(members.len(), 1);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use bumpalo::Bump;
|
|||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, Has, HasAbilities,
|
||||
HasAbility, HasClause, HasImpls, Header, Module, Pattern, RecordBuilderField, Spaced,
|
||||
Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
WhenBranch,
|
||||
AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
|
||||
Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, Module,
|
||||
Pattern, RecordBuilderField, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation,
|
||||
TypeDef, TypeHeader, ValueDef, WhenBranch,
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
||||
|
|
@ -507,14 +507,14 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
|||
},
|
||||
Ability {
|
||||
header: TypeHeader { name, vars },
|
||||
loc_has,
|
||||
loc_implements: loc_has,
|
||||
members,
|
||||
} => Ability {
|
||||
header: TypeHeader {
|
||||
name: name.remove_spaces(arena),
|
||||
vars: vars.remove_spaces(arena),
|
||||
},
|
||||
loc_has: loc_has.remove_spaces(arena),
|
||||
loc_implements: loc_has.remove_spaces(arena),
|
||||
members: members.remove_spaces(arena),
|
||||
},
|
||||
}
|
||||
|
|
@ -569,9 +569,9 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for Has<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for Implements<'a> {
|
||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||
Has::Has
|
||||
Implements::Implements
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -870,9 +870,9 @@ impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for HasClause<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
HasClause {
|
||||
ImplementsClause {
|
||||
var: self.var.remove_spaces(arena),
|
||||
abilities: self.abilities.remove_spaces(arena),
|
||||
}
|
||||
|
|
@ -893,38 +893,43 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for HasImpls<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
HasImpls::HasImpls(impls) => HasImpls::HasImpls(impls.remove_spaces(arena)),
|
||||
HasImpls::SpaceBefore(has, _) | HasImpls::SpaceAfter(has, _) => {
|
||||
AbilityImpls::AbilityImpls(impls) => {
|
||||
AbilityImpls::AbilityImpls(impls.remove_spaces(arena))
|
||||
}
|
||||
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for HasAbility<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
HasAbility::HasAbility { ability, impls } => HasAbility::HasAbility {
|
||||
ability: ability.remove_spaces(arena),
|
||||
impls: impls.remove_spaces(arena),
|
||||
},
|
||||
HasAbility::SpaceBefore(has, _) | HasAbility::SpaceAfter(has, _) => {
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
ImplementsAbility::ImplementsAbility {
|
||||
ability: ability.remove_spaces(arena),
|
||||
impls: impls.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
|
||||
has.remove_spaces(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemoveSpaces<'a> for HasAbilities<'a> {
|
||||
impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> {
|
||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||
match *self {
|
||||
HasAbilities::Has(derived) => HasAbilities::Has(derived.remove_spaces(arena)),
|
||||
HasAbilities::SpaceBefore(derived, _) | HasAbilities::SpaceAfter(derived, _) => {
|
||||
derived.remove_spaces(arena)
|
||||
ImplementsAbilities::Implements(derived) => {
|
||||
ImplementsAbilities::Implements(derived.remove_spaces(arena))
|
||||
}
|
||||
ImplementsAbilities::SpaceBefore(derived, _)
|
||||
| ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -985,7 +985,8 @@ impl<
|
|||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
ASM::mov_freg64_freg64(&mut self.buf, dst_reg, CC::FLOAT_RETURN_REGS[0]);
|
||||
}
|
||||
LayoutRepr::I128 | LayoutRepr::U128 => {
|
||||
// Note that on windows there is only 1 general return register so we can't use this optimisation
|
||||
LayoutRepr::I128 | LayoutRepr::U128 if CC::GENERAL_RETURN_REGS.len() > 1 => {
|
||||
let offset = self.storage_manager.claim_stack_area(dst, 16);
|
||||
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset, CC::GENERAL_RETURN_REGS[0]);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
|||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||
};
|
||||
|
||||
use super::{CompareOperation, RegisterWidth};
|
||||
|
|
@ -546,7 +546,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
|||
}
|
||||
}
|
||||
|
||||
fn copy_symbol_to_stack<'a, CC>(
|
||||
fn copy_symbol_to_stack_offset<'a, CC>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
storage_manager: &mut X86_64StorageManager<'a, '_, CC>,
|
||||
sym: Symbol,
|
||||
|
|
@ -600,6 +600,59 @@ where
|
|||
size
|
||||
}
|
||||
|
||||
fn copy_to_base_offset<GeneralReg, FloatReg, ASM>(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst_base_offset: i32,
|
||||
stack_size: u32,
|
||||
ptr_reg: GeneralReg,
|
||||
tmp_reg: GeneralReg,
|
||||
read_offset: i32,
|
||||
) where
|
||||
FloatReg: RegTrait,
|
||||
GeneralReg: RegTrait,
|
||||
ASM: Assembler<GeneralReg, FloatReg>,
|
||||
{
|
||||
let mut copied = 0;
|
||||
let size = stack_size as i32;
|
||||
let base_offset = dst_base_offset;
|
||||
|
||||
if size - copied >= 8 {
|
||||
for _ in (0..(size - copied)).step_by(8) {
|
||||
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||
ASM::mov_base32_reg64(buf, base_offset + copied, tmp_reg);
|
||||
|
||||
copied += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if size - copied >= 4 {
|
||||
for _ in (0..(size - copied)).step_by(4) {
|
||||
ASM::mov_reg32_mem32_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||
ASM::mov_base32_reg32(buf, base_offset + copied, tmp_reg);
|
||||
|
||||
copied += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if size - copied >= 2 {
|
||||
for _ in (0..(size - copied)).step_by(2) {
|
||||
ASM::mov_reg16_mem16_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||
ASM::mov_base32_reg16(buf, base_offset + copied, tmp_reg);
|
||||
|
||||
copied += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if size - copied >= 1 {
|
||||
for _ in (0..(size - copied)).step_by(1) {
|
||||
ASM::mov_reg8_mem8_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||
ASM::mov_base32_reg8(buf, base_offset + copied, tmp_reg);
|
||||
|
||||
copied += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct X64_64SystemVStoreArgs {
|
||||
general_i: usize,
|
||||
float_i: usize,
|
||||
|
|
@ -658,7 +711,8 @@ impl X64_64SystemVStoreArgs {
|
|||
// Just copy onto the stack.
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
let size =
|
||||
copy_symbol_to_stack_offset(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
|
|
@ -672,14 +726,16 @@ impl X64_64SystemVStoreArgs {
|
|||
LayoutRepr::Struct { .. } => {
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
let size =
|
||||
copy_symbol_to_stack_offset(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive(_)) => {
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
let size =
|
||||
copy_symbol_to_stack_offset(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
|
|
@ -762,6 +818,8 @@ impl X64_64WindowsFastCallStoreArgs {
|
|||
sym: Symbol,
|
||||
in_layout: InLayout<'a>,
|
||||
) {
|
||||
type ASM = X86_64Assembler;
|
||||
|
||||
// we use the return register as a temporary register; it will be overwritten anyway
|
||||
let tmp_reg = Self::GENERAL_RETURN_REGS[0];
|
||||
|
||||
|
|
@ -776,31 +834,50 @@ impl X64_64WindowsFastCallStoreArgs {
|
|||
let reg1 = Self::GENERAL_PARAM_REGS[self.general_i];
|
||||
let reg2 = Self::GENERAL_PARAM_REGS[self.general_i + 1];
|
||||
|
||||
X86_64Assembler::mov_reg64_base32(buf, reg1, offset);
|
||||
X86_64Assembler::mov_reg64_base32(buf, reg2, offset + 8);
|
||||
ASM::mov_reg64_base32(buf, reg1, offset);
|
||||
ASM::mov_reg64_base32(buf, reg2, offset + 8);
|
||||
|
||||
self.general_i += 2;
|
||||
} else {
|
||||
// Copy to stack using return reg as buffer.
|
||||
let reg = Self::GENERAL_RETURN_REGS[0];
|
||||
|
||||
X86_64Assembler::mov_reg64_base32(buf, reg, offset);
|
||||
X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset, reg);
|
||||
ASM::mov_reg64_base32(buf, reg, offset);
|
||||
ASM::mov_stack32_reg64(buf, self.tmp_stack_offset, reg);
|
||||
|
||||
X86_64Assembler::mov_reg64_base32(buf, reg, offset + 8);
|
||||
X86_64Assembler::mov_stack32_reg64(buf, self.tmp_stack_offset + 8, reg);
|
||||
ASM::mov_reg64_base32(buf, reg, offset + 8);
|
||||
ASM::mov_stack32_reg64(buf, self.tmp_stack_offset + 8, reg);
|
||||
|
||||
self.tmp_stack_offset += 16;
|
||||
}
|
||||
}
|
||||
_ if layout_interner.stack_size(in_layout) == 0 => {}
|
||||
_ if layout_interner.stack_size(in_layout) > 16 => {
|
||||
// for now, just copy onto the stack.
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
// Reference: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#parameter-passing
|
||||
match Self::GENERAL_PARAM_REGS.get(self.general_i) {
|
||||
Some(reg) => {
|
||||
// if there is a general purpose register available, use it to store a pointer to the value
|
||||
let (base_offset, _size) = storage_manager.stack_offset_and_size(&sym);
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
ASM::add_reg64_reg64_imm32(buf, *reg, X86_64GeneralReg::RBP, base_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
self.general_i += 1;
|
||||
}
|
||||
None => {
|
||||
// else, pass the value implicitly by copying to the stack (of the new frame)
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack_offset(
|
||||
buf,
|
||||
storage_manager,
|
||||
sym,
|
||||
tmp_reg,
|
||||
stack_offset,
|
||||
);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
LayoutRepr::LambdaSet(lambda_set) => self.store_arg(
|
||||
buf,
|
||||
|
|
@ -813,14 +890,16 @@ impl X64_64WindowsFastCallStoreArgs {
|
|||
// for now, just also store this on the stack
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
let size =
|
||||
copy_symbol_to_stack_offset(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
LayoutRepr::Union(UnionLayout::NonRecursive(_)) => {
|
||||
let stack_offset = self.tmp_stack_offset;
|
||||
|
||||
let size = copy_symbol_to_stack(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
let size =
|
||||
copy_symbol_to_stack_offset(buf, storage_manager, sym, tmp_reg, stack_offset);
|
||||
|
||||
self.tmp_stack_offset += size as i32;
|
||||
}
|
||||
|
|
@ -977,11 +1056,14 @@ struct X64_64WindowsFastCallLoadArgs {
|
|||
impl X64_64WindowsFastCallLoadArgs {
|
||||
fn load_arg<'a>(
|
||||
&mut self,
|
||||
buf: &mut Vec<'a, u8>,
|
||||
storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
sym: Symbol,
|
||||
in_layout: InLayout<'a>,
|
||||
) {
|
||||
type ASM = X86_64Assembler;
|
||||
|
||||
let stack_size = layout_interner.stack_size(in_layout);
|
||||
match layout_interner.get_repr(in_layout) {
|
||||
single_register_integers!() => self.load_arg_general(storage_manager, sym),
|
||||
|
|
@ -991,11 +1073,33 @@ impl X64_64WindowsFastCallLoadArgs {
|
|||
storage_manager.no_data(&sym);
|
||||
}
|
||||
_ if stack_size > 16 => {
|
||||
// TODO: Double check this.
|
||||
storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size);
|
||||
self.argument_offset += stack_size as i32;
|
||||
// Reference: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#parameter-passing
|
||||
match X86_64WindowsFastcall::GENERAL_PARAM_REGS.get(self.general_i) {
|
||||
Some(ptr_reg) => {
|
||||
// if there is a general purpose register available, use it to store a pointer to the value
|
||||
let base_offset = storage_manager.claim_stack_area(&sym, stack_size);
|
||||
let tmp_reg = X86_64WindowsFastcall::GENERAL_RETURN_REGS[0];
|
||||
|
||||
copy_to_base_offset::<_, _, ASM>(
|
||||
buf,
|
||||
base_offset,
|
||||
stack_size,
|
||||
*ptr_reg,
|
||||
tmp_reg,
|
||||
0,
|
||||
);
|
||||
|
||||
self.general_i += 1;
|
||||
}
|
||||
None => {
|
||||
// else, pass the value implicitly by copying to the stack (of the new frame)
|
||||
storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size);
|
||||
self.argument_offset += stack_size as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
LayoutRepr::LambdaSet(lambda_set) => self.load_arg(
|
||||
buf,
|
||||
storage_manager,
|
||||
layout_interner,
|
||||
sym,
|
||||
|
|
@ -1130,6 +1234,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
];
|
||||
const SHADOW_SPACE_SIZE: u8 = 32;
|
||||
|
||||
// Refer https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#callercallee-saved-registers
|
||||
// > The x64 ABI considers registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-XMM15 nonvolatile.
|
||||
// > They must be saved and restored by a function that uses them.
|
||||
#[inline(always)]
|
||||
fn general_callee_saved(reg: &X86_64GeneralReg) -> bool {
|
||||
matches!(
|
||||
|
|
@ -1146,16 +1253,23 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
)
|
||||
}
|
||||
|
||||
// Refer https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#callercallee-saved-registers
|
||||
// > The x64 ABI considers registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-XMM15 nonvolatile.
|
||||
// > They must be saved and restored by a function that uses them.
|
||||
#[inline(always)]
|
||||
fn float_callee_saved(reg: &X86_64FloatReg) -> bool {
|
||||
matches!(
|
||||
reg,
|
||||
X86_64FloatReg::XMM0
|
||||
| X86_64FloatReg::XMM1
|
||||
| X86_64FloatReg::XMM2
|
||||
| X86_64FloatReg::XMM3
|
||||
| X86_64FloatReg::XMM4
|
||||
| X86_64FloatReg::XMM5
|
||||
X86_64FloatReg::XMM6
|
||||
| X86_64FloatReg::XMM7
|
||||
| X86_64FloatReg::XMM8
|
||||
| X86_64FloatReg::XMM9
|
||||
| X86_64FloatReg::XMM10
|
||||
| X86_64FloatReg::XMM11
|
||||
| X86_64FloatReg::XMM12
|
||||
| X86_64FloatReg::XMM13
|
||||
| X86_64FloatReg::XMM14
|
||||
| X86_64FloatReg::XMM15
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1195,7 +1309,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
buf: &mut Vec<'a, u8>,
|
||||
storage_manager: &mut X86_64StorageManager<'a, '_, X86_64WindowsFastcall>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
args: &'a [(InLayout<'a>, Symbol)],
|
||||
|
|
@ -1216,7 +1330,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
}
|
||||
|
||||
for (in_layout, sym) in args.iter() {
|
||||
state.load_arg(storage_manager, layout_interner, *sym, *in_layout);
|
||||
state.load_arg(buf, storage_manager, layout_interner, *sym, *in_layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1286,6 +1400,16 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
single_register_layouts!() => {
|
||||
internal_error!("single register layouts are not complex symbols");
|
||||
}
|
||||
// For windows (and zig 0.9 changes in zig 0.10) we need to match what zig does,
|
||||
// in this case uses RAX & RDX to return the value
|
||||
LayoutRepr::I128 | LayoutRepr::U128 => {
|
||||
let (base_offset, size) = storage_manager.stack_offset_and_size(sym);
|
||||
debug_assert_eq!(base_offset % 8, 0);
|
||||
debug_assert_eq!(size, 16);
|
||||
|
||||
X86_64Assembler::mov_reg64_base32(buf, X86_64GeneralReg::RAX, base_offset);
|
||||
X86_64Assembler::mov_reg64_base32(buf, X86_64GeneralReg::RDX, base_offset + 0x08);
|
||||
}
|
||||
_ if layout_interner.stack_size(*layout) == 0 => {}
|
||||
_ if !Self::returns_via_arg_pointer(layout_interner, layout) => {
|
||||
let (base_offset, size) = storage_manager.stack_offset_and_size(sym);
|
||||
|
|
@ -1333,6 +1457,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
single_register_layouts!() => {
|
||||
internal_error!("single register layouts are not complex symbols");
|
||||
}
|
||||
// For windows (and zig 0.9 changes in zig 0.10) we need to match what zig does,
|
||||
// in this case uses RAX & RDX to return the value
|
||||
LayoutRepr::I128 | LayoutRepr::U128 => {
|
||||
let size = layout_interner.stack_size(*layout);
|
||||
let offset = storage_manager.claim_stack_area(sym, size);
|
||||
X86_64Assembler::mov_base32_reg64(buf, offset, X86_64GeneralReg::RAX);
|
||||
X86_64Assembler::mov_base32_reg64(buf, offset + 0x08, X86_64GeneralReg::RDX);
|
||||
}
|
||||
_ if layout_interner.stack_size(*layout) == 0 => {
|
||||
storage_manager.no_data(sym);
|
||||
}
|
||||
|
|
@ -1446,12 +1578,16 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
type ASM = X86_64Assembler;
|
||||
|
||||
// a *const RocStr
|
||||
let roc_str_ptr = R11;
|
||||
ASM::add_reg64_reg64_imm32(buf, roc_str_ptr, RSP, 16 + 24); // 24 is width of a rocstr
|
||||
let roc_str_ptr = RCX;
|
||||
debug_assert_eq!(roc_str_ptr, Self::GENERAL_PARAM_REGS[0]);
|
||||
|
||||
// a 32-bit integer
|
||||
let panic_tag = RCX;
|
||||
debug_assert_eq!(panic_tag, Self::GENERAL_PARAM_REGS[0]);
|
||||
let panic_tag = RDX;
|
||||
debug_assert_eq!(panic_tag, Self::GENERAL_PARAM_REGS[1]);
|
||||
|
||||
// move the crash tag into a temporary register. We add 1 to it because the 0 value
|
||||
// is already used for "no crash occurred"
|
||||
ASM::add_reg64_reg64_imm32(buf, R10, panic_tag, 0x01);
|
||||
|
||||
// the setlongjmp_buffer
|
||||
let env = R8;
|
||||
|
|
@ -1485,16 +1621,12 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
|||
ASM::mov_reg64_mem64_offset32(buf, result_pointer, env, 0x58);
|
||||
|
||||
// a pointer to the error message
|
||||
ASM::mov_reg64_imm64(buf, R10, 0x60);
|
||||
ASM::add_reg64_reg64_reg64(buf, R10, R10, env);
|
||||
ASM::add_reg64_reg64_imm32(buf, R11, env, 0x60);
|
||||
|
||||
// write a pointer to the error message into result_pointer
|
||||
ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R10);
|
||||
ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x00, R11);
|
||||
|
||||
// the panic_tag; 1 is added to differentiate from 0 (which indicates success)
|
||||
ASM::add_reg64_reg64_imm32(buf, R10, panic_tag, 1);
|
||||
|
||||
// write the panic tag into the result_pointer
|
||||
// write the panic tag (now in R10) into the result_pointer
|
||||
ASM::mov_mem64_offset32_reg64(buf, result_pointer, 0x08, R10);
|
||||
|
||||
jmp_reg64_offset8(buf, env, 0x50)
|
||||
|
|
@ -1508,7 +1640,10 @@ impl X86_64WindowsFastcall {
|
|||
) -> bool {
|
||||
// TODO: This is not fully correct there are some exceptions for "vector" types.
|
||||
// details here: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#return-values
|
||||
interner.stack_size(*ret_layout) > 8
|
||||
match *ret_layout {
|
||||
Layout::I128 | Layout::U128 => false,
|
||||
_ => interner.stack_size(*ret_layout) > 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@ impl AssemblyBackendMode {
|
|||
AssemblyBackendMode::Test => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_roc_panic(self) -> bool {
|
||||
match self {
|
||||
AssemblyBackendMode::Binary => false,
|
||||
AssemblyBackendMode::Test => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::generic64::{aarch64, new_backend_64bit, x86_64};
|
||||
use crate::{Backend, Env, Relocation};
|
||||
use crate::{AssemblyBackendMode, Backend, Env, Relocation};
|
||||
use bumpalo::collections::Vec;
|
||||
use object::write::{self, SectionId, SymbolId};
|
||||
use object::write::{Object, StandardSection, StandardSegment, Symbol, SymbolSection};
|
||||
|
|
@ -312,11 +312,13 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
);
|
||||
*/
|
||||
|
||||
define_setlongjmp_buffer(&mut output);
|
||||
if backend.env().mode.generate_roc_panic() {
|
||||
define_setlongjmp_buffer(&mut output);
|
||||
|
||||
generate_roc_panic(&mut backend, &mut output);
|
||||
generate_setjmp(&mut backend, &mut output);
|
||||
generate_longjmp(&mut backend, &mut output);
|
||||
generate_roc_panic(&mut backend, &mut output);
|
||||
generate_setjmp(&mut backend, &mut output);
|
||||
generate_longjmp(&mut backend, &mut output);
|
||||
}
|
||||
|
||||
if backend.env().mode.generate_allocators() {
|
||||
generate_wrapper(
|
||||
|
|
@ -402,15 +404,17 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
|
||||
// println!("{}", test_helper.to_pretty(backend.interner(), 200, true));
|
||||
|
||||
build_proc_symbol(
|
||||
&mut output,
|
||||
&mut layout_ids,
|
||||
&mut procs,
|
||||
&mut backend,
|
||||
layout,
|
||||
test_helper,
|
||||
Exposed::TestMain,
|
||||
);
|
||||
if let AssemblyBackendMode::Test = backend.env().mode {
|
||||
build_proc_symbol(
|
||||
&mut output,
|
||||
&mut layout_ids,
|
||||
&mut procs,
|
||||
&mut backend,
|
||||
layout,
|
||||
test_helper,
|
||||
Exposed::TestMain,
|
||||
);
|
||||
}
|
||||
|
||||
build_proc_symbol(
|
||||
&mut output,
|
||||
|
|
@ -599,7 +603,6 @@ fn build_exposed_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'a>) -> P
|
|||
closure_data_layout: None,
|
||||
ret_layout: proc.ret_layout,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
|
@ -681,7 +684,6 @@ fn build_exposed_generic_proc<'a, B: Backend<'a>>(backend: &mut B, proc: &Proc<'
|
|||
closure_data_layout: None,
|
||||
ret_layout: roc_mono::layout::Layout::UNIT,
|
||||
is_self_recursive: roc_mono::ir::SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: roc_mono::ir::HostExposedLayouts::NotHostExposed,
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ use roc_error_macros::{internal_error, todo_lambda_erasure};
|
|||
use roc_module::symbol::{Interns, Symbol};
|
||||
use roc_mono::ir::{
|
||||
BranchInfo, CallType, CrashTag, EntryPoint, GlueLayouts, HostExposedLambdaSet,
|
||||
ListLiteralElement, ModifyRc, OptLevel, ProcLayout, SingleEntryPoint,
|
||||
HostExposedLambdaSets, ListLiteralElement, ModifyRc, OptLevel, ProcLayout, SingleEntryPoint,
|
||||
};
|
||||
use roc_mono::layout::{
|
||||
Builtin, InLayout, LambdaName, LambdaSet, Layout, LayoutIds, LayoutInterner, LayoutRepr, Niche,
|
||||
|
|
@ -4920,6 +4920,7 @@ pub fn build_procedures<'a>(
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
opt_level: OptLevel,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
entry_point: EntryPoint<'a>,
|
||||
debug_output_file: Option<&Path>,
|
||||
glue_layouts: &GlueLayouts<'a>,
|
||||
|
|
@ -4929,6 +4930,7 @@ pub fn build_procedures<'a>(
|
|||
layout_interner,
|
||||
opt_level,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
entry_point,
|
||||
debug_output_file,
|
||||
);
|
||||
|
|
@ -4982,6 +4984,7 @@ pub fn build_wasm_test_wrapper<'a, 'ctx>(
|
|||
layout_interner,
|
||||
opt_level,
|
||||
procedures,
|
||||
vec![],
|
||||
EntryPoint::Single(entry_point),
|
||||
Some(&std::env::temp_dir().join("test.ll")),
|
||||
);
|
||||
|
|
@ -5000,6 +5003,7 @@ pub fn build_procedures_return_main<'a, 'ctx>(
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
opt_level: OptLevel,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
entry_point: SingleEntryPoint<'a>,
|
||||
) -> (&'static str, FunctionValue<'ctx>) {
|
||||
let mod_solutions = build_procedures_help(
|
||||
|
|
@ -5007,6 +5011,7 @@ pub fn build_procedures_return_main<'a, 'ctx>(
|
|||
layout_interner,
|
||||
opt_level,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
EntryPoint::Single(entry_point),
|
||||
Some(&std::env::temp_dir().join("test.ll")),
|
||||
);
|
||||
|
|
@ -5034,6 +5039,7 @@ pub fn build_procedures_expose_expects<'a>(
|
|||
layout_interner,
|
||||
opt_level,
|
||||
procedures,
|
||||
vec![],
|
||||
entry_point,
|
||||
Some(&std::env::temp_dir().join("test.ll")),
|
||||
);
|
||||
|
|
@ -5096,20 +5102,23 @@ fn build_procedures_help<'a>(
|
|||
layout_interner: &STLayoutInterner<'a>,
|
||||
opt_level: OptLevel,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
entry_point: EntryPoint<'a>,
|
||||
debug_output_file: Option<&Path>,
|
||||
) -> &'a ModSolutions {
|
||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||
let mut scope = Scope::default();
|
||||
|
||||
let it = procedures.iter().map(|x| x.1);
|
||||
let it1 = procedures.iter().map(|x| x.1);
|
||||
let it2 = host_exposed_lambda_sets.iter().map(|(_, _, hels)| hels);
|
||||
|
||||
let solutions = match roc_alias_analysis::spec_program(
|
||||
env.arena,
|
||||
layout_interner,
|
||||
opt_level,
|
||||
entry_point,
|
||||
it,
|
||||
it1,
|
||||
it2,
|
||||
) {
|
||||
Err(e) => panic!("Error in alias analysis: {e}"),
|
||||
Ok(solutions) => solutions,
|
||||
|
|
@ -5147,7 +5156,6 @@ fn build_procedures_help<'a>(
|
|||
build_proc(
|
||||
env,
|
||||
layout_interner,
|
||||
mod_solutions,
|
||||
&mut layout_ids,
|
||||
func_spec_solutions,
|
||||
scope.clone(),
|
||||
|
|
@ -5192,6 +5200,26 @@ fn build_procedures_help<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
use LlvmBackendMode::*;
|
||||
match env.mode {
|
||||
GenTest | WasmGenTest | CliTest => { /* no host, or exposing types is not supported */ }
|
||||
Binary | BinaryDev | BinaryGlue => {
|
||||
for (proc_name, alias_name, hels) in host_exposed_lambda_sets.iter() {
|
||||
let ident_string = proc_name.name().as_str(&env.interns);
|
||||
let fn_name: String = format!("{}_{}", ident_string, hels.id.0);
|
||||
|
||||
expose_alias_to_host(
|
||||
env,
|
||||
layout_interner,
|
||||
mod_solutions,
|
||||
&fn_name,
|
||||
*alias_name,
|
||||
hels,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod_solutions
|
||||
}
|
||||
|
||||
|
|
@ -5558,43 +5586,12 @@ fn build_host_exposed_alias_size_help<'a, 'ctx>(
|
|||
fn build_proc<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
mod_solutions: &'a ModSolutions,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
func_spec_solutions: &FuncSpecSolutions,
|
||||
mut scope: Scope<'a, 'ctx>,
|
||||
proc: &roc_mono::ir::Proc<'a>,
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
) {
|
||||
use roc_mono::ir::HostExposedLayouts;
|
||||
|
||||
match &proc.host_exposed_layouts {
|
||||
HostExposedLayouts::NotHostExposed => {}
|
||||
HostExposedLayouts::HostExposed { aliases, .. } => {
|
||||
use LlvmBackendMode::*;
|
||||
|
||||
match env.mode {
|
||||
GenTest | WasmGenTest | CliTest => {
|
||||
/* no host, or exposing types is not supported */
|
||||
}
|
||||
Binary | BinaryDev | BinaryGlue => {
|
||||
for (alias_name, hels) in aliases.iter() {
|
||||
let ident_string = proc.name.name().as_str(&env.interns);
|
||||
let fn_name: String = format!("{}_{}", ident_string, hels.id.0);
|
||||
|
||||
expose_alias_to_host(
|
||||
env,
|
||||
layout_interner,
|
||||
mod_solutions,
|
||||
&fn_name,
|
||||
*alias_name,
|
||||
hels,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let args = proc.args;
|
||||
let context = &env.context;
|
||||
|
||||
|
|
|
|||
|
|
@ -1160,8 +1160,13 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
|
|||
*******************************************************************/
|
||||
|
||||
fn expr_literal(&mut self, lit: &Literal<'a>, storage: &StoredValue) {
|
||||
let invalid_error =
|
||||
|| internal_error!("Literal value {:?} has invalid storage {:?}", lit, storage);
|
||||
let invalid_error = || {
|
||||
internal_error!(
|
||||
"Literal value {:?} implements invalid storage {:?}",
|
||||
lit,
|
||||
storage
|
||||
)
|
||||
};
|
||||
|
||||
match storage {
|
||||
StoredValue::VirtualMachineStack { value_type, .. } => {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const MODULES: &[(ModuleId, &str)] = &[
|
|||
(ModuleId::ENCODE, "Encode.roc"),
|
||||
(ModuleId::DECODE, "Decode.roc"),
|
||||
(ModuleId::HASH, "Hash.roc"),
|
||||
(ModuleId::INSPECT, "Inspect.roc"),
|
||||
(ModuleId::JSON, "TotallyNotJson.roc"),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -200,18 +200,27 @@ pub fn load_and_typecheck_str<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! include_bytes_align_as {
|
||||
($align_ty:ty, $path:expr) => {{
|
||||
// const block expression to encapsulate the static
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AlignedAs<Align, Bytes: ?Sized> {
|
||||
pub _align: [Align; 0],
|
||||
pub bytes: Bytes,
|
||||
}
|
||||
|
||||
// this assignment is made possible by CoerceUnsized
|
||||
static ALIGNED: &AlignedAs<$align_ty, [u8]> = &AlignedAs {
|
||||
_align: [],
|
||||
bytes: *include_bytes!($path),
|
||||
};
|
||||
|
||||
&ALIGNED.bytes
|
||||
}};
|
||||
}
|
||||
|
||||
// IFTTT: crates/compiler/load/build.rs
|
||||
const BOOL: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Bool.dat")) as &[_];
|
||||
const DICT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Dict.dat")) as &[_];
|
||||
const SET: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Set.dat")) as &[_];
|
||||
const RESULT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Result.dat")) as &[_];
|
||||
const NUM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Num.dat")) as &[_];
|
||||
const LIST: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/List.dat")) as &[_];
|
||||
const STR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Str.dat")) as &[_];
|
||||
const BOX: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Box.dat")) as &[_];
|
||||
const ENCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Encode.dat")) as &[_];
|
||||
const DECODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Decode.dat")) as &[_];
|
||||
const HASH: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/Hash.dat")) as &[_];
|
||||
|
||||
fn deserialize_help(bytes: &[u8]) -> TypeState {
|
||||
let (state, _offset) = TypeState::deserialize(bytes);
|
||||
|
|
@ -221,27 +230,41 @@ fn deserialize_help(bytes: &[u8]) -> TypeState {
|
|||
}
|
||||
|
||||
fn read_cached_types() -> MutMap<ModuleId, TypeState> {
|
||||
let mod_bool = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Bool.dat"));
|
||||
let mod_dict = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Dict.dat"));
|
||||
let mod_set = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Set.dat"));
|
||||
let mod_result = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Result.dat"));
|
||||
let mod_num = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Num.dat"));
|
||||
let mod_list = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/List.dat"));
|
||||
let mod_str = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Str.dat"));
|
||||
let mod_box = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Box.dat"));
|
||||
let mod_encode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Encode.dat"));
|
||||
let mod_decode = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Decode.dat"));
|
||||
let mod_hash = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Hash.dat"));
|
||||
let mod_inspect = include_bytes_align_as!(u128, concat!(env!("OUT_DIR"), "/Inspect.dat"));
|
||||
|
||||
let mut output = MutMap::default();
|
||||
|
||||
// Wasm seems to re-order definitions between build time and runtime, but only in release mode.
|
||||
// That is very strange, but we can solve it separately
|
||||
if !cfg!(target_family = "wasm") && !cfg!(windows) && !SKIP_SUBS_CACHE {
|
||||
output.insert(ModuleId::BOOL, deserialize_help(BOOL));
|
||||
output.insert(ModuleId::BOOL, deserialize_help(mod_bool));
|
||||
|
||||
output.insert(ModuleId::RESULT, deserialize_help(RESULT));
|
||||
output.insert(ModuleId::NUM, deserialize_help(NUM));
|
||||
output.insert(ModuleId::RESULT, deserialize_help(mod_result));
|
||||
output.insert(ModuleId::NUM, deserialize_help(mod_num));
|
||||
|
||||
output.insert(ModuleId::LIST, deserialize_help(LIST));
|
||||
output.insert(ModuleId::STR, deserialize_help(STR));
|
||||
output.insert(ModuleId::BOX, deserialize_help(BOX));
|
||||
output.insert(ModuleId::LIST, deserialize_help(mod_list));
|
||||
output.insert(ModuleId::STR, deserialize_help(mod_str));
|
||||
output.insert(ModuleId::BOX, deserialize_help(mod_box));
|
||||
|
||||
output.insert(ModuleId::DICT, deserialize_help(DICT));
|
||||
output.insert(ModuleId::SET, deserialize_help(SET));
|
||||
output.insert(ModuleId::DICT, deserialize_help(mod_dict));
|
||||
output.insert(ModuleId::SET, deserialize_help(mod_set));
|
||||
|
||||
output.insert(ModuleId::ENCODE, deserialize_help(ENCODE));
|
||||
output.insert(ModuleId::DECODE, deserialize_help(DECODE));
|
||||
output.insert(ModuleId::ENCODE, deserialize_help(mod_encode));
|
||||
output.insert(ModuleId::DECODE, deserialize_help(mod_decode));
|
||||
|
||||
output.insert(ModuleId::HASH, deserialize_help(HASH));
|
||||
output.insert(ModuleId::HASH, deserialize_help(mod_hash));
|
||||
output.insert(ModuleId::INSPECT, deserialize_help(mod_inspect));
|
||||
}
|
||||
|
||||
output
|
||||
|
|
|
|||
|
|
@ -54,11 +54,30 @@ pub enum TypeAnnotation {
|
|||
fields: Vec<RecordField>,
|
||||
extension: Box<TypeAnnotation>,
|
||||
},
|
||||
Tuple {
|
||||
elems: Vec<TypeAnnotation>,
|
||||
extension: Box<TypeAnnotation>,
|
||||
},
|
||||
Ability {
|
||||
members: Vec<AbilityMember>,
|
||||
},
|
||||
Wildcard,
|
||||
NoTypeAnn,
|
||||
Where {
|
||||
ann: Box<TypeAnnotation>,
|
||||
implements: Vec<ImplementsClause>,
|
||||
},
|
||||
As {
|
||||
ann: Box<TypeAnnotation>,
|
||||
name: String,
|
||||
vars: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImplementsClause {
|
||||
pub name: String,
|
||||
pub abilities: Vec<TypeAnnotation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -450,7 +469,7 @@ fn contains_unexposed_type(
|
|||
false
|
||||
}
|
||||
Where(loc_ann, _loc_has_clauses) => {
|
||||
// We assume all the abilities in the `has` clause are from exported modules.
|
||||
// We assume all the abilities in the `implements` clause are from exported modules.
|
||||
// TODO don't assume this! Instead, look them up and verify.
|
||||
contains_unexposed_type(&loc_ann.value, exposed_module_ids, module_ids)
|
||||
}
|
||||
|
|
@ -540,7 +559,57 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
}
|
||||
}
|
||||
ast::TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
|
||||
_ => NoTypeAnn,
|
||||
ast::TypeAnnotation::As(loc_ann, _comments, type_header) => TypeAnnotation::As {
|
||||
ann: Box::new(type_to_docs(in_func_type_ann, loc_ann.value)),
|
||||
name: type_header.name.value.to_string(),
|
||||
vars: type_header
|
||||
.vars
|
||||
.iter()
|
||||
.filter_map(|loc_pattern| match loc_pattern.value {
|
||||
ast::Pattern::Identifier(ident) => Some(ident.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
ast::TypeAnnotation::Tuple { elems, ext } => {
|
||||
let mut doc_elems = Vec::new();
|
||||
|
||||
for loc_ann in elems.items {
|
||||
doc_elems.push(type_to_docs(in_func_type_ann, loc_ann.value));
|
||||
}
|
||||
|
||||
let extension = match ext {
|
||||
None => NoTypeAnn,
|
||||
Some(ext_type_ann) => type_to_docs(in_func_type_ann, ext_type_ann.value),
|
||||
};
|
||||
|
||||
TypeAnnotation::Tuple {
|
||||
elems: doc_elems,
|
||||
extension: Box::new(extension),
|
||||
}
|
||||
}
|
||||
ast::TypeAnnotation::Where(loc_ann, implements) => TypeAnnotation::Where {
|
||||
ann: Box::new(type_to_docs(in_func_type_ann, loc_ann.value)),
|
||||
implements: implements
|
||||
.iter()
|
||||
.map(|clause| {
|
||||
let abilities = clause
|
||||
.value
|
||||
.abilities
|
||||
.iter()
|
||||
.map(|ability| type_to_docs(in_func_type_ann, ability.value))
|
||||
.collect();
|
||||
|
||||
ImplementsClause {
|
||||
name: clause.value.var.value.item().to_string(),
|
||||
abilities,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
ast::TypeAnnotation::Malformed(_) | ast::TypeAnnotation::Inferred => {
|
||||
TypeAnnotation::NoTypeAnn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +622,7 @@ fn ability_member_type_to_docs(
|
|||
let has_clauses = has_clauses
|
||||
.iter()
|
||||
.map(|hc| {
|
||||
let ast::HasClause { var, abilities } = hc.value;
|
||||
let ast::ImplementsClause { var, abilities } = hc.value;
|
||||
(
|
||||
var.value.extract_spaces().item.to_string(),
|
||||
abilities
|
||||
|
|
|
|||
|
|
@ -38,10 +38,9 @@ use roc_module::symbol::{
|
|||
PackageQualified, Symbol,
|
||||
};
|
||||
use roc_mono::ir::{
|
||||
CapturedSymbols, ExternalSpecializations, GlueLayouts, PartialProc, Proc, ProcLayout, Procs,
|
||||
ProcsBase, UpdateModeIds, UsageTrackingMap,
|
||||
CapturedSymbols, ExternalSpecializations, GlueLayouts, HostExposedLambdaSets, PartialProc,
|
||||
Proc, ProcLayout, Procs, ProcsBase, UpdateModeIds, UsageTrackingMap,
|
||||
};
|
||||
use roc_mono::layout::LayoutInterner;
|
||||
use roc_mono::layout::{
|
||||
GlobalLayoutInterner, LambdaName, Layout, LayoutCache, LayoutProblem, Niche, STLayoutInterner,
|
||||
};
|
||||
|
|
@ -614,6 +613,7 @@ enum Msg<'a> {
|
|||
external_specializations_requested: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
procs_base: ProcsBase<'a>,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
update_mode_ids: UpdateModeIds,
|
||||
module_timing: ModuleTiming,
|
||||
subs: Subs,
|
||||
|
|
@ -709,6 +709,7 @@ struct State<'a> {
|
|||
pub module_cache: ModuleCache<'a>,
|
||||
pub dependencies: Dependencies<'a>,
|
||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
pub host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
pub toplevel_expects: ToplevelExpects,
|
||||
pub exposed_to_host: ExposedToHost,
|
||||
|
||||
|
|
@ -789,6 +790,7 @@ impl<'a> State<'a> {
|
|||
module_cache: ModuleCache::default(),
|
||||
dependencies,
|
||||
procedures: MutMap::default(),
|
||||
host_exposed_lambda_sets: std::vec::Vec::new(),
|
||||
toplevel_expects: ToplevelExpects::default(),
|
||||
exposed_to_host: ExposedToHost::default(),
|
||||
exposed_modules: &[],
|
||||
|
|
@ -2288,6 +2290,7 @@ fn update<'a>(
|
|||
extend_header_with_builtin(header, ModuleId::ENCODE);
|
||||
extend_header_with_builtin(header, ModuleId::DECODE);
|
||||
extend_header_with_builtin(header, ModuleId::HASH);
|
||||
extend_header_with_builtin(header, ModuleId::INSPECT);
|
||||
}
|
||||
|
||||
state
|
||||
|
|
@ -2650,6 +2653,7 @@ fn update<'a>(
|
|||
subs,
|
||||
procs_base,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
external_specializations_requested,
|
||||
module_timing,
|
||||
layout_cache,
|
||||
|
|
@ -2667,6 +2671,9 @@ fn update<'a>(
|
|||
let _ = layout_cache;
|
||||
|
||||
state.procedures.extend(procedures);
|
||||
state
|
||||
.host_exposed_lambda_sets
|
||||
.extend(host_exposed_lambda_sets);
|
||||
state.module_cache.late_specializations.insert(
|
||||
module_id,
|
||||
LateSpecializationsModule {
|
||||
|
|
@ -2863,7 +2870,7 @@ fn update<'a>(
|
|||
// # Default module
|
||||
// interface Default exposes [default, getDefault]
|
||||
//
|
||||
// Default has default : {} -> a | a has Default
|
||||
// Default implements default : {} -> a where a implements Default
|
||||
//
|
||||
// getDefault = \{} -> default {}
|
||||
//
|
||||
|
|
@ -2982,8 +2989,8 @@ fn finish_specialization<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
subs: Subs,
|
||||
mut layout_interner: STLayoutInterner<'a>,
|
||||
mut exposed_to_host: ExposedToHost,
|
||||
layout_interner: STLayoutInterner<'a>,
|
||||
exposed_to_host: ExposedToHost,
|
||||
module_expectations: VecMap<ModuleId, Expectations>,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
|
||||
if false {
|
||||
|
|
@ -3082,6 +3089,7 @@ fn finish_specialization<'a>(
|
|||
let State {
|
||||
toplevel_expects,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
module_cache,
|
||||
output_path,
|
||||
platform_data,
|
||||
|
|
@ -3101,53 +3109,6 @@ fn finish_specialization<'a>(
|
|||
.collect();
|
||||
|
||||
let module_id = state.root_id;
|
||||
let mut glue_getters = Vec::new();
|
||||
|
||||
// the REPL does not have any platform data
|
||||
if let (
|
||||
EntryPoint::Executable {
|
||||
exposed_to_host: exposed_top_levels,
|
||||
..
|
||||
},
|
||||
Some(platform_data),
|
||||
) = (&entry_point, platform_data.as_ref())
|
||||
{
|
||||
// Expose glue for the platform, not for the app module!
|
||||
let module_id = platform_data.module_id;
|
||||
|
||||
for (_name, proc_layout) in exposed_top_levels.iter() {
|
||||
let ret = &proc_layout.result;
|
||||
for in_layout in proc_layout.arguments.iter().chain([ret]) {
|
||||
let layout = layout_interner.get(*in_layout);
|
||||
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||
let all_glue_procs = roc_mono::ir::generate_glue_procs(
|
||||
module_id,
|
||||
ident_ids,
|
||||
arena,
|
||||
&mut layout_interner,
|
||||
arena.alloc(layout),
|
||||
);
|
||||
|
||||
let lambda_set_names = all_glue_procs
|
||||
.legacy_layout_based_extern_names
|
||||
.iter()
|
||||
.map(|(lambda_set_id, _)| (*_name, *lambda_set_id));
|
||||
exposed_to_host.lambda_sets.extend(lambda_set_names);
|
||||
|
||||
let getter_names = all_glue_procs
|
||||
.getters
|
||||
.iter()
|
||||
.flat_map(|(_, glue_procs)| glue_procs.iter().map(|glue_proc| glue_proc.name));
|
||||
exposed_to_host.getters.extend(getter_names);
|
||||
|
||||
glue_getters.extend(all_glue_procs.getters.iter().flat_map(|(_, glue_procs)| {
|
||||
glue_procs
|
||||
.iter()
|
||||
.map(|glue_proc| (glue_proc.name, glue_proc.proc_layout))
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output_path = match output_path {
|
||||
Some(path_str) => Path::new(path_str).into(),
|
||||
|
|
@ -3172,13 +3133,12 @@ fn finish_specialization<'a>(
|
|||
interns,
|
||||
layout_interner,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
entry_point,
|
||||
sources,
|
||||
timings: state.timings,
|
||||
toplevel_expects,
|
||||
glue_layouts: GlueLayouts {
|
||||
getters: glue_getters,
|
||||
},
|
||||
glue_layouts: GlueLayouts { getters: vec![] },
|
||||
uses_prebuilt_platform,
|
||||
})
|
||||
}
|
||||
|
|
@ -3535,6 +3495,7 @@ fn load_module<'a>(
|
|||
"Encode", ModuleId::ENCODE
|
||||
"Decode", ModuleId::DECODE
|
||||
"Hash", ModuleId::HASH
|
||||
"Inspect", ModuleId::INSPECT
|
||||
"TotallyNotJson", ModuleId::JSON
|
||||
}
|
||||
|
||||
|
|
@ -4326,7 +4287,7 @@ fn build_header<'a>(
|
|||
// created an IdentId for this, when it was imported exposed
|
||||
// in a dependent module.
|
||||
//
|
||||
// For example, if module A has [B.{ foo }], then
|
||||
// For example, if module A implements [B.{ foo }], then
|
||||
// when we get here for B, `foo` will already have
|
||||
// an IdentId. We must reuse that!
|
||||
let ident_id = ident_ids.get_or_insert(loc_exposed.value.as_str());
|
||||
|
|
@ -4350,7 +4311,7 @@ fn build_header<'a>(
|
|||
// created an IdentId for this, when it was imported exposed
|
||||
// in a dependent module.
|
||||
//
|
||||
// For example, if module A has [B.{ foo }], then
|
||||
// For example, if module A implements [B.{ foo }], then
|
||||
// when we get here for B, `foo` will already have
|
||||
// an IdentId. We must reuse that!
|
||||
let ident_id = ident_ids.get_or_insert(loc_name.value.as_str());
|
||||
|
|
@ -5298,6 +5259,7 @@ fn canonicalize_and_constrain<'a>(
|
|||
| ModuleId::DICT
|
||||
| ModuleId::SET
|
||||
| ModuleId::HASH
|
||||
| ModuleId::INSPECT
|
||||
);
|
||||
|
||||
if !name.is_builtin() || should_include_builtin {
|
||||
|
|
@ -5563,7 +5525,8 @@ fn make_specializations<'a>(
|
|||
);
|
||||
|
||||
let external_specializations_requested = procs.externals_we_need.clone();
|
||||
let (procedures, restored_procs_base) = procs.get_specialized_procs_without_rc();
|
||||
let (procedures, host_exposed_lambda_sets, restored_procs_base) =
|
||||
procs.get_specialized_procs_without_rc();
|
||||
|
||||
// Turn `Bytes.Decode.IdentId(238)` into `Bytes.Decode.238`, we rely on this in mono tests
|
||||
mono_env.home.register_debug_idents(mono_env.ident_ids);
|
||||
|
|
@ -5579,6 +5542,7 @@ fn make_specializations<'a>(
|
|||
layout_cache,
|
||||
procs_base: restored_procs_base,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
update_mode_ids,
|
||||
subs,
|
||||
expectations,
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@ pub const BUILTIN_MODULES: &[(ModuleId, &str)] = &[
|
|||
(ModuleId::ENCODE, "Encode"),
|
||||
(ModuleId::DECODE, "Decode"),
|
||||
(ModuleId::HASH, "Hash"),
|
||||
(ModuleId::INSPECT, "Inspect"),
|
||||
(ModuleId::JSON, "TotallyNotJson"),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use roc_module::ident::Ident;
|
|||
use roc_module::symbol::{
|
||||
IdentIds, IdentIdsByModule, Interns, ModuleId, PQModuleName, PackageQualified, Symbol,
|
||||
};
|
||||
use roc_mono::ir::{GlueLayouts, LambdaSetId, Proc, ProcLayout, ProcsBase};
|
||||
use roc_mono::ir::{GlueLayouts, HostExposedLambdaSets, LambdaSetId, Proc, ProcLayout, ProcsBase};
|
||||
use roc_mono::layout::{LayoutCache, STLayoutInterner};
|
||||
use roc_parse::ast::{CommentOrNewline, Defs, TypeAnnotation, ValueDef};
|
||||
use roc_parse::header::{HeaderType, PackageName};
|
||||
|
|
@ -167,6 +167,7 @@ pub struct MonomorphizedModule<'a> {
|
|||
pub can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||
pub type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
pub host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
pub toplevel_expects: ToplevelExpects,
|
||||
pub entry_point: EntryPoint<'a>,
|
||||
pub exposed_to_host: ExposedToHost,
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ impl Default for ModuleCache<'_> {
|
|||
ENCODE,
|
||||
DECODE,
|
||||
HASH,
|
||||
INSPECT,
|
||||
JSON,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Model position :
|
|||
}
|
||||
|
||||
|
||||
initialModel : position -> Model position | position has Hash & Eq
|
||||
initialModel : position -> Model position where position implements Hash & Eq
|
||||
initialModel = \start ->
|
||||
{ evaluated : Set.empty {}
|
||||
, openSet : Set.single start
|
||||
|
|
@ -22,7 +22,7 @@ initialModel = \start ->
|
|||
}
|
||||
|
||||
|
||||
cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] | position has Hash & Eq
|
||||
cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] where position implements Hash & Eq
|
||||
cheapestOpen = \costFunction, model ->
|
||||
|
||||
folder = \resSmallestSoFar, position ->
|
||||
|
|
@ -47,7 +47,7 @@ cheapestOpen = \costFunction, model ->
|
|||
|
||||
|
||||
|
||||
reconstructPath : Dict position position, position -> List position | position has Hash & Eq
|
||||
reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
|
||||
reconstructPath = \cameFrom, goal ->
|
||||
when Dict.get cameFrom goal is
|
||||
Err KeyNotFound ->
|
||||
|
|
@ -56,7 +56,7 @@ reconstructPath = \cameFrom, goal ->
|
|||
Ok next ->
|
||||
List.append (reconstructPath cameFrom next) goal
|
||||
|
||||
updateCost : position, position, Model position -> Model position | position has Hash & Eq
|
||||
updateCost : position, position, Model position -> Model position where position implements Hash & Eq
|
||||
updateCost = \current, neighbour, model ->
|
||||
newCameFrom = Dict.insert model.cameFrom neighbour current
|
||||
|
||||
|
|
@ -80,12 +80,12 @@ updateCost = \current, neighbour, model ->
|
|||
model
|
||||
|
||||
|
||||
findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] | position has Hash & Eq
|
||||
findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] where position implements Hash & Eq
|
||||
findPath = \{ costFunction, moveFunction, start, end } ->
|
||||
astar costFunction moveFunction end (initialModel start)
|
||||
|
||||
|
||||
astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq
|
||||
astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] where position implements Hash & Eq
|
||||
astar = \costFn, moveFn, goal, model ->
|
||||
when cheapestOpen (\position -> costFn goal position) model is
|
||||
Err _ ->
|
||||
|
|
|
|||
|
|
@ -514,12 +514,12 @@ fn load_astar() {
|
|||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"findPath" => "{ costFunction : position, position -> F64, end : position, moveFunction : position -> Set position, start : position } -> Result (List position) [KeyNotFound] | position has Hash & Eq",
|
||||
"initialModel" => "position -> Model position | position has Hash & Eq",
|
||||
"reconstructPath" => "Dict position position, position -> List position | position has Hash & Eq",
|
||||
"updateCost" => "position, position, Model position -> Model position | position has Hash & Eq",
|
||||
"cheapestOpen" => "(position -> F64), Model position -> Result position [KeyNotFound] | position has Hash & Eq",
|
||||
"astar" => "(position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq",
|
||||
"findPath" => "{ costFunction : position, position -> F64, end : position, moveFunction : position -> Set position, start : position } -> Result (List position) [KeyNotFound] where position implements Hash & Eq",
|
||||
"initialModel" => "position -> Model position where position implements Hash & Eq",
|
||||
"reconstructPath" => "Dict position position, position -> List position where position implements Hash & Eq",
|
||||
"updateCost" => "position, position, Model position -> Model position where position implements Hash & Eq",
|
||||
"cheapestOpen" => "(position -> F64), Model position -> Result position [KeyNotFound] where position implements Hash & Eq",
|
||||
"astar" => "(position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] where position implements Hash & Eq",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -940,8 +940,8 @@ fn issue_2863_module_type_does_not_exist() {
|
|||
Did you mean one of these?
|
||||
|
||||
Decoding
|
||||
Result
|
||||
Dict
|
||||
Result
|
||||
DecodeError
|
||||
"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ impl ModuleName {
|
|||
pub const ENCODE: &'static str = "Encode";
|
||||
pub const DECODE: &'static str = "Decode";
|
||||
pub const HASH: &'static str = "Hash";
|
||||
pub const INSPECT: &'static str = "Inspect";
|
||||
pub const JSON: &'static str = "TotallyNotJson";
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
|
|
|
|||
|
|
@ -1431,6 +1431,7 @@ define_builtins! {
|
|||
80 LIST_ITER_HELP: "iterHelp"
|
||||
81 LIST_RELEASE_EXCESS_CAPACITY: "releaseExcessCapacity"
|
||||
82 LIST_UPDATE: "update"
|
||||
83 LIST_WALK_WITH_INDEX: "walkWithIndex"
|
||||
}
|
||||
7 RESULT: "Result" => {
|
||||
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
|
||||
|
|
@ -1588,9 +1589,49 @@ define_builtins! {
|
|||
20 HASH_HASH_LIST: "hashList"
|
||||
21 HASH_HASH_UNORDERED: "hashUnordered"
|
||||
}
|
||||
14 JSON: "TotallyNotJson" => {
|
||||
14 INSPECT: "Inspect" => {
|
||||
0 INSPECT_INSPECT_ABILITY: "Inspect" exposed_type=true
|
||||
1 INSPECT_INSPECTOR: "Inspector" exposed_type=true
|
||||
2 INSPECT_INSPECT_FORMATTER: "InspectFormatter" exposed_type=true
|
||||
3 INSPECT_ELEM_WALKER: "ElemWalker" exposed_type=true
|
||||
4 INSPECT_KEY_VAL_WALKER: "KeyValWalker" exposed_type=true
|
||||
5 INSPECT_INSPECT: "inspect"
|
||||
6 INSPECT_INIT: "init"
|
||||
7 INSPECT_LIST: "list"
|
||||
8 INSPECT_SET: "set"
|
||||
9 INSPECT_DICT: "dict"
|
||||
10 INSPECT_TAG: "tag"
|
||||
11 INSPECT_TUPLE: "tuple"
|
||||
12 INSPECT_RECORD: "record"
|
||||
13 INSPECT_BOOL: "bool"
|
||||
14 INSPECT_STR: "str"
|
||||
15 INSPECT_OPAQUE: "opaque"
|
||||
16 INSPECT_U8: "u8"
|
||||
17 INSPECT_I8: "i8"
|
||||
18 INSPECT_U16: "u16"
|
||||
19 INSPECT_I16: "i16"
|
||||
20 INSPECT_U32: "u32"
|
||||
21 INSPECT_I32: "i32"
|
||||
22 INSPECT_U64: "u64"
|
||||
23 INSPECT_I64: "i64"
|
||||
24 INSPECT_U128: "u128"
|
||||
25 INSPECT_I128: "i128"
|
||||
26 INSPECT_F32: "f32"
|
||||
27 INSPECT_F64: "f64"
|
||||
28 INSPECT_DEC: "dec"
|
||||
29 INSPECT_CUSTOM: "custom"
|
||||
30 INSPECT_APPLY: "apply"
|
||||
31 INSPECT_TO_INSPECTOR: "toInspector"
|
||||
}
|
||||
15 JSON: "TotallyNotJson" => {
|
||||
0 JSON_JSON: "TotallyNotJson"
|
||||
1 JSON_FIELD_NAME_MAPPING: "FieldNameMapping"
|
||||
2 JSON_NUMBER_STATE: "NumberState"
|
||||
3 JSON_STRING_STATE: "StringState"
|
||||
4 JSON_ARRAY_OPENING_STATE: "ArrayOpeningState"
|
||||
5 JSON_ARRAY_CLOSING_STATE: "ArrayClosingState"
|
||||
6 JSON_OBJECT_STATE: "ObjectState"
|
||||
}
|
||||
|
||||
num_modules: 15 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
|
||||
num_modules: 16 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
|||
use roc_target::TargetInfo;
|
||||
|
||||
use crate::ir::{
|
||||
BranchInfo, Call, CallSpecId, CallType, Expr, HostExposedLayouts, JoinPointId, Literal,
|
||||
ModifyRc, PassedFunction, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
||||
BranchInfo, Call, CallSpecId, CallType, Expr, JoinPointId, Literal, ModifyRc, PassedFunction,
|
||||
Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
||||
};
|
||||
use crate::layout::{
|
||||
Builtin, InLayout, LambdaName, Layout, LayoutInterner, LayoutRepr, LayoutWrapper, Niche,
|
||||
|
|
@ -452,7 +452,6 @@ impl<'a> CodeGenHelp<'a> {
|
|||
closure_data_layout: None,
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
});
|
||||
|
||||
|
|
@ -772,7 +771,6 @@ impl<'a> CallerProc<'a> {
|
|||
closure_data_layout: None,
|
||||
ret_layout: Layout::UNIT,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
@ -888,10 +886,6 @@ pub fn test_helper<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: output_layout,
|
||||
is_self_recursive: main_proc.is_self_recursive,
|
||||
host_exposed_layouts: HostExposedLayouts::HostExposed {
|
||||
rigids: Default::default(),
|
||||
aliases: Default::default(),
|
||||
},
|
||||
is_erased: false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,7 +307,6 @@ pub struct Proc<'a> {
|
|||
pub closure_data_layout: Option<InLayout<'a>>,
|
||||
pub ret_layout: InLayout<'a>,
|
||||
pub is_self_recursive: SelfRecursive,
|
||||
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||
pub is_erased: bool,
|
||||
}
|
||||
|
||||
|
|
@ -320,15 +319,6 @@ pub struct HostExposedLambdaSet<'a> {
|
|||
pub raw_function_layout: RawFunctionLayout<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum HostExposedLayouts<'a> {
|
||||
NotHostExposed,
|
||||
HostExposed {
|
||||
rigids: BumpMap<Lowercase, InLayout<'a>>,
|
||||
aliases: BumpMap<Symbol, HostExposedLambdaSet<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SelfRecursive {
|
||||
NotSelfRecursive,
|
||||
|
|
@ -905,12 +895,16 @@ impl<'a> SpecializationStack<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub type HostExposedLambdaSets<'a> =
|
||||
std::vec::Vec<(LambdaName<'a>, Symbol, HostExposedLambdaSet<'a>)>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Procs<'a> {
|
||||
pub partial_procs: PartialProcs<'a>,
|
||||
ability_member_aliases: AbilityAliases,
|
||||
pending_specializations: PendingSpecializations<'a>,
|
||||
specialized: Specialized<'a>,
|
||||
host_exposed_lambda_sets: HostExposedLambdaSets<'a>,
|
||||
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
||||
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations<'a>>,
|
||||
symbol_specializations: SymbolSpecializations<'a>,
|
||||
|
|
@ -930,6 +924,7 @@ impl<'a> Procs<'a> {
|
|||
specialized: Specialized::default(),
|
||||
runtime_errors: BumpMap::new_in(arena),
|
||||
externals_we_need: BumpMap::new_in(arena),
|
||||
host_exposed_lambda_sets: std::vec::Vec::new(),
|
||||
symbol_specializations: Default::default(),
|
||||
specialization_stack: SpecializationStack(Vec::with_capacity_in(16, arena)),
|
||||
|
||||
|
|
@ -995,7 +990,11 @@ impl<'a> Procs<'a> {
|
|||
|
||||
pub fn get_specialized_procs_without_rc(
|
||||
self,
|
||||
) -> (MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, ProcsBase<'a>) {
|
||||
) -> (
|
||||
MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||
HostExposedLambdaSets<'a>,
|
||||
ProcsBase<'a>,
|
||||
) {
|
||||
let mut specialized_procs =
|
||||
MutMap::with_capacity_and_hasher(self.specialized.len(), default_hasher());
|
||||
|
||||
|
|
@ -1013,7 +1012,11 @@ impl<'a> Procs<'a> {
|
|||
imported_module_thunks: self.imported_module_thunks,
|
||||
};
|
||||
|
||||
(specialized_procs, restored_procs_base)
|
||||
(
|
||||
specialized_procs,
|
||||
self.host_exposed_lambda_sets,
|
||||
restored_procs_base,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO trim these down
|
||||
|
|
@ -3062,16 +3065,14 @@ fn specialize_host_specializations<'a>(
|
|||
|
||||
let offset_variable = StorageSubs::merge_into(store, env.subs);
|
||||
|
||||
for (symbol, from_app, opt_from_platform) in it {
|
||||
for (lambda_name, from_app, opt_from_platform) in it {
|
||||
let from_app = offset_variable(from_app);
|
||||
let index = specialize_external_help(env, procs, layout_cache, symbol, from_app);
|
||||
let index = specialize_external_help(env, procs, layout_cache, lambda_name, from_app);
|
||||
|
||||
let Some(from_platform) = opt_from_platform else { continue };
|
||||
|
||||
// now run the lambda set numbering scheme
|
||||
let mut layout_env =
|
||||
layout::Env::from_components(layout_cache, env.subs, env.arena, env.target_info);
|
||||
let hels = find_lambda_sets(&mut layout_env, from_platform);
|
||||
let hels = find_lambda_sets(env.arena, env.subs, from_platform);
|
||||
|
||||
// now unify
|
||||
let mut unify_env = roc_unify::Env::new(
|
||||
|
|
@ -3097,12 +3098,18 @@ fn specialize_host_specializations<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let mut aliases = BumpMap::default();
|
||||
|
||||
for (id, _, raw_function_layout) in hels {
|
||||
for (var, id) in hels {
|
||||
let symbol = env.unique_symbol();
|
||||
let lambda_name = LambdaName::no_niche(symbol);
|
||||
|
||||
let mut layout_env =
|
||||
layout::Env::from_components(layout_cache, env.subs, env.arena, env.target_info);
|
||||
let lambda_set = env.subs.get_lambda_set(var);
|
||||
let raw_function_layout =
|
||||
RawFunctionLayout::from_var(&mut layout_env, lambda_set.ambient_function)
|
||||
.value()
|
||||
.unwrap();
|
||||
|
||||
let (key, (top_level, proc)) = generate_host_exposed_function(
|
||||
env,
|
||||
procs,
|
||||
|
|
@ -3122,20 +3129,10 @@ fn specialize_host_specializations<'a>(
|
|||
raw_function_layout,
|
||||
};
|
||||
|
||||
aliases.insert(key, hels);
|
||||
}
|
||||
let in_progress = &mut procs.specialized.procedures[index.0];
|
||||
let InProgressProc::Done(proc) = in_progress else { unreachable!() };
|
||||
|
||||
let in_progress = &mut procs.specialized.procedures[index.0];
|
||||
let InProgressProc::Done(proc) = in_progress else { unreachable!() };
|
||||
|
||||
match &mut proc.host_exposed_layouts {
|
||||
HostExposedLayouts::HostExposed { aliases: old, .. } => old.extend(aliases),
|
||||
hep @ HostExposedLayouts::NotHostExposed => {
|
||||
*hep = HostExposedLayouts::HostExposed {
|
||||
aliases,
|
||||
rigids: Default::default(),
|
||||
};
|
||||
}
|
||||
procs.host_exposed_lambda_sets.push((proc.name, key, hels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3302,7 +3299,6 @@ fn generate_runtime_error_function<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
|
|
@ -3398,7 +3394,6 @@ fn generate_host_exposed_function<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: result,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
@ -3463,7 +3458,6 @@ fn generate_host_exposed_lambda_set<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: return_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
@ -3527,9 +3521,6 @@ fn specialize_proc_help<'a>(
|
|||
let body = partial_proc.body.clone();
|
||||
let body_var = partial_proc.body_var;
|
||||
|
||||
// host-exposed functions are tagged on later
|
||||
let host_exposed_layouts = HostExposedLayouts::NotHostExposed;
|
||||
|
||||
let mut specialized_body = from_can(env, body_var, body, procs, layout_cache);
|
||||
|
||||
let specialized_proc = match specialized {
|
||||
|
|
@ -3561,7 +3552,6 @@ fn specialize_proc_help<'a>(
|
|||
closure_data_layout: Some(closure_data_layout),
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
|
|
@ -3764,7 +3754,6 @@ fn specialize_proc_help<'a>(
|
|||
closure_data_layout,
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
is_erased,
|
||||
}
|
||||
}
|
||||
|
|
@ -3976,7 +3965,7 @@ fn build_specialized_proc<'a>(
|
|||
}
|
||||
}
|
||||
Ordering::Less => panic!(
|
||||
"more argument symbols than arguments (according to the layout) for {proc_name:?}"
|
||||
"more argument symbols than arguments (according to the layout) for {proc_name:?}. Pattern symbols: {:?}\n\nPattern layouts: {:?}", pattern_symbols, pattern_layouts_len,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -10001,16 +9990,17 @@ impl LambdaSetId {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_lambda_sets<'a>(
|
||||
env: &mut crate::layout::Env<'a, '_>,
|
||||
pub fn find_lambda_sets(
|
||||
arena: &Bump,
|
||||
subs: &Subs,
|
||||
initial: Variable,
|
||||
) -> Vec<'a, (LambdaSetId, Variable, RawFunctionLayout<'a>)> {
|
||||
let mut stack = bumpalo::collections::Vec::new_in(env.arena);
|
||||
) -> MutMap<Variable, LambdaSetId> {
|
||||
let mut stack = bumpalo::collections::Vec::new_in(arena);
|
||||
|
||||
// ignore the lambda set of top-level functions
|
||||
match env.subs.get_without_compacting(initial).content {
|
||||
match subs.get_without_compacting(initial).content {
|
||||
Content::Structure(FlatType::Func(arguments, _, result)) => {
|
||||
let arguments = &env.subs.variables[arguments.indices()];
|
||||
let arguments = &subs.variables[arguments.indices()];
|
||||
|
||||
stack.extend(arguments.iter().copied());
|
||||
stack.push(result);
|
||||
|
|
@ -10020,24 +10010,10 @@ fn find_lambda_sets<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let lambda_set_variables = find_lambda_sets_help(env.subs, stack);
|
||||
let mut answer =
|
||||
bumpalo::collections::Vec::with_capacity_in(lambda_set_variables.len(), env.arena);
|
||||
|
||||
for (variable, lambda_set_id) in lambda_set_variables {
|
||||
let lambda_set = env.subs.get_lambda_set(variable);
|
||||
let raw_function_layout = RawFunctionLayout::from_var(env, lambda_set.ambient_function)
|
||||
.value()
|
||||
.unwrap();
|
||||
|
||||
let key = (lambda_set_id, variable, raw_function_layout);
|
||||
answer.push(key);
|
||||
}
|
||||
|
||||
answer
|
||||
find_lambda_sets_help(subs, stack)
|
||||
}
|
||||
|
||||
pub fn find_lambda_sets_help(
|
||||
fn find_lambda_sets_help(
|
||||
subs: &Subs,
|
||||
mut stack: Vec<'_, Variable>,
|
||||
) -> MutMap<Variable, LambdaSetId> {
|
||||
|
|
@ -10337,7 +10313,6 @@ where
|
|||
closure_data_layout: None,
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
@ -10433,7 +10408,6 @@ where
|
|||
closure_data_layout: None,
|
||||
ret_layout: *field,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -580,7 +580,9 @@ impl<'a> RawFunctionLayout<'a> {
|
|||
cacheable(Ok(Self::ZeroArgumentThunk(Layout::usize(env.target_info))))
|
||||
}
|
||||
|
||||
Alias(symbol, _, _, _) if symbol.is_builtin() => {
|
||||
Alias(Symbol::INSPECT_ELEM_WALKER | Symbol::INSPECT_KEY_VAL_WALKER, _, var, _) => Self::from_var(env, var),
|
||||
|
||||
Alias(symbol, _, var, _) if symbol.is_builtin() => {
|
||||
Layout::new_help(env, var, content).then(Self::ZeroArgumentThunk)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -802,7 +802,6 @@ impl<'a> TrmcEnv<'a> {
|
|||
closure_data_layout: proc.closure_data_layout,
|
||||
ret_layout: proc.ret_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: proc.host_exposed_layouts.clone(),
|
||||
is_erased: proc.is_erased,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ impl<'a, T> Spaced<'a, T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn item(&self) -> &T {
|
||||
match self {
|
||||
Spaced::Item(answer) => answer,
|
||||
Spaced::SpaceBefore(next, _spaces) | Spaced::SpaceAfter(next, _spaces) => next.item(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||
|
|
@ -357,15 +364,15 @@ impl<'a> TypeHeader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The `has` keyword associated with ability definitions.
|
||||
/// The `implements` keyword associated with ability definitions.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Has<'a> {
|
||||
Has,
|
||||
SpaceBefore(&'a Has<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Has<'a>, &'a [CommentOrNewline<'a>]),
|
||||
pub enum Implements<'a> {
|
||||
Implements,
|
||||
SpaceBefore(&'a Implements<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Implements<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
/// An ability demand is a value defining the ability; for example `hash : a -> U64 | a has Hash`
|
||||
/// An ability demand is a value defining the ability; for example `hash : a -> U64 where a implements Hash`
|
||||
/// for a `Hash` ability.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct AbilityMember<'a> {
|
||||
|
|
@ -394,15 +401,15 @@ pub enum TypeDef<'a> {
|
|||
Opaque {
|
||||
header: TypeHeader<'a>,
|
||||
typ: Loc<TypeAnnotation<'a>>,
|
||||
derived: Option<Loc<HasAbilities<'a>>>,
|
||||
derived: Option<Loc<ImplementsAbilities<'a>>>,
|
||||
},
|
||||
|
||||
/// An ability definition. E.g.
|
||||
/// Hash has
|
||||
/// hash : a -> U64 | a has Hash
|
||||
/// Hash implements
|
||||
/// hash : a -> U64 where a implements Hash
|
||||
Ability {
|
||||
header: TypeHeader<'a>,
|
||||
loc_has: Loc<Has<'a>>,
|
||||
loc_implements: Loc<Implements<'a>>,
|
||||
members: &'a [AbilityMember<'a>],
|
||||
},
|
||||
}
|
||||
|
|
@ -538,54 +545,54 @@ impl<'a> Defs<'a> {
|
|||
pub type AbilityName<'a> = Loc<TypeAnnotation<'a>>;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct HasClause<'a> {
|
||||
pub struct ImplementsClause<'a> {
|
||||
pub var: Loc<Spaced<'a, &'a str>>,
|
||||
pub abilities: &'a [AbilityName<'a>],
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum HasImpls<'a> {
|
||||
pub enum AbilityImpls<'a> {
|
||||
// `{ eq: myEq }`
|
||||
HasImpls(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>),
|
||||
AbilityImpls(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>),
|
||||
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
SpaceBefore(&'a HasImpls<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a HasImpls<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceBefore(&'a AbilityImpls<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a AbilityImpls<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
/// `Eq` or `Eq { eq: myEq }`
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum HasAbility<'a> {
|
||||
HasAbility {
|
||||
pub enum ImplementsAbility<'a> {
|
||||
ImplementsAbility {
|
||||
/// Should be a zero-argument `Apply` or an error; we'll check this in canonicalization
|
||||
ability: Loc<TypeAnnotation<'a>>,
|
||||
impls: Option<Loc<HasImpls<'a>>>,
|
||||
impls: Option<Loc<AbilityImpls<'a>>>,
|
||||
},
|
||||
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
SpaceBefore(&'a HasAbility<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a HasAbility<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceBefore(&'a ImplementsAbility<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImplementsAbility<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum HasAbilities<'a> {
|
||||
/// `has [Eq { eq: myEq }, Hash]`
|
||||
Has(Collection<'a, Loc<HasAbility<'a>>>),
|
||||
pub enum ImplementsAbilities<'a> {
|
||||
/// `implements [Eq { eq: myEq }, Hash]`
|
||||
Implements(Collection<'a, Loc<ImplementsAbility<'a>>>),
|
||||
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
SpaceBefore(&'a HasAbilities<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a HasAbilities<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceBefore(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl HasAbilities<'_> {
|
||||
pub fn collection(&self) -> &Collection<Loc<HasAbility>> {
|
||||
impl ImplementsAbilities<'_> {
|
||||
pub fn collection(&self) -> &Collection<Loc<ImplementsAbility>> {
|
||||
let mut it = self;
|
||||
loop {
|
||||
match it {
|
||||
Self::SpaceBefore(inner, _) | Self::SpaceAfter(inner, _) => {
|
||||
it = inner;
|
||||
}
|
||||
Self::Has(collection) => return collection,
|
||||
Self::Implements(collection) => return collection,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -641,8 +648,8 @@ pub enum TypeAnnotation<'a> {
|
|||
/// The `*` type variable, e.g. in (List *)
|
||||
Wildcard,
|
||||
|
||||
/// A "where" clause demanding abilities designated by a `|`, e.g. `a -> U64 | a has Hash`
|
||||
Where(&'a Loc<TypeAnnotation<'a>>, &'a [Loc<HasClause<'a>>]),
|
||||
/// A "where" clause demanding abilities designated by a `where`, e.g. `a -> U64 where a implements Hash`
|
||||
Where(&'a Loc<TypeAnnotation<'a>>, &'a [Loc<ImplementsClause<'a>>]),
|
||||
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
SpaceBefore(&'a TypeAnnotation<'a>, &'a [CommentOrNewline<'a>]),
|
||||
|
|
@ -1245,39 +1252,39 @@ impl<'a> Spaceable<'a> for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for Has<'a> {
|
||||
impl<'a> Spaceable<'a> for Implements<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Has::SpaceBefore(self, spaces)
|
||||
Implements::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Has::SpaceAfter(self, spaces)
|
||||
Implements::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for HasImpls<'a> {
|
||||
impl<'a> Spaceable<'a> for AbilityImpls<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasImpls::SpaceBefore(self, spaces)
|
||||
AbilityImpls::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasImpls::SpaceAfter(self, spaces)
|
||||
AbilityImpls::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for HasAbility<'a> {
|
||||
impl<'a> Spaceable<'a> for ImplementsAbility<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasAbility::SpaceBefore(self, spaces)
|
||||
ImplementsAbility::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasAbility::SpaceAfter(self, spaces)
|
||||
ImplementsAbility::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for HasAbilities<'a> {
|
||||
impl<'a> Spaceable<'a> for ImplementsAbilities<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasAbilities::SpaceBefore(self, spaces)
|
||||
ImplementsAbilities::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
HasAbilities::SpaceAfter(self, spaces)
|
||||
ImplementsAbilities::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1368,7 +1375,7 @@ impl_extract_spaces!(Pattern);
|
|||
impl_extract_spaces!(Tag);
|
||||
impl_extract_spaces!(AssignedField<T>);
|
||||
impl_extract_spaces!(TypeAnnotation);
|
||||
impl_extract_spaces!(HasAbility);
|
||||
impl_extract_spaces!(ImplementsAbility);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
|
@ -1422,43 +1429,43 @@ impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> ExtractSpaces<'a> for HasImpls<'a> {
|
||||
impl<'a> ExtractSpaces<'a> for AbilityImpls<'a> {
|
||||
type Item = Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>;
|
||||
|
||||
fn extract_spaces(&self) -> Spaces<'a, Self::Item> {
|
||||
match self {
|
||||
HasImpls::HasImpls(inner) => Spaces {
|
||||
AbilityImpls::AbilityImpls(inner) => Spaces {
|
||||
before: &[],
|
||||
item: *inner,
|
||||
after: &[],
|
||||
},
|
||||
HasImpls::SpaceBefore(item, before) => match item {
|
||||
HasImpls::HasImpls(inner) => Spaces {
|
||||
AbilityImpls::SpaceBefore(item, before) => match item {
|
||||
AbilityImpls::AbilityImpls(inner) => Spaces {
|
||||
before,
|
||||
item: *inner,
|
||||
after: &[],
|
||||
},
|
||||
HasImpls::SpaceBefore(_, _) => todo!(),
|
||||
HasImpls::SpaceAfter(HasImpls::HasImpls(inner), after) => Spaces {
|
||||
AbilityImpls::SpaceBefore(_, _) => todo!(),
|
||||
AbilityImpls::SpaceAfter(AbilityImpls::AbilityImpls(inner), after) => Spaces {
|
||||
before,
|
||||
item: *inner,
|
||||
after,
|
||||
},
|
||||
HasImpls::SpaceAfter(_, _) => todo!(),
|
||||
AbilityImpls::SpaceAfter(_, _) => todo!(),
|
||||
},
|
||||
HasImpls::SpaceAfter(item, after) => match item {
|
||||
HasImpls::HasImpls(inner) => Spaces {
|
||||
AbilityImpls::SpaceAfter(item, after) => match item {
|
||||
AbilityImpls::AbilityImpls(inner) => Spaces {
|
||||
before: &[],
|
||||
item: *inner,
|
||||
after,
|
||||
},
|
||||
HasImpls::SpaceBefore(HasImpls::HasImpls(inner), before) => Spaces {
|
||||
AbilityImpls::SpaceBefore(AbilityImpls::AbilityImpls(inner), before) => Spaces {
|
||||
before,
|
||||
item: *inner,
|
||||
after,
|
||||
},
|
||||
HasImpls::SpaceBefore(_, _) => todo!(),
|
||||
HasImpls::SpaceAfter(_, _) => todo!(),
|
||||
AbilityImpls::SpaceBefore(_, _) => todo!(),
|
||||
AbilityImpls::SpaceAfter(_, _) => todo!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1681,11 +1688,11 @@ impl<'a> Malformed for TypeDef<'a> {
|
|||
} => header.is_malformed() || typ.is_malformed() || derived.is_malformed(),
|
||||
TypeDef::Ability {
|
||||
header,
|
||||
loc_has,
|
||||
loc_implements,
|
||||
members,
|
||||
} => {
|
||||
header.is_malformed()
|
||||
|| loc_has.is_malformed()
|
||||
|| loc_implements.is_malformed()
|
||||
|| members.iter().any(|member| member.is_malformed())
|
||||
}
|
||||
}
|
||||
|
|
@ -1698,42 +1705,48 @@ impl<'a> Malformed for AbilityMember<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for Has<'a> {
|
||||
impl<'a> Malformed for Implements<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
Has::Has => false,
|
||||
Has::SpaceBefore(has, _) | Has::SpaceAfter(has, _) => has.is_malformed(),
|
||||
Implements::Implements => false,
|
||||
Implements::SpaceBefore(has, _) | Implements::SpaceAfter(has, _) => has.is_malformed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for HasAbility<'a> {
|
||||
impl<'a> Malformed for ImplementsAbility<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
HasAbility::HasAbility { ability, impls } => {
|
||||
ImplementsAbility::ImplementsAbility { ability, impls } => {
|
||||
ability.is_malformed() || impls.iter().any(|impl_| impl_.is_malformed())
|
||||
}
|
||||
HasAbility::SpaceBefore(has, _) | HasAbility::SpaceAfter(has, _) => has.is_malformed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for HasAbilities<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
HasAbilities::Has(abilities) => abilities.iter().any(|ability| ability.is_malformed()),
|
||||
HasAbilities::SpaceBefore(has, _) | HasAbilities::SpaceAfter(has, _) => {
|
||||
ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
|
||||
has.is_malformed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for HasImpls<'a> {
|
||||
impl<'a> Malformed for ImplementsAbilities<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
HasImpls::HasImpls(impls) => impls.iter().any(|ability| ability.is_malformed()),
|
||||
HasImpls::SpaceBefore(has, _) | HasImpls::SpaceAfter(has, _) => has.is_malformed(),
|
||||
ImplementsAbilities::Implements(abilities) => {
|
||||
abilities.iter().any(|ability| ability.is_malformed())
|
||||
}
|
||||
ImplementsAbilities::SpaceBefore(has, _) | ImplementsAbilities::SpaceAfter(has, _) => {
|
||||
has.is_malformed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for AbilityImpls<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
AbilityImpls::AbilityImpls(impls) => impls.iter().any(|ability| ability.is_malformed()),
|
||||
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
|
||||
has.is_malformed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1823,7 +1836,7 @@ impl<'a> Malformed for Tag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for HasClause<'a> {
|
||||
impl<'a> Malformed for ImplementsClause<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
self.abilities.iter().any(|ability| ability.is_malformed())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Has, HasAbilities,
|
||||
Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements,
|
||||
ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
|
|
@ -14,7 +15,7 @@ use crate::parser::{
|
|||
word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString,
|
||||
EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::{closure_param, loc_has_parser};
|
||||
use crate::pattern::{closure_param, loc_implements_parser};
|
||||
use crate::state::State;
|
||||
use crate::string_literal::StrLikeLiteral;
|
||||
use crate::type_annotation;
|
||||
|
|
@ -616,14 +617,14 @@ pub fn parse_single_def<'a>(
|
|||
};
|
||||
|
||||
if let Some((name, name_region, args)) = opt_tag_and_args {
|
||||
if let Ok((_, loc_has, state)) =
|
||||
loc_has_parser().parse(arena, state.clone(), min_indent)
|
||||
if let Ok((_, loc_implements, state)) =
|
||||
loc_implements_parser().parse(arena, state.clone(), min_indent)
|
||||
{
|
||||
let (_, (type_def, def_region), state) = finish_parsing_ability_def_help(
|
||||
min_indent,
|
||||
Loc::at(name_region, name),
|
||||
args,
|
||||
loc_has,
|
||||
loc_implements,
|
||||
arena,
|
||||
state,
|
||||
)?;
|
||||
|
|
@ -1063,8 +1064,14 @@ fn alias_signature_with_space_before<'a>() -> impl Parser<'a, Loc<TypeAnnotation
|
|||
))
|
||||
}
|
||||
|
||||
fn opaque_signature_with_space_before<'a>(
|
||||
) -> impl Parser<'a, (Loc<TypeAnnotation<'a>>, Option<Loc<HasAbilities<'a>>>), EExpr<'a>> {
|
||||
fn opaque_signature_with_space_before<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
Loc<TypeAnnotation<'a>>,
|
||||
Option<Loc<ImplementsAbilities<'a>>>,
|
||||
),
|
||||
EExpr<'a>,
|
||||
> {
|
||||
and!(
|
||||
specialize(
|
||||
EExpr::Type,
|
||||
|
|
@ -1075,7 +1082,7 @@ fn opaque_signature_with_space_before<'a>(
|
|||
),
|
||||
optional(backtrackable(specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(type_annotation::has_abilities(), EType::TIndentStart,),
|
||||
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart,),
|
||||
)))
|
||||
)
|
||||
}
|
||||
|
|
@ -1279,7 +1286,7 @@ mod ability {
|
|||
Exact(u32),
|
||||
}
|
||||
|
||||
/// Parses an ability demand like `hash : a -> U64 | a has Hash`, in the context of a larger
|
||||
/// Parses an ability demand like `hash : a -> U64 where a implements Hash`, in the context of a larger
|
||||
/// ability definition.
|
||||
/// This is basically the same as parsing a free-floating annotation, but with stricter rules.
|
||||
pub fn parse_demand<'a>(
|
||||
|
|
@ -1363,7 +1370,7 @@ fn finish_parsing_ability_def_help<'a>(
|
|||
start_column: u32,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
loc_has: Loc<Has<'a>>,
|
||||
loc_implements: Loc<Implements<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> {
|
||||
|
|
@ -1401,7 +1408,7 @@ fn finish_parsing_ability_def_help<'a>(
|
|||
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
|
||||
let type_def = TypeDef::Ability {
|
||||
header: TypeHeader { name, vars: args },
|
||||
loc_has,
|
||||
loc_implements,
|
||||
members: demands.into_bump_slice(),
|
||||
};
|
||||
|
||||
|
|
@ -1634,13 +1641,13 @@ fn parse_expr_end<'a>(
|
|||
value:
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: "has",
|
||||
ident: crate::keyword::IMPLEMENTS,
|
||||
},
|
||||
..
|
||||
},
|
||||
state,
|
||||
)) if matches!(expr_state.expr.value, Expr::Tag(..)) => {
|
||||
// This is an ability definition, `Ability arg1 ... has ...`.
|
||||
// This is an ability definition, `Ability arg1 ... implements ...`.
|
||||
|
||||
let name = expr_state.expr.map_owned(|e| match e {
|
||||
Expr::Tag(name) => name,
|
||||
|
|
@ -1661,13 +1668,13 @@ fn parse_expr_end<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// Attach any spaces to the `has` keyword
|
||||
// Attach any spaces to the `implements` keyword
|
||||
let has = if !expr_state.spaces_after.is_empty() {
|
||||
arena
|
||||
.alloc(Has::Has)
|
||||
.alloc(Implements::Implements)
|
||||
.with_spaces_before(expr_state.spaces_after, has.region)
|
||||
} else {
|
||||
Loc::at(has.region, Has::Has)
|
||||
Loc::at(has.region, Implements::Implements)
|
||||
};
|
||||
|
||||
let args = arguments.into_bump_slice();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// These keywords are valid in expressions
|
||||
pub const IF: &str = "if";
|
||||
pub const THEN: &str = "then";
|
||||
pub const ELSE: &str = "else";
|
||||
|
|
@ -9,4 +10,10 @@ pub const EXPECT: &str = "expect";
|
|||
pub const EXPECT_FX: &str = "expect-fx";
|
||||
pub const CRASH: &str = "crash";
|
||||
|
||||
pub const KEYWORDS: [&str; 10] = [IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH];
|
||||
// These keywords are valid in types
|
||||
pub const IMPLEMENTS: &str = "implements";
|
||||
pub const WHERE: &str = "where";
|
||||
|
||||
pub const KEYWORDS: [&str; 11] = [
|
||||
IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH, WHERE,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ pub enum EType<'a> {
|
|||
TEnd(Position),
|
||||
TFunctionArgument(Position),
|
||||
TWhereBar(Position),
|
||||
THasClause(Position),
|
||||
TImplementsClause(Position),
|
||||
TAbilityImpl(ETypeAbilityImpl<'a>, Position),
|
||||
///
|
||||
TIndentStart(Position),
|
||||
|
|
@ -1524,6 +1524,23 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn word<'a, ToError, E>(word: &'static str, to_error: ToError) -> impl Parser<'a, (), E>
|
||||
where
|
||||
ToError: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
debug_assert!(!word.contains('\n'));
|
||||
|
||||
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| {
|
||||
if state.bytes().starts_with(word.as_bytes()) {
|
||||
let state = state.advance(word.len());
|
||||
Ok((MadeProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, to_error(state.pos())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn word1<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E>
|
||||
where
|
||||
ToError: Fn(Position) -> E,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ast::{Has, Pattern, PatternAs, Spaceable};
|
||||
use crate::ast::{Implements, Pattern, PatternAs, Spaceable};
|
||||
use crate::blankspace::{space0_e, spaces, spaces_before};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
|
||||
use crate::keyword;
|
||||
|
|
@ -116,7 +116,7 @@ fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>,
|
|||
zero_or_more!(loc_tag_pattern_arg(false))
|
||||
}
|
||||
|
||||
/// Like `loc_tag_pattern_args_help`, but stops if a "has" keyword is seen (indicating an ability).
|
||||
/// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability).
|
||||
fn loc_type_def_tag_pattern_args_help<'a>(
|
||||
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
|
||||
zero_or_more!(loc_tag_pattern_arg(true))
|
||||
|
|
@ -138,7 +138,7 @@ fn loc_tag_pattern_arg<'a>(
|
|||
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
if stop_on_has_kw && matches!(value, Pattern::Identifier("has")) {
|
||||
if stop_on_has_kw && matches!(value, Pattern::Identifier(crate::keyword::IMPLEMENTS)) {
|
||||
Err((NoProgress, EPattern::End(original_state.pos())))
|
||||
} else {
|
||||
Ok((
|
||||
|
|
@ -154,12 +154,19 @@ fn loc_tag_pattern_arg<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn loc_has_parser<'a>() -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
|
||||
pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPattern<'a>> {
|
||||
then(
|
||||
loc_tag_pattern_arg(false),
|
||||
|_arena, state, progress, pattern| {
|
||||
if matches!(pattern.value, Pattern::Identifier("has")) {
|
||||
Ok((progress, Loc::at(pattern.region, Has::Has), state))
|
||||
if matches!(
|
||||
pattern.value,
|
||||
Pattern::Identifier(crate::keyword::IMPLEMENTS)
|
||||
) {
|
||||
Ok((
|
||||
progress,
|
||||
Loc::at(pattern.region, Implements::Implements),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
Err((progress, EPattern::End(state.pos())))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ast::{
|
||||
AssignedField, CommentOrNewline, Expr, HasAbilities, HasAbility, HasClause, HasImpls, Pattern,
|
||||
Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader,
|
||||
AbilityImpls, AssignedField, CommentOrNewline, Expr, ImplementsAbilities, ImplementsAbility,
|
||||
ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
|
||||
|
|
@ -12,7 +12,7 @@ use crate::parser::{
|
|||
absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl,
|
||||
};
|
||||
use crate::parser::{
|
||||
allocated, backtrackable, fail, optional, specialize, specialize_ref, word1, word2, word3,
|
||||
allocated, backtrackable, fail, optional, specialize, specialize_ref, word, word1, word2,
|
||||
EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
|
||||
Progress::{self, *},
|
||||
};
|
||||
|
|
@ -426,7 +426,7 @@ fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, ETyp
|
|||
EType::TIndentEnd,
|
||||
),
|
||||
zero_or_more!(skip_first!(
|
||||
word1(b'&', EType::THasClause),
|
||||
word1(b'&', EType::TImplementsClause),
|
||||
space0_before_optional_after(
|
||||
specialize(EType::TApply, loc!(concrete_type())),
|
||||
EType::TIndentStart,
|
||||
|
|
@ -444,9 +444,9 @@ fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, ETyp
|
|||
)
|
||||
}
|
||||
|
||||
fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
|
||||
fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
|
||||
map!(
|
||||
// Suppose we are trying to parse "a has Hash"
|
||||
// Suppose we are trying to parse "a implements Hash"
|
||||
and!(
|
||||
space0_around_ee(
|
||||
// Parse "a", with appropriate spaces
|
||||
|
|
@ -458,8 +458,8 @@ fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
|
|||
EType::TIndentEnd
|
||||
),
|
||||
skip_first!(
|
||||
// Parse "has"; we don't care about this keyword
|
||||
word3(b'h', b'a', b's', EType::THasClause),
|
||||
// Parse "implements"; we don't care about this keyword
|
||||
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
|
||||
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
|
||||
absolute_column_min_indent(ability_chain())
|
||||
)
|
||||
|
|
@ -470,30 +470,34 @@ fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
|
|||
&abilities.last().unwrap().region,
|
||||
);
|
||||
let region = Region::span_across(&var.region, &abilities_region);
|
||||
let has_clause = HasClause {
|
||||
let implements_clause = ImplementsClause {
|
||||
var,
|
||||
abilities: abilities.into_bump_slice(),
|
||||
};
|
||||
Loc::at(region, has_clause)
|
||||
Loc::at(region, implements_clause)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse a chain of `has` clauses, e.g. " | a has Hash, b has Eq".
|
||||
/// Returns the clauses and spaces before the starting "|", if there were any.
|
||||
fn has_clause_chain<'a>(
|
||||
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<HasClause<'a>>]), EType<'a>> {
|
||||
/// Parse a chain of `implements` clauses, e.g. " where a implements Hash, b implements Eq".
|
||||
/// Returns the clauses and spaces before the starting "where", if there were any.
|
||||
fn implements_clause_chain<'a>(
|
||||
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<ImplementsClause<'a>>]), EType<'a>> {
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let (_, (spaces_before, ()), state) =
|
||||
and!(space0_e(EType::TIndentStart), word1(b'|', EType::TWhereBar))
|
||||
.parse(arena, state, min_indent)?;
|
||||
let (_, (spaces_before, ()), state) = and!(
|
||||
space0_e(EType::TIndentStart),
|
||||
word(crate::keyword::WHERE, EType::TWhereBar)
|
||||
)
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
// Parse the first clause (there must be one), then the rest
|
||||
let (_, first_clause, state) = has_clause().parse(arena, state, min_indent)?;
|
||||
let (_, first_clause, state) = implements_clause().parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, mut clauses, state) =
|
||||
zero_or_more!(skip_first!(word1(b',', EType::THasClause), has_clause()))
|
||||
.parse(arena, state, min_indent)?;
|
||||
let (_, mut clauses, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', EType::TImplementsClause),
|
||||
implements_clause()
|
||||
))
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
// Usually the number of clauses shouldn't be too large, so this is okay
|
||||
clauses.insert(0, first_clause);
|
||||
|
|
@ -506,30 +510,30 @@ fn has_clause_chain<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a has-abilities clause, e.g. `has [Eq, Hash]`.
|
||||
pub fn has_abilities<'a>() -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>> {
|
||||
/// Parse a implements-abilities clause, e.g. `implements [Eq, Hash]`.
|
||||
pub fn implements_abilities<'a>() -> impl Parser<'a, Loc<ImplementsAbilities<'a>>, EType<'a>> {
|
||||
increment_min_indent(skip_first!(
|
||||
// Parse "has"; we don't care about this keyword
|
||||
word3(b'h', b'a', b's', EType::THasClause),
|
||||
// Parse "implements"; we don't care about this keyword
|
||||
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
|
||||
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
|
||||
space0_before_e(
|
||||
loc!(map!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EType::TStart),
|
||||
loc!(parse_has_ability()),
|
||||
loc!(parse_implements_ability()),
|
||||
word1(b',', EType::TEnd),
|
||||
word1(b']', EType::TEnd),
|
||||
HasAbility::SpaceBefore
|
||||
ImplementsAbility::SpaceBefore
|
||||
),
|
||||
HasAbilities::Has
|
||||
ImplementsAbilities::Implements
|
||||
)),
|
||||
EType::TIndentEnd,
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
|
||||
increment_min_indent(record!(HasAbility::HasAbility {
|
||||
fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, EType<'a>> {
|
||||
increment_min_indent(record!(ImplementsAbility::ImplementsAbility {
|
||||
ability: loc!(specialize(EType::TApply, concrete_type())),
|
||||
impls: optional(backtrackable(space0_before_e(
|
||||
loc!(map!(
|
||||
|
|
@ -543,7 +547,7 @@ fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
|
|||
AssignedField::SpaceBefore
|
||||
)
|
||||
),
|
||||
HasImpls::HasImpls
|
||||
AbilityImpls::AbilityImpls
|
||||
)),
|
||||
EType::TIndentEnd
|
||||
)))
|
||||
|
|
@ -642,12 +646,13 @@ fn expression<'a>(
|
|||
|
||||
// Finally, try to parse a where clause if there is one.
|
||||
// The where clause must be at least as deep as where the type annotation started.
|
||||
match has_clause_chain().parse(arena, state.clone(), min_indent) {
|
||||
Ok((where_progress, (spaces_before, has_chain), state)) => {
|
||||
let region = Region::span_across(&annot.region, &has_chain.last().unwrap().region);
|
||||
match implements_clause_chain().parse(arena, state.clone(), min_indent) {
|
||||
Ok((where_progress, (spaces_before, implements_chain), state)) => {
|
||||
let region =
|
||||
Region::span_across(&annot.region, &implements_chain.last().unwrap().region);
|
||||
let type_annot = if !spaces_before.is_empty() {
|
||||
// We're transforming the spaces_before the '|'
|
||||
// into spaces_after the thing before the '|'
|
||||
// We're transforming the spaces_before the 'where'
|
||||
// into spaces_after the thing before the 'where'
|
||||
let spaced = arena
|
||||
.alloc(annot.value)
|
||||
.with_spaces_after(spaces_before, annot.region);
|
||||
|
|
@ -655,7 +660,7 @@ fn expression<'a>(
|
|||
} else {
|
||||
&*arena.alloc(annot)
|
||||
};
|
||||
let where_annot = TypeAnnotation::Where(type_annot, has_chain);
|
||||
let where_annot = TypeAnnotation::Where(type_annot, implements_chain);
|
||||
Ok((
|
||||
where_progress.or(progress),
|
||||
Loc::at(region, where_annot),
|
||||
|
|
@ -724,7 +729,7 @@ fn parse_type_variable<'a>(
|
|||
min_indent,
|
||||
) {
|
||||
Ok((_, name, state)) => {
|
||||
if name == "has" && stop_at_surface_has {
|
||||
if name == crate::keyword::IMPLEMENTS && stop_at_surface_has {
|
||||
Err((NoProgress, EType::TEnd(state.pos())))
|
||||
} else {
|
||||
let answer = TypeAnnotation::BoundVariable(name);
|
||||
|
|
|
|||
|
|
@ -117,17 +117,17 @@ pub enum Problem {
|
|||
name: Symbol,
|
||||
variables_region: Region,
|
||||
},
|
||||
HasClauseIsNotAbility {
|
||||
ImplementsClauseIsNotAbility {
|
||||
region: Region,
|
||||
},
|
||||
IllegalHasClause {
|
||||
IllegalImplementsClause {
|
||||
region: Region,
|
||||
},
|
||||
DuplicateHasAbility {
|
||||
DuplicateImplementsAbility {
|
||||
ability: Symbol,
|
||||
region: Region,
|
||||
},
|
||||
AbilityMemberMissingHasClause {
|
||||
AbilityMemberMissingImplementsClause {
|
||||
member: Symbol,
|
||||
ability: Symbol,
|
||||
region: Region,
|
||||
|
|
@ -135,7 +135,7 @@ pub enum Problem {
|
|||
AbilityMemberMultipleBoundVars {
|
||||
member: Symbol,
|
||||
ability: Symbol,
|
||||
span_has_clauses: Region,
|
||||
span_implements_clauses: Region,
|
||||
bound_var_names: Vec<Lowercase>,
|
||||
},
|
||||
AbilityNotOnToplevel {
|
||||
|
|
@ -245,10 +245,10 @@ impl Problem {
|
|||
Problem::NestedDatatype { .. } => RuntimeError,
|
||||
Problem::InvalidExtensionType { .. } => RuntimeError,
|
||||
Problem::AbilityHasTypeVariables { .. } => RuntimeError,
|
||||
Problem::HasClauseIsNotAbility { .. } => RuntimeError,
|
||||
Problem::IllegalHasClause { .. } => RuntimeError,
|
||||
Problem::DuplicateHasAbility { .. } => Warning,
|
||||
Problem::AbilityMemberMissingHasClause { .. } => RuntimeError,
|
||||
Problem::ImplementsClauseIsNotAbility { .. } => RuntimeError,
|
||||
Problem::IllegalImplementsClause { .. } => RuntimeError,
|
||||
Problem::DuplicateImplementsAbility { .. } => Warning,
|
||||
Problem::AbilityMemberMissingImplementsClause { .. } => RuntimeError,
|
||||
Problem::AbilityMemberMultipleBoundVars { .. } => RuntimeError,
|
||||
Problem::AbilityNotOnToplevel { .. } => RuntimeError, // Ideally, could be compiled
|
||||
Problem::AbilityUsedAsType(_, _, _) => RuntimeError,
|
||||
|
|
@ -379,12 +379,12 @@ impl Problem {
|
|||
variables_region: region,
|
||||
..
|
||||
}
|
||||
| Problem::HasClauseIsNotAbility { region }
|
||||
| Problem::IllegalHasClause { region }
|
||||
| Problem::DuplicateHasAbility { region, .. }
|
||||
| Problem::AbilityMemberMissingHasClause { region, .. }
|
||||
| Problem::ImplementsClauseIsNotAbility { region }
|
||||
| Problem::IllegalImplementsClause { region }
|
||||
| Problem::DuplicateImplementsAbility { region, .. }
|
||||
| Problem::AbilityMemberMissingImplementsClause { region, .. }
|
||||
| Problem::AbilityMemberMultipleBoundVars {
|
||||
span_has_clauses: region,
|
||||
span_implements_clauses: region,
|
||||
..
|
||||
}
|
||||
| Problem::AbilityNotOnToplevel { region }
|
||||
|
|
|
|||
|
|
@ -741,7 +741,7 @@ trait DerivableVisitor {
|
|||
) {
|
||||
// TODO: currently, just we suppose the presence of a flex var may
|
||||
// include more or less things which we can derive. But, we should
|
||||
// instead recurse here, and add a `t ~ u | u has Decode` constraint as needed.
|
||||
// instead recurse here, and add a `t ~ u where u implements Decode` constraint as needed.
|
||||
stack.push(ext);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,8 +142,7 @@ impl RegisterVariable {
|
|||
TypeTag::EmptyTagUnion => Direct(Variable::EMPTY_TAG_UNION),
|
||||
TypeTag::DelayedAlias { shared }
|
||||
| TypeTag::StructuralAlias { shared, .. }
|
||||
| TypeTag::OpaqueAlias { shared, .. }
|
||||
| TypeTag::HostExposedAlias { shared, .. } => {
|
||||
| TypeTag::OpaqueAlias { shared, .. } => {
|
||||
let AliasShared { symbol, .. } = types[shared];
|
||||
if let Some(reserved) = Variable::get_reserved(symbol) {
|
||||
let direct_var = if rank.is_generalized() {
|
||||
|
|
@ -752,92 +751,6 @@ pub(crate) fn type_to_var_help(
|
|||
|
||||
env.register_with_known_var(destination, rank, content)
|
||||
}
|
||||
HostExposedAlias {
|
||||
shared,
|
||||
actual_type: alias_type,
|
||||
actual_variable: actual_var,
|
||||
} => {
|
||||
let AliasShared {
|
||||
symbol,
|
||||
type_argument_abilities: _,
|
||||
type_argument_regions: _,
|
||||
lambda_set_variables,
|
||||
infer_ext_in_output_variables: _, // TODO
|
||||
} = types[shared];
|
||||
|
||||
let type_arguments = types.get_type_arguments(typ_index);
|
||||
|
||||
let alias_variables = {
|
||||
let length = type_arguments.len() + lambda_set_variables.len();
|
||||
let new_variables = VariableSubsSlice::reserve_into_subs(env.subs, length);
|
||||
|
||||
for (target_index, arg_type) in
|
||||
(new_variables.indices()).zip(type_arguments.into_iter())
|
||||
{
|
||||
let copy_var = helper!(arg_type);
|
||||
env.subs.variables[target_index] = copy_var;
|
||||
}
|
||||
let it = (new_variables.indices().skip(type_arguments.len()))
|
||||
.zip(lambda_set_variables.into_iter());
|
||||
for (target_index, ls) in it {
|
||||
// We MUST do this now, otherwise when linking the ambient function during
|
||||
// instantiation of the real var, there will be nothing to link against.
|
||||
let copy_var = type_to_var_help(
|
||||
env,
|
||||
rank,
|
||||
problems,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
arena,
|
||||
aliases,
|
||||
types,
|
||||
ls,
|
||||
true,
|
||||
);
|
||||
env.subs.variables[target_index] = copy_var;
|
||||
}
|
||||
|
||||
AliasVariables {
|
||||
variables_start: new_variables.start,
|
||||
type_variables_len: type_arguments.len() as _,
|
||||
lambda_set_variables_len: lambda_set_variables.len() as _,
|
||||
all_variables_len: length as _,
|
||||
}
|
||||
};
|
||||
|
||||
// cannot use helper! here because this variable may be involved in unification below
|
||||
let alias_variable = type_to_var_help(
|
||||
env,
|
||||
rank,
|
||||
problems,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
arena,
|
||||
aliases,
|
||||
types,
|
||||
alias_type,
|
||||
false,
|
||||
);
|
||||
// TODO(opaques): I think host-exposed aliases should always be structural
|
||||
// (when does it make sense to give a host an opaque type?)
|
||||
let content = Content::Alias(
|
||||
symbol,
|
||||
alias_variables,
|
||||
alias_variable,
|
||||
AliasKind::Structural,
|
||||
);
|
||||
let result = env.register_with_known_var(destination, rank, content);
|
||||
|
||||
// We only want to unify the actual_var with the alias once
|
||||
// if it's already redirected (and therefore, redundant)
|
||||
// don't do it again
|
||||
if !env.subs.redundant(actual_var) {
|
||||
let descriptor = env.subs.get(result);
|
||||
env.subs.union(result, actual_var, descriptor);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
Error => {
|
||||
let content = Content::Error;
|
||||
|
||||
|
|
|
|||
|
|
@ -3146,7 +3146,7 @@ mod solve_expr {
|
|||
Dict.insert
|
||||
"#
|
||||
),
|
||||
"Dict k v, k, v -> Dict k v | k has Hash & Eq",
|
||||
"Dict k v, k, v -> Dict k v where k implements Hash & Eq",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -3407,7 +3407,7 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
reconstructPath : Dict position position, position -> List position | position has Hash & Eq
|
||||
reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
|
||||
reconstructPath = \cameFrom, goal ->
|
||||
when Dict.get cameFrom goal is
|
||||
Err KeyNotFound ->
|
||||
|
|
@ -3419,7 +3419,7 @@ mod solve_expr {
|
|||
reconstructPath
|
||||
"#
|
||||
),
|
||||
"Dict position position, position -> List position | position has Hash & Eq",
|
||||
"Dict position position, position -> List position where position implements Hash & Eq",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -3454,7 +3454,7 @@ mod solve_expr {
|
|||
|
||||
Model position : { openSet : Set position }
|
||||
|
||||
cheapestOpen : Model position -> Result position [KeyNotFound] | position has Hash & Eq
|
||||
cheapestOpen : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
|
||||
cheapestOpen = \model ->
|
||||
|
||||
folder = \resSmallestSoFar, position ->
|
||||
|
|
@ -3469,14 +3469,14 @@ mod solve_expr {
|
|||
Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder
|
||||
|> Result.map (\x -> x.position)
|
||||
|
||||
astar : Model position -> Result position [KeyNotFound] | position has Hash & Eq
|
||||
astar : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
|
||||
astar = \model -> cheapestOpen model
|
||||
|
||||
main =
|
||||
astar
|
||||
"#
|
||||
),
|
||||
"Model position -> Result position [KeyNotFound] | position has Hash & Eq",
|
||||
"Model position -> Result position [KeyNotFound] where position implements Hash & Eq",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -4118,7 +4118,7 @@ mod solve_expr {
|
|||
|
||||
Key k : Num k
|
||||
|
||||
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
|
||||
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
|
||||
removeHelpEQGT = \targetKey, dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
|
|
@ -4232,7 +4232,7 @@ mod solve_expr {
|
|||
_ ->
|
||||
Empty
|
||||
|
||||
removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
|
||||
removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
|
|
@ -4320,7 +4320,7 @@ mod solve_expr {
|
|||
|
||||
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
|
||||
|
||||
removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v | k has Hash & Eq
|
||||
removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v where k implements Hash & Eq
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
|
|
@ -4355,7 +4355,7 @@ mod solve_expr {
|
|||
|
||||
removeHelpPrepEQGT : Key k, RBTree (Key k) v, NodeColor, (Key k), v, RBTree (Key k) v, RBTree (Key k) v -> RBTree (Key k) v
|
||||
|
||||
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
|
||||
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
|
||||
removeHelpEQGT = \targetKey, dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ fn derivable_record_ext_flex_var() {
|
|||
fn derivable_record_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
Decoder,
|
||||
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ),
|
||||
v!({ a: v!(STR), }a implements Symbol::DECODE_DECODER ),
|
||||
DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into()])),
|
||||
);
|
||||
}
|
||||
|
|
@ -106,8 +106,8 @@ fn list() {
|
|||
derive_test(Decoder, v!(Symbol::LIST_LIST v!(STR)), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for List Str
|
||||
# Decoder (List val) fmt | fmt has DecoderFormatting, val has Decoding
|
||||
# List U8, fmt -[[custom(3)]]-> { rest : List U8, result : [Err [TooShort], Ok (List val)] } | fmt has DecoderFormatting, val has Decoding
|
||||
# Decoder (List val) fmt where fmt implements DecoderFormatting, val implements Decoding
|
||||
# List U8, fmt -[[custom(3)]]-> { rest : List U8, result : [Err [TooShort], Ok (List val)] } where fmt implements DecoderFormatting, val implements Decoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(3)]]
|
||||
#Derived.decoder_list =
|
||||
|
|
@ -124,8 +124,8 @@ fn record_2_fields() {
|
|||
derive_test(Decoder, v!({first: v!(STR), second: v!(STR),}), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { first : Str, second : Str }
|
||||
# Decoder { first : val, second : val1 } fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# Decoder { first : val, second : val1 } fmt where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
|
||||
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(22)]]
|
||||
#Derived.decoder_{first,second} =
|
||||
|
|
@ -181,8 +181,8 @@ fn tuple_2_fields() {
|
|||
derive_test(Decoder, v!((v!(STR), v!(U8),)), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for ( Str, U8 )*
|
||||
# Decoder ( val, val1 )* fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding
|
||||
# Decoder ( val, val1 )* fmt where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
|
||||
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(22)]]
|
||||
#Derived.decoder_(arity:2) =
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ fn derivable_record_ext_flex_var() {
|
|||
fn derivable_record_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
ToEncoder,
|
||||
v!({ a: v!(STR), }a has Symbol::ENCODE_TO_ENCODER),
|
||||
v!({ a: v!(STR), }a implements Symbol::ENCODE_TO_ENCODER),
|
||||
DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into()])),
|
||||
);
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ fn derivable_tag_ext_flex_var() {
|
|||
fn derivable_tag_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
ToEncoder,
|
||||
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER),
|
||||
v!([ A v!(STR) ]a implements Symbol::ENCODE_TO_ENCODER),
|
||||
DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![("A".into(), 1)])),
|
||||
);
|
||||
}
|
||||
|
|
@ -188,8 +188,8 @@ fn empty_record() {
|
|||
derive_test(ToEncoder, v!(EMPTY_RECORD), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for {}
|
||||
# {} -[[toEncoder_{}(0)]]-> Encoder fmt | fmt has EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) | fmt has EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) where fmt implements EncoderFormatting
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_{}(0)]]
|
||||
# @<2>: [[custom(2) {}]]
|
||||
|
|
@ -208,8 +208,8 @@ fn zero_field_record() {
|
|||
derive_test(ToEncoder, v!({}), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for {}
|
||||
# {} -[[toEncoder_{}(0)]]-> Encoder fmt | fmt has EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) | fmt has EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) where fmt implements EncoderFormatting
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_{}(0)]]
|
||||
# @<2>: [[custom(2) {}]]
|
||||
|
|
@ -228,11 +228,11 @@ fn one_field_record() {
|
|||
derive_test(ToEncoder, v!({ a: v!(U8), }), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { a : U8 }
|
||||
# { a : val } -[[toEncoder_{a}(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding
|
||||
# { a : val } -[[toEncoder_{a}(0)]]-> (List U8, fmt -[[custom(2) { a : val }]]-> List U8) | fmt has EncoderFormatting, val has Encoding
|
||||
# { a : val } -[[toEncoder_{a}(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding
|
||||
# { a : val } -[[toEncoder_{a}(0)]]-> (List U8, fmt -[[custom(2) { a : val }]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_{a}(0)]]
|
||||
# @<2>: [[custom(2) { a : val }]] | val has Encoding
|
||||
# @<2>: [[custom(2) { a : val }]] where val implements Encoding
|
||||
#Derived.toEncoder_{a} =
|
||||
\#Derived.rcd ->
|
||||
custom
|
||||
|
|
@ -251,11 +251,11 @@ fn two_field_record() {
|
|||
derive_test(ToEncoder, v!({ a: v!(U8), b: v!(STR), }), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { a : U8, b : Str }
|
||||
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> (List U8, fmt -[[custom(2) { a : val, b : val1 }]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> (List U8, fmt -[[custom(2) { a : val, b : val1 }]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_{a,b}(0)]]
|
||||
# @<2>: [[custom(2) { a : val, b : val1 }]] | val has Encoding, val1 has Encoding
|
||||
# @<2>: [[custom(2) { a : val, b : val1 }]] where val implements Encoding, val1 implements Encoding
|
||||
#Derived.toEncoder_{a,b} =
|
||||
\#Derived.rcd ->
|
||||
custom
|
||||
|
|
@ -278,11 +278,11 @@ fn two_field_tuple() {
|
|||
derive_test(ToEncoder, v!((v!(U8), v!(STR),)), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for ( U8, Str )*
|
||||
# ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_(arity:2)(0)]]
|
||||
# @<2>: [[custom(2) ( val, val1 )*]] | val has Encoding, val1 has Encoding
|
||||
# @<2>: [[custom(2) ( val, val1 )*]] where val implements Encoding, val1 implements Encoding
|
||||
#Derived.toEncoder_(arity:2) =
|
||||
\#Derived.tup ->
|
||||
custom
|
||||
|
|
@ -314,8 +314,8 @@ fn tag_one_label_zero_args() {
|
|||
derive_test(ToEncoder, v!([A]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A]
|
||||
# [A] -[[toEncoder_[A 0](0)]]-> Encoder fmt | fmt has EncoderFormatting
|
||||
# [A] -[[toEncoder_[A 0](0)]]-> (List U8, fmt -[[custom(2) [A]]]-> List U8) | fmt has EncoderFormatting
|
||||
# [A] -[[toEncoder_[A 0](0)]]-> Encoder fmt where fmt implements EncoderFormatting
|
||||
# [A] -[[toEncoder_[A 0](0)]]-> (List U8, fmt -[[custom(2) [A]]]-> List U8) where fmt implements EncoderFormatting
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[A 0](0)]]
|
||||
# @<2>: [[custom(2) [A]]]
|
||||
|
|
@ -338,11 +338,11 @@ fn tag_one_label_two_args() {
|
|||
derive_test(ToEncoder, v!([A v!(U8) v!(STR)]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str]
|
||||
# [A val val1] -[[toEncoder_[A 2](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1] -[[toEncoder_[A 2](0)]]-> (List U8, fmt -[[custom(4) [A val val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1] -[[toEncoder_[A 2](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# [A val val1] -[[toEncoder_[A 2](0)]]-> (List U8, fmt -[[custom(4) [A val val1]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[A 2](0)]]
|
||||
# @<2>: [[custom(4) [A val val1]]] | val has Encoding, val1 has Encoding
|
||||
# @<2>: [[custom(4) [A val val1]]] where val implements Encoding, val1 implements Encoding
|
||||
#Derived.toEncoder_[A 2] =
|
||||
\#Derived.tag ->
|
||||
custom
|
||||
|
|
@ -366,11 +366,11 @@ fn tag_two_labels() {
|
|||
|golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str U16, B Str]
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[A 3,B 1](0)]]
|
||||
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding
|
||||
# @<2>: [[custom(6) [A val val1 val1, B val1]]] where val implements Encoding, val1 implements Encoding
|
||||
#Derived.toEncoder_[A 3,B 1] =
|
||||
\#Derived.tag ->
|
||||
custom
|
||||
|
|
@ -402,11 +402,11 @@ fn recursive_tag_union() {
|
|||
|golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [Cons U8 $rec, Nil] as $rec
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
|
||||
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding
|
||||
# @<2>: [[custom(4) [Cons val val1, Nil]]] where val implements Encoding, val1 implements Encoding
|
||||
#Derived.toEncoder_[Cons 2,Nil 0] =
|
||||
\#Derived.tag ->
|
||||
custom
|
||||
|
|
@ -429,11 +429,11 @@ fn list() {
|
|||
derive_test(ToEncoder, v!(Symbol::LIST_LIST v!(STR)), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for List Str
|
||||
# List val -[[toEncoder_list(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding
|
||||
# List val -[[toEncoder_list(0)]]-> (List U8, fmt -[[custom(4) (List val)]]-> List U8) | fmt has EncoderFormatting, val has Encoding
|
||||
# List val -[[toEncoder_list(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding
|
||||
# List val -[[toEncoder_list(0)]]-> (List U8, fmt -[[custom(4) (List val)]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_list(0)]]
|
||||
# @<2>: [[custom(4) (List val)]] | val has Encoding
|
||||
# @<2>: [[custom(4) (List val)]] where val implements Encoding
|
||||
#Derived.toEncoder_list =
|
||||
\#Derived.lst ->
|
||||
custom
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ fn derivable_record_ext_flex_var() {
|
|||
fn derivable_record_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
Hash,
|
||||
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ),
|
||||
v!({ a: v!(STR), }a implements Symbol::DECODE_DECODER ),
|
||||
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
|
||||
);
|
||||
}
|
||||
|
|
@ -129,7 +129,7 @@ fn derivable_tag_ext_flex_var() {
|
|||
fn derivable_tag_ext_flex_able_var() {
|
||||
check_derivable(
|
||||
Hash,
|
||||
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER),
|
||||
v!([ A v!(STR) ]a implements Symbol::ENCODE_TO_ENCODER),
|
||||
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
|
||||
);
|
||||
}
|
||||
|
|
@ -151,8 +151,8 @@ fn empty_record() {
|
|||
derive_test(Hash, v!(EMPTY_RECORD), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for {}
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_{}(0)]]
|
||||
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
|
||||
|
|
@ -166,8 +166,8 @@ fn zero_field_record() {
|
|||
derive_test(Hash, v!({}), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for {}
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
||||
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_{}(0)]]
|
||||
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
|
||||
|
|
@ -181,8 +181,8 @@ fn one_field_record() {
|
|||
derive_test(Hash, v!({ a: v!(U8), }), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { a : U8 }
|
||||
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher
|
||||
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher
|
||||
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
|
||||
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_{a}(0)]]
|
||||
#Derived.hash_{a} =
|
||||
|
|
@ -197,8 +197,8 @@ fn two_field_record() {
|
|||
derive_test(Hash, v!({ a: v!(U8), b: v!(STR), }), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for { a : U8, b : Str }
|
||||
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_{a,b}(0)]]
|
||||
#Derived.hash_{a,b} =
|
||||
|
|
@ -214,8 +214,8 @@ fn two_element_tuple() {
|
|||
derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for ( U8, Str )*
|
||||
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_(arity:2)(0)]]
|
||||
#Derived.hash_(arity:2) =
|
||||
|
|
@ -231,8 +231,8 @@ fn tag_one_label_no_payloads() {
|
|||
derive_test(Hash, v!([A]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A]
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
|
||||
# hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 0](0)]]
|
||||
#Derived.hash_[A 0] = \#Derived.hasher, A -> #Derived.hasher
|
||||
|
|
@ -246,8 +246,8 @@ fn tag_one_label_newtype() {
|
|||
derive_test(Hash, v!([A v!(U8) v!(STR)]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str]
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 2](0)]]
|
||||
#Derived.hash_[A 2] =
|
||||
|
|
@ -263,8 +263,8 @@ fn tag_two_labels() {
|
|||
derive_test(Hash, v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str U16, B Str]
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
|
||||
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 3,B 1](0)]]
|
||||
#Derived.hash_[A 3,B 1] =
|
||||
|
|
@ -285,8 +285,8 @@ fn tag_two_labels_no_payloads() {
|
|||
derive_test(Hash, v!([A, B]), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A, B]
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
|
||||
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[A 0,B 0](0)]]
|
||||
#Derived.hash_[A 0,B 0] =
|
||||
|
|
@ -304,8 +304,8 @@ fn recursive_tag_union() {
|
|||
derive_test(Hash, v!([Nil, Cons v!(U8) v!(^lst) ] as lst), |golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [Cons U8 $rec, Nil] as $rec
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
|
||||
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_[Cons 2,Nil 0](0)]]
|
||||
#Derived.hash_[Cons 2,Nil 0] =
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ macro_rules! v {
|
|||
use roc_types::subs::{Subs, Content};
|
||||
|subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) }
|
||||
}};
|
||||
($name:ident has $ability:path) => {{
|
||||
($name:ident implements $ability:path) => {{
|
||||
use roc_types::subs::{Subs, SubsIndex, SubsSlice, Content};
|
||||
|subs: &mut Subs| {
|
||||
let name_index =
|
||||
|
|
|
|||
|
|
@ -18,7 +18,13 @@ wasi_libc_sys = { path = "../../wasi-libc-sys" }
|
|||
|
||||
tempfile.workspace = true
|
||||
|
||||
[dependencies]
|
||||
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
||||
inkwell = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
roc_gen_dev = { path = "../gen_dev" }
|
||||
roc_gen_wasm = { path = "../gen_wasm" }
|
||||
roc_bitcode = { path = "../builtins/bitcode" }
|
||||
roc_build = { path = "../build", features = ["target-aarch64", "target-x86_64", "target-wasm32"] }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
|
|
@ -28,9 +34,6 @@ roc_command_utils = { path = "../../utils/command" }
|
|||
roc_constrain = { path = "../constrain" }
|
||||
roc_debug_flags = { path = "../debug_flags" }
|
||||
roc_error_macros = { path = "../../error_macros" }
|
||||
roc_gen_dev = { path = "../gen_dev" }
|
||||
roc_gen_llvm = { path = "../gen_llvm" }
|
||||
roc_gen_wasm = { path = "../gen_wasm" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_mono = { path = "../mono" }
|
||||
|
|
@ -50,8 +53,6 @@ roc_wasm_module = { path = "../../wasm_module" }
|
|||
bumpalo.workspace = true
|
||||
criterion.workspace = true
|
||||
indoc.workspace = true
|
||||
inkwell.workspace = true
|
||||
lazy_static.workspace = true
|
||||
libc.workspace = true
|
||||
libloading.workspace = true
|
||||
target-lexicon.workspace = true
|
||||
|
|
@ -61,7 +62,7 @@ tempfile.workspace = true
|
|||
[features]
|
||||
default = ["gen-llvm"]
|
||||
gen-dev = []
|
||||
gen-llvm = []
|
||||
gen-llvm = ["roc_gen_llvm", "inkwell"]
|
||||
gen-llvm-wasm = ["gen-llvm"]
|
||||
gen-wasm = []
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,13 @@ fn roc_function<'a, 'b>(
|
|||
};
|
||||
|
||||
let context = inkwell::context::Context::create();
|
||||
let (main_fn_name, errors, lib) =
|
||||
helpers::llvm::helper(arena, config, source, arena.alloc(context));
|
||||
let (main_fn_name, errors, lib) = helpers::llvm::helper(
|
||||
arena,
|
||||
config,
|
||||
source,
|
||||
arena.alloc(context),
|
||||
roc_load::FunctionKind::LambdaSet,
|
||||
);
|
||||
|
||||
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
|
||||
|
||||
|
|
|
|||
|
|
@ -91,8 +91,13 @@ fn roc_function<'a>(
|
|||
};
|
||||
|
||||
let context = inkwell::context::Context::create();
|
||||
let (main_fn_name, errors, lib) =
|
||||
helpers::llvm::helper(arena, config, source, arena.alloc(context));
|
||||
let (main_fn_name, errors, lib) = helpers::llvm::helper(
|
||||
arena,
|
||||
config,
|
||||
source,
|
||||
arena.alloc(context),
|
||||
roc_load::FunctionKind::LambdaSet,
|
||||
);
|
||||
|
||||
assert!(errors.is_empty(), "Encountered errors:\n{errors}");
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ fn hash_specialization() {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
Id := U64 has [MHash {hash}]
|
||||
Id := U64 implements [MHash {hash}]
|
||||
|
||||
hash = \@Id n -> n
|
||||
|
||||
|
|
@ -45,14 +45,14 @@ fn hash_specialization_multiple_add() {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
Id := U64 has [ MHash {hash: hashId} ]
|
||||
Id := U64 implements [ MHash {hash: hashId} ]
|
||||
|
||||
hashId = \@Id n -> n
|
||||
|
||||
One := {} has [ MHash {hash: hashOne} ]
|
||||
One := {} implements [ MHash {hash: hashOne} ]
|
||||
|
||||
hashOne = \@One _ -> 1
|
||||
|
||||
|
|
@ -72,10 +72,10 @@ fn alias_member_specialization() {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
Id := U64 has [MHash {hash}]
|
||||
Id := U64 implements [MHash {hash}]
|
||||
|
||||
hash = \@Id n -> n
|
||||
|
||||
|
|
@ -97,13 +97,13 @@ fn ability_constrained_in_non_member_usage() {
|
|||
r#"
|
||||
app "test" provides [result] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
mulMHashes : a, a -> U64 | a has MHash
|
||||
mulMHashes : a, a -> U64 where a implements MHash
|
||||
mulMHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64 has [MHash {hash}]
|
||||
Id := U64 implements [MHash {hash}]
|
||||
hash = \@Id n -> n
|
||||
|
||||
result = mulMHashes (@Id 5) (@Id 7)
|
||||
|
|
@ -122,12 +122,12 @@ fn ability_constrained_in_non_member_usage_inferred() {
|
|||
r#"
|
||||
app "test" provides [result] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
mulMHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64 has [MHash {hash}]
|
||||
Id := U64 implements [MHash {hash}]
|
||||
hash = \@Id n -> n
|
||||
|
||||
result = mulMHashes (@Id 5) (@Id 7)
|
||||
|
|
@ -146,16 +146,16 @@ fn ability_constrained_in_non_member_multiple_specializations() {
|
|||
r#"
|
||||
app "test" provides [result] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
mulMHashes : a, b -> U64 | a has MHash, b has MHash
|
||||
mulMHashes : a, b -> U64 where a implements MHash, b implements MHash
|
||||
mulMHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64 has [MHash { hash: hashId }]
|
||||
Id := U64 implements [MHash { hash: hashId }]
|
||||
hashId = \@Id n -> n
|
||||
|
||||
Three := {} has [MHash { hash: hashThree }]
|
||||
Three := {} implements [MHash { hash: hashThree }]
|
||||
hashThree = \@Three _ -> 3
|
||||
|
||||
result = mulMHashes (@Id 100) (@Three {})
|
||||
|
|
@ -174,15 +174,15 @@ fn ability_constrained_in_non_member_multiple_specializations_inferred() {
|
|||
r#"
|
||||
app "test" provides [result] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
mulMHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64 has [MHash { hash: hashId }]
|
||||
Id := U64 implements [MHash { hash: hashId }]
|
||||
hashId = \@Id n -> n
|
||||
|
||||
Three := {} has [MHash { hash: hashThree }]
|
||||
Three := {} implements [MHash { hash: hashThree }]
|
||||
hashThree = \@Three _ -> 3
|
||||
|
||||
result = mulMHashes (@Id 100) (@Three {})
|
||||
|
|
@ -201,16 +201,16 @@ fn ability_used_as_type_still_compiles() {
|
|||
r#"
|
||||
app "test" provides [result] to "./platform"
|
||||
|
||||
MHash has
|
||||
hash : a -> U64 | a has MHash
|
||||
MHash implements
|
||||
hash : a -> U64 where a implements MHash
|
||||
|
||||
mulMHashes : MHash, MHash -> U64
|
||||
mulMHashes = \x, y -> hash x * hash y
|
||||
|
||||
Id := U64 has [MHash { hash: hashId }]
|
||||
Id := U64 implements [MHash { hash: hashId }]
|
||||
hashId = \@Id n -> n
|
||||
|
||||
Three := {} has [MHash { hash: hashThree }]
|
||||
Three := {} implements [MHash { hash: hashThree }]
|
||||
hashThree = \@Three _ -> 3
|
||||
|
||||
result = mulMHashes (@Id 100) (@Three {})
|
||||
|
|
@ -229,15 +229,15 @@ fn bounds_to_multiple_abilities() {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Idempot has idempot : a -> a | a has Idempot
|
||||
Consume has consume : a -> Str | a has Consume
|
||||
Idempot implements idempot : a -> a where a implements Idempot
|
||||
Consume implements consume : a -> Str where a implements Consume
|
||||
|
||||
Hello := Str has [Idempot { idempot: idempotHello }, Consume { consume: consumeHello }]
|
||||
Hello := Str implements [Idempot { idempot: idempotHello }, Consume { consume: consumeHello }]
|
||||
|
||||
idempotHello = \@Hello msg -> @Hello msg
|
||||
consumeHello = \@Hello msg -> msg
|
||||
|
||||
lifecycle : a -> Str | a has Idempot & Consume
|
||||
lifecycle : a -> Str where a implements Idempot & Consume
|
||||
lifecycle = \x -> idempot x |> consume
|
||||
|
||||
main = lifecycle (@Hello "hello world")
|
||||
|
|
@ -256,26 +256,26 @@ fn encode() {
|
|||
r#"
|
||||
app "test" provides [myU8Bytes] to "./platform"
|
||||
|
||||
MEncoder fmt := List U8, fmt -> List U8 | fmt has Format
|
||||
MEncoder fmt := List U8, fmt -> List U8 where fmt implements Format
|
||||
|
||||
MEncoding has
|
||||
toEncoder : val -> MEncoder fmt | val has MEncoding, fmt has Format
|
||||
MEncoding implements
|
||||
toEncoder : val -> MEncoder fmt where val implements MEncoding, fmt implements Format
|
||||
|
||||
Format has
|
||||
u8 : U8 -> MEncoder fmt | fmt has Format
|
||||
Format implements
|
||||
u8 : U8 -> MEncoder fmt where fmt implements Format
|
||||
|
||||
appendWith : List U8, MEncoder fmt, fmt -> List U8 | fmt has Format
|
||||
appendWith : List U8, MEncoder fmt, fmt -> List U8 where fmt implements Format
|
||||
appendWith = \lst, (@MEncoder doFormat), fmt -> doFormat lst fmt
|
||||
|
||||
toBytes : val, fmt -> List U8 | val has MEncoding, fmt has Format
|
||||
toBytes : val, fmt -> List U8 where val implements MEncoding, fmt implements Format
|
||||
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
|
||||
|
||||
|
||||
Linear := {} has [Format {u8}]
|
||||
Linear := {} implements [Format {u8}]
|
||||
|
||||
u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n)
|
||||
|
||||
Rgba := { r : U8, g : U8, b : U8, a : U8 } has [MEncoding {toEncoder}]
|
||||
Rgba := { r : U8, g : U8, b : U8, a : U8 } implements [MEncoding {toEncoder}]
|
||||
|
||||
toEncoder = \@Rgba {r, g, b, a} ->
|
||||
@MEncoder \lst, fmt -> lst
|
||||
|
|
@ -303,19 +303,19 @@ fn decode() {
|
|||
|
||||
MDecodeError : [TooShort, Leftover (List U8)]
|
||||
|
||||
MDecoder val fmt := List U8, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting
|
||||
MDecoder val fmt := List U8, fmt -> { result: Result val MDecodeError, rest: List U8 } where fmt implements MDecoderFormatting
|
||||
|
||||
MDecoding has
|
||||
decoder : MDecoder val fmt | val has MDecoding, fmt has MDecoderFormatting
|
||||
MDecoding implements
|
||||
decoder : MDecoder val fmt where val implements MDecoding, fmt implements MDecoderFormatting
|
||||
|
||||
MDecoderFormatting has
|
||||
u8 : MDecoder U8 fmt | fmt has MDecoderFormatting
|
||||
MDecoderFormatting implements
|
||||
u8 : MDecoder U8 fmt where fmt implements MDecoderFormatting
|
||||
|
||||
decodeWith : List U8, MDecoder val fmt, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting
|
||||
decodeWith : List U8, MDecoder val fmt, fmt -> { result: Result val MDecodeError, rest: List U8 } where fmt implements MDecoderFormatting
|
||||
decodeWith = \lst, (@MDecoder doDecode), fmt -> doDecode lst fmt
|
||||
|
||||
fromBytes : List U8, fmt -> Result val MDecodeError
|
||||
| fmt has MDecoderFormatting, val has MDecoding
|
||||
where fmt implements MDecoderFormatting, val implements MDecoding
|
||||
fromBytes = \lst, fmt ->
|
||||
when decodeWith lst decoder fmt is
|
||||
{ result, rest } ->
|
||||
|
|
@ -325,14 +325,14 @@ fn decode() {
|
|||
else Err (Leftover rest)
|
||||
|
||||
|
||||
Linear := {} has [MDecoderFormatting {u8}]
|
||||
Linear := {} implements [MDecoderFormatting {u8}]
|
||||
|
||||
u8 = @MDecoder \lst, @Linear {} ->
|
||||
when List.first lst is
|
||||
Ok n -> { result: Ok n, rest: List.dropFirst lst }
|
||||
Err _ -> { result: Err TooShort, rest: [] }
|
||||
|
||||
MyU8 := U8 has [MDecoding {decoder}]
|
||||
MyU8 := U8 implements [MDecoding {decoder}]
|
||||
|
||||
# impl MDecoding for MyU8
|
||||
decoder = @MDecoder \lst, fmt ->
|
||||
|
|
@ -360,7 +360,7 @@ fn encode_use_stdlib() {
|
|||
imports [Encode, TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := {} has [Encoding {toEncoder}]
|
||||
HelloWorld := {} implements [Encoding {toEncoder}]
|
||||
toEncoder = \@HelloWorld {} ->
|
||||
Encode.custom \bytes, fmt ->
|
||||
bytes
|
||||
|
|
@ -388,7 +388,7 @@ fn encode_use_stdlib_without_wrapping_custom() {
|
|||
imports [Encode, TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := {} has [Encoding {toEncoder}]
|
||||
HelloWorld := {} implements [Encoding {toEncoder}]
|
||||
toEncoder = \@HelloWorld {} -> Encode.string "Hello, World!\n"
|
||||
|
||||
main =
|
||||
|
|
@ -414,7 +414,7 @@ fn encode_derive_to_encoder_for_opaque() {
|
|||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := { a: Str } has [Encoding]
|
||||
HelloWorld := { a: Str } implements [Encoding]
|
||||
|
||||
main =
|
||||
result = Str.fromUtf8 (Encode.toBytes (@HelloWorld { a: "Hello, World!" }) TotallyNotJson.json)
|
||||
|
|
@ -438,7 +438,7 @@ fn to_encoder_encode_custom_has_capture() {
|
|||
imports [Encode, TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := Str has [Encoding {toEncoder}]
|
||||
HelloWorld := Str implements [Encoding {toEncoder}]
|
||||
toEncoder = \@HelloWorld s1 ->
|
||||
Encode.custom \bytes, fmt ->
|
||||
bytes
|
||||
|
|
@ -891,7 +891,7 @@ fn encode_derived_generic_record_with_different_field_types() {
|
|||
imports [Encode, TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
Q a b := {a: a, b: b} has [Encoding]
|
||||
Q a b := {a: a, b: b} implements [Encoding]
|
||||
|
||||
q = @Q {a: 10u32, b: "fieldb"}
|
||||
|
||||
|
|
@ -917,7 +917,7 @@ fn encode_derived_generic_tag_with_different_field_types() {
|
|||
imports [Encode, TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
Q a b := [A a, B b] has [Encoding]
|
||||
Q a b := [A a, B b] implements [Encoding]
|
||||
|
||||
q : Q Str U32
|
||||
q = @Q (B 67)
|
||||
|
|
@ -969,7 +969,7 @@ fn decode_use_stdlib() {
|
|||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
MyNum := U8 has [Decoding {decoder: myDecoder}]
|
||||
MyNum := U8 implements [Decoding {decoder: myDecoder}]
|
||||
|
||||
myDecoder =
|
||||
Decode.custom \bytes, fmt ->
|
||||
|
|
@ -1003,7 +1003,7 @@ fn decode_derive_decoder_for_opaque() {
|
|||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := { a: Str } has [Decoding]
|
||||
HelloWorld := { a: Str } implements [Decoding]
|
||||
|
||||
main =
|
||||
when Str.toUtf8 """{"a":"Hello, World!"}""" |> Decode.fromBytes TotallyNotJson.json is
|
||||
|
|
@ -1026,7 +1026,7 @@ fn decode_use_stdlib_json_list() {
|
|||
imports [TotallyNotJson]
|
||||
provides [main] to "./platform"
|
||||
|
||||
MyNumList := List U8 has [Decoding {decoder: myDecoder}]
|
||||
MyNumList := List U8 implements [Decoding {decoder: myDecoder}]
|
||||
|
||||
myDecoder =
|
||||
Decode.custom \bytes, fmt ->
|
||||
|
|
@ -1459,7 +1459,7 @@ mod hash {
|
|||
|
||||
const TEST_HASHER: &str = indoc!(
|
||||
r#"
|
||||
THasher := List U8 has [Hasher {
|
||||
THasher := List U8 implements [Hasher {
|
||||
addBytes: tAddBytes,
|
||||
addU8: tAddU8,
|
||||
addU16: tAddU16,
|
||||
|
|
@ -2002,7 +2002,7 @@ mod hash {
|
|||
|
||||
{}
|
||||
|
||||
Q := {{ a: U8, b: U8, c: U8 }} has [Hash]
|
||||
Q := {{ a: U8, b: U8, c: U8 }} implements [Hash]
|
||||
|
||||
q = @Q {{ a: 15, b: 27, c: 31 }}
|
||||
|
||||
|
|
@ -2055,7 +2055,7 @@ mod eq {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
LyingEq := U8 has [Eq {isEq}]
|
||||
LyingEq := U8 implements [Eq {isEq}]
|
||||
|
||||
isEq = \@LyingEq m, @LyingEq n -> m != n
|
||||
|
||||
|
|
@ -2081,7 +2081,7 @@ mod eq {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||
Q := ({} -> Str) implements [Eq {isEq: isEqQ}]
|
||||
|
||||
isEqQ = \@Q _, @Q _ -> Bool.true
|
||||
|
||||
|
|
@ -2101,7 +2101,7 @@ mod eq {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Q := ({} -> Str) has [Eq {isEq: isEqQ}]
|
||||
Q := ({} -> Str) implements [Eq {isEq: isEqQ}]
|
||||
|
||||
isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {})
|
||||
|
||||
|
|
@ -2135,7 +2135,7 @@ mod eq {
|
|||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Q := U8 has [Eq]
|
||||
Q := U8 implements [Eq]
|
||||
|
||||
main = (@Q 15) == (@Q 15)
|
||||
"#
|
||||
|
|
|
|||
|
|
@ -959,7 +959,7 @@ fn list_walk_implements_position() {
|
|||
r#"
|
||||
Option a : [Some a, None]
|
||||
|
||||
find : List a, a -> Option Nat | a has Eq
|
||||
find : List a, a -> Option Nat where a implements Eq
|
||||
find = \list, needle ->
|
||||
findHelp list needle
|
||||
|> .v
|
||||
|
|
@ -3692,7 +3692,7 @@ fn list_walk_backwards_implements_position() {
|
|||
r#"
|
||||
Option a : [Some a, None]
|
||||
|
||||
find : List a, a -> Option Nat | a has Eq
|
||||
find : List a, a -> Option Nat where a implements Eq
|
||||
find = \list, needle ->
|
||||
findHelp list needle
|
||||
|> .v
|
||||
|
|
@ -3810,6 +3810,9 @@ fn list_range_length_overflow() {
|
|||
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
mod pattern_match {
|
||||
#[allow(unused_imports)]
|
||||
use crate::helpers::with_larger_debug_stack;
|
||||
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
|
||||
|
|
@ -3819,8 +3822,6 @@ mod pattern_match {
|
|||
#[cfg(feature = "gen-dev")]
|
||||
use crate::helpers::dev::assert_evals_to;
|
||||
|
||||
use crate::helpers::with_larger_debug_stack;
|
||||
|
||||
use super::RocList;
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -891,7 +891,7 @@ fn gen_wrap_int_neq() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
wrappedNotEq : a, a -> Bool | a has Eq
|
||||
wrappedNotEq : a, a -> Bool where a implements Eq
|
||||
wrappedNotEq = \num1, num2 ->
|
||||
num1 != num2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
#[allow(unused_imports)]
|
||||
use crate::helpers::with_larger_debug_stack;
|
||||
|
||||
#[cfg(feature = "gen-llvm")]
|
||||
use crate::helpers::llvm::assert_evals_to;
|
||||
|
||||
|
|
@ -14,8 +17,6 @@ use roc_mono::layout::{LayoutRepr, STLayoutInterner};
|
|||
#[cfg(test)]
|
||||
use roc_std::{RocList, RocStr, U128};
|
||||
|
||||
use crate::helpers::with_larger_debug_stack;
|
||||
|
||||
#[test]
|
||||
fn width_and_alignment_u8_u8() {
|
||||
use roc_mono::layout::Layout;
|
||||
|
|
@ -2254,15 +2255,15 @@ fn recursive_tag_id_in_allocation_basic() {
|
|||
|
||||
main =
|
||||
when x is
|
||||
A _ -> "A"
|
||||
B _ -> "B"
|
||||
C _ -> "C"
|
||||
D _ -> "D"
|
||||
E _ -> "E"
|
||||
F _ -> "F"
|
||||
G _ -> "G"
|
||||
H _ -> "H"
|
||||
I _ -> "I"
|
||||
A _ -> "A"
|
||||
B _ -> "B"
|
||||
C _ -> "C"
|
||||
D _ -> "D"
|
||||
E _ -> "E"
|
||||
F _ -> "F"
|
||||
G _ -> "G"
|
||||
H _ -> "H"
|
||||
I _ -> "I"
|
||||
"###
|
||||
),
|
||||
RocStr::from("H"),
|
||||
|
|
|
|||
|
|
@ -215,9 +215,10 @@ pub fn helper(
|
|||
let builtins_host_tempfile =
|
||||
roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile");
|
||||
|
||||
// TODO make this an envrionment variable
|
||||
// TODO make this an environment variable
|
||||
if false {
|
||||
let file_path = std::env::temp_dir().join("app.o");
|
||||
println!("gen-test object file written to {}", file_path.display());
|
||||
std::fs::copy(&app_o_file, file_path).unwrap();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::mem::MaybeUninit;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use inkwell::module::Module;
|
||||
use libloading::Library;
|
||||
|
|
@ -101,6 +102,7 @@ fn create_llvm_module<'a>(
|
|||
use roc_load::MonomorphizedModule;
|
||||
let MonomorphizedModule {
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
interns,
|
||||
layout_interner,
|
||||
..
|
||||
|
|
@ -270,6 +272,7 @@ fn create_llvm_module<'a>(
|
|||
&layout_interner,
|
||||
config.opt_level,
|
||||
procedures,
|
||||
host_exposed_lambda_sets,
|
||||
entry_point,
|
||||
),
|
||||
};
|
||||
|
|
@ -414,10 +417,6 @@ fn write_final_wasm() -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref TEMP_DIR: tempfile::TempDir = tempfile::tempdir().unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn compile_to_wasm_bytes<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
|
|
@ -426,13 +425,17 @@ fn compile_to_wasm_bytes<'a>(
|
|||
context: &'a inkwell::context::Context,
|
||||
function_kind: FunctionKind,
|
||||
) -> Vec<u8> {
|
||||
// globally cache the temporary directory
|
||||
static TEMP_DIR: OnceLock<tempfile::TempDir> = OnceLock::new();
|
||||
let temp_dir = TEMP_DIR.get_or_init(|| tempfile::tempdir().unwrap());
|
||||
|
||||
let target = wasm32_target_tripple();
|
||||
|
||||
let (_main_fn_name, _delayed_errors, llvm_module) =
|
||||
create_llvm_module(arena, src, config, context, &target, function_kind);
|
||||
|
||||
let content_hash = crate::helpers::src_hash(src);
|
||||
let wasm_file = llvm_module_to_wasm_file(&TEMP_DIR, content_hash, llvm_module);
|
||||
let wasm_file = llvm_module_to_wasm_file(temp_dir, content_hash, llvm_module);
|
||||
let compiled_bytes = std::fs::read(wasm_file).unwrap();
|
||||
|
||||
if write_final_wasm() {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ use roc_module::symbol::{
|
|||
Symbol,
|
||||
};
|
||||
use roc_mono::ir::{
|
||||
Call, CallType, Expr, HostExposedLayouts, Literal, Proc, ProcLayout, SelfRecursive, Stmt,
|
||||
UpdateModeId,
|
||||
Call, CallType, Expr, Literal, Proc, ProcLayout, SelfRecursive, Stmt, UpdateModeId,
|
||||
};
|
||||
use roc_mono::layout::{LambdaName, Layout, Niche, STLayoutInterner};
|
||||
use roc_wasm_interp::{wasi, ImportDispatcher, Instance, WasiDispatcher};
|
||||
|
|
@ -116,7 +115,6 @@ fn build_app_mono<'a>(
|
|||
closure_data_layout: None,
|
||||
ret_layout: int_layout,
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
is_erased: false,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,97 +2,97 @@ procedure Bool.11 (#Attr.2, #Attr.3):
|
|||
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.24;
|
||||
|
||||
procedure List.26 (List.159, List.160, List.161):
|
||||
let List.536 : [C U64, C U64] = CallByName List.93 List.159 List.160 List.161;
|
||||
let List.539 : U8 = 1i64;
|
||||
let List.540 : U8 = GetTagId List.536;
|
||||
let List.541 : Int1 = lowlevel Eq List.539 List.540;
|
||||
if List.541 then
|
||||
let List.162 : U64 = UnionAtIndex (Id 1) (Index 0) List.536;
|
||||
ret List.162;
|
||||
procedure List.26 (List.172, List.173, List.174):
|
||||
let List.550 : [C U64, C U64] = CallByName List.96 List.172 List.173 List.174;
|
||||
let List.553 : U8 = 1i64;
|
||||
let List.554 : U8 = GetTagId List.550;
|
||||
let List.555 : Int1 = lowlevel Eq List.553 List.554;
|
||||
if List.555 then
|
||||
let List.175 : U64 = UnionAtIndex (Id 1) (Index 0) List.550;
|
||||
ret List.175;
|
||||
else
|
||||
let List.163 : U64 = UnionAtIndex (Id 0) (Index 0) List.536;
|
||||
ret List.163;
|
||||
let List.176 : U64 = UnionAtIndex (Id 0) (Index 0) List.550;
|
||||
ret List.176;
|
||||
|
||||
procedure List.29 (List.304, List.305):
|
||||
let List.535 : U64 = CallByName List.6 List.304;
|
||||
let List.306 : U64 = CallByName Num.77 List.535 List.305;
|
||||
let List.521 : List U8 = CallByName List.43 List.304 List.306;
|
||||
ret List.521;
|
||||
procedure List.29 (List.317, List.318):
|
||||
let List.549 : U64 = CallByName List.6 List.317;
|
||||
let List.319 : U64 = CallByName Num.77 List.549 List.318;
|
||||
let List.535 : List U8 = CallByName List.43 List.317 List.319;
|
||||
ret List.535;
|
||||
|
||||
procedure List.43 (List.302, List.303):
|
||||
let List.533 : U64 = CallByName List.6 List.302;
|
||||
let List.532 : U64 = CallByName Num.77 List.533 List.303;
|
||||
let List.523 : {U64, U64} = Struct {List.303, List.532};
|
||||
let List.522 : List U8 = CallByName List.49 List.302 List.523;
|
||||
ret List.522;
|
||||
procedure List.43 (List.315, List.316):
|
||||
let List.547 : U64 = CallByName List.6 List.315;
|
||||
let List.546 : U64 = CallByName Num.77 List.547 List.316;
|
||||
let List.537 : {U64, U64} = Struct {List.316, List.546};
|
||||
let List.536 : List U8 = CallByName List.49 List.315 List.537;
|
||||
ret List.536;
|
||||
|
||||
procedure List.49 (List.376, List.377):
|
||||
let List.530 : U64 = StructAtIndex 0 List.377;
|
||||
let List.531 : U64 = 0i64;
|
||||
let List.528 : Int1 = CallByName Bool.11 List.530 List.531;
|
||||
if List.528 then
|
||||
dec List.376;
|
||||
let List.529 : List U8 = Array [];
|
||||
ret List.529;
|
||||
procedure List.49 (List.390, List.391):
|
||||
let List.544 : U64 = StructAtIndex 0 List.391;
|
||||
let List.545 : U64 = 0i64;
|
||||
let List.542 : Int1 = CallByName Bool.11 List.544 List.545;
|
||||
if List.542 then
|
||||
dec List.390;
|
||||
let List.543 : List U8 = Array [];
|
||||
ret List.543;
|
||||
else
|
||||
let List.525 : U64 = StructAtIndex 1 List.377;
|
||||
let List.526 : U64 = StructAtIndex 0 List.377;
|
||||
let List.524 : List U8 = CallByName List.72 List.376 List.525 List.526;
|
||||
ret List.524;
|
||||
let List.539 : U64 = StructAtIndex 1 List.391;
|
||||
let List.540 : U64 = StructAtIndex 0 List.391;
|
||||
let List.538 : List U8 = CallByName List.72 List.390 List.539 List.540;
|
||||
ret List.538;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.534 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.534;
|
||||
let List.548 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.548;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.557 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.557;
|
||||
let List.571 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.571;
|
||||
|
||||
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.527 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.527;
|
||||
let List.541 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.541;
|
||||
|
||||
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
|
||||
joinpoint List.545 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.547 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.547 then
|
||||
let List.556 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.548 : [C U64, C U64] = CallByName Test.4 List.440 List.556;
|
||||
let List.553 : U8 = 1i64;
|
||||
let List.554 : U8 = GetTagId List.548;
|
||||
let List.555 : Int1 = lowlevel Eq List.553 List.554;
|
||||
if List.555 then
|
||||
let List.444 : U64 = UnionAtIndex (Id 1) (Index 0) List.548;
|
||||
let List.551 : U64 = 1i64;
|
||||
let List.550 : U64 = CallByName Num.19 List.442 List.551;
|
||||
jump List.545 List.439 List.444 List.441 List.550 List.443;
|
||||
joinpoint List.559 List.453 List.454 List.455 List.456 List.457:
|
||||
let List.561 : Int1 = CallByName Num.22 List.456 List.457;
|
||||
if List.561 then
|
||||
let List.570 : U8 = CallByName List.66 List.453 List.456;
|
||||
let List.562 : [C U64, C U64] = CallByName Test.4 List.454 List.570;
|
||||
let List.567 : U8 = 1i64;
|
||||
let List.568 : U8 = GetTagId List.562;
|
||||
let List.569 : Int1 = lowlevel Eq List.567 List.568;
|
||||
if List.569 then
|
||||
let List.458 : U64 = UnionAtIndex (Id 1) (Index 0) List.562;
|
||||
let List.565 : U64 = 1i64;
|
||||
let List.564 : U64 = CallByName Num.51 List.456 List.565;
|
||||
jump List.559 List.453 List.458 List.455 List.564 List.457;
|
||||
else
|
||||
dec List.439;
|
||||
let List.445 : U64 = UnionAtIndex (Id 0) (Index 0) List.548;
|
||||
let List.552 : [C U64, C U64] = TagId(0) List.445;
|
||||
ret List.552;
|
||||
dec List.453;
|
||||
let List.459 : U64 = UnionAtIndex (Id 0) (Index 0) List.562;
|
||||
let List.566 : [C U64, C U64] = TagId(0) List.459;
|
||||
ret List.566;
|
||||
else
|
||||
dec List.439;
|
||||
let List.546 : [C U64, C U64] = TagId(1) List.440;
|
||||
ret List.546;
|
||||
dec List.453;
|
||||
let List.560 : [C U64, C U64] = TagId(1) List.454;
|
||||
ret List.560;
|
||||
in
|
||||
jump List.545 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
|
||||
jump List.559 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.543 : U64 = 0i64;
|
||||
let List.544 : U64 = CallByName List.6 List.436;
|
||||
let List.542 : [C U64, C U64] = CallByName List.80 List.436 List.437 List.438 List.543 List.544;
|
||||
ret List.542;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.294 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.294;
|
||||
procedure List.96 (List.450, List.451, List.452):
|
||||
let List.557 : U64 = 0i64;
|
||||
let List.558 : U64 = CallByName List.6 List.450;
|
||||
let List.556 : [C U64, C U64] = CallByName List.80 List.450 List.451 List.452 List.557 List.558;
|
||||
ret List.556;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.295 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.295;
|
||||
|
||||
procedure Num.51 (#Attr.2, #Attr.3):
|
||||
let Num.294 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
|
||||
ret Num.294;
|
||||
|
||||
procedure Num.77 (#Attr.2, #Attr.3):
|
||||
let Num.293 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3;
|
||||
ret Num.293;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.521 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
let List.535 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.521;
|
||||
ret List.535;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.521 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
let List.535 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.521;
|
||||
ret List.535;
|
||||
|
||||
procedure Test.2 (Test.3):
|
||||
let Test.7 : {} = Struct {};
|
||||
|
|
|
|||
|
|
@ -1,52 +1,44 @@
|
|||
procedure List.145 (List.146, List.147, List.144):
|
||||
let List.540 : [<r>C {}, C *self {{}, []}] = CallByName Test.29 List.146 List.147 List.144;
|
||||
ret List.540;
|
||||
|
||||
procedure List.18 (List.142, List.143, List.144):
|
||||
let List.521 : [<r>C {}, C *self {{}, []}] = CallByName List.93 List.142 List.143 List.144;
|
||||
ret List.521;
|
||||
procedure List.18 (List.145, List.146, List.147):
|
||||
let List.536 : U64 = 0i64;
|
||||
let List.537 : U64 = CallByName List.6 List.145;
|
||||
let List.535 : [<r>C {}, C *self {{}, []}] = CallByName List.86 List.145 List.146 List.147 List.536 List.537;
|
||||
ret List.535;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.538 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.538;
|
||||
let List.546 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.546;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.537 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.537;
|
||||
let List.545 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.545;
|
||||
|
||||
procedure List.80 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
|
||||
joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.529 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.529 then
|
||||
let List.536 : [] = CallByName List.66 List.439 List.442;
|
||||
let List.530 : [<r>C {}, C *self {{}, []}] = CallByName List.145 List.440 List.536 List.441;
|
||||
let List.533 : U64 = 1i64;
|
||||
let List.532 : U64 = CallByName Num.19 List.442 List.533;
|
||||
jump List.527 List.439 List.530 List.441 List.532 List.443;
|
||||
procedure List.86 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17):
|
||||
joinpoint List.538 List.148 List.149 List.150 List.151 List.152:
|
||||
let List.540 : Int1 = CallByName Num.22 List.151 List.152;
|
||||
if List.540 then
|
||||
let List.544 : [] = CallByName List.66 List.148 List.151;
|
||||
let List.153 : [<r>C {}, C *self {{}, []}] = CallByName Test.29 List.149 List.544 List.150;
|
||||
let List.543 : U64 = 1i64;
|
||||
let List.542 : U64 = CallByName Num.51 List.151 List.543;
|
||||
jump List.538 List.148 List.153 List.150 List.542 List.152;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
dec List.148;
|
||||
ret List.149;
|
||||
in
|
||||
jump List.527 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.525 : U64 = 0i64;
|
||||
let List.526 : U64 = CallByName List.6 List.436;
|
||||
let List.524 : [<r>C {}, C *self {{}, []}] = CallByName List.80 List.436 List.437 List.438 List.525 List.526;
|
||||
ret List.524;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.292 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.292;
|
||||
jump List.538 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.293 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.293;
|
||||
|
||||
procedure Num.51 (#Attr.2, #Attr.3):
|
||||
let Num.292 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
|
||||
ret Num.292;
|
||||
|
||||
procedure Test.10 (Test.66, #Attr.12):
|
||||
let Test.9 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||
let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.18 then
|
||||
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.20 then
|
||||
free #Attr.12;
|
||||
ret Test.9;
|
||||
else
|
||||
|
|
@ -60,7 +52,7 @@ procedure Test.10 (Test.66, #Attr.12):
|
|||
procedure Test.14 (Test.45, #Attr.12):
|
||||
let Test.13 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
let Test.12 : [<r>C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
joinpoint #Derived_gen.19:
|
||||
joinpoint #Derived_gen.18:
|
||||
let Test.50 : {} = Struct {};
|
||||
let Test.51 : U8 = GetTagId Test.12;
|
||||
joinpoint Test.52 Test.15:
|
||||
|
|
@ -87,14 +79,14 @@ procedure Test.14 (Test.45, #Attr.12):
|
|||
jump Test.52 Test.53;
|
||||
|
||||
in
|
||||
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.20 then
|
||||
let #Derived_gen.19 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.19 then
|
||||
free #Attr.12;
|
||||
jump #Derived_gen.19;
|
||||
jump #Derived_gen.18;
|
||||
else
|
||||
inc Test.12;
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.19;
|
||||
jump #Derived_gen.18;
|
||||
|
||||
procedure Test.20 (Test.21, Test.18):
|
||||
let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18;
|
||||
|
|
@ -156,8 +148,16 @@ procedure Test.80 (Test.81):
|
|||
ret Test.83;
|
||||
|
||||
procedure Test.84 (Test.86, #Attr.12):
|
||||
let Test.87 : Str = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||
Crash Test.87
|
||||
let Test.87 : U8 = GetTagId #Attr.12;
|
||||
switch Test.87:
|
||||
case 0:
|
||||
let Test.85 : {} = CallByName Test.10 Test.86 #Attr.12;
|
||||
ret Test.85;
|
||||
|
||||
default:
|
||||
let Test.85 : {} = CallByName Test.14 Test.86 #Attr.12;
|
||||
ret Test.85;
|
||||
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.35 : List [] = Array [];
|
||||
|
|
|
|||
|
|
@ -2,49 +2,49 @@ procedure Bool.1 ():
|
|||
let Bool.24 : Int1 = false;
|
||||
ret Bool.24;
|
||||
|
||||
procedure List.2 (List.97, List.98):
|
||||
let List.535 : U64 = CallByName List.6 List.97;
|
||||
let List.531 : Int1 = CallByName Num.22 List.98 List.535;
|
||||
if List.531 then
|
||||
let List.533 : Str = CallByName List.66 List.97 List.98;
|
||||
inc List.533;
|
||||
dec List.97;
|
||||
let List.532 : [C {}, C Str] = TagId(1) List.533;
|
||||
ret List.532;
|
||||
procedure List.2 (List.100, List.101):
|
||||
let List.549 : U64 = CallByName List.6 List.100;
|
||||
let List.545 : Int1 = CallByName Num.22 List.101 List.549;
|
||||
if List.545 then
|
||||
let List.547 : Str = CallByName List.66 List.100 List.101;
|
||||
inc List.547;
|
||||
dec List.100;
|
||||
let List.546 : [C {}, C Str] = TagId(1) List.547;
|
||||
ret List.546;
|
||||
else
|
||||
dec List.97;
|
||||
let List.530 : {} = Struct {};
|
||||
let List.529 : [C {}, C Str] = TagId(0) List.530;
|
||||
ret List.529;
|
||||
dec List.100;
|
||||
let List.544 : {} = Struct {};
|
||||
let List.543 : [C {}, C Str] = TagId(0) List.544;
|
||||
ret List.543;
|
||||
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let List.537 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3;
|
||||
let List.551 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3;
|
||||
decref #Attr.2;
|
||||
ret List.537;
|
||||
ret List.551;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.536 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.536;
|
||||
let List.550 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.550;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.534 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.534;
|
||||
let List.548 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.548;
|
||||
|
||||
procedure List.9 (List.293):
|
||||
let List.528 : U64 = 0i64;
|
||||
let List.521 : [C {}, C Str] = CallByName List.2 List.293 List.528;
|
||||
let List.525 : U8 = 1i64;
|
||||
let List.526 : U8 = GetTagId List.521;
|
||||
let List.527 : Int1 = lowlevel Eq List.525 List.526;
|
||||
if List.527 then
|
||||
let List.294 : Str = UnionAtIndex (Id 1) (Index 0) List.521;
|
||||
let List.522 : [C {}, C Str] = TagId(1) List.294;
|
||||
ret List.522;
|
||||
procedure List.9 (List.306):
|
||||
let List.542 : U64 = 0i64;
|
||||
let List.535 : [C {}, C Str] = CallByName List.2 List.306 List.542;
|
||||
let List.539 : U8 = 1i64;
|
||||
let List.540 : U8 = GetTagId List.535;
|
||||
let List.541 : Int1 = lowlevel Eq List.539 List.540;
|
||||
if List.541 then
|
||||
let List.307 : Str = UnionAtIndex (Id 1) (Index 0) List.535;
|
||||
let List.536 : [C {}, C Str] = TagId(1) List.307;
|
||||
ret List.536;
|
||||
else
|
||||
dec List.521;
|
||||
let List.524 : {} = Struct {};
|
||||
let List.523 : [C {}, C Str] = TagId(0) List.524;
|
||||
ret List.523;
|
||||
dec List.535;
|
||||
let List.538 : {} = Struct {};
|
||||
let List.537 : [C {}, C Str] = TagId(0) List.538;
|
||||
ret List.537;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.292 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
procedure List.6 (#Attr.2):
|
||||
let List.521 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.521;
|
||||
let List.535 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.535;
|
||||
|
||||
procedure Test.1 (Test.5):
|
||||
let Test.2 : I64 = 41i64;
|
||||
|
|
|
|||
|
|
@ -2,51 +2,43 @@ procedure Bool.2 ():
|
|||
let Bool.23 : Int1 = true;
|
||||
ret Bool.23;
|
||||
|
||||
procedure List.145 (List.146, List.147, List.144):
|
||||
let List.540 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName Test.6 List.146 List.147 List.144;
|
||||
ret List.540;
|
||||
|
||||
procedure List.18 (List.142, List.143, List.144):
|
||||
let List.521 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName List.93 List.142 List.143 List.144;
|
||||
ret List.521;
|
||||
procedure List.18 (List.145, List.146, List.147):
|
||||
let List.536 : U64 = 0i64;
|
||||
let List.537 : U64 = CallByName List.6 List.145;
|
||||
let List.535 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName List.86 List.145 List.146 List.147 List.536 List.537;
|
||||
ret List.535;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.538 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.538;
|
||||
let List.546 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.546;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.537 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.537;
|
||||
let List.545 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.545;
|
||||
|
||||
procedure List.80 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
|
||||
joinpoint List.527 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.529 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.529 then
|
||||
let List.536 : Int1 = CallByName List.66 List.439 List.442;
|
||||
let List.530 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName List.145 List.440 List.536 List.441;
|
||||
let List.533 : U64 = 1i64;
|
||||
let List.532 : U64 = CallByName Num.19 List.442 List.533;
|
||||
jump List.527 List.439 List.530 List.441 List.532 List.443;
|
||||
procedure List.86 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
|
||||
joinpoint List.538 List.148 List.149 List.150 List.151 List.152:
|
||||
let List.540 : Int1 = CallByName Num.22 List.151 List.152;
|
||||
if List.540 then
|
||||
let List.544 : Int1 = CallByName List.66 List.148 List.151;
|
||||
let List.153 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName Test.6 List.149 List.544 List.150;
|
||||
let List.543 : U64 = 1i64;
|
||||
let List.542 : U64 = CallByName Num.51 List.151 List.543;
|
||||
jump List.538 List.148 List.153 List.150 List.542 List.152;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
dec List.148;
|
||||
ret List.149;
|
||||
in
|
||||
jump List.527 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.525 : U64 = 0i64;
|
||||
let List.526 : U64 = CallByName List.6 List.436;
|
||||
let List.524 : [<rnw><null>, C *self Int1, C *self Int1] = CallByName List.80 List.436 List.437 List.438 List.525 List.526;
|
||||
ret List.524;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.292 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.292;
|
||||
jump List.538 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.293 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.293;
|
||||
|
||||
procedure Num.51 (#Attr.2, #Attr.3):
|
||||
let Num.292 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
|
||||
ret Num.292;
|
||||
|
||||
procedure Str.3 (#Attr.2, #Attr.3):
|
||||
let Str.291 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||
ret Str.291;
|
||||
|
|
@ -54,11 +46,11 @@ procedure Str.3 (#Attr.2, #Attr.3):
|
|||
procedure Test.1 (Test.5):
|
||||
ret Test.5;
|
||||
|
||||
procedure Test.11 (#Derived_gen.10, #Derived_gen.11):
|
||||
procedure Test.11 (#Derived_gen.8, #Derived_gen.9):
|
||||
joinpoint Test.27 Test.12 #Attr.12:
|
||||
let Test.8 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12;
|
||||
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12;
|
||||
joinpoint #Derived_gen.14:
|
||||
joinpoint #Derived_gen.12:
|
||||
joinpoint Test.31 Test.29:
|
||||
let Test.30 : U8 = GetTagId Test.7;
|
||||
switch Test.30:
|
||||
|
|
@ -85,16 +77,16 @@ procedure Test.11 (#Derived_gen.10, #Derived_gen.11):
|
|||
jump Test.31 Test.32;
|
||||
|
||||
in
|
||||
let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.15 then
|
||||
let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.13 then
|
||||
free #Attr.12;
|
||||
jump #Derived_gen.14;
|
||||
jump #Derived_gen.12;
|
||||
else
|
||||
inc Test.7;
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.14;
|
||||
jump #Derived_gen.12;
|
||||
in
|
||||
jump Test.27 #Derived_gen.10 #Derived_gen.11;
|
||||
jump Test.27 #Derived_gen.8 #Derived_gen.9;
|
||||
|
||||
procedure Test.2 (Test.13):
|
||||
ret Test.13;
|
||||
|
|
@ -125,7 +117,7 @@ procedure Test.6 (Test.7, Test.8, Test.5):
|
|||
procedure Test.9 (Test.10, #Attr.12):
|
||||
let Test.8 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
let Test.7 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||
joinpoint #Derived_gen.12:
|
||||
joinpoint #Derived_gen.14:
|
||||
let Test.37 : U8 = GetTagId Test.7;
|
||||
joinpoint Test.38 Test.36:
|
||||
switch Test.8:
|
||||
|
|
@ -153,14 +145,14 @@ procedure Test.9 (Test.10, #Attr.12):
|
|||
jump Test.38 Test.39;
|
||||
|
||||
in
|
||||
let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.13 then
|
||||
let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12;
|
||||
if #Derived_gen.15 then
|
||||
free #Attr.12;
|
||||
jump #Derived_gen.12;
|
||||
jump #Derived_gen.14;
|
||||
else
|
||||
inc Test.7;
|
||||
decref #Attr.12;
|
||||
jump #Derived_gen.12;
|
||||
jump #Derived_gen.14;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.41 : Int1 = false;
|
||||
|
|
|
|||
|
|
@ -24,68 +24,68 @@ procedure Dict.4 (Dict.562):
|
|||
dec #Derived_gen.6;
|
||||
ret Dict.100;
|
||||
|
||||
procedure List.11 (List.121, List.122):
|
||||
let List.522 : List I8 = CallByName List.68 List.122;
|
||||
let List.521 : List I8 = CallByName List.83 List.121 List.122 List.522;
|
||||
ret List.521;
|
||||
procedure List.11 (List.124, List.125):
|
||||
let List.536 : List I8 = CallByName List.68 List.125;
|
||||
let List.535 : List I8 = CallByName List.84 List.124 List.125 List.536;
|
||||
ret List.535;
|
||||
|
||||
procedure List.11 (List.121, List.122):
|
||||
let List.534 : List U64 = CallByName List.68 List.122;
|
||||
let List.533 : List U64 = CallByName List.83 List.121 List.122 List.534;
|
||||
ret List.533;
|
||||
procedure List.11 (List.124, List.125):
|
||||
let List.548 : List U64 = CallByName List.68 List.125;
|
||||
let List.547 : List U64 = CallByName List.84 List.124 List.125 List.548;
|
||||
ret List.547;
|
||||
|
||||
procedure List.68 (#Attr.2):
|
||||
let List.532 : List I8 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.532;
|
||||
let List.546 : List I8 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.546;
|
||||
|
||||
procedure List.68 (#Attr.2):
|
||||
let List.544 : List U64 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.544;
|
||||
let List.558 : List U64 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.558;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.529 : List I8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.529;
|
||||
let List.543 : List I8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.543;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.541 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.541;
|
||||
let List.555 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.555;
|
||||
|
||||
procedure List.83 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
|
||||
joinpoint List.523 List.123 List.124 List.125:
|
||||
let List.531 : U64 = 0i64;
|
||||
let List.525 : Int1 = CallByName Num.24 List.124 List.531;
|
||||
if List.525 then
|
||||
let List.530 : U64 = 1i64;
|
||||
let List.527 : U64 = CallByName Num.20 List.124 List.530;
|
||||
let List.528 : List I8 = CallByName List.71 List.125 List.123;
|
||||
jump List.523 List.123 List.527 List.528;
|
||||
procedure List.84 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
|
||||
joinpoint List.537 List.126 List.127 List.128:
|
||||
let List.545 : U64 = 0i64;
|
||||
let List.539 : Int1 = CallByName Num.24 List.127 List.545;
|
||||
if List.539 then
|
||||
let List.544 : U64 = 1i64;
|
||||
let List.541 : U64 = CallByName Num.75 List.127 List.544;
|
||||
let List.542 : List I8 = CallByName List.71 List.128 List.126;
|
||||
jump List.537 List.126 List.541 List.542;
|
||||
else
|
||||
ret List.125;
|
||||
ret List.128;
|
||||
in
|
||||
jump List.523 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
|
||||
jump List.537 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
|
||||
|
||||
procedure List.83 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
|
||||
joinpoint List.535 List.123 List.124 List.125:
|
||||
let List.543 : U64 = 0i64;
|
||||
let List.537 : Int1 = CallByName Num.24 List.124 List.543;
|
||||
if List.537 then
|
||||
let List.542 : U64 = 1i64;
|
||||
let List.539 : U64 = CallByName Num.20 List.124 List.542;
|
||||
let List.540 : List U64 = CallByName List.71 List.125 List.123;
|
||||
jump List.535 List.123 List.539 List.540;
|
||||
procedure List.84 (#Derived_gen.3, #Derived_gen.4, #Derived_gen.5):
|
||||
joinpoint List.549 List.126 List.127 List.128:
|
||||
let List.557 : U64 = 0i64;
|
||||
let List.551 : Int1 = CallByName Num.24 List.127 List.557;
|
||||
if List.551 then
|
||||
let List.556 : U64 = 1i64;
|
||||
let List.553 : U64 = CallByName Num.75 List.127 List.556;
|
||||
let List.554 : List U64 = CallByName List.71 List.128 List.126;
|
||||
jump List.549 List.126 List.553 List.554;
|
||||
else
|
||||
ret List.125;
|
||||
ret List.128;
|
||||
in
|
||||
jump List.535 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.293 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.293;
|
||||
jump List.549 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Num.295 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||
ret Num.295;
|
||||
|
||||
procedure Num.75 (#Attr.2, #Attr.3):
|
||||
let Num.293 : U64 = lowlevel NumSubWrap #Attr.2 #Attr.3;
|
||||
ret Num.293;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.3 : {} = Struct {};
|
||||
let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1 Test.3;
|
||||
|
|
|
|||
|
|
@ -2,27 +2,27 @@ procedure Bool.1 ():
|
|||
let Bool.23 : Int1 = false;
|
||||
ret Bool.23;
|
||||
|
||||
procedure List.2 (List.97, List.98):
|
||||
let List.527 : U64 = CallByName List.6 List.97;
|
||||
let List.523 : Int1 = CallByName Num.22 List.98 List.527;
|
||||
if List.523 then
|
||||
let List.525 : {} = CallByName List.66 List.97 List.98;
|
||||
dec List.97;
|
||||
let List.524 : [C {}, C {}] = TagId(1) List.525;
|
||||
ret List.524;
|
||||
procedure List.2 (List.100, List.101):
|
||||
let List.541 : U64 = CallByName List.6 List.100;
|
||||
let List.537 : Int1 = CallByName Num.22 List.101 List.541;
|
||||
if List.537 then
|
||||
let List.539 : {} = CallByName List.66 List.100 List.101;
|
||||
dec List.100;
|
||||
let List.538 : [C {}, C {}] = TagId(1) List.539;
|
||||
ret List.538;
|
||||
else
|
||||
dec List.97;
|
||||
let List.522 : {} = Struct {};
|
||||
let List.521 : [C {}, C {}] = TagId(0) List.522;
|
||||
ret List.521;
|
||||
dec List.100;
|
||||
let List.536 : {} = Struct {};
|
||||
let List.535 : [C {}, C {}] = TagId(0) List.536;
|
||||
ret List.535;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.528 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.528;
|
||||
let List.542 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.542;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.526 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.526;
|
||||
let List.540 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.540;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.292 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
procedure List.4 (List.113, List.114):
|
||||
let List.524 : U64 = 1i64;
|
||||
let List.522 : List U8 = CallByName List.70 List.113 List.524;
|
||||
let List.521 : List U8 = CallByName List.71 List.522 List.114;
|
||||
ret List.521;
|
||||
procedure List.4 (List.116, List.117):
|
||||
let List.538 : U64 = 1i64;
|
||||
let List.536 : List U8 = CallByName List.70 List.116 List.538;
|
||||
let List.535 : List U8 = CallByName List.71 List.536 List.117;
|
||||
ret List.535;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.525 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.525;
|
||||
let List.539 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.539;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.523 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.523;
|
||||
let List.537 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.537;
|
||||
|
||||
procedure Test.23 (Test.24, Test.35, Test.22):
|
||||
let Test.37 : List U8 = CallByName List.4 Test.24 Test.22;
|
||||
|
|
|
|||
|
|
@ -78,271 +78,247 @@ procedure Encode.26 (Encode.105, Encode.106):
|
|||
ret Encode.108;
|
||||
|
||||
procedure List.13 (#Attr.2, #Attr.3):
|
||||
let List.689 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3;
|
||||
ret List.689;
|
||||
let List.679 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3;
|
||||
ret List.679;
|
||||
|
||||
procedure List.145 (List.146, List.147, List.144):
|
||||
let List.569 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144;
|
||||
ret List.569;
|
||||
procedure List.18 (List.145, List.146, List.147):
|
||||
let List.565 : U64 = 0i64;
|
||||
let List.566 : U64 = CallByName List.6 List.145;
|
||||
let List.564 : {List U8, U64} = CallByName List.86 List.145 List.146 List.147 List.565 List.566;
|
||||
ret List.564;
|
||||
|
||||
procedure List.145 (List.146, List.147, List.144):
|
||||
let List.637 : {List U8, U64} = CallByName TotallyNotJson.237 List.146 List.147 List.144;
|
||||
ret List.637;
|
||||
procedure List.18 (List.145, List.146, List.147):
|
||||
let List.625 : U64 = 0i64;
|
||||
let List.626 : U64 = CallByName List.6 List.145;
|
||||
let List.624 : {List U8, U64} = CallByName List.86 List.145 List.146 List.147 List.625 List.626;
|
||||
ret List.624;
|
||||
|
||||
procedure List.145 (List.146, List.147, List.144):
|
||||
let List.657 : List U8 = CallByName TotallyNotJson.215 List.146 List.147;
|
||||
ret List.657;
|
||||
procedure List.18 (List.145, List.146, List.147):
|
||||
let List.637 : U64 = 0i64;
|
||||
let List.638 : U64 = CallByName List.6 List.145;
|
||||
let List.636 : List U8 = CallByName List.86 List.145 List.146 List.147 List.637 List.638;
|
||||
ret List.636;
|
||||
|
||||
procedure List.18 (List.142, List.143, List.144):
|
||||
let List.550 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144;
|
||||
ret List.550;
|
||||
|
||||
procedure List.18 (List.142, List.143, List.144):
|
||||
let List.618 : {List U8, U64} = CallByName List.93 List.142 List.143 List.144;
|
||||
ret List.618;
|
||||
|
||||
procedure List.18 (List.142, List.143, List.144):
|
||||
let List.638 : List U8 = CallByName List.93 List.142 List.143 List.144;
|
||||
ret List.638;
|
||||
|
||||
procedure List.26 (List.159, List.160, List.161):
|
||||
let List.706 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.93 List.159 List.160 List.161;
|
||||
let List.709 : U8 = 1i64;
|
||||
let List.710 : U8 = GetTagId List.706;
|
||||
let List.711 : Int1 = lowlevel Eq List.709 List.710;
|
||||
if List.711 then
|
||||
let List.162 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.706;
|
||||
ret List.162;
|
||||
procedure List.26 (List.172, List.173, List.174):
|
||||
let List.696 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.96 List.172 List.173 List.174;
|
||||
let List.699 : U8 = 1i64;
|
||||
let List.700 : U8 = GetTagId List.696;
|
||||
let List.701 : Int1 = lowlevel Eq List.699 List.700;
|
||||
if List.701 then
|
||||
let List.175 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.696;
|
||||
ret List.175;
|
||||
else
|
||||
let List.163 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.706;
|
||||
ret List.163;
|
||||
let List.176 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.696;
|
||||
ret List.176;
|
||||
|
||||
procedure List.31 (#Attr.2, #Attr.3):
|
||||
let List.671 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3;
|
||||
ret List.671;
|
||||
let List.661 : List Str = lowlevel ListDropAt #Attr.2 #Attr.3;
|
||||
ret List.661;
|
||||
|
||||
procedure List.38 (List.298):
|
||||
let List.679 : U64 = 0i64;
|
||||
let List.678 : List Str = CallByName List.31 List.298 List.679;
|
||||
procedure List.38 (List.311):
|
||||
let List.669 : U64 = 0i64;
|
||||
let List.668 : List Str = CallByName List.31 List.311 List.669;
|
||||
ret List.668;
|
||||
|
||||
procedure List.4 (List.116, List.117):
|
||||
let List.620 : U64 = 1i64;
|
||||
let List.619 : List Str = CallByName List.70 List.116 List.620;
|
||||
let List.618 : List Str = CallByName List.71 List.619 List.117;
|
||||
ret List.618;
|
||||
|
||||
procedure List.4 (List.116, List.117):
|
||||
let List.623 : U64 = 1i64;
|
||||
let List.622 : List U8 = CallByName List.70 List.116 List.623;
|
||||
let List.621 : List U8 = CallByName List.71 List.622 List.117;
|
||||
ret List.621;
|
||||
|
||||
procedure List.49 (List.390, List.391):
|
||||
let List.688 : U64 = StructAtIndex 0 List.391;
|
||||
let List.689 : U64 = 0i64;
|
||||
let List.686 : Int1 = CallByName Bool.11 List.688 List.689;
|
||||
if List.686 then
|
||||
dec List.390;
|
||||
let List.687 : List U8 = Array [];
|
||||
ret List.687;
|
||||
else
|
||||
let List.683 : U64 = StructAtIndex 1 List.391;
|
||||
let List.684 : U64 = StructAtIndex 0 List.391;
|
||||
let List.682 : List U8 = CallByName List.72 List.390 List.683 List.684;
|
||||
ret List.682;
|
||||
|
||||
procedure List.52 (List.405, List.406):
|
||||
let List.407 : U64 = CallByName List.6 List.405;
|
||||
joinpoint List.694 List.408:
|
||||
let List.692 : U64 = 0i64;
|
||||
let List.691 : {U64, U64} = Struct {List.408, List.692};
|
||||
inc List.405;
|
||||
let List.409 : List U8 = CallByName List.49 List.405 List.691;
|
||||
let List.690 : U64 = CallByName Num.75 List.407 List.408;
|
||||
let List.681 : {U64, U64} = Struct {List.690, List.408};
|
||||
let List.410 : List U8 = CallByName List.49 List.405 List.681;
|
||||
let List.680 : {List U8, List U8} = Struct {List.409, List.410};
|
||||
ret List.680;
|
||||
in
|
||||
let List.695 : Int1 = CallByName Num.24 List.407 List.406;
|
||||
if List.695 then
|
||||
jump List.694 List.406;
|
||||
else
|
||||
jump List.694 List.407;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.594 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.594;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.675 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.675;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.676 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.676;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.678 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.678;
|
||||
|
||||
procedure List.4 (List.113, List.114):
|
||||
let List.614 : U64 = 1i64;
|
||||
let List.613 : List Str = CallByName List.70 List.113 List.614;
|
||||
let List.612 : List Str = CallByName List.71 List.613 List.114;
|
||||
ret List.612;
|
||||
|
||||
procedure List.4 (List.113, List.114):
|
||||
let List.617 : U64 = 1i64;
|
||||
let List.616 : List U8 = CallByName List.70 List.113 List.617;
|
||||
let List.615 : List U8 = CallByName List.71 List.616 List.114;
|
||||
ret List.615;
|
||||
|
||||
procedure List.49 (List.376, List.377):
|
||||
let List.698 : U64 = StructAtIndex 0 List.377;
|
||||
let List.699 : U64 = 0i64;
|
||||
let List.696 : Int1 = CallByName Bool.11 List.698 List.699;
|
||||
if List.696 then
|
||||
dec List.376;
|
||||
let List.697 : List U8 = Array [];
|
||||
ret List.697;
|
||||
else
|
||||
let List.693 : U64 = StructAtIndex 1 List.377;
|
||||
let List.694 : U64 = StructAtIndex 0 List.377;
|
||||
let List.692 : List U8 = CallByName List.72 List.376 List.693 List.694;
|
||||
ret List.692;
|
||||
|
||||
procedure List.52 (List.391, List.392):
|
||||
let List.393 : U64 = CallByName List.6 List.391;
|
||||
joinpoint List.704 List.394:
|
||||
let List.702 : U64 = 0i64;
|
||||
let List.701 : {U64, U64} = Struct {List.394, List.702};
|
||||
inc List.391;
|
||||
let List.395 : List U8 = CallByName List.49 List.391 List.701;
|
||||
let List.700 : U64 = CallByName Num.20 List.393 List.394;
|
||||
let List.691 : {U64, U64} = Struct {List.700, List.394};
|
||||
let List.396 : List U8 = CallByName List.49 List.391 List.691;
|
||||
let List.690 : {List U8, List U8} = Struct {List.395, List.396};
|
||||
ret List.690;
|
||||
in
|
||||
let List.705 : Int1 = CallByName Num.24 List.393 List.392;
|
||||
if List.705 then
|
||||
jump List.704 List.392;
|
||||
else
|
||||
jump List.704 List.393;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.588 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.588;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.685 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.685;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.686 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.686;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.688 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.688;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.566 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.566;
|
||||
let List.574 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.574;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.634 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.634;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.654 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.654;
|
||||
let List.646 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.646;
|
||||
|
||||
procedure List.68 (#Attr.2):
|
||||
let List.681 : List Str = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.681;
|
||||
let List.671 : List Str = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.671;
|
||||
|
||||
procedure List.68 (#Attr.2):
|
||||
let List.683 : List U8 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.683;
|
||||
let List.673 : List U8 = lowlevel ListWithCapacity #Attr.2;
|
||||
ret List.673;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.594 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.594;
|
||||
let List.600 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.600;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.611 : List Str = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.611;
|
||||
let List.617 : List Str = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.617;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.592 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.592;
|
||||
let List.598 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.598;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.609 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.609;
|
||||
let List.615 : List Str = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.615;
|
||||
|
||||
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.695 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.695;
|
||||
let List.685 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.685;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.660 : List Str = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.660;
|
||||
let List.650 : List Str = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.650;
|
||||
|
||||
procedure List.8 (#Attr.2, #Attr.3):
|
||||
let List.668 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.668;
|
||||
let List.658 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
|
||||
ret List.658;
|
||||
|
||||
procedure List.80 (#Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34):
|
||||
joinpoint List.715 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.717 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.717 then
|
||||
let List.726 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.718 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.440 List.726;
|
||||
let List.723 : U8 = 1i64;
|
||||
let List.724 : U8 = GetTagId List.718;
|
||||
let List.725 : Int1 = lowlevel Eq List.723 List.724;
|
||||
if List.725 then
|
||||
let List.444 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.718;
|
||||
let List.721 : U64 = 1i64;
|
||||
let List.720 : U64 = CallByName Num.19 List.442 List.721;
|
||||
jump List.715 List.439 List.444 List.441 List.720 List.443;
|
||||
joinpoint List.705 List.453 List.454 List.455 List.456 List.457:
|
||||
let List.707 : Int1 = CallByName Num.22 List.456 List.457;
|
||||
if List.707 then
|
||||
let List.716 : U8 = CallByName List.66 List.453 List.456;
|
||||
let List.708 : [C {U64, Int1}, C {U64, Int1}] = CallByName TotallyNotJson.189 List.454 List.716;
|
||||
let List.713 : U8 = 1i64;
|
||||
let List.714 : U8 = GetTagId List.708;
|
||||
let List.715 : Int1 = lowlevel Eq List.713 List.714;
|
||||
if List.715 then
|
||||
let List.458 : {U64, Int1} = UnionAtIndex (Id 1) (Index 0) List.708;
|
||||
let List.711 : U64 = 1i64;
|
||||
let List.710 : U64 = CallByName Num.51 List.456 List.711;
|
||||
jump List.705 List.453 List.458 List.455 List.710 List.457;
|
||||
else
|
||||
dec List.439;
|
||||
let List.445 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.718;
|
||||
let List.722 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.445;
|
||||
ret List.722;
|
||||
dec List.453;
|
||||
let List.459 : {U64, Int1} = UnionAtIndex (Id 0) (Index 0) List.708;
|
||||
let List.712 : [C {U64, Int1}, C {U64, Int1}] = TagId(0) List.459;
|
||||
ret List.712;
|
||||
else
|
||||
dec List.439;
|
||||
let List.716 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.440;
|
||||
ret List.716;
|
||||
dec List.453;
|
||||
let List.706 : [C {U64, Int1}, C {U64, Int1}] = TagId(1) List.454;
|
||||
ret List.706;
|
||||
in
|
||||
jump List.715 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
|
||||
jump List.705 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34;
|
||||
|
||||
procedure List.80 (#Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40):
|
||||
joinpoint List.644 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.646 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.646 then
|
||||
let List.653 : U8 = CallByName List.66 List.439 List.442;
|
||||
let List.647 : List U8 = CallByName List.145 List.440 List.653 List.441;
|
||||
let List.650 : U64 = 1i64;
|
||||
let List.649 : U64 = CallByName Num.19 List.442 List.650;
|
||||
jump List.644 List.439 List.647 List.441 List.649 List.443;
|
||||
procedure List.86 (#Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39):
|
||||
joinpoint List.567 List.148 List.149 List.150 List.151 List.152:
|
||||
let List.569 : Int1 = CallByName Num.22 List.151 List.152;
|
||||
if List.569 then
|
||||
let List.573 : {Str, Str} = CallByName List.66 List.148 List.151;
|
||||
inc List.573;
|
||||
let List.153 : {List U8, U64} = CallByName TotallyNotJson.237 List.149 List.573 List.150;
|
||||
let List.572 : U64 = 1i64;
|
||||
let List.571 : U64 = CallByName Num.51 List.151 List.572;
|
||||
jump List.567 List.148 List.153 List.150 List.571 List.152;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
dec List.148;
|
||||
ret List.149;
|
||||
in
|
||||
jump List.644 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40;
|
||||
jump List.567 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39;
|
||||
|
||||
procedure List.80 (#Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45):
|
||||
joinpoint List.624 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.626 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.626 then
|
||||
let List.633 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
procedure List.86 (#Derived_gen.46, #Derived_gen.47, #Derived_gen.48, #Derived_gen.49, #Derived_gen.50):
|
||||
joinpoint List.639 List.148 List.149 List.150 List.151 List.152:
|
||||
let List.641 : Int1 = CallByName Num.22 List.151 List.152;
|
||||
if List.641 then
|
||||
let List.645 : U8 = CallByName List.66 List.148 List.151;
|
||||
let List.153 : List U8 = CallByName TotallyNotJson.215 List.149 List.645;
|
||||
let List.644 : U64 = 1i64;
|
||||
let List.643 : U64 = CallByName Num.51 List.151 List.644;
|
||||
jump List.639 List.148 List.153 List.150 List.643 List.152;
|
||||
else
|
||||
dec List.148;
|
||||
ret List.149;
|
||||
in
|
||||
jump List.639 #Derived_gen.46 #Derived_gen.47 #Derived_gen.48 #Derived_gen.49 #Derived_gen.50;
|
||||
|
||||
procedure List.86 (#Derived_gen.52, #Derived_gen.53, #Derived_gen.54, #Derived_gen.55, #Derived_gen.56):
|
||||
joinpoint List.627 List.148 List.149 List.150 List.151 List.152:
|
||||
let List.629 : Int1 = CallByName Num.22 List.151 List.152;
|
||||
if List.629 then
|
||||
let List.633 : {Str, Str} = CallByName List.66 List.148 List.151;
|
||||
inc List.633;
|
||||
let List.627 : {List U8, U64} = CallByName List.145 List.440 List.633 List.441;
|
||||
let List.630 : U64 = 1i64;
|
||||
let List.629 : U64 = CallByName Num.19 List.442 List.630;
|
||||
jump List.624 List.439 List.627 List.441 List.629 List.443;
|
||||
let List.153 : {List U8, U64} = CallByName TotallyNotJson.237 List.149 List.633 List.150;
|
||||
let List.632 : U64 = 1i64;
|
||||
let List.631 : U64 = CallByName Num.51 List.151 List.632;
|
||||
jump List.627 List.148 List.153 List.150 List.631 List.152;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
dec List.148;
|
||||
ret List.149;
|
||||
in
|
||||
jump List.624 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45;
|
||||
jump List.627 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56;
|
||||
|
||||
procedure List.80 (#Derived_gen.52, #Derived_gen.53, #Derived_gen.54, #Derived_gen.55, #Derived_gen.56):
|
||||
joinpoint List.556 List.439 List.440 List.441 List.442 List.443:
|
||||
let List.558 : Int1 = CallByName Num.22 List.442 List.443;
|
||||
if List.558 then
|
||||
let List.565 : {Str, Str} = CallByName List.66 List.439 List.442;
|
||||
inc List.565;
|
||||
let List.559 : {List U8, U64} = CallByName List.145 List.440 List.565 List.441;
|
||||
let List.562 : U64 = 1i64;
|
||||
let List.561 : U64 = CallByName Num.19 List.442 List.562;
|
||||
jump List.556 List.439 List.559 List.441 List.561 List.443;
|
||||
else
|
||||
dec List.439;
|
||||
ret List.440;
|
||||
in
|
||||
jump List.556 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.554 : U64 = 0i64;
|
||||
let List.555 : U64 = CallByName List.6 List.436;
|
||||
let List.553 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.554 List.555;
|
||||
ret List.553;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.622 : U64 = 0i64;
|
||||
let List.623 : U64 = CallByName List.6 List.436;
|
||||
let List.621 : {List U8, U64} = CallByName List.80 List.436 List.437 List.438 List.622 List.623;
|
||||
ret List.621;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.642 : U64 = 0i64;
|
||||
let List.643 : U64 = CallByName List.6 List.436;
|
||||
let List.641 : List U8 = CallByName List.80 List.436 List.437 List.438 List.642 List.643;
|
||||
ret List.641;
|
||||
|
||||
procedure List.93 (List.436, List.437, List.438):
|
||||
let List.713 : U64 = 0i64;
|
||||
let List.714 : U64 = CallByName List.6 List.436;
|
||||
let List.712 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.436 List.437 List.438 List.713 List.714;
|
||||
ret List.712;
|
||||
procedure List.96 (List.450, List.451, List.452):
|
||||
let List.703 : U64 = 0i64;
|
||||
let List.704 : U64 = CallByName List.6 List.450;
|
||||
let List.702 : [C {U64, Int1}, C {U64, Int1}] = CallByName List.80 List.450 List.451 List.452 List.703 List.704;
|
||||
ret List.702;
|
||||
|
||||
procedure Num.127 (#Attr.2):
|
||||
let Num.307 : U8 = lowlevel NumIntCast #Attr.2;
|
||||
ret Num.307;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.316 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.316;
|
||||
let Num.311 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.311;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.320 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.320;
|
||||
let Num.308 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.308;
|
||||
|
||||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.313 : U64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
|
@ -356,6 +332,14 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
let Num.321 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||
ret Num.321;
|
||||
|
||||
procedure Num.51 (#Attr.2, #Attr.3):
|
||||
let Num.316 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3;
|
||||
ret Num.316;
|
||||
|
||||
procedure Num.75 (#Attr.2, #Attr.3):
|
||||
let Num.320 : U64 = lowlevel NumSubWrap #Attr.2 #Attr.3;
|
||||
ret Num.320;
|
||||
|
||||
procedure Num.94 (#Attr.2, #Attr.3):
|
||||
let Num.312 : U64 = lowlevel NumDivCeilUnchecked #Attr.2 #Attr.3;
|
||||
ret Num.312;
|
||||
|
|
@ -388,8 +372,8 @@ procedure Str.9 (Str.79):
|
|||
else
|
||||
let Str.291 : U8 = StructAtIndex 3 Str.80;
|
||||
let Str.292 : U64 = StructAtIndex 0 Str.80;
|
||||
let #Derived_gen.57 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.57;
|
||||
let #Derived_gen.58 : Str = StructAtIndex 1 Str.80;
|
||||
dec #Derived_gen.58;
|
||||
let Str.290 : {U64, U8} = Struct {Str.292, Str.291};
|
||||
let Str.289 : [C {U64, U8}, C Str] = TagId(0) Str.290;
|
||||
ret Str.289;
|
||||
|
|
@ -1019,8 +1003,8 @@ procedure TotallyNotJson.102 (TotallyNotJson.852):
|
|||
else
|
||||
let TotallyNotJson.1691 : Str = "Z";
|
||||
let TotallyNotJson.1692 : Int1 = lowlevel Eq TotallyNotJson.1691 TotallyNotJson.852;
|
||||
dec TotallyNotJson.852;
|
||||
dec TotallyNotJson.1691;
|
||||
dec TotallyNotJson.852;
|
||||
if TotallyNotJson.1692 then
|
||||
let TotallyNotJson.1689 : Int1 = CallByName Bool.2;
|
||||
ret TotallyNotJson.1689;
|
||||
|
|
@ -1093,10 +1077,6 @@ procedure TotallyNotJson.189 (TotallyNotJson.1930, TotallyNotJson.192):
|
|||
ret TotallyNotJson.1955;
|
||||
|
||||
|
||||
procedure TotallyNotJson.2 ():
|
||||
let TotallyNotJson.1172 : [C , C [], C , C , C , C ] = TagId(2) ;
|
||||
ret TotallyNotJson.1172;
|
||||
|
||||
procedure TotallyNotJson.215 (TotallyNotJson.216, TotallyNotJson.217):
|
||||
let TotallyNotJson.1901 : List U8 = CallByName TotallyNotJson.27 TotallyNotJson.217;
|
||||
let TotallyNotJson.1900 : List U8 = CallByName List.8 TotallyNotJson.216 TotallyNotJson.1901;
|
||||
|
|
@ -1300,6 +1280,10 @@ procedure TotallyNotJson.29 (TotallyNotJson.233):
|
|||
let TotallyNotJson.1526 : List {Str, Str} = CallByName Encode.23 TotallyNotJson.233;
|
||||
ret TotallyNotJson.1526;
|
||||
|
||||
procedure TotallyNotJson.8 ():
|
||||
let TotallyNotJson.1172 : [C , C [], C , C , C , C ] = TagId(2) ;
|
||||
ret TotallyNotJson.1172;
|
||||
|
||||
procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
|
||||
let TotallyNotJson.1873 : U8 = GetTagId TotallyNotJson.803;
|
||||
switch TotallyNotJson.1873:
|
||||
|
|
@ -1331,14 +1315,14 @@ procedure TotallyNotJson.82 (TotallyNotJson.802, TotallyNotJson.803):
|
|||
|
||||
procedure TotallyNotJson.832 (TotallyNotJson.1493):
|
||||
let TotallyNotJson.1845 : List Str = StructAtIndex 1 TotallyNotJson.1493;
|
||||
let #Derived_gen.58 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.58;
|
||||
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1493;
|
||||
dec #Derived_gen.59;
|
||||
ret TotallyNotJson.1845;
|
||||
|
||||
procedure TotallyNotJson.840 (TotallyNotJson.1214):
|
||||
let TotallyNotJson.1566 : List Str = StructAtIndex 1 TotallyNotJson.1214;
|
||||
let #Derived_gen.59 : List Str = StructAtIndex 0 TotallyNotJson.1214;
|
||||
dec #Derived_gen.59;
|
||||
let #Derived_gen.57 : List Str = StructAtIndex 0 TotallyNotJson.1214;
|
||||
dec #Derived_gen.57;
|
||||
ret TotallyNotJson.1566;
|
||||
|
||||
procedure TotallyNotJson.87 (TotallyNotJson.809):
|
||||
|
|
@ -1389,11 +1373,11 @@ procedure TotallyNotJson.95 (TotallyNotJson.829):
|
|||
let TotallyNotJson.1840 : List Str = CallByName TotallyNotJson.832 TotallyNotJson.1842;
|
||||
let TotallyNotJson.1841 : Str = "";
|
||||
let TotallyNotJson.1839 : Str = CallByName Str.4 TotallyNotJson.1840 TotallyNotJson.1841;
|
||||
dec TotallyNotJson.1840;
|
||||
dec TotallyNotJson.1841;
|
||||
dec TotallyNotJson.1840;
|
||||
ret TotallyNotJson.1839;
|
||||
|
||||
procedure TotallyNotJson.96 (#Derived_gen.29):
|
||||
procedure TotallyNotJson.96 (#Derived_gen.51):
|
||||
joinpoint TotallyNotJson.1847 TotallyNotJson.1168:
|
||||
let TotallyNotJson.834 : List Str = StructAtIndex 0 TotallyNotJson.1168;
|
||||
let TotallyNotJson.833 : List Str = StructAtIndex 1 TotallyNotJson.1168;
|
||||
|
|
@ -1429,7 +1413,7 @@ procedure TotallyNotJson.96 (#Derived_gen.29):
|
|||
let TotallyNotJson.1848 : {List Str, List Str} = Struct {TotallyNotJson.834, TotallyNotJson.833};
|
||||
ret TotallyNotJson.1848;
|
||||
in
|
||||
jump TotallyNotJson.1847 #Derived_gen.29;
|
||||
jump TotallyNotJson.1847 #Derived_gen.51;
|
||||
|
||||
procedure TotallyNotJson.97 (TotallyNotJson.837):
|
||||
let TotallyNotJson.838 : List Str = CallByName Str.55 TotallyNotJson.837;
|
||||
|
|
@ -1446,7 +1430,7 @@ procedure TotallyNotJson.97 (TotallyNotJson.837):
|
|||
dec TotallyNotJson.1562;
|
||||
ret TotallyNotJson.1560;
|
||||
|
||||
procedure TotallyNotJson.98 (#Derived_gen.35):
|
||||
procedure TotallyNotJson.98 (#Derived_gen.29):
|
||||
joinpoint TotallyNotJson.1568 TotallyNotJson.1169:
|
||||
let TotallyNotJson.842 : List Str = StructAtIndex 0 TotallyNotJson.1169;
|
||||
let TotallyNotJson.841 : List Str = StructAtIndex 1 TotallyNotJson.1169;
|
||||
|
|
@ -1482,11 +1466,11 @@ procedure TotallyNotJson.98 (#Derived_gen.35):
|
|||
let TotallyNotJson.1569 : {List Str, List Str} = Struct {TotallyNotJson.842, TotallyNotJson.841};
|
||||
ret TotallyNotJson.1569;
|
||||
in
|
||||
jump TotallyNotJson.1568 #Derived_gen.35;
|
||||
jump TotallyNotJson.1568 #Derived_gen.29;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.12 : Str = "bar";
|
||||
let Test.10 : [C , C [], C , C , C , C ] = CallByName TotallyNotJson.2;
|
||||
let Test.10 : [C , C [], C , C , C , C ] = CallByName TotallyNotJson.8;
|
||||
let Test.8 : List U8 = CallByName Encode.26 Test.12 Test.10;
|
||||
let Test.1 : [C {U64, U8}, C Str] = CallByName Str.9 Test.8;
|
||||
let Test.5 : U8 = 1i64;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue