Merge remote-tracking branch 'origin/main' into expect-print-values

This commit is contained in:
Folkert 2022-12-08 23:42:03 +01:00
commit 13d0b75bc1
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
124 changed files with 5745 additions and 2274 deletions

View file

@ -24,10 +24,12 @@ ROC_WORKSPACE_DIR = { value = "", relative = true }
# Set = "1" to turn a debug flag on. # Set = "1" to turn a debug flag on.
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0" ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
ROC_PRINT_UNIFICATIONS = "0" ROC_PRINT_UNIFICATIONS = "0"
ROC_PRINT_UNDERIVABLE = "0"
ROC_TRACE_COMPACTION = "0" ROC_TRACE_COMPACTION = "0"
ROC_PRINT_UNIFICATIONS_DERIVED = "0" ROC_PRINT_UNIFICATIONS_DERIVED = "0"
ROC_PRINT_MISMATCHES = "0" ROC_PRINT_MISMATCHES = "0"
ROC_VERIFY_RIGID_LET_GENERALIZED = "0" ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
ROC_CHECK_MONO_IR = "0"
ROC_PRINT_IR_AFTER_SPECIALIZATION = "0" ROC_PRINT_IR_AFTER_SPECIALIZATION = "0"
ROC_PRINT_IR_AFTER_RESET_REUSE = "0" ROC_PRINT_IR_AFTER_RESET_REUSE = "0"
ROC_PRINT_IR_AFTER_REFCOUNT = "0" ROC_PRINT_IR_AFTER_REFCOUNT = "0"

View file

@ -33,24 +33,16 @@ jobs:
# build has to be done before tests #2572 # build has to be done before tests #2572
- name: build release - name: build release
uses: actions-rs/cargo@v1 run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
with: # target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
command: build
args: --release --locked
- name: execute rust tests if macos 12 - name: execute rust tests if macos 12
if: endsWith(matrix.os, '12') if: endsWith(matrix.os, '12')
uses: actions-rs/cargo@v1 run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal
with:
command: test
args: --release --locked -- --skip opaque_wrap_function --skip bool_list_literal
- name: execute rust tests if macos 11 - name: execute rust tests if macos 11
if: endsWith(matrix.os, '11') if: endsWith(matrix.os, '11')
uses: actions-rs/cargo@v1 run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
with:
command: test
args: --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine # swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
# this issue may be caused by using older versions of XCode # this issue may be caused by using older versions of XCode

View file

@ -19,6 +19,9 @@ jobs:
with: with:
clean: "true" clean: "true"
- name: test building default.nix
run: nix-build
- name: execute tests with --release - name: execute tests with --release
run: nix develop -c cargo test --locked --release run: nix develop -c cargo test --locked --release

View file

@ -31,6 +31,12 @@ jobs:
- name: execute tests with --release - name: execute tests with --release
run: nix develop -c cargo test --locked --release run: nix develop -c cargo test --locked --release
- name: make a libapp.so for the next step
run: nix develop -c cargo run -- gen-stub-lib examples/platform-switching/rocLovesRust.roc
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked
- name: test launching the editor - 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: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs

View file

@ -29,3 +29,9 @@ jobs:
- name: execute cli_run tests only, the full tests take too long but are run nightly - name: execute cli_run tests only, the full tests take too long but are run nightly
run: nix develop -c cargo test --locked --release -p roc_cli run: nix develop -c cargo test --locked --release -p roc_cli
- name: make a libapp.so for the next step
run: nix develop -c cargo run -- gen-stub-lib examples/platform-switching/rocLovesRust.roc
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && nix develop -c cargo test --release --locked

View file

@ -8,6 +8,7 @@ jobs:
test-nightly: test-nightly:
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04 name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ macos-11, macos-12, ubuntu-20.04, ubuntu-22.04 ] os: [ macos-11, macos-12, ubuntu-20.04, ubuntu-22.04 ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}

View file

@ -34,7 +34,10 @@ jobs:
run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh
- name: regular rust tests - name: regular rust tests
run: cargo test --locked --release --features with_sound serde --workspace && sccache --show-stats run: cargo test --locked --release && sccache --show-stats
- name: check that the platform`s produced dylib is loadable
run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked
- name: test launching the editor - 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: cargo test --release --locked editor_launch_test::launch -- --ignored # `--ignored` to run this test that is ignored for "normal" runs

3
.gitignore vendored
View file

@ -67,3 +67,6 @@ roc_linux_x86_64.tar.gz
*.roc-format-failed *.roc-format-failed
*.roc-format-failed-ast-after *.roc-format-failed-ast-after
*.roc-format-failed-ast-before *.roc-format-failed-ast-before
# nix
result

14
Cargo.lock generated
View file

@ -279,9 +279,9 @@ dependencies = [
[[package]] [[package]]
name = "blake3" name = "blake3"
version = "1.3.1" version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"arrayvec 0.7.2", "arrayvec 0.7.2",
@ -719,9 +719,9 @@ dependencies = [
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.1.5" version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
[[package]] [[package]]
name = "copyless" name = "copyless"
@ -1982,7 +1982,7 @@ checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
[[package]] [[package]]
name = "inkwell" name = "inkwell"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/roc-lang/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" source = "git+https://github.com/roc-lang/inkwell?branch=master#9b63d543eaf996aa91fdeb20a2bc8b8558775648"
dependencies = [ dependencies = [
"either", "either",
"inkwell_internals", "inkwell_internals",
@ -1995,7 +1995,7 @@ dependencies = [
[[package]] [[package]]
name = "inkwell_internals" name = "inkwell_internals"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/roc-lang/inkwell?branch=master#accd406858a40ca2a1463ff77d79f3c5e4c96f4e" source = "git+https://github.com/roc-lang/inkwell?branch=master#9b63d543eaf996aa91fdeb20a2bc8b8558775648"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3880,6 +3880,7 @@ dependencies = [
"roc_collections", "roc_collections",
"roc_error_macros", "roc_error_macros",
"roc_module", "roc_module",
"roc_problem",
"roc_region", "roc_region",
] ]
@ -4247,6 +4248,7 @@ dependencies = [
"roc_mono", "roc_mono",
"roc_packaging", "roc_packaging",
"roc_parse", "roc_parse",
"roc_problem",
"roc_region", "roc_region",
"roc_reporting", "roc_reporting",
"roc_std", "roc_std",

10
FAQ.md
View file

@ -140,7 +140,7 @@ and `Optional` (like in Java).
By design, Roc does not have one of these. There are several reasons for this. By design, Roc does not have one of these. There are several reasons for this.
First, if a function returns a potential error, Roc has the convention to use `Result` with an error type that First, if a function returns a potential error, Roc has the convention to use `Result` with an error type that
has a single tag describing what went wrong. (For example, `List.first : List a -> Result a [ListWasEmpty]*` has a single tag describing what went wrong. (For example, `List.first : List a -> Result a [ListWasEmpty]`
instead of `List.first : List a -> Maybe a`.) This is not only more self-descriptive, it also composes better with instead of `List.first : List a -> Maybe a`.) This is not only more self-descriptive, it also composes better with
other operations that can fail; there's no need to have functions like `Result.toMaybe` or `Maybe.toResult`, other operations that can fail; there's no need to have functions like `Result.toMaybe` or `Maybe.toResult`,
because in Roc, the convention is that operations that can fail always use `Result`. because in Roc, the convention is that operations that can fail always use `Result`.
@ -170,7 +170,7 @@ for using `Maybe` even when it's less self-descriptive), we'd have to rewrite al
helper functions. As such, a subtle downside of these helper functions is that they discourage any change to helper functions. As such, a subtle downside of these helper functions is that they discourage any change to
the data model that would break their call sites, even if that change would improve the data model overall. the data model that would break their call sites, even if that change would improve the data model overall.
On a historical note, `Maybe` may have been thought of as a substitute for null references—as opposed to something that emerged organically based on specific motivating use cases after `Result` already existed. That said, in languages that do not have an equivalent of Roc's tag unions, it's much less ergonomic to write something like `Result a [ListWasEmpty]*`, so that design would not fit those languages as well as it fits Roc. On a historical note, `Maybe` may have been thought of as a substitute for null references—as opposed to something that emerged organically based on specific motivating use cases after `Result` already existed. That said, in languages that do not have an equivalent of Roc's tag unions, it's much less ergonomic to write something like `Result a [ListWasEmpty]`, so that design would not fit those languages as well as it fits Roc.
## Why doesn't Roc have higher-kinded polymorphism or arbitrary-rank types? ## Why doesn't Roc have higher-kinded polymorphism or arbitrary-rank types?
@ -204,9 +204,7 @@ No implementation of Rank-2 types can remove any of these downsides. Thus far, w
with sufficiently nice APIs that only require Rank-1 types, and we haven't seen a really compelling use case with sufficiently nice APIs that only require Rank-1 types, and we haven't seen a really compelling use case
where the gap between the Rank-2 and Rank-1 designs was big enough to justify switching to Rank-2. where the gap between the Rank-2 and Rank-1 designs was big enough to justify switching to Rank-2.
Since I prefer Roc being simpler and having a faster compiler with nicer error messages, my hope is that Roc As such, the plan is for Roc to stick with Rank-1 types indefinitely. In Roc's case, the benefits of Rank-1's faster compilation with nicer error messages and a simpler type system outweigh Rank-2's benefits of expanded API options.
will never get Rank-2 types. However, it may turn out that in the future we learn about currently-unknown
upsides that somehow outweigh these downsides, so I'm open to considering the possibility - while rooting against it.
### Higher-kinded polymorphism ### Higher-kinded polymorphism
@ -289,8 +287,6 @@ Roc also has a different standard library from Elm. Some of the differences come
- `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. - `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 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 `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 `Debug.log` - the editor can do a better job at this, or you can write `expect x != x` to see what `x` is when the expectation fails. Using the editor means your code doesn't change, and using `expect` gives a natural reminder to remove the debugging code before shipping: the build will fail.
- No `Debug.todo` - instead you can write a type annotation with no implementation below it; the type checker will treat it normally, but attempting to use the value will cause a runtime exception. This is a feature I've often wanted in Elm, because I like prototyping APIs by writing out the types only, but then when I want the compiler to type-check them for me, I end up having to add `Debug.todo` in various places.
- No `Maybe`. See the "Why doesn't Roc have a `Maybe`/`Option`/`Optional` type" FAQ question - No `Maybe`. See the "Why doesn't Roc have a `Maybe`/`Option`/`Optional` type" FAQ question
## Why aren't Roc functions curried by default? ## Why aren't Roc functions curried by default?

View file

@ -3,5 +3,11 @@
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -euxo pipefail set -euxo pipefail
# print all found executable files in the dir examples
fd --type executable . './examples'
# remove all executables from the dir examples
fd --type executable . './examples' -X rm
cp target/release/roc ./roc # to be able to exclude "target" later in the tar command cp target/release/roc ./roc # to be able to exclude "target" later in the tar command
tar -czvf $1 --exclude="target" --exclude="zig-cache" roc LICENSE LEGAL_DETAILS examples/helloWorld.roc examples/platform-switching examples/cli crates/roc_std tar -czvf $1 --exclude="target" --exclude="zig-cache" --exclude='*.o' roc LICENSE LEGAL_DETAILS examples/helloWorld.roc examples/platform-switching examples/cli crates/roc_std crates/compiler/builtins/bitcode/src

View file

@ -359,8 +359,9 @@ pub fn test(_matches: &ArgMatches, _triple: Triple) -> io::Result<i32> {
#[cfg(not(windows))] #[cfg(not(windows))]
pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> { pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
use roc_build::program::report_problems_monomorphized;
use roc_gen_llvm::llvm::build::LlvmBackendMode; use roc_gen_llvm::llvm::build::LlvmBackendMode;
use roc_load::{ExecutionMode, LoadConfig}; use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
use roc_packaging::cache; use roc_packaging::cache;
use roc_target::TargetInfo; use roc_target::TargetInfo;
@ -425,18 +426,26 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
threading, threading,
exec_mode: ExecutionMode::Test, exec_mode: ExecutionMode::Test,
}; };
let loaded = roc_load::load_and_monomorphize( let load_result = roc_load::load_and_monomorphize(
arena, arena,
path.to_path_buf(), path.to_path_buf(),
subs_by_module, subs_by_module,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()), RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
load_config, load_config,
) );
.unwrap();
let mut loaded = match load_result {
Ok(loaded) => loaded,
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
return handle_loading_problem(problem);
}
Err(LoadMonomorphizedError::ErrorModule(module)) => {
return handle_error_module(module, start_time.elapsed(), filename, false);
}
};
let problems = report_problems_monomorphized(&mut loaded);
let mut loaded = loaded;
let mut expectations = std::mem::take(&mut loaded.expectations); let mut expectations = std::mem::take(&mut loaded.expectations);
let loaded = loaded;
let interns = loaded.interns.clone(); let interns = loaded.interns.clone();
@ -449,6 +458,19 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
) )
.unwrap(); .unwrap();
// Print warnings before running tests.
{
debug_assert_eq!(
problems.errors, 0,
"if there were errors, we would have already exited."
);
if problems.warnings > 0 {
print_problems(problems, start_time.elapsed());
println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "".repeat(80));
}
}
// Run the tests.
let arena = &bumpalo::Bump::new(); let arena = &bumpalo::Bump::new();
let interns = arena.alloc(interns); let interns = arena.alloc(interns);
@ -733,35 +755,51 @@ pub fn build(
} }
} }
} }
Err(BuildFileError::ErrorModule { Err(BuildFileError::ErrorModule { module, total_time }) => {
mut module, handle_error_module(module, total_time, filename, true)
total_time, }
}) => { Err(BuildFileError::LoadingProblem(problem)) => handle_loading_problem(problem),
}
}
fn handle_error_module(
mut module: roc_load::LoadedModule,
total_time: std::time::Duration,
filename: &OsStr,
print_run_anyway_hint: bool,
) -> io::Result<i32> {
debug_assert!(module.total_problems() > 0); debug_assert!(module.total_problems() > 0);
let problems = roc_build::program::report_problems_typechecked(&mut module); let problems = roc_build::program::report_problems_typechecked(&mut module);
print_problems(problems, total_time); print_problems(problems, total_time);
print!(".\n\nYou can run the program anyway with \x1B[32mroc run"); if print_run_anyway_hint {
// If you're running "main.roc" then you can just do `roc run` // If you're running "main.roc" then you can just do `roc run`
// to re-run the program. // to re-run the program.
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
if filename != DEFAULT_ROC_FILENAME { if filename != DEFAULT_ROC_FILENAME {
print!(" {}", &filename.to_string_lossy()); print!(" {}", &filename.to_string_lossy());
} }
println!("\x1B[39m"); println!("\x1B[39m");
}
Ok(problems.exit_code()) Ok(problems.exit_code())
} }
Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
print!("{}", report);
fn handle_loading_problem(problem: LoadingProblem) -> io::Result<i32> {
match problem {
LoadingProblem::FormattedReport(report) => {
print!("{}", report);
Ok(1) Ok(1)
} }
Err(other) => { _ => {
panic!("build_file failed with error:\n{:?}", other); // TODO: tighten up the types here, we should always end up with a
// formatted report from load.
print!("Failed with error: {:?}", problem);
Ok(1)
} }
} }
} }

View file

@ -840,13 +840,25 @@ mod cli_run {
let file_name = cli_testing_dir("benchmarks").join(roc_filename); let file_name = cli_testing_dir("benchmarks").join(roc_filename);
// TODO fix QuicksortApp and then remove this! // TODO fix QuicksortApp and then remove this!
if roc_filename == "QuicksortApp.roc" { match roc_filename {
"QuicksortApp.roc" => {
eprintln!( eprintln!(
"WARNING: skipping testing benchmark {} because the test is broken right now!", "WARNING: skipping testing benchmark {} because the test is broken right now!",
roc_filename roc_filename
); );
return; return;
} }
"TestAStar.roc" => {
if cfg!(feature = "wasm32-cli-run") {
eprintln!(
"WARNING: skipping testing benchmark {} because it currently does not work on wasm32 due to dictionaries.",
roc_filename
);
return;
}
}
_ => {}
}
#[cfg(all(not(feature = "wasm32-cli-run"), not(feature = "i386-cli-run")))] #[cfg(all(not(feature = "wasm32-cli-run"), not(feature = "i386-cli-run")))]
check_output_regular( check_output_regular(

View file

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

View file

@ -4,6 +4,8 @@ const UpdateMode = utils.UpdateMode;
const mem = std.mem; const mem = std.mem;
const math = std.math; const math = std.math;
const expect = std.testing.expect;
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool; const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8; const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8;
const Opaque = ?[*]u8; const Opaque = ?[*]u8;
@ -770,7 +772,7 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
// This first call must use mem.copy because the slices might overlap. // This first call must use mem.copy because the slices might overlap.
const byte_count_a = list_a.len() * element_width; const byte_count_a = list_a.len() * element_width;
const byte_count_b = list_b.len() * element_width; const byte_count_b = list_b.len() * element_width;
mem.copy(u8, source_b[byte_count_a .. byte_count_a + byte_count_b], source_b[0..byte_count_b]); mem.copyBackwards(u8, source_b[byte_count_a .. byte_count_a + byte_count_b], source_b[0..byte_count_b]);
@memcpy(source_b, source_a, byte_count_a); @memcpy(source_b, source_a, byte_count_a);
// decrement list a. // decrement list a.
@ -854,3 +856,21 @@ pub fn listIsUnique(
) callconv(.C) bool { ) callconv(.C) bool {
return list.isEmpty() or list.isUnique(); return list.isEmpty() or list.isUnique();
} }
test "listConcat: non-unique with unique overlapping" {
var nonUnique = RocList.fromSlice(u8, ([_]u8{1})[0..]);
var bytes: [*]u8 = @ptrCast([*]u8, nonUnique.bytes);
const ptr_width = @sizeOf(usize);
const refcount_ptr = @ptrCast([*]isize, @alignCast(ptr_width, bytes) - ptr_width);
utils.increfC(&refcount_ptr[0], 1);
defer nonUnique.deinit(u8); // listConcat will dec the other refcount
var unique = RocList.fromSlice(u8, ([_]u8{ 2, 3, 4 })[0..]);
defer unique.deinit(u8);
var concatted = listConcat(nonUnique, unique, 1, 1);
var wanted = RocList.fromSlice(u8, ([_]u8{ 1, 2, 3, 4 })[0..]);
defer wanted.deinit(u8);
try expect(concatted.eql(wanted));
}

View file

@ -4,13 +4,18 @@ interface Dict
empty, empty,
withCapacity, withCapacity,
single, single,
get, clear,
walk, capacity,
insert,
len, len,
get,
contains,
insert,
remove, remove,
update, update,
contains, walk,
walkUntil,
toList,
fromList,
keys, keys,
values, values,
insertAll, insertAll,
@ -22,8 +27,8 @@ interface Dict
Result.{ Result }, Result.{ Result },
List, List,
Str, Str,
Num.{ Nat, U64, U8 }, Num.{ Nat, U64, U8, I8 },
Hash.{ Hasher }, Hash.{ Hasher, Hash },
] ]
## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you ## A [dictionary](https://en.wikipedia.org/wiki/Associative_array) that lets you
@ -74,45 +79,103 @@ interface Dict
## does. It removes an element and moves the most recent insertion into the ## does. It removes an element and moves the most recent insertion into the
## vacated spot. ## vacated spot.
## ##
## This move is done as a performance optimization, and it lets [Dict.remove] ## This move is done as a performance optimization, and it lets [remove] have
## have [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time). ## [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time). ##
## ##
## ### Equality ## Dict is inspired by [IndexMap](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html).
## ## The internal implementation of a dictionary is similar to [absl::flat_hash_map](https://abseil.io/docs/cpp/guides/container).
## Two dictionaries are equal when their contents and orderings match. This ## It has a list of keys value pairs that is ordered based on insertion.
## means that when `dict1 == dict2`, the expression `fn dict1 == fn dict2` will ## It uses a list of indices into the data as the backing of a hash map.
## also evaluate to `Bool.true`. The function `fn` can count on the ordering of Dict k v := {
## values in the dictionary to also match. # TODO: Add hashflooding ordered map fall back.
Dict k v := List [Pair k v] has [Eq] # TODO: Add Groups and SIMD h1 key comparison (initial tests where slower, but with proper SIMD should be fast).
# 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?
metadata : List I8,
dataIndices : List Nat,
data : List (T k v),
size : Nat,
} | k has Hash & Eq
## Return an empty dictionary. ## Return an empty dictionary.
empty : Dict k v empty : Dict k v | k has Hash & Eq
empty = @Dict [] empty =
@Dict {
metadata: List.repeat emptySlot 8,
dataIndices: List.repeat 0 8,
data: [],
size: 0,
}
## Returns the max number of elements the dictionary can hold before requiring a rehash.
capacity : Dict k v -> Nat | k has Hash & Eq
capacity = \@Dict { dataIndices } ->
cap = List.len dataIndices
cap - Num.shiftRightZfBy cap 3
## Return a dictionary with space allocated for a number of entries. This ## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimisation if you know how many entries will be ## may provide a performance optimisation if you know how many entries will be
## inserted. ## inserted.
withCapacity : Nat -> Dict k v withCapacity : Nat -> Dict k v | k has Hash & Eq
withCapacity = \n -> @Dict (List.withCapacity n) withCapacity = \_ ->
# TODO: power of 2 * 8 and actual implementation
empty
## Get the value for a given key. If there is a value for the specified key it ## Returns a dictionary containing the key and value provided as input.
## will return [Ok value], otherwise return [Err KeyNotFound].
## ##
## dictionary = ## expect
## 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 ->
insert empty k v
## Returns dictionary with the keys and values specified by the input [List].
##
## expect
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Bool.isEq (Dict.fromList [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"])
fromList : List (T k v) -> Dict k v | k has 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, T k v -> insert dict k v)
## Returns the number of values in the dictionary.
##
## expect
## Dict.empty ## Dict.empty
## |> Dict.insert 1 "Apple" ## |> Dict.insert "One" "A Song"
## |> Dict.insert 2 "Orange" ## |> Dict.insert "Two" "Candy Canes"
## ## |> Dict.insert "Three" "Boughs of Holly"
## expect Dict.get dictionary 1 == Ok "Apple" ## |> Dict.len
## expect Dict.get dictionary 2000 == Err KeyNotFound ## |> Bool.isEq 3
get : Dict k v, k -> Result v [KeyNotFound] | k has Eq len : Dict k v -> Nat | k has Hash & Eq
get = \@Dict list, needle -> len = \@Dict { size } ->
when List.findFirst list (\Pair key _ -> key == needle) is size
Ok (Pair _ v) ->
Ok v
Err NotFound -> ## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
Err KeyNotFound clear : Dict k v -> Dict k v | k has Hash & Eq
clear = \@Dict { metadata, dataIndices, data } ->
cap = List.len dataIndices
# Only clear large allocations.
if cap > 128 * 8 then
empty
else
@Dict {
metadata: List.map metadata (\_ -> emptySlot),
# just leave data indicies as garbage, no need to clear.
dataIndices,
# use takeFirst to keep around the capacity.
data: List.takeFirst data 0,
size: 0,
}
## Iterate through the keys and values in the dictionary and call the provided ## Iterate through the keys and values in the dictionary and call the provided
## function with signature `state, k, v -> state` for each value, with an ## function with signature `state, k, v -> state` for each value, with an
@ -124,9 +187,78 @@ get = \@Dict list, needle ->
## |> Dict.insert "Orange" 24 ## |> Dict.insert "Orange" 24
## |> Dict.walk 0 (\count, _, qty -> count + qty) ## |> Dict.walk 0 (\count, _, qty -> count + qty)
## |> Bool.isEq 36 ## |> Bool.isEq 36
walk : Dict k v, state, (state, k, v -> state) -> state walk : Dict k v, state, (state, k, v -> state) -> state | k has Hash & Eq
walk = \@Dict list, initialState, transform -> walk = \@Dict { data }, initialState, transform ->
List.walk list initialState (\state, Pair k v -> transform state k v) List.walk data initialState (\state, T k v -> transform state k v)
## Same as [Dict.walk], except you can stop walking early.
##
## ## Performance Details
##
## Compared to [Dict.walk], this can potentially visit fewer elements (which can
## improve performance) at the cost of making each step take longer.
## However, the added cost to each step is extremely small, and can easily
## be outweighed if it results in skipping even a small number of elements.
##
## As such, it is typically better for performance to use this over [Dict.walk]
## if returning `Break` earlier than the last element is expected to be common.
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state | k has Hash & Eq
walkUntil = \@Dict { data }, initialState, transform ->
List.walkUntil data initialState (\state, T k v -> transform state k v)
## Get the value for a given key. If there is a value for the specified key it
## will return [Ok value], otherwise return [Err KeyNotFound].
##
## dictionary =
## Dict.empty
## |> Dict.insert 1 "Apple"
## |> Dict.insert 2 "Orange"
##
## 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 { metadata, dataIndices, data }, key ->
hashKey =
createLowLevelHasher {}
|> Hash.hash key
|> complete
h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
Ok index ->
dataIndex = listGetUnsafe dataIndices index
(T _ v) = listGetUnsafe data dataIndex
Ok v
Err NotFound ->
Err KeyNotFound
## Check if the dictionary has a value for a specified key.
##
## expect
## Dict.empty
## |> Dict.insert 1234 "5678"
## |> Dict.contains 1234
## |> Bool.isEq Bool.true
contains : Dict k v, k -> Bool | k has Hash & Eq
contains = \@Dict { metadata, dataIndices, data }, key ->
hashKey =
createLowLevelHasher {}
|> Hash.hash key
|> complete
h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
Ok _ ->
Bool.true
Err NotFound ->
Bool.false
## Insert a value into the dictionary at a specified key. ## Insert a value into the dictionary at a specified key.
## ##
@ -135,29 +267,42 @@ walk = \@Dict list, initialState, transform ->
## |> Dict.insert "Apples" 12 ## |> Dict.insert "Apples" 12
## |> Dict.get "Apples" ## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12) ## |> Bool.isEq (Ok 12)
insert : Dict k v, k, v -> Dict k v | k has Eq insert : Dict k v, k, v -> Dict k v | k has Hash & Eq
insert = \@Dict list, k, v -> insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
when List.findFirstIndex list (\Pair key _ -> key == k) is hashKey =
Err NotFound -> createLowLevelHasher {}
insertFresh (@Dict list) k v |> Hash.hash key
|> complete
h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
Ok index -> Ok index ->
list dataIndex = listGetUnsafe dataIndices index
|> List.set index (Pair k v)
|> @Dict
## Returns the number of values in the dictionary. @Dict {
## metadata,
## expect dataIndices,
## Dict.empty data: List.set data dataIndex (T key value),
## |> Dict.insert "One" "A Song" size,
## |> Dict.insert "Two" "Candy Canes" }
## |> Dict.insert "Three" "Boughs of Holly"
## |> Dict.len Err NotFound ->
## |> Bool.isEq 3 # The dictionary has grown, it might need to rehash.
len : Dict k v -> Nat rehashedDict =
len = \@Dict list -> maybeRehash
List.len list (
@Dict {
metadata,
dataIndices,
data,
size: size + 1,
}
)
# Need to rescan searching for the first empty or deleted cell.
insertNotFoundHelper rehashedDict key value h1Key h2Key
## Remove a value from the dictionary for a specified key. ## Remove a value from the dictionary for a specified key.
## ##
@ -167,19 +312,34 @@ len = \@Dict list ->
## |> Dict.remove "Some" ## |> Dict.remove "Some"
## |> Dict.len ## |> Dict.len
## |> Bool.isEq 0 ## |> Bool.isEq 0
remove : Dict k v, k -> Dict k v | k has Eq remove : Dict k v, k -> Dict k v | k has Hash & Eq
remove = \@Dict list, key -> remove = \@Dict { metadata, dataIndices, data, size }, key ->
when List.findFirstIndex list (\Pair k _ -> k == key) is # TODO: change this from swap remove to tombstone and test is performance is still good.
Err NotFound -> hashKey =
@Dict list createLowLevelHasher {}
|> Hash.hash key
|> complete
h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
Ok index -> Ok index ->
lastIndex = List.len list - 1 last = List.len data - 1
dataIndex = listGetUnsafe dataIndices index
list if dataIndex == last then
|> List.swap index lastIndex @Dict {
|> List.dropLast metadata: List.set metadata index deletedSlot,
|> @Dict dataIndices,
data: List.dropLast data,
size: size - 1,
}
else
swapAndUpdateDataIndex (@Dict { metadata, dataIndices, data, size }) index last
Err NotFound ->
@Dict { metadata, dataIndices, data, size }
## Insert or remove a value for a specified key. This function enables a ## Insert or remove a value for a specified key. This function enables a
## performance optimisation for the use case of providing a default when a value ## performance optimisation for the use case of providing a default when a value
@ -195,8 +355,9 @@ remove = \@Dict list, key ->
## expect Dict.update Dict.empty "a" alterValue == Dict.single "a" Bool.false ## expect Dict.update Dict.empty "a" alterValue == Dict.single "a" Bool.false
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true ## 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 ## 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 Eq update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq
update = \dict, key, alter -> update = \dict, key, alter ->
# TODO: look into optimizing by merging substeps and reducing lookups.
possibleValue = possibleValue =
get dict key get dict key
|> Result.map Present |> Result.map Present
@ -206,46 +367,22 @@ update = \dict, key, alter ->
Present value -> insert dict key value Present value -> insert dict key value
Missing -> remove dict key Missing -> remove dict key
# Internal for testing only ## Returns the keys and values of a dictionary as a [List].
alterValue : [Present Bool, Missing] -> [Present Bool, Missing] ## This requires allocating a temporary list, prefer using [Dict.toList] or [Dict.walk] instead.
alterValue = \possibleValue ->
when possibleValue is
Missing -> Present Bool.false
Present value -> if value then Missing else Present Bool.true
expect update empty "a" alterValue == single "a" Bool.false
expect update (single "a" Bool.false) "a" alterValue == single "a" Bool.true
expect update (single "a" Bool.true) "a" alterValue == empty
## Check if the dictionary has a value for a specified key.
## ##
## expect ## expect
## Dict.empty ## Dict.single 1 "One"
## |> Dict.insert 1234 "5678" ## |> Dict.insert 2 "Two"
## |> Dict.contains 1234 ## |> Dict.insert 3 "Three"
contains : Dict k v, k -> Bool | k has Eq ## |> Dict.insert 4 "Four"
contains = \@Dict list, needle -> ## |> Dict.toList
List.any list \Pair key _val -> key == needle ## |> Bool.isEq [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"]
toList : Dict k v -> List (T k v) | k has Hash & Eq
expect contains empty "a" == Bool.false toList = \@Dict { data } ->
expect contains (single "a" {}) "a" == Bool.true data
expect contains (single "b" {}) "a" == Bool.false
expect
Dict.empty
|> Dict.insert 1234 "5678"
|> Dict.contains 1234
|> Bool.isEq Bool.true
## Returns a dictionary containing the key and value provided as input.
##
## expect
## Dict.single "A" "B"
## |> Bool.isEq (Dict.insert Dict.empty "A" "B")
single : k, v -> Dict k v
single = \key, value ->
@Dict [Pair key value]
## Returns the keys of a dictionary as a [List]. ## Returns the keys of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
## ##
## expect ## expect
## Dict.single 1 "One" ## Dict.single 1 "One"
@ -254,11 +391,12 @@ single = \key, value ->
## |> Dict.insert 4 "Four" ## |> Dict.insert 4 "Four"
## |> Dict.keys ## |> Dict.keys
## |> Bool.isEq [1,2,3,4] ## |> Bool.isEq [1,2,3,4]
keys : Dict k v -> List k keys : Dict k v -> List k | k has Hash & Eq
keys = \@Dict list -> keys = \@Dict { data } ->
List.map list (\Pair k _ -> k) List.map data (\T k _ -> k)
## Returns the values of a dictionary as a [List]. ## Returns the values of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
## ##
## expect ## expect
## Dict.single 1 "One" ## Dict.single 1 "One"
@ -267,22 +405,22 @@ keys = \@Dict list ->
## |> Dict.insert 4 "Four" ## |> Dict.insert 4 "Four"
## |> Dict.values ## |> Dict.values
## |> Bool.isEq ["One","Two","Three","Four"] ## |> Bool.isEq ["One","Two","Three","Four"]
values : Dict k v -> List v values : Dict k v -> List v | k has Hash & Eq
values = \@Dict list -> values = \@Dict { data } ->
List.map list (\Pair _ v -> v) List.map data (\T _ v -> v)
## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) ## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory))
## of all the key-value pairs. This means that all the key-value pairs in ## of all the key-value pairs. This means that all the key-value pairs in
## both dictionaries will be combined. Note that where there are pairs ## both dictionaries will be combined. Note that where there are pairs
## with the same key, the value contained in the first input will be ## with the same key, the value contained in the second input will be
## retained, and the value in the second input will be removed. ## retained, and the value in the first input will be removed.
## ##
## first = ## first =
## Dict.single 1 "Keep Me" ## Dict.single 1 "Not Me"
## |> Dict.insert 2 "And Me" ## |> Dict.insert 2 "And Me"
## ##
## second = ## second =
## Dict.single 1 "Not Me" ## Dict.single 1 "Keep Me"
## |> Dict.insert 3 "Me Too" ## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me" ## |> Dict.insert 4 "And Also Me"
## ##
@ -294,9 +432,9 @@ values = \@Dict list ->
## ##
## expect ## expect
## Dict.insertAll first second == expected ## Dict.insertAll first second == expected
insertAll : Dict k v, Dict k v -> Dict k v | k has Eq insertAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
insertAll = \xs, @Dict ys -> insertAll = \xs, ys ->
List.walk ys xs (\state, Pair k v -> Dict.insertIfVacant state k v) walk ys xs insert
## Combine two dictionaries by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) ## Combine two dictionaries by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory))
## of all the key-value pairs. This means that we keep only those pairs ## of all the key-value pairs. This means that we keep only those pairs
@ -315,10 +453,17 @@ insertAll = \xs, @Dict ys ->
## |> Dict.insert 4 "Or Me" ## |> Dict.insert 4 "Or Me"
## ##
## expect Dict.keepShared first second == first ## expect Dict.keepShared first second == first
keepShared : Dict k v, Dict k v -> Dict k v | k has Eq keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
keepShared = \@Dict xs, ys -> keepShared = \xs, ys ->
List.keepIf xs (\Pair k _ -> Dict.contains ys k) walk
|> @Dict xs
empty
(\state, k, v ->
if contains ys k then
insert state k v
else
state
)
## Remove the key-value pairs in the first input that are also in the second ## Remove the key-value pairs in the first input that are also in the second
## using the [set difference](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement) ## using the [set difference](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement)
@ -339,25 +484,349 @@ keepShared = \@Dict xs, ys ->
## |> Dict.insert 2 "And Me" ## |> Dict.insert 2 "And Me"
## ##
## expect Dict.removeAll first second == expected ## expect Dict.removeAll first second == expected
removeAll : Dict k v, Dict k v -> Dict k v | k has Eq removeAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
removeAll = \xs, @Dict ys -> removeAll = \xs, ys ->
List.walk ys xs (\state, Pair k _ -> Dict.remove state k) walk ys xs (\state, k, _ -> remove state k)
## Internal helper function to insert a new association swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k has Hash & Eq
## swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
## Precondition: `k` should not exist in the Dict yet. (T key _) = listGetUnsafe data lastIndex
insertFresh : Dict k v, k, v -> Dict k v hashKey =
insertFresh = \@Dict list, k, v -> createLowLevelHasher {}
list |> Hash.hash key
|> List.append (Pair k v) |> complete
|> @Dict h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
insertIfVacant : Dict k v, k, v -> Dict k v | k has Eq when findIndexHelper metadata dataIndices data h2Key key probe 0 is
insertIfVacant = \dict, key, value -> Ok index ->
if Dict.contains dict key then dataIndex = listGetUnsafe dataIndices removedIndex
dict # Swap and remove data.
nextData =
data
|> List.swap dataIndex lastIndex
|> List.dropLast
@Dict {
# Set old metadata as deleted.
metadata: List.set metadata removedIndex deletedSlot,
# Update index of swaped element.
dataIndices: List.set dataIndices index dataIndex,
data: nextData,
size: size - 1,
}
Err NotFound ->
# This should be impossible.
crash "unreachable state in dict swapAndUpdateDataIndex hit. Definitely a standard library bug."
insertNotFoundHelper : Dict k v, k, v, U64, I8 -> Dict k v
insertNotFoundHelper = \@Dict { metadata, dataIndices, data, size }, key, value, h1Key, h2Key ->
probe = newProbe h1Key (div8 (List.len metadata))
index = nextEmptyOrDeletedHelper metadata probe 0
dataIndex = List.len data
nextData = List.append data (T key value)
@Dict {
metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex,
data: nextData,
size,
}
nextEmptyOrDeletedHelper : List I8, Probe, Nat -> Nat
nextEmptyOrDeletedHelper = \metadata, probe, offset ->
# For inserting, we can use deleted indices.
index = Num.addWrap (mul8 probe.slotIndex) offset
md = listGetUnsafe metadata index
if md < 0 then
# Empty or deleted slot, no possibility of the element.
index
else if offset == 7 then
nextEmptyOrDeletedHelper metadata (nextProbe probe) 0
else else
Dict.insert dict key value nextEmptyOrDeletedHelper metadata probe (Num.addWrap offset 1)
# 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 (T k v), I8, k, Probe, Nat -> Result Nat [NotFound] | k has 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
md = listGetUnsafe metadata index
if md == emptySlot then
# Empty slot, no possibility of the element.
Err NotFound
else if md == h2Key then
# Potentially matching slot, check if the key is a match.
dataIndex = listGetUnsafe dataIndices index
(T k _) = listGetUnsafe data dataIndex
if k == key then
# We have a match, return its index.
Ok index
else if offset == 7 then
# No match, keep checking.
findIndexHelper metadata dataIndices data h2Key key (nextProbe probe) 0
else
findIndexHelper metadata dataIndices data h2Key key probe (Num.addWrap offset 1)
else if offset == 7 then
# Used slot, check next slot.
findIndexHelper metadata dataIndices data h2Key key (nextProbe probe) 0
else
findIndexHelper metadata dataIndices data h2Key key probe (Num.addWrap offset 1)
# 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 { 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
if size > maxLoadCap then
rehash (@Dict { metadata, dataIndices, data, size })
else
@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 { metadata, dataIndices, data, size } ->
newLen = 2 * List.len dataIndices
newDict =
@Dict {
metadata: List.repeat emptySlot newLen,
dataIndices: List.repeat 0 newLen,
data,
size,
}
rehashHelper newDict metadata dataIndices data 0
rehashHelper : Dict k v, List I8, List Nat, List (T k v), Nat -> Dict k v | k has Hash & Eq
rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
when List.get oldMetadata index is
Ok md ->
nextDict =
if md >= 0 then
# We have an actual element here
dataIndex = listGetUnsafe oldDataIndices index
(T k _) = listGetUnsafe oldData dataIndex
insertForRehash dict k dataIndex
else
# Empty or deleted data
dict
rehashHelper nextDict oldMetadata oldDataIndices oldData (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 { metadata, dataIndices, data, size }, key, dataIndex ->
hashKey =
createLowLevelHasher {}
|> Hash.hash key
|> complete
h1Key = h1 hashKey
h2Key = h2 hashKey
probe = newProbe h1Key (div8 (List.len metadata))
index = nextEmptyOrDeletedHelper metadata probe 0
@Dict {
metadata: List.set metadata index h2Key,
dataIndices: List.set dataIndices index dataIndex,
data,
size,
}
emptySlot : I8
emptySlot = -128
deletedSlot : I8
deletedSlot = -2
T k v : [T k v]
# Capacity must be a power of 2.
# We still will use slots of 8 even though this version has no true slots.
# We just move an element at a time.
# Thus, the true index is slotIndex * 8 + offset.
Probe : { slotIndex : Nat, probeI : Nat, mask : Nat }
newProbe : U64, Nat -> Probe
newProbe = \h1Key, slots ->
mask = Num.subSaturated slots 1
slotIndex = Num.bitwiseAnd (Num.toNat h1Key) mask
{ slotIndex, probeI: 1, mask }
nextProbe : Probe -> Probe
nextProbe = \{ slotIndex, probeI, mask } ->
nextSlotIndex = Num.bitwiseAnd (Num.addWrap slotIndex probeI) mask
{ slotIndex: nextSlotIndex, probeI: Num.addWrap probeI 1, mask }
mul8 = \val -> Num.shiftLeftBy val 3
div8 = \val -> Num.shiftRightZfBy val 3
h1 : U64 -> U64
h1 = \hashKey ->
Num.shiftRightZfBy hashKey 7
h2 : U64 -> I8
h2 = \hashKey ->
Num.toI8 (Num.bitwiseAnd hashKey 0b0111_1111)
expect
val =
empty
|> insert "foo" "bar"
|> get "foo"
val == Ok "bar"
expect
val =
empty
|> insert "foo" "bar"
|> insert "foo" "baz"
|> get "foo"
val == Ok "baz"
expect
val =
empty
|> insert "foo" "bar"
|> get "bar"
val == Err KeyNotFound
expect
empty
|> insert "foo" {}
|> contains "foo"
expect
dict =
empty
|> insert "foo" {}
|> insert "bar" {}
|> insert "baz" {}
contains dict "baz" && Bool.not (contains dict "other")
expect
dict =
fromList [T 1u8 1u8, T 2 2, T 3 3]
|> remove 1
|> remove 3
keys dict == [2]
expect
list =
fromList [T 1u8 1u8, T 2u8 2u8, T 3 3]
|> remove 1
|> insert 0 0
|> remove 3
|> keys
list == [0, 2]
# Reach capacity, no rehash.
expect
val =
empty
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
|> insert "d" 3
|> insert "e" 4
|> insert "f" 5
|> insert "g" 6
|> capacity
val == 7
expect
dict =
empty
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
|> insert "d" 3
|> insert "e" 4
|> insert "f" 5
|> insert "g" 6
(get dict "a" == Ok 0)
&& (get dict "b" == Ok 1)
&& (get dict "c" == Ok 2)
&& (get dict "d" == Ok 3)
&& (get dict "e" == Ok 4)
&& (get dict "f" == Ok 5)
&& (get dict "g" == Ok 6)
# Force rehash.
expect
val =
empty
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
|> insert "d" 3
|> insert "e" 4
|> insert "f" 5
|> insert "g" 6
|> insert "h" 7
|> capacity
val == 14
expect
dict =
empty
|> insert "a" 0
|> insert "b" 1
|> insert "c" 2
|> insert "d" 3
|> insert "e" 4
|> insert "f" 5
|> insert "g" 6
|> insert "h" 7
(get dict "a" == Ok 0)
&& (get dict "b" == Ok 1)
&& (get dict "c" == Ok 2)
&& (get dict "d" == Ok 3)
&& (get dict "e" == Ok 4)
&& (get dict "f" == Ok 5)
&& (get dict "g" == Ok 6)
&& (get dict "h" == Ok 7)
expect
empty
|> insert "Some" "Value"
|> remove "Some"
|> len
|> Bool.isEq 0
# Makes sure a Dict with Nat keys works
expect
empty
|> insert 7nat "Testing"
|> get 7
|> Bool.isEq (Ok "Testing")
# We have decided not to expose the standard roc hashing algorithm. # We have decided not to expose the standard roc hashing algorithm.
# This is to avoid external dependence and the need for versioning. # This is to avoid external dependence and the need for versioning.
@ -524,10 +993,32 @@ wymix = \a, b ->
wymum : U64, U64 -> { lower : U64, upper : U64 } wymum : U64, U64 -> { lower : U64, upper : U64 }
wymum = \a, b -> wymum = \a, b ->
r = Num.toU128 a * Num.toU128 b # uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
lower = Num.toU64 r # uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
upper = Num.shiftRightZfBy r 64 |> Num.toU64 # lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;
ha = Num.shiftRightZfBy a 32
hb = Num.shiftRightZfBy b 32
la = Num.bitwiseAnd a 0x0000_0000_FFFF_FFFF
lb = Num.bitwiseAnd b 0x0000_0000_FFFF_FFFF
rh = ha * hb
rm0 = ha * lb
rm1 = hb * la
rl = la * lb
t = Num.addWrap rl (Num.shiftLeftBy rm0 32)
c = if t < rl then 1 else 0
lower = Num.addWrap t (Num.shiftLeftBy rm1 32)
c2 = c + (if lower < t then 1 else 0)
upper =
rh
|> Num.addWrap (Num.shiftRightZfBy rm0 32)
|> Num.addWrap (Num.shiftRightZfBy rm1 32)
|> Num.addWrap c2
# TODO: switch back to this once wasm supports bit shifting a U128.
# The above code is manually doing the 128bit multiplication.
# r = Num.toU128 a * Num.toU128 b
# lower = Num.toU64 r
# upper = Num.shiftRightZfBy r 64 |> Num.toU64
# This is the more robust form. # This is the more robust form.
# { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper } # { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper }
{ lower, upper } { lower, upper }

View file

@ -14,14 +14,16 @@ interface Hash
hashI32, hashI32,
hashI64, hashI64,
hashI128, hashI128,
hashNat,
complete, complete,
hashStrBytes, hashStrBytes,
hashList, hashList,
hashUnordered, hashUnordered,
] imports [ ] imports [
Bool.{ isEq },
List, List,
Str, Str,
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128 }, Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, Nat },
] ]
## A value that can hashed. ## A value that can hashed.
@ -88,6 +90,21 @@ hashI64 = \hasher, n -> addU64 hasher (Num.toU64 n)
hashI128 : a, I128 -> a | a has Hasher hashI128 : a, I128 -> a | a has Hasher
hashI128 = \hasher, n -> addU128 hasher (Num.toU128 n) hashI128 = \hasher, n -> addU128 hasher (Num.toU128 n)
## Adds a single Nat to a hasher.
hashNat : a, Nat -> a | a has Hasher
hashNat = \hasher, n ->
isPlatform32bit =
x : Nat
x = 0xffff_ffff
y = Num.addWrap x 1
y == 0
if isPlatform32bit then
addU32 hasher (Num.toU32 n)
else
addU64 hasher (Num.toU64 n)
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element. ## Adds a container of [Hash]able elements to a [Hasher] by hashing each element.
## The container is iterated using the walk method passed in. ## The container is iterated using the walk method passed in.
## The order of the elements does not affect the final hash. ## The order of the elements does not affect the final hash.

View file

@ -205,7 +205,7 @@ takeWhile = \list, predicate ->
helper { taken: [], rest: list } helper { taken: [], rest: list }
digits : List U8 digits : List U8
digits = List.range '0' ('9' + 1) digits = List.range { start: At '0', end: At '9' }
takeDigits = \bytes -> takeDigits = \bytes ->
takeWhile bytes \n -> List.contains digits n takeWhile bytes \n -> List.contains digits n

View file

@ -637,26 +637,130 @@ mapWithIndexHelp = \src, dest, func, index, length ->
else else
dest dest
## Returns a list of all the integers between one and another, ## Returns a list of all the integers between `start` and `end`.
## including both of the given numbers.
## ##
## >>> List.range 2 8 ## To include the `start` and `end` integers themselves, use `At` like so:
range : Int a, Int a -> List (Int a) ##
range = \start, end -> ## List.range { start: At 2, end: At 5 } # returns [2, 3, 4, 5]
when Num.compare start end is ##
GT -> [] ## To exclude them, use `After` and `Before`, like so:
EQ -> [start] ##
LT -> ## List.range { start: After 2, end: Before 5 } # returns [3, 4]
length = Num.intCast (end - start) ##
## You can have the list end at a certain length rather than a certain integer:
##
## List.range { start: At 6, end: Length 4 } # returns [6, 7, 8, 9]
##
## If `step` is specified, each integer increases by that much. (`step: 1` is the default.)
##
## List.range { start: After 1, end: Before 10, step: 3 } # returns [2, 5, 8]
##
## All of these options are compatible with the others. For example, you can use `At` or `After`
## with `start` regardless of what `end` and `step` are set to.
# TODO: Make the type annotation work
# range :
# {
# start : [At (Int a), After (Int a)],
# end : [At (Int a), Before (Int a), Length Nat],
# # TODO: We want this to be Int *, but that requires the ability to convert or add from Int * to Int a
# step ? Int a
# }
# -> List (Int a) | a has Bool.Eq
range = \{ start, end, step ? 0 } ->
{ incByStep, stepIsPositive } =
if step == 0 then
when T start end is
T (At x) (At y) | T (At x) (Before y) | T (After x) (At y) | T (After x) (Before y) ->
if x < y then
{
incByStep: \i -> i + 1,
stepIsPositive: Bool.true,
}
else
{
incByStep: \i -> i - 1,
stepIsPositive: Bool.false,
}
rangeHelp (List.withCapacity length) start end T (At _) (Length _) | T (After _) (Length _) ->
{
incByStep: \i -> i + 1,
stepIsPositive: Bool.true,
}
else
{
incByStep: \i -> i + step,
stepIsPositive: step > 0,
}
rangeHelp : List (Int a), Int a, Int a -> List (Int a) inclusiveStart =
rangeHelp = \accum, start, end -> when start is
if end <= start then At x -> x
After x -> incByStep x
when end is
At at ->
isComplete =
if stepIsPositive then
\i -> i > at
else
\i -> i < at
# TODO: switch to List.withCapacity
rangeHelp [] inclusiveStart incByStep isComplete
Before before ->
isComplete =
if stepIsPositive then
\i -> i >= before
else
\i -> i <= before
# TODO: switch to List.withCapacity
rangeHelp [] inclusiveStart incByStep isComplete
Length l ->
rangeLengthHelp (List.withCapacity l) inclusiveStart l incByStep
rangeHelp = \accum, i, incByStep, isComplete ->
if isComplete i then
accum accum
else else
rangeHelp (List.appendUnsafe accum start) (start + 1) end # TODO: change this to List.appendUnsafe once capacity is set correctly
rangeHelp (List.append accum i) (incByStep i) incByStep isComplete
rangeLengthHelp = \accum, i, remaining, incByStep ->
if remaining == 0 then
accum
else
rangeLengthHelp (List.appendUnsafe accum i) (incByStep i) (remaining - 1) incByStep
expect
List.range { start: At 0, end: At 4 } == [0, 1, 2, 3, 4]
expect
List.range { start: After 0, end: At 4 } == [1, 2, 3, 4]
expect
List.range { start: At 0, end: At 4, step: 2 } == [0, 2, 4]
expect
List.range { start: At 0, end: Before 4 } == [0, 1, 2, 3]
expect
List.range { start: After 0, end: Before 4 } == [1, 2, 3]
expect
List.range { start: At 0, end: Before 4, step: 2 } == [0, 2]
expect
List.range { start: At 4, end: Length 5 } == [4, 5, 6, 7, 8]
expect
List.range { start: At 4, end: Length 5, step: 10 } == [4, 14, 24, 34, 44]
expect
List.range { start: At 4, end: Length 5, step: -3 } == [4, 1, -2, -5, -8]
## Sort with a custom comparison function ## Sort with a custom comparison function
sortWith : List a, (a, a -> [LT, EQ, GT]) -> List a sortWith : List a, (a, a -> [LT, EQ, GT]) -> List a

View file

@ -14,99 +14,192 @@ interface Set
intersection, intersection,
difference, difference,
] ]
imports [List, Bool.{ Bool, Eq }, Dict.{ Dict }, Num.{ Nat }] imports [
List,
Bool.{ Bool, Eq },
Dict.{ Dict },
Num.{ Nat },
Hash.{ Hash },
]
Set k := Dict.Dict k {} has [Eq] # We should have this line above the next has.
# It causes the formatter to fail currently.
# | k has Hash & Eq
Set k := Dict.Dict k {}
has [
Eq {
isEq,
},
]
fromDict : Dict k {} -> Set k isEq : Set k, Set k -> Bool | k has Hash & Eq
fromDict = \dict -> @Set dict isEq = \xs, ys ->
if len xs != len ys then
toDict : Set k -> Dict k {} Bool.false
toDict = \@Set dict -> dict else
walkUntil xs Bool.true \_, elem ->
if contains ys elem then
Continue Bool.true
else
Break Bool.false
## An empty set. ## An empty set.
empty : Set k empty : Set k | k has Hash & Eq
empty = fromDict Dict.empty empty = @Set Dict.empty
single : k -> Set k single : k -> Set k | k has Hash & Eq
single = \key -> single = \key ->
@Set (Dict.single key {}) Dict.single key {} |> @Set
## Make sure never to insert a *NaN* to a [Set]! Because *NaN* is defined to be insert : Set k, k -> Set k | k has Hash & Eq
## unequal to *NaN*, adding a *NaN* results in an entry that can never be
## retrieved or removed from the [Set].
insert : Set k, k -> Set k | k has Eq
insert = \@Set dict, key -> insert = \@Set dict, key ->
dict Dict.insert dict key {} |> @Set
|> Dict.insert key {}
|> @Set
# Inserting a duplicate key has no effect. # Inserting a duplicate key has no effect.
expect expect
actual = actual =
Set.empty empty
|> Set.insert "foo" |> insert "foo"
|> Set.insert "bar" |> insert "bar"
|> Set.insert "foo" |> insert "foo"
|> Set.insert "baz" |> insert "baz"
expected = expected =
Set.empty empty
|> Set.insert "foo" |> insert "foo"
|> Set.insert "bar" |> insert "bar"
|> Set.insert "baz" |> insert "baz"
expected == actual expected == actual
len : Set k -> Nat len : Set k -> Nat | k has Hash & Eq
len = \@Set dict -> len = \@Set dict ->
Dict.len dict Dict.len dict
# Inserting a duplicate key has no effect on length. # Inserting a duplicate key has no effect on length.
expect expect
actual = actual =
Set.empty empty
|> Set.insert "foo" |> insert "foo"
|> Set.insert "bar" |> insert "bar"
|> Set.insert "foo" |> insert "foo"
|> Set.insert "baz" |> insert "baz"
|> Set.len |> len
actual == 3 actual == 3
## Drops the given element from the set. ## Drops the given element from the set.
remove : Set k, k -> Set k | k has Eq remove : Set k, k -> Set k | k has Hash & Eq
remove = \@Set dict, key -> remove = \@Set dict, key ->
@Set (Dict.remove dict key) Dict.remove dict key |> @Set
contains : Set k, k -> Bool | k has Eq contains : Set k, k -> Bool | k has Hash & Eq
contains = \set, key -> contains = \@Set dict, key ->
set Dict.contains dict key
|> Set.toDict
|> Dict.contains key
toList : Set k -> List k toList : Set k -> List k | k has Hash & Eq
toList = \@Set dict -> toList = \@Set dict ->
Dict.keys dict Dict.keys dict
fromList : List k -> Set k | k has Eq fromList : List k -> Set k | k has Hash & Eq
fromList = \list -> fromList = \list ->
initial = @Set (Dict.withCapacity (List.len list)) initial = @Set (Dict.withCapacity (List.len list))
List.walk list initial \set, key -> Set.insert set key List.walk list initial insert
union : Set k, Set k -> Set k | k has Eq union : Set k, Set k -> Set k | k has Hash & Eq
union = \@Set dict1, @Set dict2 -> union = \@Set dict1, @Set dict2 ->
@Set (Dict.insertAll dict1 dict2) Dict.insertAll dict1 dict2 |> @Set
intersection : Set k, Set k -> Set k | k has Eq intersection : Set k, Set k -> Set k | k has Hash & Eq
intersection = \@Set dict1, @Set dict2 -> intersection = \@Set dict1, @Set dict2 ->
@Set (Dict.keepShared dict1 dict2) Dict.keepShared dict1 dict2 |> @Set
difference : Set k, Set k -> Set k | k has Eq difference : Set k, Set k -> Set k | k has Hash & Eq
difference = \@Set dict1, @Set dict2 -> difference = \@Set dict1, @Set dict2 ->
@Set (Dict.removeAll dict1 dict2) Dict.removeAll dict1 dict2 |> @Set
walk : Set k, state, (state, k -> state) -> state walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq
walk = \set, state, step -> walk = \@Set dict, state, step ->
Dict.walk (Set.toDict set) state (\s, k, _ -> step s k) Dict.walk dict state (\s, k, _ -> step s k)
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state | k has Hash & Eq
walkUntil = \@Set dict, state, step ->
Dict.walkUntil dict state (\s, k, _ -> step s k)
expect
first =
single "Keep Me"
|> insert "And Me"
|> insert "Remove Me"
second =
single "Remove Me"
|> insert "I do nothing..."
expected =
single "Keep Me"
|> insert "And Me"
difference first second == expected
expect
first =
single "Keep Me"
|> insert "And Me"
|> insert "Remove Me"
second =
single "Remove Me"
|> insert "I do nothing..."
expected =
single "Keep Me"
|> insert "And Me"
difference first second == expected
expect
first =
single 1
|> insert 2
second =
single 1
|> insert 3
|> insert 4
expected =
single 1
|> insert 2
|> insert 3
|> insert 4
union first second == expected
expect
base =
single "Remove Me"
|> insert "Keep Me"
|> insert "And Me"
expected =
single "Keep Me"
|> insert "And Me"
remove base "Remove Me" == expected
expect
x =
single 0
|> insert 1
|> insert 2
|> insert 3
|> insert 4
|> insert 5
|> insert 6
|> insert 7
|> insert 8
|> insert 9
x == fromList (toList x)

View file

@ -488,17 +488,19 @@ impl Constraints {
/// then need to find their way to the pool, and a convenient approach turned out to be to /// then need to find their way to the pool, and a convenient approach turned out to be to
/// tag them onto the `Let` that we used to add the imported values. /// tag them onto the `Let` that we used to add the imported values.
#[inline(always)] #[inline(always)]
pub fn let_import_constraint<I1, I2>( pub fn let_import_constraint<I1, I2, I3>(
&mut self, &mut self,
rigid_vars: I1, rigid_vars: I1,
def_types: I2, flex_vars: I2,
def_types: I3,
module_constraint: Constraint, module_constraint: Constraint,
pool_variables: &[Variable], pool_variables: &[Variable],
) -> Constraint ) -> Constraint
where where
I1: IntoIterator<Item = Variable>, I1: IntoIterator<Item = Variable>,
I2: IntoIterator<Item = (Symbol, Loc<TypeOrVar>)>, I2: IntoIterator<Item = Variable>,
I2::IntoIter: ExactSizeIterator, I3: IntoIterator<Item = (Symbol, Loc<TypeOrVar>)>,
I3::IntoIter: ExactSizeIterator,
{ {
// defs and ret constraint are stored consequtively, so we only need to store one index // defs and ret constraint are stored consequtively, so we only need to store one index
let defs_and_ret_constraint = Index::new(self.constraints.len() as _); let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
@ -508,7 +510,7 @@ impl Constraints {
let let_contraint = LetConstraint { let let_contraint = LetConstraint {
rigid_vars: self.variable_slice(rigid_vars), rigid_vars: self.variable_slice(rigid_vars),
flex_vars: Slice::default(), flex_vars: self.variable_slice(flex_vars),
def_types: self.def_types_slice(def_types), def_types: self.def_types_slice(def_types),
defs_and_ret_constraint, defs_and_ret_constraint,
}; };

View file

@ -302,7 +302,7 @@ fn sort_type_defs_before_introduction(
matrix matrix
.strongly_connected_components_all() .strongly_connected_components_all()
.groups() .groups()
.flat_map(|group| group.iter_ones()) .flat_map(|(group, _)| group.iter_ones())
.map(|index| symbols[index]) .map(|index| symbols[index])
.collect() .collect()
} }
@ -1547,10 +1547,12 @@ impl DefOrdering {
#[inline(always)] #[inline(always)]
pub(crate) fn sort_can_defs_new( pub(crate) fn sort_can_defs_new(
env: &mut Env<'_>,
scope: &mut Scope, scope: &mut Scope,
var_store: &mut VarStore, var_store: &mut VarStore,
defs: CanDefs, defs: CanDefs,
mut output: Output, mut output: Output,
exposed_symbols: &VecSet<Symbol>,
) -> (Declarations, Output) { ) -> (Declarations, Output) {
let CanDefs { let CanDefs {
defs, defs,
@ -1611,7 +1613,7 @@ pub(crate) fn sort_can_defs_new(
sccs.reorder(&mut defs); sccs.reorder(&mut defs);
for group in sccs.groups().rev() { for (group, is_initial) in sccs.groups().rev() {
match group.count_ones() { match group.count_ones() {
1 => { 1 => {
// a group with a single Def, nice and simple // a group with a single Def, nice and simple
@ -1636,6 +1638,10 @@ pub(crate) fn sort_can_defs_new(
} }
}; };
if is_initial && !exposed_symbols.contains(&symbol) {
env.problem(Problem::DefsOnlyUsedInRecursion(1, def.region()));
}
match def.loc_expr.value { match def.loc_expr.value {
Closure(closure_data) => { Closure(closure_data) => {
declarations.push_recursive_def( declarations.push_recursive_def(
@ -1720,6 +1726,9 @@ pub(crate) fn sort_can_defs_new(
let cycle_mark = IllegalCycleMark::new(var_store); let cycle_mark = IllegalCycleMark::new(var_store);
declarations.push_recursive_group(group_length as u16, cycle_mark); declarations.push_recursive_group(group_length as u16, cycle_mark);
let mut group_is_initial = is_initial;
let mut whole_region = None;
// then push the definitions of this group // then push the definitions of this group
for def in group_defs { for def in group_defs {
let (symbol, specializes) = match def.loc_pattern.value { let (symbol, specializes) = match def.loc_pattern.value {
@ -1734,6 +1743,12 @@ pub(crate) fn sort_can_defs_new(
} }
}; };
group_is_initial = group_is_initial && !exposed_symbols.contains(&symbol);
whole_region = match whole_region {
None => Some(def.region()),
Some(r) => Some(Region::span_across(&r, &def.region())),
};
match def.loc_expr.value { match def.loc_expr.value {
Closure(closure_data) => { Closure(closure_data) => {
declarations.push_recursive_def( declarations.push_recursive_def(
@ -1755,6 +1770,13 @@ pub(crate) fn sort_can_defs_new(
} }
} }
} }
if group_is_initial {
env.problem(Problem::DefsOnlyUsedInRecursion(
group_length,
whole_region.unwrap(),
));
}
} }
} }
} }
@ -1803,7 +1825,7 @@ pub(crate) fn sort_can_defs(
let mut declarations = Vec::with_capacity(defs.len()); let mut declarations = Vec::with_capacity(defs.len());
for group in sccs.groups() { for (group, is_initial) in sccs.groups() {
if group.count_ones() == 1 { if group.count_ones() == 1 {
// a group with a single Def, nice and simple // a group with a single Def, nice and simple
let index = group.iter_ones().next().unwrap(); let index = group.iter_ones().next().unwrap();
@ -1817,6 +1839,16 @@ pub(crate) fn sort_can_defs(
let declaration = if def_ordering.references.get_row_col(index, index) { let declaration = if def_ordering.references.get_row_col(index, index) {
debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {:?} now, that's a bug!", def); debug_assert!(!is_specialization, "Self-recursive specializations can only be determined during solving - but it was determined for {:?} now, that's a bug!", def);
if is_initial
&& !def
.pattern_vars
.keys()
.any(|sym| output.references.has_value_lookup(*sym))
{
// This defs is only used in recursion with itself.
env.problem(Problem::DefsOnlyUsedInRecursion(1, def.region()));
}
// this function calls itself, and must be typechecked as a recursive def // this function calls itself, and must be typechecked as a recursive def
Declaration::DeclareRec(vec![mark_def_recursive(def)], IllegalCycleMark::empty()) Declaration::DeclareRec(vec![mark_def_recursive(def)], IllegalCycleMark::empty())
} else { } else {
@ -1862,11 +1894,26 @@ pub(crate) fn sort_can_defs(
Declaration::InvalidCycle(entries) Declaration::InvalidCycle(entries)
} else { } else {
let rec_defs = group let rec_defs: Vec<Def> = group
.iter_ones() .iter_ones()
.map(|index| mark_def_recursive(take_def!(index))) .map(|index| mark_def_recursive(take_def!(index)))
.collect(); .collect();
if is_initial
&& !rec_defs.iter().any(|def| {
def.pattern_vars
.keys()
.any(|sym| output.references.has_value_lookup(*sym))
})
{
// These defs are only used in mutual recursion with themselves.
let region = Region::span_across(
&rec_defs.first().unwrap().region(),
&rec_defs.last().unwrap().region(),
);
env.problem(Problem::DefsOnlyUsedInRecursion(rec_defs.len(), region));
}
Declaration::DeclareRec(rec_defs, IllegalCycleMark::new(var_store)) Declaration::DeclareRec(rec_defs, IllegalCycleMark::new(var_store))
}; };
@ -2312,10 +2359,15 @@ pub fn can_defs_with_return<'a>(
output output
.introduced_variables .introduced_variables
.union(&defs_output.introduced_variables); .union(&defs_output.introduced_variables);
// Sort the defs with the output of the return expression - we'll use this to catch unused defs
// due only to recursion.
let (declarations, mut output) = sort_can_defs(env, var_store, unsorted, output);
output.references.union_mut(&defs_output.references); output.references.union_mut(&defs_output.references);
// Now that we've collected all the references, check to see if any of the new idents // Now that we've collected all the references, check to see if any of the new idents
// we defined went unused by the return expression. If any were unused, report it. // we defined went unused by the return expression or any other def.
for (symbol, region) in symbols_introduced { for (symbol, region) in symbols_introduced {
if !output.references.has_type_or_value_lookup(symbol) if !output.references.has_type_or_value_lookup(symbol)
&& !scope.abilities_store.is_specialization_name(symbol) && !scope.abilities_store.is_specialization_name(symbol)
@ -2324,8 +2376,6 @@ pub fn can_defs_with_return<'a>(
} }
} }
let (declarations, output) = sort_can_defs(env, var_store, unsorted, output);
let mut loc_expr: Loc<Expr> = ret_expr; let mut loc_expr: Loc<Expr> = ret_expr;
for declaration in declarations.into_iter().rev() { for declaration in declarations.into_iter().rev() {
@ -2738,12 +2788,12 @@ fn correct_mutual_recursive_type_alias<'a>(
// this is needed. // this is needed.
let scratchpad_capacity = sccs let scratchpad_capacity = sccs
.groups() .groups()
.map(|r| r.count_ones()) .map(|(r, _)| r.count_ones())
.max() .max()
.unwrap_or_default(); .unwrap_or_default();
let mut scratchpad = Vec::with_capacity(scratchpad_capacity); let mut scratchpad = Vec::with_capacity(scratchpad_capacity);
for cycle in sccs.groups() { for (cycle, _is_initial) in sccs.groups() {
debug_assert!(cycle.count_ones() > 0); debug_assert!(cycle.count_ones() > 0);
// We need to instantiate the alias with any symbols in the currrent module it // We need to instantiate the alias with any symbols in the currrent module it

View file

@ -3003,7 +3003,7 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: b
let mut loc_expr = Loc::at(expect_region, expect); let mut loc_expr = Loc::at(expect_region, expect);
for stored in stack { for stored in stack.into_iter().rev() {
match stored { match stored {
StoredDef::NonRecursive(region, boxed_def) => { StoredDef::NonRecursive(region, boxed_def) => {
loc_expr = Loc::at(region, Expr::LetNonRec(boxed_def, Box::new(loc_expr))); loc_expr = Loc::at(region, Expr::LetNonRec(boxed_def, Box::new(loc_expr)));

View file

@ -376,6 +376,9 @@ pub fn canonicalize_module_defs<'a>(
// See if any of the new idents we defined went unused. // See if any of the new idents we defined went unused.
// If any were unused and also not exposed, report it. // If any were unused and also not exposed, report it.
//
// We'll catch symbols that are only referenced due to (mutual) recursion later,
// when sorting the defs.
for (symbol, region) in symbols_introduced { for (symbol, region) in symbols_introduced {
if !output.references.has_type_or_value_lookup(symbol) if !output.references.has_type_or_value_lookup(symbol)
&& !exposed_symbols.contains(&symbol) && !exposed_symbols.contains(&symbol)
@ -427,8 +430,14 @@ pub fn canonicalize_module_defs<'a>(
..Default::default() ..Default::default()
}; };
let (mut declarations, mut output) = let (mut declarations, mut output) = crate::def::sort_can_defs_new(
crate::def::sort_can_defs_new(&mut scope, var_store, defs, new_output); &mut env,
&mut scope,
var_store,
defs,
new_output,
exposed_symbols,
);
debug_assert!( debug_assert!(
output.pending_derives.is_empty(), output.pending_derives.is_empty(),

View file

@ -181,7 +181,7 @@ struct Params {
p: Vec<u32>, p: Vec<u32>,
s: Vec<u32>, s: Vec<u32>,
scc: Sccs, scc: Sccs,
scca: BitVec, scca: Vec<u32>,
} }
impl Params { impl Params {
@ -200,8 +200,10 @@ impl Params {
scc: Sccs { scc: Sccs {
matrix: ReferenceMatrix::new(length), matrix: ReferenceMatrix::new(length),
components: 0, components: 0,
not_initial: BitVec::repeat(false, length),
}, },
scca: BitVec::repeat(false, length), // use u32::MAX as the sentinel empty value
scca: vec![u32::MAX; length],
} }
} }
} }
@ -215,7 +217,7 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
params.p.push(v as u32); params.p.push(v as u32);
for w in bitvec[v * length..][..length].iter_ones() { for w in bitvec[v * length..][..length].iter_ones() {
if !params.scca[w] { if params.scca[w] == u32::MAX {
match params.preorders[w] { match params.preorders[w] {
Preorder::Filled(pw) => loop { Preorder::Filled(pw) => loop {
let index = *params.p.last().unwrap(); let index = *params.p.last().unwrap();
@ -235,6 +237,8 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
Preorder::Empty => recurse_onto(length, bitvec, w, params), Preorder::Empty => recurse_onto(length, bitvec, w, params),
Preorder::Removed => {} Preorder::Removed => {}
} }
} else {
params.scc.not_initial.set(params.scca[w] as _, true);
} }
} }
@ -246,13 +250,17 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
.scc .scc
.matrix .matrix
.set_row_col(params.scc.components, node as usize, true); .set_row_col(params.scc.components, node as usize, true);
params.scca.set(node as usize, true); params.scca[node as usize] = params.scc.components as _;
params.preorders[node as usize] = Preorder::Removed; params.preorders[node as usize] = Preorder::Removed;
if node as usize == v { if node as usize == v {
break; break;
} }
} }
if !params.s.is_empty() {
params.scc.not_initial.set(params.scc.components, true);
}
params.scc.components += 1; params.scc.components += 1;
} }
} }
@ -261,6 +269,7 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
pub struct Sccs { pub struct Sccs {
components: usize, components: usize,
matrix: ReferenceMatrix, matrix: ReferenceMatrix,
not_initial: BitVec,
} }
impl Sccs { impl Sccs {
@ -271,7 +280,7 @@ impl Sccs {
/// ///
/// It is guaranteed that a group is non-empty, and that flattening the groups gives a valid /// It is guaranteed that a group is non-empty, and that flattening the groups gives a valid
/// topological ordering. /// topological ordering.
pub fn groups(&self) -> std::iter::Take<bitvec::slice::ChunksExact<'_, Element, Order>> { pub fn groups(&self) -> impl DoubleEndedIterator<Item = (&'_ BitSlice, bool)> {
// work around a panic when requesting a chunk size of 0 // work around a panic when requesting a chunk size of 0
let length = if self.matrix.length == 0 { let length = if self.matrix.length == 0 {
// the `.take(self.components)` ensures the resulting iterator will be empty // the `.take(self.components)` ensures the resulting iterator will be empty
@ -286,13 +295,15 @@ impl Sccs {
.bitvec .bitvec
.chunks_exact(length) .chunks_exact(length)
.take(self.components) .take(self.components)
.enumerate()
.map(|(c, slice)| (slice, !self.not_initial[c]))
} }
/// Reorder the input slice based on the SCCs. This produces a topological sort /// Reorder the input slice based on the SCCs. This produces a topological sort
pub fn reorder<T>(&self, slice: &mut [T]) { pub fn reorder<T>(&self, slice: &mut [T]) {
debug_assert_eq!(self.matrix.length, slice.len()); debug_assert_eq!(self.matrix.length, slice.len());
let mut indices: Vec<_> = self.groups().flat_map(|s| s.iter_ones()).collect(); let mut indices: Vec<_> = self.groups().flat_map(|(s, _)| s.iter_ones()).collect();
for i in 0..slice.len() { for i in 0..slice.len() {
let mut index = indices[i]; let mut index = indices[i];

View file

@ -79,6 +79,9 @@ flags! {
/// Only use this in single-threaded mode! /// Only use this in single-threaded mode!
ROC_PRINT_UNIFICATIONS ROC_PRINT_UNIFICATIONS
/// Prints types whose ability impls failed to be derived.
ROC_PRINT_UNDERIVABLE
/// Prints traces of unspecialized lambda set compaction /// Prints traces of unspecialized lambda set compaction
ROC_TRACE_COMPACTION ROC_TRACE_COMPACTION
@ -116,6 +119,9 @@ flags! {
// ===Mono=== // ===Mono===
/// Type-checks the mono IR after specialization.
ROC_CHECK_MONO_IR
/// Writes a pretty-printed mono IR to stderr after function specialization. /// Writes a pretty-printed mono IR to stderr after function specialization.
ROC_PRINT_IR_AFTER_SPECIALIZATION ROC_PRINT_IR_AFTER_SPECIALIZATION

View file

@ -174,6 +174,9 @@ const fn num_symbol_to_hash_lambda(symbol: Symbol) -> Option<FlatHash> {
Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => { Symbol::NUM_I128 | Symbol::NUM_SIGNED128 => {
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I128)) Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I128))
} }
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_NAT))
}
_ => None, _ => None,
} }
} }

View file

@ -11,3 +11,4 @@ roc_collections = { path = "../collections" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_error_macros = { path = "../../error_macros" } roc_error_macros = { path = "../../error_macros" }
roc_problem = { path = "../problem" }

View file

@ -7,6 +7,7 @@ use roc_module::{
ident::{Lowercase, TagIdIntType, TagName}, ident::{Lowercase, TagIdIntType, TagName},
symbol::Symbol, symbol::Symbol,
}; };
use roc_problem::Severity;
use roc_region::all::Region; use roc_region::all::Region;
use self::Pattern::*; use self::Pattern::*;
@ -149,6 +150,17 @@ pub enum Error {
}, },
} }
impl Error {
pub fn severity(&self) -> Severity {
use Severity::*;
match self {
Error::Incomplete(..) => RuntimeError,
Error::Redundant { .. } => Warning,
Error::Unmatchable { .. } => Warning,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Context { pub enum Context {
BadArg, BadArg,

View file

@ -298,17 +298,7 @@ impl<'a> Formattable for Expr<'a> {
} }
SingleQuote(string) => { SingleQuote(string) => {
buf.indent(indent); buf.indent(indent);
buf.push('\''); format_sq_literal(buf, string);
for c in string.chars() {
if c == '"' {
buf.push_char_literal('"')
} else {
for escaped in c.escape_default() {
buf.push_char_literal(escaped);
}
}
}
buf.push('\'');
} }
&NonBase10Int { &NonBase10Int {
base, base,
@ -438,6 +428,20 @@ impl<'a> Formattable for Expr<'a> {
} }
} }
pub(crate) fn format_sq_literal(buf: &mut Buf, s: &str) {
buf.push('\'');
for c in s.chars() {
if c == '"' {
buf.push_char_literal('"')
} else {
for escaped in c.escape_default() {
buf.push_char_literal(escaped);
}
}
}
buf.push('\'');
}
fn starts_with_newline(expr: &Expr) -> bool { fn starts_with_newline(expr: &Expr) -> bool {
use roc_parse::ast::Expr::*; use roc_parse::ast::Expr::*;

View file

@ -1,5 +1,5 @@
use crate::annotation::{Formattable, Newlines, Parens}; use crate::annotation::{Formattable, Newlines, Parens};
use crate::expr::fmt_str_literal; use crate::expr::{fmt_str_literal, format_sq_literal};
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt}; use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt};
use crate::Buf; use crate::Buf;
use roc_parse::ast::{Base, CommentOrNewline, Pattern}; use roc_parse::ast::{Base, CommentOrNewline, Pattern};
@ -155,9 +155,7 @@ impl<'a> Formattable for Pattern<'a> {
StrLiteral(literal) => fmt_str_literal(buf, *literal, indent), StrLiteral(literal) => fmt_str_literal(buf, *literal, indent),
SingleQuote(string) => { SingleQuote(string) => {
buf.indent(indent); buf.indent(indent);
buf.push('\''); format_sq_literal(buf, string);
buf.push_str(string);
buf.push('\'');
} }
Underscore(name) => { Underscore(name) => {
buf.indent(indent); buf.indent(indent);

View file

@ -5673,6 +5673,18 @@ mod test_fmt {
)); ));
} }
#[test]
fn format_char_pattern() {
expr_formats_same(indoc!(
r#"
when x is
' ' -> x
'\n' -> x
'\t' -> x
"#
));
}
#[test] #[test]
fn format_nested_pipeline() { fn format_nested_pipeline() {
expr_formats_same(indoc!( expr_formats_same(indoc!(

View file

@ -37,6 +37,7 @@ use roc_collections::all::{ImMap, MutMap, MutSet};
use roc_debug_flags::dbg_do; use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION; use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_error_macros::internal_error;
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{ use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc, BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
@ -5610,3 +5611,23 @@ pub fn add_func<'ctx>(
fn_val fn_val
} }
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum WhenRecursive<'a> {
Unreachable,
Loop(UnionLayout<'a>),
}
impl<'a> WhenRecursive<'a> {
pub fn unwrap_recursive_pointer(&self, layout: Layout<'a>) -> Layout<'a> {
match layout {
Layout::RecursivePointer => match self {
WhenRecursive::Loop(lay) => Layout::Union(*lay),
WhenRecursive::Unreachable => {
internal_error!("cannot compare recursive pointers outside of a structure")
}
},
_ => layout,
}
}
}

View file

@ -1,4 +1,6 @@
use crate::llvm::build::{get_tag_id, tag_pointer_clear_tag_id, Env, FAST_CALL_CONV}; use crate::llvm::build::{
get_tag_id, tag_pointer_clear_tag_id, Env, WhenRecursive, FAST_CALL_CONV,
};
use crate::llvm::build_list::{list_len, load_list_ptr}; use crate::llvm::build_list::{list_len, load_list_ptr};
use crate::llvm::build_str::str_equal; use crate::llvm::build_str::str_equal;
use crate::llvm::convert::basic_type_from_layout; use crate::llvm::convert::basic_type_from_layout;
@ -17,12 +19,6 @@ use super::build::{load_roc_value, use_roc_value};
use super::convert::argument_type_from_union_layout; use super::convert::argument_type_from_union_layout;
use super::lowlevel::dec_binop_with_unchecked; use super::lowlevel::dec_binop_with_unchecked;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
Loop(UnionLayout<'a>),
}
pub fn generic_eq<'a, 'ctx, 'env>( pub fn generic_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
@ -406,8 +402,9 @@ fn build_list_eq<'a, 'ctx, 'env>(
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::LIST_EQ; let symbol = Symbol::LIST_EQ;
let element_layout = when_recursive.unwrap_recursive_pointer(*element_layout);
let fn_name = layout_ids let fn_name = layout_ids
.get(symbol, element_layout) .get(symbol, &element_layout)
.to_symbol_string(symbol, &env.interns); .to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
@ -427,7 +424,7 @@ fn build_list_eq<'a, 'ctx, 'env>(
layout_ids, layout_ids,
when_recursive, when_recursive,
function_value, function_value,
element_layout, &element_layout,
); );
function_value function_value

View file

@ -2,7 +2,7 @@ use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn; use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{ use crate::llvm::build::{
add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env, add_func, cast_basic_basic, get_tag_id, tag_pointer_clear_tag_id, use_roc_value, Env,
FAST_CALL_CONV, WhenRecursive, FAST_CALL_CONV,
}; };
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list}; use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
use crate::llvm::convert::{basic_type_from_layout, RocUnion}; use crate::llvm::convert::{basic_type_from_layout, RocUnion};
@ -399,14 +399,8 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
match builtin { match builtin {
List(element_layout) => { List(element_layout) => {
let function = modify_refcount_list( let function =
env, modify_refcount_list(env, layout_ids, mode, when_recursive, element_layout);
layout_ids,
mode,
when_recursive,
layout,
element_layout,
);
Some(function) Some(function)
} }
@ -437,12 +431,6 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
); );
} }
#[derive(Clone, Debug, PartialEq)]
enum WhenRecursive<'a> {
Unreachable,
Loop(UnionLayout<'a>),
}
fn modify_refcount_layout_help<'a, 'ctx, 'env>( fn modify_refcount_layout_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
@ -609,25 +597,26 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
mode: Mode, mode: Mode,
when_recursive: &WhenRecursive<'a>, when_recursive: &WhenRecursive<'a>,
layout: &Layout<'a>,
element_layout: &Layout<'a>, element_layout: &Layout<'a>,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function"); let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap(); let di_location = env.builder.get_current_debug_location().unwrap();
let element_layout = when_recursive.unwrap_recursive_pointer(*element_layout);
let list_layout = &Layout::Builtin(Builtin::List(env.arena.alloc(element_layout)));
let (_, fn_name) = function_name_from_mode( let (_, fn_name) = function_name_from_mode(
layout_ids, layout_ids,
&env.interns, &env.interns,
"increment_list", "increment_list",
"decrement_list", "decrement_list",
layout, list_layout,
mode, mode,
); );
let function = match env.module.get_function(fn_name.as_str()) { let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value, Some(function_value) => function_value,
None => { None => {
let basic_type = argument_type_from_layout(env, layout); let basic_type = argument_type_from_layout(env, list_layout);
let function_value = build_header(env, basic_type, mode, &fn_name); let function_value = build_header(env, basic_type, mode, &fn_name);
modify_refcount_list_help( modify_refcount_list_help(
@ -635,8 +624,8 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
layout_ids, layout_ids,
mode, mode,
when_recursive, when_recursive,
layout, list_layout,
element_layout, &element_layout,
function_value, function_value,
); );

View file

@ -744,7 +744,7 @@ impl<'a> WasmBackend<'a> {
let mut current_stmt = stmt; let mut current_stmt = stmt;
while let Stmt::Let(sym, expr, layout, following) = current_stmt { while let Stmt::Let(sym, expr, layout, following) = current_stmt {
if DEBUG_SETTINGS.let_stmt_ir { if DEBUG_SETTINGS.let_stmt_ir {
print!("\nlet {:?} = {}", sym, expr.to_pretty(200)); print!("\nlet {:?} = {}", sym, expr.to_pretty(200, true));
} }
let kind = match following { let kind = match following {
@ -974,7 +974,7 @@ impl<'a> WasmBackend<'a> {
self.register_symbol_debug_names(); self.register_symbol_debug_names();
println!( println!(
"## rc_stmt:\n{}\n{:?}", "## rc_stmt:\n{}\n{:?}",
rc_stmt.to_pretty(self.env.layout_interner, 200), rc_stmt.to_pretty(self.env.layout_interner, 200, true),
rc_stmt rc_stmt
); );
} }
@ -1078,7 +1078,7 @@ impl<'a> WasmBackend<'a> {
Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage), Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
Expr::RuntimeErrorFunction(_) => { Expr::RuntimeErrorFunction(_) => {
todo!("Expression `{}`", expr.to_pretty(100)) todo!("Expression `{}`", expr.to_pretty(100, false))
} }
} }
} }

View file

@ -143,7 +143,7 @@ pub fn build_app_module<'a>(
if DEBUG_SETTINGS.user_procs_ir { if DEBUG_SETTINGS.user_procs_ir {
println!("## procs"); println!("## procs");
for proc in procs.iter() { for proc in procs.iter() {
println!("{}", proc.to_pretty(env.layout_interner, 200)); println!("{}", proc.to_pretty(env.layout_interner, 200, true));
// println!("{:?}", proc); // println!("{:?}", proc);
} }
} }
@ -161,7 +161,7 @@ pub fn build_app_module<'a>(
if DEBUG_SETTINGS.helper_procs_ir { if DEBUG_SETTINGS.helper_procs_ir {
println!("## helper_procs"); println!("## helper_procs");
for proc in helper_procs.iter() { for proc in helper_procs.iter() {
println!("{}", proc.to_pretty(env.layout_interner, 200)); println!("{}", proc.to_pretty(env.layout_interner, 200, true));
// println!("{:#?}", proc); // println!("{:#?}", proc);
} }
} }

View file

@ -101,14 +101,14 @@ pub fn load_and_monomorphize_from_str<'a>(
exposed_types: ExposedByModule, exposed_types: ExposedByModule,
roc_cache_dir: RocCacheDir<'_>, roc_cache_dir: RocCacheDir<'_>,
load_config: LoadConfig, load_config: LoadConfig,
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> { ) -> Result<MonomorphizedModule<'a>, LoadMonomorphizedError<'a>> {
use LoadResult::*; use LoadResult::*;
let load_start = LoadStart::from_str(arena, filename, src, roc_cache_dir, src_dir)?; let load_start = LoadStart::from_str(arena, filename, src, roc_cache_dir, src_dir)?;
match load(arena, load_start, exposed_types, roc_cache_dir, load_config)? { match load(arena, load_start, exposed_types, roc_cache_dir, load_config)? {
Monomorphized(module) => Ok(module), Monomorphized(module) => Ok(module),
TypeChecked(_) => unreachable!(""), TypeChecked(module) => Err(LoadMonomorphizedError::ErrorModule(module)),
} }
} }

View file

@ -1,3 +1,5 @@
#![allow(clippy::too_many_arguments)]
use crate::docs::ModuleDocumentation; use crate::docs::ModuleDocumentation;
use bumpalo::Bump; use bumpalo::Bump;
use crossbeam::channel::{bounded, Sender}; use crossbeam::channel::{bounded, Sender};
@ -17,8 +19,8 @@ use roc_constrain::module::constrain_module;
use roc_debug_flags::dbg_do; use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use roc_debug_flags::{ use roc_debug_flags::{
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_CHECK_MONO_IR, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
ROC_PRINT_LOAD_LOG, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG,
}; };
use roc_derive::SharedDerivedModule; use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
@ -45,12 +47,13 @@ use roc_parse::header::{HeaderFor, ModuleNameEnum, PackageName};
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError}; use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError};
use roc_problem::Severity;
use roc_region::all::{LineInfo, Loc, Region}; use roc_region::all::{LineInfo, Loc, Region};
use roc_reporting::report::{Annotation, Palette, RenderTarget}; use roc_reporting::report::{Annotation, Palette, RenderTarget};
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule}; use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
use roc_solve_problem::TypeError; use roc_solve_problem::TypeError;
use roc_target::TargetInfo; use roc_target::TargetInfo;
use roc_types::subs::{ExposedTypesStorageSubs, Subs, VarStore, Variable}; use roc_types::subs::{CopiedImport, ExposedTypesStorageSubs, Subs, VarStore, Variable};
use roc_types::types::{Alias, Types}; use roc_types::types::{Alias, Types};
use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap; use std::collections::HashMap;
@ -97,20 +100,28 @@ pub struct LoadConfig {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum ExecutionMode { pub enum ExecutionMode {
Test,
Check, Check,
Executable, Executable,
/// Like [`ExecutionMode::Executable`], but stops in the presence of type errors. /// Like [`ExecutionMode::Executable`], but stops in the presence of type errors.
ExecutableIfCheck, ExecutableIfCheck,
/// Test is like [`ExecutionMode::ExecutableIfCheck`], but rather than producing a proper
/// executable, run tests.
Test,
} }
impl ExecutionMode { impl ExecutionMode {
fn goal_phase(&self) -> Phase { fn goal_phase(&self) -> Phase {
match self { match self {
ExecutionMode::Test | ExecutionMode::Executable => Phase::MakeSpecializations, ExecutionMode::Executable => Phase::MakeSpecializations,
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck => Phase::SolveTypes, ExecutionMode::Check | ExecutionMode::ExecutableIfCheck | ExecutionMode::Test => {
Phase::SolveTypes
} }
} }
}
fn build_if_checks(&self) -> bool {
matches!(self, Self::ExecutableIfCheck | Self::Test)
}
} }
/// Struct storing various intermediate stages by their ModuleId /// Struct storing various intermediate stages by their ModuleId
@ -141,18 +152,22 @@ struct ModuleCache<'a> {
} }
impl<'a> ModuleCache<'a> { impl<'a> ModuleCache<'a> {
pub fn total_problems(&self) -> usize { fn has_can_errors(&self) -> bool {
let mut total = 0; self.can_problems
.values()
for problems in self.can_problems.values() { .flatten()
total += problems.len(); .any(|problem| problem.severity() == Severity::RuntimeError)
} }
for problems in self.type_problems.values() { fn has_type_errors(&self) -> bool {
total += problems.len(); self.type_problems
.values()
.flatten()
.any(|problem| problem.severity() == Severity::RuntimeError)
} }
total pub fn has_errors(&self) -> bool {
self.has_can_errors() || self.has_type_errors()
} }
} }
@ -963,7 +978,6 @@ impl<'a> State<'a> {
self.exec_mode.goal_phase() self.exec_mode.goal_phase()
} }
#[allow(clippy::too_many_arguments)]
fn new( fn new(
root_id: ModuleId, root_id: ModuleId,
target_info: TargetInfo, target_info: TargetInfo,
@ -1208,7 +1222,6 @@ fn enqueue_task<'a>(
Ok(()) Ok(())
} }
#[allow(clippy::too_many_arguments)]
pub fn load_and_typecheck_str<'a>( pub fn load_and_typecheck_str<'a>(
arena: &'a Bump, arena: &'a Bump,
filename: PathBuf, filename: PathBuf,
@ -1495,7 +1508,6 @@ pub enum Threading {
/// and then linking them together, and possibly caching them by the hash of their /// and then linking them together, and possibly caching them by the hash of their
/// specializations, so if none of their specializations changed, we don't even need /// specializations, so if none of their specializations changed, we don't even need
/// to rebuild the module and can link in the cached one directly.) /// to rebuild the module and can link in the cached one directly.)
#[allow(clippy::too_many_arguments)]
pub fn load<'a>( pub fn load<'a>(
arena: &'a Bump, arena: &'a Bump,
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
@ -1556,7 +1568,6 @@ pub fn load<'a>(
} }
/// Load using only a single thread; used when compiling to webassembly /// Load using only a single thread; used when compiling to webassembly
#[allow(clippy::too_many_arguments)]
pub fn load_single_threaded<'a>( pub fn load_single_threaded<'a>(
arena: &'a Bump, arena: &'a Bump,
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
@ -1821,7 +1832,6 @@ fn state_thread_step<'a>(
} }
} }
#[allow(clippy::too_many_arguments)]
fn load_multi_threaded<'a>( fn load_multi_threaded<'a>(
arena: &'a Bump, arena: &'a Bump,
load_start: LoadStart<'a>, load_start: LoadStart<'a>,
@ -1997,7 +2007,6 @@ fn load_multi_threaded<'a>(
.unwrap() .unwrap()
} }
#[allow(clippy::too_many_arguments)]
fn worker_task_step<'a>( fn worker_task_step<'a>(
worker_arena: &'a Bump, worker_arena: &'a Bump,
worker: &Worker<BuildTask<'a>>, worker: &Worker<BuildTask<'a>>,
@ -2072,7 +2081,6 @@ fn worker_task_step<'a>(
} }
} }
#[allow(clippy::too_many_arguments)]
fn worker_task<'a>( fn worker_task<'a>(
worker_arena: &'a Bump, worker_arena: &'a Bump,
worker: Worker<BuildTask<'a>>, worker: Worker<BuildTask<'a>>,
@ -2164,7 +2172,7 @@ macro_rules! debug_print_ir {
let procs_string = $state let procs_string = $state
.procedures .procedures
.values() .values()
.map(|proc| proc.to_pretty($interner, 200)) .map(|proc| proc.to_pretty($interner, 200, true))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let result = procs_string.join("\n"); let result = procs_string.join("\n");
@ -2174,6 +2182,27 @@ macro_rules! debug_print_ir {
}; };
} }
macro_rules! debug_check_ir {
($state:expr, $arena:expr, $interner:expr, $flag:path) => {
dbg_do!($flag, {
use roc_mono::debug::{check_procs, format_problems};
let interns = Interns {
module_ids: $state.arc_modules.lock().clone().into_module_ids(),
all_ident_ids: $state.constrained_ident_ids.clone(),
};
let procedures = &$state.procedures;
let problems = check_procs($arena, $interner, procedures);
if !problems.is_empty() {
let formatted = format_problems(&interns, $interner, problems);
eprintln!("IR PROBLEMS FOUND:\n{formatted}");
}
})
};
}
/// Report modules that are imported, but from which nothing is used /// Report modules that are imported, but from which nothing is used
fn report_unused_imported_modules<'a>( fn report_unused_imported_modules<'a>(
state: &mut State<'a>, state: &mut State<'a>,
@ -2615,7 +2644,7 @@ fn update<'a>(
let finish_type_checking = is_host_exposed && let finish_type_checking = is_host_exposed &&
(state.goal_phase() == Phase::SolveTypes) (state.goal_phase() == Phase::SolveTypes)
// If we're running in check-and-then-build mode, only exit now there are errors. // If we're running in check-and-then-build mode, only exit now there are errors.
&& (!matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) || state.module_cache.total_problems() > 0); && (!state.exec_mode.build_if_checks() || state.module_cache.has_errors());
if finish_type_checking { if finish_type_checking {
debug_assert!(work.is_empty()); debug_assert!(work.is_empty());
@ -2623,7 +2652,7 @@ fn update<'a>(
state.timings.insert(module_id, module_timing); state.timings.insert(module_id, module_timing);
if matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) { if state.exec_mode.build_if_checks() {
// We there may outstanding modules in the typecheked cache whose ident IDs // We there may outstanding modules in the typecheked cache whose ident IDs
// aren't registered; transfer all of their idents over to the state, since // aren't registered; transfer all of their idents over to the state, since
// we're now done and ready to report errors. // we're now done and ready to report errors.
@ -2677,9 +2706,7 @@ fn update<'a>(
}, },
); );
if state.goal_phase() > Phase::SolveTypes if state.goal_phase() > Phase::SolveTypes || state.exec_mode.build_if_checks() {
|| matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
let layout_cache = state.layout_caches.pop().unwrap_or_else(|| { let layout_cache = state.layout_caches.pop().unwrap_or_else(|| {
LayoutCache::new(state.layout_interner.fork(), state.target_info) LayoutCache::new(state.layout_interner.fork(), state.target_info)
}); });
@ -2703,17 +2730,12 @@ fn update<'a>(
state.timings.insert(module_id, module_timing); state.timings.insert(module_id, module_timing);
} }
let work = if is_host_exposed let work = if is_host_exposed && state.exec_mode.build_if_checks() {
&& matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
debug_assert!( debug_assert!(
work.is_empty(), work.is_empty(),
"work left over after host exposed is checked" "work left over after host exposed is checked"
); );
// Update the goal phase to target full codegen.
state.exec_mode = ExecutionMode::Executable;
// Load the find + make specializations portion of the dependency graph. // Load the find + make specializations portion of the dependency graph.
state state
.dependencies .dependencies
@ -2785,7 +2807,10 @@ fn update<'a>(
layout_cache, layout_cache,
.. ..
} => { } => {
debug_assert!(state.goal_phase() == Phase::MakeSpecializations); debug_assert!(
state.goal_phase() == Phase::MakeSpecializations
|| state.exec_mode.build_if_checks()
);
log!("made specializations for {:?}", module_id); log!("made specializations for {:?}", module_id);
@ -2896,6 +2921,7 @@ fn update<'a>(
log!("specializations complete from {:?}", module_id); log!("specializations complete from {:?}", module_id);
debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_SPECIALIZATION); debug_print_ir!(state, &layout_interner, ROC_PRINT_IR_AFTER_SPECIALIZATION);
debug_check_ir!(state, arena, &layout_interner, ROC_CHECK_MONO_IR);
let ident_ids = state.constrained_ident_ids.get_mut(&module_id).unwrap(); let ident_ids = state.constrained_ident_ids.get_mut(&module_id).unwrap();
@ -3205,7 +3231,6 @@ fn finish_specialization<'a>(
}) })
} }
#[allow(clippy::too_many_arguments)]
fn finish( fn finish(
mut state: State, mut state: State,
solved: Solved<Subs>, solved: Solved<Subs>,
@ -3644,7 +3669,6 @@ fn verify_interface_matches_file_path<'a>(
Err(problem) Err(problem)
} }
#[allow(clippy::too_many_arguments)]
fn parse_header<'a>( fn parse_header<'a>(
arena: &'a Bump, arena: &'a Bump,
read_file_duration: Duration, read_file_duration: Duration,
@ -3931,7 +3955,6 @@ fn parse_header<'a>(
} }
/// Load a module by its filename /// Load a module by its filename
#[allow(clippy::too_many_arguments)]
fn load_filename<'a>( fn load_filename<'a>(
arena: &'a Bump, arena: &'a Bump,
filename: PathBuf, filename: PathBuf,
@ -3970,7 +3993,6 @@ fn load_filename<'a>(
/// Load a module from a str /// Load a module from a str
/// the `filename` is never read, but used for the module name /// the `filename` is never read, but used for the module name
#[allow(clippy::too_many_arguments)]
fn load_from_str<'a>( fn load_from_str<'a>(
arena: &'a Bump, arena: &'a Bump,
filename: PathBuf, filename: PathBuf,
@ -4010,7 +4032,6 @@ struct HeaderInfo<'a> {
extra: HeaderFor<'a>, extra: HeaderFor<'a>,
} }
#[allow(clippy::too_many_arguments)]
fn build_header<'a>( fn build_header<'a>(
info: HeaderInfo<'a>, info: HeaderInfo<'a>,
parse_state: roc_parse::state::State<'a>, parse_state: roc_parse::state::State<'a>,
@ -4236,7 +4257,6 @@ struct PlatformHeaderInfo<'a> {
} }
// TODO refactor so more logic is shared with `send_header` // TODO refactor so more logic is shared with `send_header`
#[allow(clippy::too_many_arguments)]
fn send_header_two<'a>( fn send_header_two<'a>(
info: PlatformHeaderInfo<'a>, info: PlatformHeaderInfo<'a>,
parse_state: roc_parse::state::State<'a>, parse_state: roc_parse::state::State<'a>,
@ -4488,7 +4508,6 @@ fn send_header_two<'a>(
impl<'a> BuildTask<'a> { impl<'a> BuildTask<'a> {
// TODO trim down these arguments - possibly by moving Constraint into Module // TODO trim down these arguments - possibly by moving Constraint into Module
#[allow(clippy::too_many_arguments)]
fn solve_module( fn solve_module(
module: Module, module: Module,
ident_ids: IdentIds, ident_ids: IdentIds,
@ -4575,61 +4594,26 @@ pub fn add_imports(
mut pending_abilities: PendingAbilitiesStore, mut pending_abilities: PendingAbilitiesStore,
exposed_for_module: &ExposedForModule, exposed_for_module: &ExposedForModule,
def_types: &mut Vec<(Symbol, Loc<TypeOrVar>)>, def_types: &mut Vec<(Symbol, Loc<TypeOrVar>)>,
rigid_vars: &mut Vec<Variable>, imported_rigid_vars: &mut Vec<Variable>,
imported_flex_vars: &mut Vec<Variable>,
) -> (Vec<Variable>, AbilitiesStore) { ) -> (Vec<Variable>, AbilitiesStore) {
let mut import_variables = Vec::new(); let mut import_variables = Vec::new();
let mut cached_symbol_vars = VecMap::default(); let mut cached_symbol_vars = VecMap::default();
macro_rules! import_var_for_symbol {
($subs:expr, $exposed_by_module:expr, $symbol:ident, $break:stmt) => {
let module_id = $symbol.module_id();
match $exposed_by_module.get(&module_id) {
Some(ExposedModuleTypes {
exposed_types_storage_subs: exposed_types,
resolved_implementations: _,
}) => {
let variable = match exposed_types.stored_vars_by_symbol.iter().find(|(s, _)| **s == $symbol) {
None => {
// Today we define builtins in each module that uses them
// so even though they have a different module name from
// the surrounding module, they are not technically imported
debug_assert!($symbol.is_builtin());
$break
}
Some((_, x)) => *x,
};
let copied_import = exposed_types.storage_subs.export_variable_to($subs, variable);
let copied_import_index = constraints.push_variable(copied_import.variable);
def_types.push((
$symbol,
Loc::at_zero(copied_import_index),
));
// not a typo; rigids are turned into flex during type inference, but when imported we must
// consider them rigid variables
rigid_vars.extend(copied_import.rigid);
rigid_vars.extend(copied_import.flex);
// Rigid vars bound to abilities are also treated like rigids.
rigid_vars.extend(copied_import.rigid_able);
rigid_vars.extend(copied_import.flex_able);
import_variables.extend(copied_import.registered);
cached_symbol_vars.insert($symbol, copied_import.variable);
}
None => {
internal_error!("Imported module {:?} is not available", module_id)
}
}
}
}
for &symbol in &exposed_for_module.imported_values { for &symbol in &exposed_for_module.imported_values {
import_var_for_symbol!(subs, exposed_for_module.exposed_by_module, symbol, continue); import_variable_for_symbol(
subs,
constraints,
def_types,
&mut import_variables,
imported_rigid_vars,
imported_flex_vars,
&mut cached_symbol_vars,
&exposed_for_module.exposed_by_module,
symbol,
OnSymbolNotFound::AssertIsBuiltin,
);
} }
// Patch used symbols from circular dependencies. // Patch used symbols from circular dependencies.
@ -4656,6 +4640,9 @@ pub fn add_imports(
struct Ctx<'a> { struct Ctx<'a> {
subs: &'a mut Subs, subs: &'a mut Subs,
exposed_by_module: &'a ExposedByModule, exposed_by_module: &'a ExposedByModule,
imported_variables: &'a mut Vec<Variable>,
imported_rigids: &'a mut Vec<Variable>,
imported_flex: &'a mut Vec<Variable>,
} }
let abilities_store = pending_abilities.resolve_for_module( let abilities_store = pending_abilities.resolve_for_module(
@ -4663,16 +4650,26 @@ pub fn add_imports(
&mut Ctx { &mut Ctx {
subs, subs,
exposed_by_module: &exposed_for_module.exposed_by_module, exposed_by_module: &exposed_for_module.exposed_by_module,
imported_variables: &mut import_variables,
imported_rigids: imported_rigid_vars,
imported_flex: imported_flex_vars,
}, },
|ctx, symbol| match cached_symbol_vars.get(&symbol).copied() { |ctx, symbol| match cached_symbol_vars.get(&symbol).copied() {
Some(var) => var, Some(var) => var,
None => { None => {
import_var_for_symbol!( import_variable_for_symbol(
ctx.subs, ctx.subs,
ctx.exposed_by_module, constraints,
def_types,
ctx.imported_variables,
ctx.imported_rigids,
ctx.imported_flex,
&mut cached_symbol_vars,
&exposed_for_module.exposed_by_module,
symbol, symbol,
internal_error!("Import ability member {:?} not available", symbol) OnSymbolNotFound::AbilityMemberMustBeAvailable,
); );
*cached_symbol_vars.get(&symbol).unwrap() *cached_symbol_vars.get(&symbol).unwrap()
} }
}, },
@ -4690,7 +4687,15 @@ pub fn add_imports(
.storage_subs .storage_subs
.export_variable_to(ctx.subs, *var); .export_variable_to(ctx.subs, *var);
copied_import.variable #[allow(clippy::let_and_return)]
let copied_import_var = extend_imports_data_with_copied_import(
copied_import,
ctx.imported_variables,
ctx.imported_rigids,
ctx.imported_flex,
);
copied_import_var
} }
None => internal_error!("Imported module {:?} is not available", module), None => internal_error!("Imported module {:?} is not available", module),
}, },
@ -4699,6 +4704,95 @@ pub fn add_imports(
(import_variables, abilities_store) (import_variables, abilities_store)
} }
enum OnSymbolNotFound {
AssertIsBuiltin,
AbilityMemberMustBeAvailable,
}
fn extend_imports_data_with_copied_import(
copied_import: CopiedImport,
imported_variables: &mut Vec<Variable>,
imported_rigids: &mut Vec<Variable>,
imported_flex: &mut Vec<Variable>,
) -> Variable {
// not a typo; rigids are turned into flex during type inference, but when imported we must
// consider them rigid variables
imported_rigids.extend(copied_import.rigid);
imported_flex.extend(copied_import.flex);
// Rigid vars bound to abilities are also treated like rigids.
imported_rigids.extend(copied_import.rigid_able);
imported_flex.extend(copied_import.flex_able);
imported_variables.extend(copied_import.registered);
copied_import.variable
}
fn import_variable_for_symbol(
subs: &mut Subs,
constraints: &mut Constraints,
def_types: &mut Vec<(Symbol, Loc<TypeOrVar>)>,
imported_variables: &mut Vec<Variable>,
imported_rigids: &mut Vec<Variable>,
imported_flex: &mut Vec<Variable>,
cached_symbol_vars: &mut VecMap<Symbol, Variable>,
exposed_by_module: &ExposedByModule,
symbol: Symbol,
on_symbol_not_found: OnSymbolNotFound,
) {
let module_id = symbol.module_id();
match exposed_by_module.get(&module_id) {
Some(ExposedModuleTypes {
exposed_types_storage_subs: exposed_types,
resolved_implementations: _,
}) => {
let variable = match exposed_types
.stored_vars_by_symbol
.iter()
.find(|(s, _)| **s == symbol)
{
None => {
use OnSymbolNotFound::*;
match on_symbol_not_found {
AssertIsBuiltin => {
// Today we define builtins in each module that uses them
// so even though they have a different module name from
// the surrounding module, they are not technically imported
debug_assert!(symbol.is_builtin());
return;
}
AbilityMemberMustBeAvailable => {
internal_error!("Import ability member {:?} not available", symbol);
}
}
}
Some((_, x)) => *x,
};
let copied_import = exposed_types
.storage_subs
.export_variable_to(subs, variable);
let copied_import_var = extend_imports_data_with_copied_import(
copied_import,
imported_variables,
imported_rigids,
imported_flex,
);
let copied_import_index = constraints.push_variable(copied_import_var);
def_types.push((symbol, Loc::at_zero(copied_import_index)));
cached_symbol_vars.insert(symbol, copied_import_var);
}
None => {
internal_error!("Imported module {:?} is not available", module_id)
}
}
}
#[allow(clippy::complexity)] #[allow(clippy::complexity)]
fn run_solve_solve( fn run_solve_solve(
exposed_for_module: ExposedForModule, exposed_for_module: ExposedForModule,
@ -4724,7 +4818,8 @@ fn run_solve_solve(
.. ..
} = module; } = module;
let mut rigid_vars: Vec<Variable> = Vec::new(); let mut imported_rigid_vars: Vec<Variable> = Vec::new();
let mut imported_flex_vars: Vec<Variable> = Vec::new();
let mut def_types: Vec<(Symbol, Loc<TypeOrVar>)> = Vec::new(); let mut def_types: Vec<(Symbol, Loc<TypeOrVar>)> = Vec::new();
let mut subs = Subs::new_from_varstore(var_store); let mut subs = Subs::new_from_varstore(var_store);
@ -4736,11 +4831,17 @@ fn run_solve_solve(
pending_abilities, pending_abilities,
&exposed_for_module, &exposed_for_module,
&mut def_types, &mut def_types,
&mut rigid_vars, &mut imported_rigid_vars,
&mut imported_flex_vars,
); );
let actual_constraint = let actual_constraint = constraints.let_import_constraint(
constraints.let_import_constraint(rigid_vars, def_types, constraint, &import_variables); imported_rigid_vars,
imported_flex_vars,
def_types,
constraint,
&import_variables,
);
let mut solve_aliases = roc_solve::solve::Aliases::with_capacity(aliases.len()); let mut solve_aliases = roc_solve::solve::Aliases::with_capacity(aliases.len());
for (name, (_, alias)) in aliases.iter() { for (name, (_, alias)) in aliases.iter() {
@ -4805,7 +4906,6 @@ fn run_solve_solve(
) )
} }
#[allow(clippy::too_many_arguments)]
fn run_solve<'a>( fn run_solve<'a>(
module: Module, module: Module,
ident_ids: IdentIds, ident_ids: IdentIds,
@ -4919,7 +5019,6 @@ fn unspace<'a, T: Copy>(arena: &'a Bump, items: &[Loc<Spaced<'a, T>>]) -> &'a [L
.into_bump_slice() .into_bump_slice()
} }
#[allow(clippy::too_many_arguments)]
fn fabricate_platform_module<'a>( fn fabricate_platform_module<'a>(
arena: &'a Bump, arena: &'a Bump,
opt_shorthand: Option<&'a str>, opt_shorthand: Option<&'a str>,
@ -4959,7 +5058,6 @@ fn fabricate_platform_module<'a>(
) )
} }
#[allow(clippy::too_many_arguments)]
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
fn canonicalize_and_constrain<'a>( fn canonicalize_and_constrain<'a>(
arena: &'a Bump, arena: &'a Bump,
@ -5233,7 +5331,6 @@ fn ident_from_exposed(entry: &Spaced<'_, ExposedName<'_>>) -> Ident {
entry.extract_spaces().item.as_str().into() entry.extract_spaces().item.as_str().into()
} }
#[allow(clippy::too_many_arguments)]
fn make_specializations<'a>( fn make_specializations<'a>(
arena: &'a Bump, arena: &'a Bump,
home: ModuleId, home: ModuleId,
@ -5310,7 +5407,6 @@ fn make_specializations<'a>(
} }
} }
#[allow(clippy::too_many_arguments)]
fn build_pending_specializations<'a>( fn build_pending_specializations<'a>(
arena: &'a Bump, arena: &'a Bump,
solved_subs: Solved<Subs>, solved_subs: Solved<Subs>,
@ -5741,7 +5837,6 @@ fn build_pending_specializations<'a>(
/// their specializations. /// their specializations.
// TODO: right now, this runs sequentially, and no other modules are mono'd in parallel to the // TODO: right now, this runs sequentially, and no other modules are mono'd in parallel to the
// derived module. // derived module.
#[allow(clippy::too_many_arguments)]
fn load_derived_partial_procs<'a>( fn load_derived_partial_procs<'a>(
home: ModuleId, home: ModuleId,
arena: &'a Bump, arena: &'a Bump,
@ -5987,7 +6082,7 @@ fn run_task<'a>(
} }
fn to_file_problem_report(filename: &Path, error: io::ErrorKind) -> String { fn to_file_problem_report(filename: &Path, error: io::ErrorKind) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
let src_lines: Vec<&str> = Vec::new(); let src_lines: Vec<&str> = Vec::new();
@ -6074,7 +6169,7 @@ fn to_import_cycle_report(
filename: PathBuf, filename: PathBuf,
render: RenderTarget, render: RenderTarget,
) -> String { ) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
// import_cycle looks like CycleModule, Import1, ..., ImportN, CycleModule // import_cycle looks like CycleModule, Import1, ..., ImportN, CycleModule
@ -6134,7 +6229,7 @@ fn to_incorrect_module_name_report<'a>(
src: &'a [u8], src: &'a [u8],
render: RenderTarget, render: RenderTarget,
) -> String { ) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
let IncorrectModuleName { let IncorrectModuleName {
@ -6220,7 +6315,7 @@ fn to_parse_problem_report<'a>(
} }
fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> String { fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE}; use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
use PlatformPath::*; use PlatformPath::*;

View file

@ -13,7 +13,7 @@ Model position :
} }
initialModel : position -> Model position initialModel : position -> Model position | position has Hash & Eq
initialModel = \start -> initialModel = \start ->
{ evaluated : Set.empty { evaluated : Set.empty
, openSet : Set.single start , openSet : Set.single start
@ -22,7 +22,7 @@ initialModel = \start ->
} }
cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] | position has Eq cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] | position has Hash & Eq
cheapestOpen = \costFunction, model -> cheapestOpen = \costFunction, model ->
folder = \resSmallestSoFar, position -> folder = \resSmallestSoFar, position ->
@ -47,7 +47,7 @@ cheapestOpen = \costFunction, model ->
reconstructPath : Dict position position, position -> List position | position has Eq reconstructPath : Dict position position, position -> List position | position has Hash & Eq
reconstructPath = \cameFrom, goal -> reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is when Dict.get cameFrom goal is
Err KeyNotFound -> Err KeyNotFound ->
@ -56,7 +56,7 @@ reconstructPath = \cameFrom, goal ->
Ok next -> Ok next ->
List.append (reconstructPath cameFrom next) goal List.append (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position | position has Eq updateCost : position, position, Model position -> Model position | position has Hash & Eq
updateCost = \current, neighbour, model -> updateCost = \current, neighbour, model ->
newCameFrom = Dict.insert model.cameFrom neighbour current newCameFrom = Dict.insert model.cameFrom neighbour current
@ -80,12 +80,12 @@ updateCost = \current, neighbour, model ->
model model
findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] | position has Eq findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] | position has Hash & Eq
findPath = \{ costFunction, moveFunction, start, end } -> findPath = \{ costFunction, moveFunction, start, end } ->
astar costFunction moveFunction end (initialModel start) astar costFunction moveFunction end (initialModel start)
astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Eq astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq
astar = \costFn, moveFn, goal, model -> astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\position -> costFn goal position) model is when cheapestOpen (\position -> costFn goal position) model is
Err _ -> Err _ ->

View file

@ -491,12 +491,12 @@ fn load_astar() {
expect_types( expect_types(
loaded_module, loaded_module,
hashmap! { hashmap! {
"findPath" => "{ costFunction : position, position -> F64, end : position, moveFunction : position -> Set position, start : position } -> Result (List position) [KeyNotFound] | position has Eq", "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", "initialModel" => "position -> Model position | position has Hash & Eq",
"reconstructPath" => "Dict position position, position -> List position | position has Eq", "reconstructPath" => "Dict position position, position -> List position | position has Hash & Eq",
"updateCost" => "position, position, Model position -> Model position | position has Eq", "updateCost" => "position, position, Model position -> Model position | position has Hash & Eq",
"cheapestOpen" => "(position -> F64), Model position -> Result position [KeyNotFound] | position has 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 Eq", "astar" => "(position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq",
}, },
); );
} }

View file

@ -1400,26 +1400,30 @@ define_builtins! {
0 DICT_DICT: "Dict" exposed_type=true // the Dict.Dict type alias 0 DICT_DICT: "Dict" exposed_type=true // the Dict.Dict type alias
1 DICT_EMPTY: "empty" 1 DICT_EMPTY: "empty"
2 DICT_SINGLE: "single" 2 DICT_SINGLE: "single"
3 DICT_GET: "get" 3 DICT_CLEAR: "clear"
4 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get 4 DICT_LEN: "len"
5 DICT_WALK: "walk" 5 DICT_GET: "get"
6 DICT_INSERT: "insert" 6 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
7 DICT_LEN: "len" 7 DICT_CONTAINS: "contains"
8 DICT_INSERT: "insert"
9 DICT_REMOVE: "remove"
8 DICT_REMOVE: "remove" 10 DICT_WALK: "walk"
9 DICT_CONTAINS: "contains" 11 DICT_WALK_UNTIL: "walkUntil"
10 DICT_KEYS: "keys" 12 DICT_FROM_LIST: "fromList"
11 DICT_VALUES: "values" 13 DICT_TO_LIST: "toList"
14 DICT_KEYS: "keys"
15 DICT_VALUES: "values"
12 DICT_INSERT_ALL: "insertAll" // union 16 DICT_INSERT_ALL: "insertAll" // union
13 DICT_KEEP_SHARED: "keepShared" // intersection 17 DICT_KEEP_SHARED: "keepShared" // intersection
14 DICT_REMOVE_ALL: "removeAll" // difference 18 DICT_REMOVE_ALL: "removeAll" // difference
15 DICT_WITH_CAPACITY: "withCapacity" 19 DICT_WITH_CAPACITY: "withCapacity"
16 DICT_CAPACITY: "capacity" 20 DICT_CAPACITY: "capacity"
17 DICT_UPDATE: "update" 21 DICT_UPDATE: "update"
18 DICT_LIST_GET_UNSAFE: "listGetUnsafe" 22 DICT_LIST_GET_UNSAFE: "listGetUnsafe"
} }
9 SET: "Set" => { 9 SET: "Set" => {
0 SET_SET: "Set" exposed_type=true // the Set.Set type alias 0 SET_SET: "Set" exposed_type=true // the Set.Set type alias
@ -1434,10 +1438,11 @@ define_builtins! {
9 SET_TO_LIST: "toList" 9 SET_TO_LIST: "toList"
10 SET_FROM_LIST: "fromList" 10 SET_FROM_LIST: "fromList"
11 SET_WALK: "walk" 11 SET_WALK: "walk"
12 SET_WALK_USER_FUNCTION: "#walk_user_function" 12 SET_WALK_UNTIL: "walkUntil"
13 SET_CONTAINS: "contains" 13 SET_WALK_USER_FUNCTION: "#walk_user_function"
14 SET_TO_DICT: "toDict" 14 SET_CONTAINS: "contains"
15 SET_CAPACITY: "capacity" 15 SET_TO_DICT: "toDict"
16 SET_CAPACITY: "capacity"
} }
10 BOX: "Box" => { 10 BOX: "Box" => {
0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type 0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type
@ -1517,10 +1522,11 @@ define_builtins! {
11 HASH_HASH_I32: "hashI32" 11 HASH_HASH_I32: "hashI32"
12 HASH_HASH_I64: "hashI64" 12 HASH_HASH_I64: "hashI64"
13 HASH_HASH_I128: "hashI128" 13 HASH_HASH_I128: "hashI128"
14 HASH_COMPLETE: "complete" 14 HASH_HASH_NAT: "hashNat"
15 HASH_HASH_STR_BYTES: "hashStrBytes" 15 HASH_COMPLETE: "complete"
16 HASH_HASH_LIST: "hashList" 16 HASH_HASH_STR_BYTES: "hashStrBytes"
17 HASH_HASH_UNORDERED: "hashUnordered" 17 HASH_HASH_LIST: "hashList"
18 HASH_HASH_UNORDERED: "hashUnordered"
} }
14 JSON: "Json" => { 14 JSON: "Json" => {
0 JSON_JSON: "Json" 0 JSON_JSON: "Json"

View file

@ -68,7 +68,7 @@ pub fn infer_borrow<'a>(
let sccs = matrix.strongly_connected_components_all(); let sccs = matrix.strongly_connected_components_all();
for group in sccs.groups() { for (group, _) in sccs.groups() {
// This is a fixed-point analysis // This is a fixed-point analysis
// //
// all functions initiall own all their parameters // all functions initiall own all their parameters

View file

@ -0,0 +1,5 @@
mod checker;
mod report;
pub use checker::{check_procs, Problem, Problems};
pub use report::format_problems;

View file

@ -0,0 +1,690 @@
//! Type-checking of the generated [ir][crate::ir::Proc].
use bumpalo::Bump;
use roc_collections::{MutMap, VecMap, VecSet};
use roc_module::symbol::Symbol;
use crate::{
ir::{
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
ModifyRc, Param, Proc, ProcLayout, Stmt,
},
layout::{Builtin, Layout, STLayoutInterner, TagIdIntType, UnionLayout},
};
pub enum UseKind {
Ret,
TagExpr,
TagReuse,
TagPayloadArg,
ListElemExpr,
CallArg,
JumpArg,
CrashArg,
SwitchCond,
ExpectCond,
ExpectLookup,
}
pub enum ProblemKind<'a> {
RedefinedSymbol {
symbol: Symbol,
old_line: usize,
},
NoSymbolInScope {
symbol: Symbol,
},
SymbolUseMismatch {
symbol: Symbol,
def_layout: Layout<'a>,
def_line: usize,
use_layout: Layout<'a>,
use_kind: UseKind,
},
SymbolDefMismatch {
symbol: Symbol,
def_layout: Layout<'a>,
expr_layout: Layout<'a>,
},
BadSwitchConditionLayout {
found_layout: Layout<'a>,
},
DuplicateSwitchBranch {},
RedefinedJoinPoint {
id: JoinPointId,
old_line: usize,
},
NoJoinPoint {
id: JoinPointId,
},
JumpArityMismatch {
def_line: usize,
num_needed: usize,
num_given: usize,
},
CallingUndefinedProc {
symbol: Symbol,
proc_layout: ProcLayout<'a>,
similar: Vec<ProcLayout<'a>>,
},
DuplicateCallSpecId {
old_call_line: usize,
},
StructIndexOOB {
structure: Symbol,
def_line: usize,
index: u64,
size: usize,
},
NotAStruct {
structure: Symbol,
def_line: usize,
},
IndexingTagIdNotInUnion {
structure: Symbol,
def_line: usize,
tag_id: u16,
union_layout: UnionLayout<'a>,
},
TagUnionStructIndexOOB {
structure: Symbol,
def_line: usize,
tag_id: u16,
index: u64,
size: usize,
},
IndexIntoNullableTag {
structure: Symbol,
def_line: usize,
tag_id: u16,
union_layout: UnionLayout<'a>,
},
UnboxNotABox {
symbol: Symbol,
def_line: usize,
},
CreatingTagIdNotInUnion {
tag_id: u16,
union_layout: UnionLayout<'a>,
},
CreateTagPayloadMismatch {
num_needed: usize,
num_given: usize,
},
}
pub struct Problem<'a> {
pub proc: &'a Proc<'a>,
pub proc_layout: ProcLayout<'a>,
pub line: usize,
pub kind: ProblemKind<'a>,
}
type Procs<'a> = MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>;
pub struct Problems<'a>(pub(crate) Vec<Problem<'a>>);
impl<'a> Problems<'a> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
pub fn check_procs<'a>(
arena: &'a Bump,
interner: &'a STLayoutInterner<'a>,
procs: &'a Procs<'a>,
) -> Problems<'a> {
let mut problems = Default::default();
for ((_, proc_layout), proc) in procs.iter() {
let mut ctx = Ctx {
arena,
interner,
proc,
proc_layout: *proc_layout,
ret_layout: proc.ret_layout,
problems: &mut problems,
call_spec_ids: Default::default(),
procs,
venv: Default::default(),
joinpoints: Default::default(),
line: 0,
};
ctx.check_proc(proc);
}
Problems(problems)
}
type VEnv<'a> = VecMap<Symbol, (usize, Layout<'a>)>;
type JoinPoints<'a> = VecMap<JoinPointId, (usize, &'a [Param<'a>])>;
type CallSpecIds = VecMap<CallSpecId, usize>;
struct Ctx<'a, 'r> {
arena: &'a Bump,
interner: &'a STLayoutInterner<'a>,
problems: &'r mut Vec<Problem<'a>>,
proc: &'a Proc<'a>,
proc_layout: ProcLayout<'a>,
procs: &'r Procs<'a>,
call_spec_ids: CallSpecIds,
ret_layout: Layout<'a>,
venv: VEnv<'a>,
joinpoints: JoinPoints<'a>,
line: usize,
}
impl<'a, 'r> Ctx<'a, 'r> {
fn alloc<T>(&self, v: T) -> &'a T {
self.arena.alloc(v)
}
fn problem(&mut self, problem_kind: ProblemKind<'a>) {
self.problems.push(Problem {
proc: self.proc,
proc_layout: self.proc_layout,
line: self.line,
kind: problem_kind,
})
}
fn in_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let old_venv = self.venv.clone();
let r = f(self);
self.venv = old_venv;
r
}
fn resolve(&mut self, mut layout: Layout<'a>) -> Layout<'a> {
// Note that we are more aggressive than the usual `runtime_representation`
// here because we need strict equality, and so cannot unwrap lambda sets
// lazily.
loop {
match layout {
Layout::LambdaSet(ls) => layout = ls.runtime_representation(self.interner),
layout => return layout,
}
}
}
fn insert(&mut self, symbol: Symbol, layout: Layout<'a>) {
if let Some((old_line, _)) = self.venv.insert(symbol, (self.line, layout)) {
self.problem(ProblemKind::RedefinedSymbol { symbol, old_line })
}
}
fn check_sym_exists(&mut self, symbol: Symbol) {
if !self.venv.contains_key(&symbol) {
self.problem(ProblemKind::NoSymbolInScope { symbol })
}
}
fn with_sym_layout<T>(
&mut self,
symbol: Symbol,
f: impl FnOnce(&mut Self, usize, Layout<'a>) -> Option<T>,
) -> Option<T> {
if let Some(&(def_line, layout)) = self.venv.get(&symbol) {
f(self, def_line, layout)
} else {
self.problem(ProblemKind::NoSymbolInScope { symbol });
None
}
}
fn check_sym_layout(&mut self, symbol: Symbol, expected_layout: Layout<'a>, use_kind: UseKind) {
if let Some(&(def_line, layout)) = self.venv.get(&symbol) {
if self.resolve(layout) != self.resolve(expected_layout) {
self.problem(ProblemKind::SymbolUseMismatch {
symbol,
def_layout: layout,
def_line,
use_layout: expected_layout,
use_kind,
});
}
} else {
self.problem(ProblemKind::NoSymbolInScope { symbol })
}
}
fn check_proc(&mut self, proc: &Proc<'a>) {
for (lay, arg) in proc.args.iter() {
self.insert(*arg, *lay);
}
self.check_stmt(&proc.body)
}
fn check_stmt(&mut self, body: &Stmt<'a>) {
self.line += 1;
match body {
Stmt::Let(x, e, x_layout, rest) => {
if let Some(e_layout) = self.check_expr(e) {
if self.resolve(e_layout) != self.resolve(*x_layout) {
self.problem(ProblemKind::SymbolDefMismatch {
symbol: *x,
def_layout: *x_layout,
expr_layout: e_layout,
})
}
}
self.insert(*x, *x_layout);
self.check_stmt(rest);
}
Stmt::Switch {
cond_symbol,
cond_layout,
branches,
default_branch,
ret_layout: _,
} => {
self.check_sym_layout(*cond_symbol, *cond_layout, UseKind::SwitchCond);
match self.resolve(*cond_layout) {
Layout::Builtin(Builtin::Int(int_width)) if !int_width.is_signed() => {}
Layout::Builtin(Builtin::Bool) => {}
_ => self.problem(ProblemKind::BadSwitchConditionLayout {
found_layout: *cond_layout,
}),
}
// TODO: need to adjust line numbers as we step through, and depending on whether
// the switch is printed as true/false or a proper switch.
let mut seen_branches = VecSet::with_capacity(branches.len());
for (match_no, _branch_info, branch) in branches.iter() {
if seen_branches.insert(match_no) {
self.problem(ProblemKind::DuplicateSwitchBranch {});
}
self.in_scope(|ctx| ctx.check_stmt(branch));
}
let (_branch_info, default_branch) = default_branch;
self.in_scope(|ctx| ctx.check_stmt(default_branch));
}
&Stmt::Ret(sym) => self.check_sym_layout(sym, self.ret_layout, UseKind::Ret),
&Stmt::Refcounting(rc, rest) => {
self.check_modify_rc(rc);
self.check_stmt(rest);
}
&Stmt::Expect {
condition,
region: _,
lookups,
layouts,
remainder,
}
| &Stmt::ExpectFx {
condition,
region: _,
lookups,
layouts,
remainder,
} => {
self.check_sym_layout(
condition,
Layout::Builtin(Builtin::Bool),
UseKind::ExpectCond,
);
for (sym, lay) in lookups.iter().zip(layouts) {
self.check_sym_layout(*sym, *lay, UseKind::ExpectLookup);
}
self.check_stmt(remainder);
}
&Stmt::Join {
id,
parameters,
body,
remainder,
} => {
if let Some((old_line, _)) = self.joinpoints.insert(id, (self.line, parameters)) {
self.problem(ProblemKind::RedefinedJoinPoint { id, old_line })
}
self.in_scope(|ctx| {
for Param {
symbol,
layout,
borrow: _,
} in parameters
{
ctx.insert(*symbol, *layout);
}
ctx.check_stmt(body)
});
self.line += 1; // `in` line
self.check_stmt(remainder);
}
&Stmt::Jump(id, symbols) => {
if let Some(&(def_line, parameters)) = self.joinpoints.get(&id) {
if symbols.len() != parameters.len() {
self.problem(ProblemKind::JumpArityMismatch {
def_line,
num_needed: parameters.len(),
num_given: symbols.len(),
});
}
for (arg, param) in symbols.iter().zip(parameters.iter()) {
let Param {
symbol: _,
borrow: _,
layout,
} = param;
self.check_sym_layout(*arg, *layout, UseKind::JumpArg);
}
} else {
self.problem(ProblemKind::NoJoinPoint { id });
}
}
&Stmt::Crash(sym, _) => {
self.check_sym_layout(sym, Layout::Builtin(Builtin::Str), UseKind::CrashArg)
}
}
}
fn check_expr(&mut self, e: &Expr<'a>) -> Option<Layout<'a>> {
match e {
Expr::Literal(_) => None,
Expr::Call(call) => self.check_call(call),
&Expr::Tag {
tag_layout,
tag_id,
arguments,
} => {
self.check_tag_expr(tag_layout, tag_id, arguments);
Some(Layout::Union(tag_layout))
}
Expr::Struct(syms) => {
for sym in syms.iter() {
self.check_sym_exists(*sym);
}
// TODO: pass the field order hash down, so we can check this
None
}
&Expr::StructAtIndex {
index,
// TODO: pass the field order hash down, so we can check this
field_layouts: _,
structure,
} => self.check_struct_at_index(structure, index),
Expr::GetTagId {
structure: _,
union_layout,
} => Some(union_layout.tag_id_layout()),
&Expr::UnionAtIndex {
structure,
tag_id,
union_layout,
index,
} => self.check_union_at_index(structure, union_layout, tag_id, index),
Expr::Array { elem_layout, elems } => {
for elem in elems.iter() {
match elem {
ListLiteralElement::Literal(_) => {}
ListLiteralElement::Symbol(sym) => {
self.check_sym_layout(*sym, *elem_layout, UseKind::ListElemExpr)
}
}
}
Some(Layout::Builtin(Builtin::List(self.alloc(*elem_layout))))
}
Expr::EmptyArray => {
// TODO don't know what the element layout is
None
}
&Expr::ExprBox { symbol } => self.with_sym_layout(symbol, |ctx, _def_line, layout| {
Some(Layout::Boxed(ctx.alloc(layout)))
}),
&Expr::ExprUnbox { symbol } => {
self.with_sym_layout(symbol, |ctx, def_line, layout| match ctx.resolve(layout) {
Layout::Boxed(inner) => Some(*inner),
_ => {
ctx.problem(ProblemKind::UnboxNotABox { symbol, def_line });
None
}
})
}
&Expr::Reuse {
symbol,
update_tag_id: _,
update_mode: _,
tag_layout,
tag_id: _,
arguments: _,
} => {
self.check_sym_layout(symbol, Layout::Union(tag_layout), UseKind::TagReuse);
// TODO also check update arguments
Some(Layout::Union(tag_layout))
}
&Expr::Reset {
symbol,
update_mode: _,
} => {
self.check_sym_exists(symbol);
None
}
Expr::RuntimeErrorFunction(_) => None,
}
}
fn check_struct_at_index(&mut self, structure: Symbol, index: u64) -> Option<Layout<'a>> {
self.with_sym_layout(structure, |ctx, def_line, layout| {
match ctx.resolve(layout) {
Layout::Struct { field_layouts, .. } => {
if index as usize >= field_layouts.len() {
ctx.problem(ProblemKind::StructIndexOOB {
structure,
def_line,
index,
size: field_layouts.len(),
});
None
} else {
Some(field_layouts[index as usize])
}
}
_ => {
ctx.problem(ProblemKind::NotAStruct {
structure,
def_line,
});
None
}
}
})
}
fn check_union_at_index(
&mut self,
structure: Symbol,
union_layout: UnionLayout<'a>,
tag_id: u16,
index: u64,
) -> Option<Layout<'a>> {
self.with_sym_layout(structure, |ctx, def_line, _layout| {
ctx.check_sym_layout(structure, Layout::Union(union_layout), UseKind::TagExpr);
match get_tag_id_payloads(union_layout, tag_id) {
TagPayloads::IdNotInUnion => {
ctx.problem(ProblemKind::IndexingTagIdNotInUnion {
structure,
def_line,
tag_id,
union_layout,
});
None
}
TagPayloads::Payloads(payloads) => {
if index as usize >= payloads.len() {
ctx.problem(ProblemKind::TagUnionStructIndexOOB {
structure,
def_line,
tag_id,
index,
size: payloads.len(),
});
return None;
}
let layout = resolve_recursive_layout(payloads[index as usize], union_layout);
Some(layout)
}
}
})
}
fn check_call(&mut self, call: &Call<'a>) -> Option<Layout<'a>> {
let Call {
call_type,
arguments,
} = call;
match call_type {
CallType::ByName {
name,
ret_layout,
arg_layouts,
specialization_id,
} => {
let proc_layout = ProcLayout {
arguments: arg_layouts,
result: **ret_layout,
captures_niche: name.captures_niche(),
};
if !self.procs.contains_key(&(name.name(), proc_layout)) {
let similar = self
.procs
.keys()
.filter(|(sym, _)| *sym == name.name())
.map(|(_, lay)| *lay)
.collect();
self.problem(ProblemKind::CallingUndefinedProc {
symbol: name.name(),
proc_layout,
similar,
});
}
for (arg, wanted_layout) in arguments.iter().zip(arg_layouts.iter()) {
self.check_sym_layout(*arg, *wanted_layout, UseKind::CallArg);
}
if let Some(old_call_line) =
self.call_spec_ids.insert(*specialization_id, self.line)
{
self.problem(ProblemKind::DuplicateCallSpecId { old_call_line });
}
Some(**ret_layout)
}
CallType::HigherOrder(HigherOrderLowLevel {
op: _,
closure_env_layout: _,
update_mode: _,
passed_function: _,
}) => {
// TODO
None
}
CallType::Foreign {
foreign_symbol: _,
ret_layout,
} => Some(**ret_layout),
CallType::LowLevel {
op: _,
update_mode: _,
} => None,
}
}
fn check_tag_expr(&mut self, union_layout: UnionLayout<'a>, tag_id: u16, arguments: &[Symbol]) {
match get_tag_id_payloads(union_layout, tag_id) {
TagPayloads::IdNotInUnion => {
self.problem(ProblemKind::CreatingTagIdNotInUnion {
tag_id,
union_layout,
});
}
TagPayloads::Payloads(payloads) => {
if arguments.len() != payloads.len() {
self.problem(ProblemKind::CreateTagPayloadMismatch {
num_needed: payloads.len(),
num_given: arguments.len(),
});
}
for (arg, wanted_layout) in arguments.iter().zip(payloads.iter()) {
let wanted_layout = resolve_recursive_layout(*wanted_layout, union_layout);
self.check_sym_layout(*arg, wanted_layout, UseKind::TagPayloadArg);
}
}
}
}
fn check_modify_rc(&mut self, rc: ModifyRc) {
match rc {
ModifyRc::Inc(sym, _) | ModifyRc::Dec(sym) | ModifyRc::DecRef(sym) => {
// TODO: also check that sym layout needs refcounting
self.check_sym_exists(sym);
}
}
}
}
fn resolve_recursive_layout<'a>(layout: Layout<'a>, when_recursive: UnionLayout<'a>) -> Layout<'a> {
// TODO check if recursive pointer not in recursive union
match layout {
Layout::RecursivePointer => Layout::Union(when_recursive),
other => other,
}
}
enum TagPayloads<'a> {
IdNotInUnion,
Payloads(&'a [Layout<'a>]),
}
fn get_tag_id_payloads(union_layout: UnionLayout, tag_id: TagIdIntType) -> TagPayloads {
macro_rules! check_tag_id_oob {
($len:expr) => {
if tag_id as usize >= $len {
return TagPayloads::IdNotInUnion;
}
};
}
match union_layout {
UnionLayout::NonRecursive(union) => {
check_tag_id_oob!(union.len());
let payloads = union[tag_id as usize];
TagPayloads::Payloads(payloads)
}
UnionLayout::Recursive(union) => {
check_tag_id_oob!(union.len());
let payloads = union[tag_id as usize];
TagPayloads::Payloads(payloads)
}
UnionLayout::NonNullableUnwrapped(payloads) => {
if tag_id != 0 {
TagPayloads::Payloads(&[])
} else {
TagPayloads::Payloads(payloads)
}
}
UnionLayout::NullableWrapped {
nullable_id,
other_tags,
} => {
if tag_id == nullable_id {
TagPayloads::Payloads(&[])
} else {
check_tag_id_oob!(other_tags.len());
let payloads = other_tags[tag_id as usize];
TagPayloads::Payloads(payloads)
}
}
UnionLayout::NullableUnwrapped {
nullable_id,
other_fields,
} => {
if tag_id == nullable_id as _ {
TagPayloads::Payloads(&[])
} else {
check_tag_id_oob!(2);
TagPayloads::Payloads(other_fields)
}
}
}
}

View file

@ -0,0 +1,499 @@
use std::fmt::Display;
use roc_intern::Interner;
use roc_module::symbol::{Interns, Symbol};
use ven_pretty::{Arena, DocAllocator, DocBuilder};
use crate::{
ir::{Parens, ProcLayout},
layout::{CapturesNiche, Layout},
};
use super::{
checker::{ProblemKind, UseKind},
Problem, Problems,
};
pub fn format_problems<'a, I>(
interns: &Interns,
interner: &I,
problems: Problems<'a>,
) -> impl Display
where
I: Interner<'a, Layout<'a>>,
{
let Problems(problems) = problems;
let f = Arena::new();
let problem_docs = problems
.into_iter()
.map(|p| format_problem(&f, interns, interner, p));
let all = f.intersperse(problem_docs, f.hardline());
all.1.pretty(80).to_string()
}
type Doc<'d> = DocBuilder<'d, Arena<'d>>;
const GUTTER_BAR: &str = "";
const HEADER_WIDTH: usize = 80;
fn format_problem<'a, 'd, I>(
f: &'d Arena<'d>,
interns: &'d Interns,
interner: &'d I,
problem: Problem<'a>,
) -> Doc<'d>
where
'a: 'd,
I: Interner<'a, Layout<'a>>,
{
let Problem {
proc,
proc_layout,
line,
kind,
} = problem;
let (title, mut docs, last_doc) = format_kind(f, interns, interner, kind);
docs.push((line, last_doc));
docs.sort_by_key(|(line, _)| *line);
let src = proc
.to_doc(f, interner, true, Parens::NotNeeded)
.1
.pretty(80)
.to_string();
let interpolated_docs = stack(
f,
docs.into_iter()
.map(|(line, doc)| format_sourced_doc(f, line, &src, doc)),
);
let header = format_header(f, title);
let proc_loc = format_proc_spec(f, interns, interner, proc.name.name(), proc_layout);
stack(
f,
[
header,
f.concat([f.reflow("in "), proc_loc]),
interpolated_docs,
],
)
}
fn format_sourced_doc<'d>(f: &'d Arena<'d>, line: usize, source: &str, doc: Doc<'d>) -> Doc<'d> {
let start_at = line.saturating_sub(1);
let source_lines = source.lines().skip(start_at).take(3);
let max_line_no_width = (start_at.to_string().len()).max((start_at + 3).to_string().len());
let pretty_lines = source_lines.enumerate().map(|(i, line_src)| {
let line_no = start_at + i;
let line_no_s = line_no.to_string();
let line_no_len = line_no_s.len();
f.text(line_no_s)
.append(f.text(" ".repeat(max_line_no_width - line_no_len)))
.append(f.text(GUTTER_BAR))
.append(f.text(if line_no == line { "> " } else { " " }))
.append(f.text(line_src.to_string()))
});
let pretty_lines = f.intersperse(pretty_lines, f.hardline());
stack(f, [pretty_lines, doc])
}
fn format_header<'d>(f: &'d Arena<'d>, title: &str) -> Doc<'d> {
let title_width = title.len() + 4;
f.text(format!(
"── {} {}",
title,
"".repeat(HEADER_WIDTH - title_width)
))
}
fn format_kind<'a, 'd, I>(
f: &'d Arena<'d>,
interns: &'d Interns,
interner: &I,
kind: ProblemKind<'a>,
) -> (&'static str, Vec<(usize, Doc<'d>)>, Doc<'d>)
where
I: Interner<'a, Layout<'a>>,
{
let title;
let docs_before;
let doc = match kind {
ProblemKind::RedefinedSymbol { symbol, old_line } => {
title = "REDEFINED SYMBOL";
docs_before = vec![(
old_line,
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" first defined here"),
]),
)];
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" re-defined here"),
])
}
ProblemKind::NoSymbolInScope { symbol } => {
title = "SYMBOL NOT DEFINED";
docs_before = vec![];
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" not found in the present scope"),
])
}
ProblemKind::SymbolUseMismatch {
symbol,
def_layout,
def_line,
use_layout,
use_kind,
} => {
title = "SYMBOL LAYOUT DOESN'T MATCH ITS USE";
docs_before = vec![(
def_line,
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" defined here with layout "),
def_layout.to_doc(f, interner, Parens::NotNeeded),
]),
)];
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" used as a "),
f.reflow(format_use_kind(use_kind)),
f.reflow(" here with layout "),
use_layout.to_doc(f, interner, Parens::NotNeeded),
])
}
ProblemKind::SymbolDefMismatch {
symbol,
def_layout,
expr_layout,
} => {
title = "SYMBOL INITIALIZER HAS THE WRONG LAYOUT";
docs_before = vec![];
f.concat([
format_symbol(f, interns, symbol),
f.reflow(" is defined as "),
def_layout.to_doc(f, interner, Parens::NotNeeded),
f.reflow(" but its initializer is "),
expr_layout.to_doc(f, interner, Parens::NotNeeded),
])
}
ProblemKind::BadSwitchConditionLayout { found_layout } => {
title = "BAD SWITCH CONDITION LAYOUT";
docs_before = vec![];
f.concat([
f.reflow("This switch condition is a "),
found_layout.to_doc(f, interner, Parens::NotNeeded),
])
}
ProblemKind::DuplicateSwitchBranch {} => {
title = "DUPLICATE SWITCH BRANCH";
docs_before = vec![];
f.reflow("The match of switch branch is reached earlier")
}
ProblemKind::RedefinedJoinPoint { id, old_line } => {
title = "DUPLICATE JOIN POINT";
docs_before = vec![(
old_line,
f.concat([
f.reflow("The join point "),
f.as_string(id.0),
f.reflow(" was previously defined here"),
]),
)];
f.reflow("and is redefined here")
}
ProblemKind::NoJoinPoint { id } => {
title = "JOIN POINT NOT DEFINED";
docs_before = vec![];
f.concat([
f.reflow("The join point "),
f.as_string(id.0),
f.reflow(" was not found in the present scope"),
])
}
ProblemKind::JumpArityMismatch {
def_line,
num_needed,
num_given,
} => {
title = "WRONG NUMBER OF ARGUMENTS IN JUMP";
docs_before = vec![(
def_line,
f.concat([
f.reflow("This join pont needs "),
f.as_string(num_needed),
f.reflow(" arguments"),
]),
)];
f.concat([
f.reflow("but this jump only gives it "),
f.as_string(num_given),
])
}
ProblemKind::CallingUndefinedProc {
symbol,
proc_layout,
similar,
} => {
title = "PROC SPECIALIZATION NOT DEFINED";
docs_before = vec![];
let no_spec_doc = stack(
f,
[
f.reflow("No specialization"),
format_proc_spec(f, interns, interner, symbol, proc_layout),
f.reflow("was found"),
],
);
let similar_doc = if similar.is_empty() {
f.nil()
} else {
let similars = similar
.into_iter()
.map(|other| format_proc_spec(f, interns, interner, symbol, other));
stack(
f,
[f.concat([
f.reflow("The following specializations of "),
format_symbol(f, interns, symbol),
f.reflow(" were built:"),
stack(f, similars),
])],
)
};
stack(f, [no_spec_doc, similar_doc])
}
ProblemKind::DuplicateCallSpecId { old_call_line } => {
title = "DUPLICATE CALL SPEC ID";
docs_before = vec![(old_call_line, f.reflow("This call has a specialization ID"))];
f.reflow("...that is the same as the specialization ID of the call here")
}
ProblemKind::StructIndexOOB {
structure,
def_line,
index,
size,
} => {
title = "STRUCT INDEX IS OUT-OF-BOUNDS";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The struct "),
format_symbol(f, interns, structure),
f.reflow(" defined here has "),
f.as_string(size),
f.reflow(" fields"),
]),
)];
f.concat([
f.reflow("but is being indexed into field "),
f.as_string(index),
])
}
ProblemKind::NotAStruct {
structure,
def_line,
} => {
title = "SYMBOL IS NOT A STRUCT";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The value "),
format_symbol(f, interns, structure),
f.reflow(" defined here"),
]),
)];
f.reflow("cannot be used as a structure here")
}
ProblemKind::IndexingTagIdNotInUnion {
structure,
def_line,
tag_id,
union_layout,
} => {
title = "TAG ID NOT IN UNION";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The union "),
format_symbol(f, interns, structure),
f.reflow(" defined here has layout "),
Layout::Union(union_layout).to_doc(f, interner, Parens::NotNeeded),
]),
)];
f.concat([f.reflow("which has no tag of id "), f.as_string(tag_id)])
}
ProblemKind::TagUnionStructIndexOOB {
structure,
def_line,
tag_id,
index,
size,
} => {
title = "UNION ID AND PAYLOAD INDEX IS OUT-OF-BOUNDS";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The union "),
format_symbol(f, interns, structure),
f.reflow(" defined here has "),
f.as_string(size),
f.reflow(" payloads at ID "),
f.as_string(tag_id),
]),
)];
f.concat([
f.reflow("but is being indexed into field "),
f.as_string(index),
f.reflow(" here"),
])
}
ProblemKind::IndexIntoNullableTag {
structure,
def_line,
tag_id,
union_layout,
} => {
title = "INDEX INTO NULLABLE TAG";
docs_before = vec![(
def_line,
f.concat([
f.reflow("The union "),
format_symbol(f, interns, structure),
f.reflow(" defined here has layout "),
Layout::Union(union_layout).to_doc(f, interner, Parens::NotNeeded),
]),
)];
f.concat([
f.reflow("but is being indexed into the nullable variant "),
f.as_string(tag_id),
f.reflow(" here"),
])
}
ProblemKind::UnboxNotABox { symbol, def_line } => {
title = "ATTEMPTING TO UNBOX A NON-BOX";
docs_before = vec![(
def_line,
f.concat([format_symbol(f, interns, symbol), f.reflow(" is not a box")]),
)];
f.reflow("but is being unboxed here")
}
ProblemKind::CreatingTagIdNotInUnion {
tag_id,
union_layout,
} => {
title = "NO SUCH ID FOR TAG UNION";
docs_before = vec![];
f.concat([
f.reflow("The variant "),
f.as_string(tag_id),
f.reflow(" is outside the target union layout "),
Layout::Union(union_layout).to_doc(f, interner, Parens::NotNeeded),
])
}
ProblemKind::CreateTagPayloadMismatch {
num_needed,
num_given,
} => {
title = "WRONG NUMBER OF ARGUMENTS IN TAG UNION";
docs_before = vec![];
f.concat([
f.reflow("This tag union payload needs "),
f.as_string(num_needed),
f.reflow(" values, but is only given "),
f.as_string(num_given),
])
}
};
(title, docs_before, doc)
}
fn format_symbol<'d>(f: &'d Arena<'d>, interns: &'d Interns, symbol: Symbol) -> Doc<'d> {
f.text(symbol.module_string(interns).to_string())
.append(f.text("."))
.append(f.text(symbol.as_str(interns)))
}
fn format_use_kind(use_kind: UseKind) -> &'static str {
match use_kind {
UseKind::Ret => "return value",
UseKind::TagExpr => "tag constructor",
UseKind::TagReuse => "tag reuse",
UseKind::TagPayloadArg => "tag's payload",
UseKind::ListElemExpr => "list element",
UseKind::CallArg => "call argument",
UseKind::JumpArg => "jump argument",
UseKind::CrashArg => "crash message",
UseKind::SwitchCond => "switch condition",
UseKind::ExpectCond => "expect condition",
UseKind::ExpectLookup => "lookup for an expect",
}
}
fn format_proc_spec<'a, 'd, I>(
f: &'d Arena<'d>,
interns: &'d Interns,
interner: &I,
symbol: Symbol,
proc_layout: ProcLayout<'a>,
) -> Doc<'d>
where
I: Interner<'a, Layout<'a>>,
{
f.concat([
f.as_string(symbol.as_str(interns)),
f.reflow(" : "),
format_proc_layout(f, interner, proc_layout),
])
}
fn format_proc_layout<'a, 'd, I>(
f: &'d Arena<'d>,
interner: &I,
proc_layout: ProcLayout<'a>,
) -> Doc<'d>
where
I: Interner<'a, Layout<'a>>,
{
let ProcLayout {
arguments,
result,
captures_niche,
} = proc_layout;
let args = f.intersperse(
arguments
.iter()
.map(|a| a.to_doc(f, interner, Parens::InFunction)),
f.reflow(", "),
);
let fun = f.concat([
f.concat([f.reflow("("), args, f.reflow(")")]),
f.reflow(" -> "),
result.to_doc(f, interner, Parens::NotNeeded),
]);
let niche = if captures_niche == CapturesNiche::no_niche() {
f.reflow("(no niche)")
} else {
f.concat([
f.reflow("(niche {"),
f.intersperse(
captures_niche
.0
.iter()
.map(|c| c.to_doc(f, interner, Parens::NotNeeded)),
f.reflow(", "),
),
f.reflow("})"),
])
};
f.concat([fun, f.space(), niche])
}
fn stack<'d>(f: &'d Arena<'d>, docs: impl IntoIterator<Item = Doc<'d>>) -> Doc<'d> {
f.intersperse(docs, f.line().append(f.line()))
}

View file

@ -425,6 +425,7 @@ fn flatten<'a>(
/// variables to "how to get their value". So a pattern like (Just (x,_)) will give /// variables to "how to get their value". So a pattern like (Just (x,_)) will give
/// us something like ("x" => value.0.0) /// us something like ("x" => value.0.0)
#[derive(Debug)]
enum Match { enum Match {
Exact(Label), Exact(Label),
GuardOnly, GuardOnly,
@ -795,7 +796,22 @@ fn to_relevant_branch_help<'a>(
elements, elements,
element_layout: _, element_layout: _,
} => match test { } => match test {
IsListLen { bound: _, len } if my_arity.covers_length(*len as _) => { IsListLen {
bound: test_bound,
len,
} if my_arity.covers_length(*len as _)
// Spread tests [_, ..] can only match spread tests, not exact-sized bounds [_].
//
// On the other hand, exact-sized tests [_] can match spread bounds [_, ..],
// because each spread bound generates 0 or more exact-sized tests.
//
// See exhaustiveness checking of lists for more details on the tests generated
// for spread bounds.
&& !matches!(
(test_bound, my_arity),
(ListLenBound::AtLeast, ListArity::Exact(..))
) =>
{
let sub_positions = elements.into_iter().enumerate().map(|(index, elem_pat)| { let sub_positions = elements.into_iter().enumerate().map(|(index, elem_pat)| {
let mut new_path = path.to_vec(); let mut new_path = path.to_vec();

View file

@ -326,6 +326,7 @@ impl<'a> Proc<'a> {
&'b self, &'b self,
alloc: &'b D, alloc: &'b D,
interner: &'b I, interner: &'b I,
pretty: bool,
_parens: Parens, _parens: Parens,
) -> DocBuilder<'b, D, A> ) -> DocBuilder<'b, D, A>
where where
@ -335,7 +336,7 @@ impl<'a> Proc<'a> {
I: Interner<'a, Layout<'a>>, I: Interner<'a, Layout<'a>>,
{ {
let args_doc = self.args.iter().map(|(layout, symbol)| { let args_doc = self.args.iter().map(|(layout, symbol)| {
let arg_doc = symbol_to_doc(alloc, *symbol); let arg_doc = symbol_to_doc(alloc, *symbol, pretty);
if pretty_print_ir_symbols() { if pretty_print_ir_symbols() {
arg_doc.append(alloc.reflow(": ")).append(layout.to_doc( arg_doc.append(alloc.reflow(": ")).append(layout.to_doc(
alloc, alloc,
@ -350,36 +351,36 @@ impl<'a> Proc<'a> {
if pretty_print_ir_symbols() { if pretty_print_ir_symbols() {
alloc alloc
.text("procedure : ") .text("procedure : ")
.append(symbol_to_doc(alloc, self.name.name())) .append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" ") .append(" ")
.append(self.ret_layout.to_doc(alloc, interner, Parens::NotNeeded)) .append(self.ret_layout.to_doc(alloc, interner, Parens::NotNeeded))
.append(alloc.hardline()) .append(alloc.hardline())
.append(alloc.text("procedure = ")) .append(alloc.text("procedure = "))
.append(symbol_to_doc(alloc, self.name.name())) .append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" (") .append(" (")
.append(alloc.intersperse(args_doc, ", ")) .append(alloc.intersperse(args_doc, ", "))
.append("):") .append("):")
.append(alloc.hardline()) .append(alloc.hardline())
.append(self.body.to_doc(alloc, interner).indent(4)) .append(self.body.to_doc(alloc, interner, pretty).indent(4))
} else { } else {
alloc alloc
.text("procedure ") .text("procedure ")
.append(symbol_to_doc(alloc, self.name.name())) .append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" (") .append(" (")
.append(alloc.intersperse(args_doc, ", ")) .append(alloc.intersperse(args_doc, ", "))
.append("):") .append("):")
.append(alloc.hardline()) .append(alloc.hardline())
.append(self.body.to_doc(alloc, interner).indent(4)) .append(self.body.to_doc(alloc, interner, pretty).indent(4))
} }
} }
pub fn to_pretty<I>(&self, interner: &I, width: usize) -> String pub fn to_pretty<I>(&self, interner: &I, width: usize, pretty: bool) -> String
where where
I: Interner<'a, Layout<'a>>, I: Interner<'a, Layout<'a>>,
{ {
let allocator = BoxAllocator; let allocator = BoxAllocator;
let mut w = std::vec::Vec::new(); let mut w = std::vec::Vec::new();
self.to_doc::<_, (), _>(&allocator, interner, Parens::NotNeeded) self.to_doc::<_, (), _>(&allocator, interner, pretty, Parens::NotNeeded)
.1 .1
.render(width, &mut w) .render(width, &mut w)
.unwrap(); .unwrap();
@ -1675,36 +1676,14 @@ pub enum BranchInfo<'a> {
} }
impl<'a> BranchInfo<'a> { impl<'a> BranchInfo<'a> {
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A> pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, _pretty: bool) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
A: Clone, A: Clone,
{ {
use BranchInfo::*;
match self {
Constructor {
tag_id,
scrutinee,
layout: _,
} if pretty_print_ir_symbols() => alloc
.hardline()
.append(" BranchInfo: { scrutinee: ")
.append(symbol_to_doc(alloc, *scrutinee))
.append(", tag_id: ")
.append(format!("{}", tag_id))
.append("} "),
_ => {
if pretty_print_ir_symbols() {
alloc.text(" <no branch info>")
} else {
alloc.text("") alloc.text("")
} }
}
}
}
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -1724,7 +1703,7 @@ pub enum ModifyRc {
} }
impl ModifyRc { impl ModifyRc {
pub fn to_doc<'a, D, A>(self, alloc: &'a D) -> DocBuilder<'a, D, A> pub fn to_doc<'a, D, A>(self, alloc: &'a D, pretty: bool) -> DocBuilder<'a, D, A>
where where
D: DocAllocator<'a, A>, D: DocAllocator<'a, A>,
D::Doc: Clone, D::Doc: Clone,
@ -1735,20 +1714,20 @@ impl ModifyRc {
match self { match self {
Inc(symbol, 1) => alloc Inc(symbol, 1) => alloc
.text("inc ") .text("inc ")
.append(symbol_to_doc(alloc, symbol)) .append(symbol_to_doc(alloc, symbol, pretty))
.append(";"), .append(";"),
Inc(symbol, n) => alloc Inc(symbol, n) => alloc
.text("inc ") .text("inc ")
.append(alloc.text(format!("{} ", n))) .append(alloc.text(format!("{} ", n)))
.append(symbol_to_doc(alloc, symbol)) .append(symbol_to_doc(alloc, symbol, pretty))
.append(";"), .append(";"),
Dec(symbol) => alloc Dec(symbol) => alloc
.text("dec ") .text("dec ")
.append(symbol_to_doc(alloc, symbol)) .append(symbol_to_doc(alloc, symbol, pretty))
.append(";"), .append(";"),
DecRef(symbol) => alloc DecRef(symbol) => alloc
.text("decref ") .text("decref ")
.append(symbol_to_doc(alloc, symbol)) .append(symbol_to_doc(alloc, symbol, pretty))
.append(";"), .append(";"),
} }
} }
@ -1808,7 +1787,7 @@ pub struct Call<'a> {
} }
impl<'a> Call<'a> { impl<'a> Call<'a> {
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A> pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, pretty: bool) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
@ -1822,19 +1801,19 @@ impl<'a> Call<'a> {
CallType::ByName { name, .. } => { CallType::ByName { name, .. } => {
let it = std::iter::once(name.name()) let it = std::iter::once(name.name())
.chain(arguments.iter().copied()) .chain(arguments.iter().copied())
.map(|s| symbol_to_doc(alloc, s)); .map(|s| symbol_to_doc(alloc, s, pretty));
alloc.text("CallByName ").append(alloc.intersperse(it, " ")) alloc.text("CallByName ").append(alloc.intersperse(it, " "))
} }
LowLevel { op: lowlevel, .. } => { LowLevel { op: lowlevel, .. } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text(format!("lowlevel {:?} ", lowlevel)) .text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " ")) .append(alloc.intersperse(it, " "))
} }
HigherOrder(higher_order) => { HigherOrder(higher_order) => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text(format!("lowlevel {:?} ", higher_order.op)) .text(format!("lowlevel {:?} ", higher_order.op))
@ -1843,7 +1822,7 @@ impl<'a> Call<'a> {
Foreign { Foreign {
ref foreign_symbol, .. ref foreign_symbol, ..
} => { } => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text(format!("foreign {:?} ", foreign_symbol.as_str())) .text(format!("foreign {:?} ", foreign_symbol.as_str()))
@ -2034,10 +2013,10 @@ impl<'a> Literal<'a> {
} }
} }
pub(crate) fn symbol_to_doc_string(symbol: Symbol) -> String { pub(crate) fn symbol_to_doc_string(symbol: Symbol, force_pretty: bool) -> String {
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
if pretty_print_ir_symbols() { if pretty_print_ir_symbols() || force_pretty {
format!("{:?}", symbol) format!("{:?}", symbol)
} else { } else {
let text = format!("{}", symbol); let text = format!("{}", symbol);
@ -2051,26 +2030,30 @@ pub(crate) fn symbol_to_doc_string(symbol: Symbol) -> String {
} }
} }
fn symbol_to_doc<'b, D, A>(alloc: &'b D, symbol: Symbol) -> DocBuilder<'b, D, A> fn symbol_to_doc<'b, D, A>(alloc: &'b D, symbol: Symbol, force_pretty: bool) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
A: Clone, A: Clone,
{ {
alloc.text(symbol_to_doc_string(symbol)) alloc.text(symbol_to_doc_string(symbol, force_pretty))
} }
fn join_point_to_doc<'b, D, A>(alloc: &'b D, symbol: JoinPointId) -> DocBuilder<'b, D, A> fn join_point_to_doc<'b, D, A>(
alloc: &'b D,
symbol: JoinPointId,
pretty: bool,
) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
A: Clone, A: Clone,
{ {
symbol_to_doc(alloc, symbol.0) symbol_to_doc(alloc, symbol.0, pretty)
} }
impl<'a> Expr<'a> { impl<'a> Expr<'a> {
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D) -> DocBuilder<'b, D, A> pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, pretty: bool) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
@ -2081,7 +2064,7 @@ impl<'a> Expr<'a> {
match self { match self {
Literal(lit) => lit.to_doc(alloc), Literal(lit) => lit.to_doc(alloc),
Call(call) => call.to_doc(alloc), Call(call) => call.to_doc(alloc, pretty),
Tag { Tag {
tag_id, arguments, .. tag_id, arguments, ..
@ -2091,7 +2074,7 @@ impl<'a> Expr<'a> {
.append(alloc.text(tag_id.to_string())) .append(alloc.text(tag_id.to_string()))
.append(")"); .append(")");
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
doc_tag doc_tag
.append(alloc.space()) .append(alloc.space())
@ -2109,11 +2092,11 @@ impl<'a> Expr<'a> {
.append(alloc.text(tag_id.to_string())) .append(alloc.text(tag_id.to_string()))
.append(")"); .append(")");
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text("Reuse ") .text("Reuse ")
.append(symbol_to_doc(alloc, *symbol)) .append(symbol_to_doc(alloc, *symbol, pretty))
.append(alloc.space()) .append(alloc.space())
.append(format!("{:?}", update_mode)) .append(format!("{:?}", update_mode))
.append(alloc.space()) .append(alloc.space())
@ -2130,7 +2113,7 @@ impl<'a> Expr<'a> {
)), )),
Struct(args) => { Struct(args) => {
let it = args.iter().map(|s| symbol_to_doc(alloc, *s)); let it = args.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text("Struct {") .text("Struct {")
@ -2140,7 +2123,7 @@ impl<'a> Expr<'a> {
Array { elems, .. } => { Array { elems, .. } => {
let it = elems.iter().map(|e| match e { let it = elems.iter().map(|e| match e {
ListLiteralElement::Literal(l) => l.to_doc(alloc), ListLiteralElement::Literal(l) => l.to_doc(alloc),
ListLiteralElement::Symbol(s) => symbol_to_doc(alloc, *s), ListLiteralElement::Symbol(s) => symbol_to_doc(alloc, *s, pretty),
}); });
alloc alloc
@ -2154,17 +2137,21 @@ impl<'a> Expr<'a> {
index, structure, .. index, structure, ..
} => alloc } => alloc
.text(format!("StructAtIndex {} ", index)) .text(format!("StructAtIndex {} ", index))
.append(symbol_to_doc(alloc, *structure)), .append(symbol_to_doc(alloc, *structure, pretty)),
RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)), RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)),
GetTagId { structure, .. } => alloc GetTagId { structure, .. } => alloc
.text("GetTagId ") .text("GetTagId ")
.append(symbol_to_doc(alloc, *structure)), .append(symbol_to_doc(alloc, *structure, pretty)),
ExprBox { symbol, .. } => alloc.text("Box ").append(symbol_to_doc(alloc, *symbol)), ExprBox { symbol, .. } => alloc
.text("Box ")
.append(symbol_to_doc(alloc, *symbol, pretty)),
ExprUnbox { symbol, .. } => alloc.text("Unbox ").append(symbol_to_doc(alloc, *symbol)), ExprUnbox { symbol, .. } => alloc
.text("Unbox ")
.append(symbol_to_doc(alloc, *symbol, pretty)),
UnionAtIndex { UnionAtIndex {
tag_id, tag_id,
@ -2173,14 +2160,14 @@ impl<'a> Expr<'a> {
.. ..
} => alloc } => alloc
.text(format!("UnionAtIndex (Id {}) (Index {}) ", tag_id, index)) .text(format!("UnionAtIndex (Id {}) (Index {}) ", tag_id, index))
.append(symbol_to_doc(alloc, *structure)), .append(symbol_to_doc(alloc, *structure, pretty)),
} }
} }
pub fn to_pretty(&self, width: usize) -> String { pub fn to_pretty(&self, width: usize, pretty: bool) -> String {
let allocator = BoxAllocator; let allocator = BoxAllocator;
let mut w = std::vec::Vec::new(); let mut w = std::vec::Vec::new();
self.to_doc::<_, ()>(&allocator) self.to_doc::<_, ()>(&allocator, pretty)
.1 .1
.render(width, &mut w) .render(width, &mut w)
.unwrap(); .unwrap();
@ -2200,7 +2187,12 @@ impl<'a> Stmt<'a> {
from_can(env, var, can_expr, procs, layout_cache) from_can(env, var, can_expr, procs, layout_cache)
} }
pub fn to_doc<'b, D, A, I>(&'b self, alloc: &'b D, interner: &I) -> DocBuilder<'b, D, A> pub fn to_doc<'b, D, A, I>(
&'b self,
alloc: &'b D,
interner: &I,
pretty: bool,
) -> DocBuilder<'b, D, A>
where where
D: DocAllocator<'b, A>, D: DocAllocator<'b, A>,
D::Doc: Clone, D::Doc: Clone,
@ -2212,19 +2204,19 @@ impl<'a> Stmt<'a> {
match self { match self {
Let(symbol, expr, layout, cont) => alloc Let(symbol, expr, layout, cont) => alloc
.text("let ") .text("let ")
.append(symbol_to_doc(alloc, *symbol)) .append(symbol_to_doc(alloc, *symbol, pretty))
.append(" : ") .append(" : ")
.append(layout.to_doc(alloc, interner, Parens::NotNeeded)) .append(layout.to_doc(alloc, interner, Parens::NotNeeded))
.append(" = ") .append(" = ")
.append(expr.to_doc(alloc)) .append(expr.to_doc(alloc, pretty))
.append(";") .append(";")
.append(alloc.hardline()) .append(alloc.hardline())
.append(cont.to_doc(alloc, interner)), .append(cont.to_doc(alloc, interner, pretty)),
Refcounting(modify, cont) => modify Refcounting(modify, cont) => modify
.to_doc(alloc) .to_doc(alloc, pretty)
.append(alloc.hardline()) .append(alloc.hardline())
.append(cont.to_doc(alloc, interner)), .append(cont.to_doc(alloc, interner, pretty)),
Expect { Expect {
condition, condition,
@ -2232,10 +2224,10 @@ impl<'a> Stmt<'a> {
.. ..
} => alloc } => alloc
.text("expect ") .text("expect ")
.append(symbol_to_doc(alloc, *condition)) .append(symbol_to_doc(alloc, *condition, pretty))
.append(";") .append(";")
.append(alloc.hardline()) .append(alloc.hardline())
.append(remainder.to_doc(alloc, interner)), .append(remainder.to_doc(alloc, interner, pretty)),
ExpectFx { ExpectFx {
condition, condition,
@ -2243,14 +2235,14 @@ impl<'a> Stmt<'a> {
.. ..
} => alloc } => alloc
.text("expect-fx ") .text("expect-fx ")
.append(symbol_to_doc(alloc, *condition)) .append(symbol_to_doc(alloc, *condition, pretty))
.append(";") .append(";")
.append(alloc.hardline()) .append(alloc.hardline())
.append(remainder.to_doc(alloc, interner)), .append(remainder.to_doc(alloc, interner, pretty)),
Ret(symbol) => alloc Ret(symbol) => alloc
.text("ret ") .text("ret ")
.append(symbol_to_doc(alloc, *symbol)) .append(symbol_to_doc(alloc, *symbol, pretty))
.append(";"), .append(";"),
Switch { Switch {
@ -2264,23 +2256,23 @@ impl<'a> Stmt<'a> {
let fail = default_branch.1; let fail = default_branch.1;
alloc alloc
.text("if ") .text("if ")
.append(symbol_to_doc(alloc, *cond_symbol)) .append(symbol_to_doc(alloc, *cond_symbol, pretty))
.append(" then") .append(" then")
.append(info.to_doc(alloc)) .append(info.to_doc(alloc, pretty))
.append(alloc.hardline()) .append(alloc.hardline())
.append(pass.to_doc(alloc, interner).indent(4)) .append(pass.to_doc(alloc, interner, pretty).indent(4))
.append(alloc.hardline()) .append(alloc.hardline())
.append(alloc.text("else")) .append(alloc.text("else"))
.append(default_branch.0.to_doc(alloc)) .append(default_branch.0.to_doc(alloc, pretty))
.append(alloc.hardline()) .append(alloc.hardline())
.append(fail.to_doc(alloc, interner).indent(4)) .append(fail.to_doc(alloc, interner, pretty).indent(4))
} }
_ => { _ => {
let default_doc = alloc let default_doc = alloc
.text("default:") .text("default:")
.append(alloc.hardline()) .append(alloc.hardline())
.append(default_branch.1.to_doc(alloc, interner).indent(4)) .append(default_branch.1.to_doc(alloc, interner, pretty).indent(4))
.indent(4); .indent(4);
let branches_docs = branches let branches_docs = branches
@ -2289,14 +2281,14 @@ impl<'a> Stmt<'a> {
alloc alloc
.text(format!("case {}:", tag)) .text(format!("case {}:", tag))
.append(alloc.hardline()) .append(alloc.hardline())
.append(expr.to_doc(alloc, interner).indent(4)) .append(expr.to_doc(alloc, interner, pretty).indent(4))
.indent(4) .indent(4)
}) })
.chain(std::iter::once(default_doc)); .chain(std::iter::once(default_doc));
// //
alloc alloc
.text("switch ") .text("switch ")
.append(symbol_to_doc(alloc, *cond_symbol)) .append(symbol_to_doc(alloc, *cond_symbol, pretty))
.append(":") .append(":")
.append(alloc.hardline()) .append(alloc.hardline())
.append(alloc.intersperse( .append(alloc.intersperse(
@ -2308,7 +2300,9 @@ impl<'a> Stmt<'a> {
} }
} }
Crash(s, _src) => alloc.text("Crash ").append(symbol_to_doc(alloc, *s)), Crash(s, _src) => alloc
.text("Crash ")
.append(symbol_to_doc(alloc, *s, pretty)),
Join { Join {
id, id,
@ -2316,29 +2310,31 @@ impl<'a> Stmt<'a> {
body: continuation, body: continuation,
remainder, remainder,
} => { } => {
let it = parameters.iter().map(|p| symbol_to_doc(alloc, p.symbol)); let it = parameters
.iter()
.map(|p| symbol_to_doc(alloc, p.symbol, pretty));
alloc.intersperse( alloc.intersperse(
vec![ vec![
alloc alloc
.text("joinpoint ") .text("joinpoint ")
.append(join_point_to_doc(alloc, *id)) .append(join_point_to_doc(alloc, *id, pretty))
.append(" ".repeat(parameters.len().min(1))) .append(" ".repeat(parameters.len().min(1)))
.append(alloc.intersperse(it, alloc.space())) .append(alloc.intersperse(it, alloc.space()))
.append(":"), .append(":"),
continuation.to_doc(alloc, interner).indent(4), continuation.to_doc(alloc, interner, pretty).indent(4),
alloc.text("in"), alloc.text("in"),
remainder.to_doc(alloc, interner), remainder.to_doc(alloc, interner, pretty),
], ],
alloc.hardline(), alloc.hardline(),
) )
} }
Jump(id, arguments) => { Jump(id, arguments) => {
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s)); let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s, pretty));
alloc alloc
.text("jump ") .text("jump ")
.append(join_point_to_doc(alloc, *id)) .append(join_point_to_doc(alloc, *id, pretty))
.append(" ".repeat(arguments.len().min(1))) .append(" ".repeat(arguments.len().min(1)))
.append(alloc.intersperse(it, alloc.space())) .append(alloc.intersperse(it, alloc.space()))
.append(";") .append(";")
@ -2346,13 +2342,13 @@ impl<'a> Stmt<'a> {
} }
} }
pub fn to_pretty<I>(&self, interner: &I, width: usize) -> String pub fn to_pretty<I>(&self, interner: &I, width: usize, pretty: bool) -> String
where where
I: Interner<'a, Layout<'a>>, I: Interner<'a, Layout<'a>>,
{ {
let allocator = BoxAllocator; let allocator = BoxAllocator;
let mut w = std::vec::Vec::new(); let mut w = std::vec::Vec::new();
self.to_doc::<_, (), _>(&allocator, interner) self.to_doc::<_, (), _>(&allocator, interner, pretty)
.1 .1
.render(width, &mut w) .render(width, &mut w)
.unwrap(); .unwrap();

View file

@ -1154,7 +1154,7 @@ struct SetElement<'a> {
impl std::fmt::Debug for SetElement<'_> { impl std::fmt::Debug for SetElement<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let symbol_string = crate::ir::symbol_to_doc_string(self.symbol); let symbol_string = crate::ir::symbol_to_doc_string(self.symbol, false);
write!(f, "( {}, {:?})", symbol_string, self.layout) write!(f, "( {}, {:?})", symbol_string, self.layout)
} }
@ -1204,7 +1204,7 @@ impl std::fmt::Debug for LambdaSet<'_> {
/// ///
/// See also https://github.com/roc-lang/roc/issues/3336. /// See also https://github.com/roc-lang/roc/issues/3336.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct CapturesNiche<'a>(&'a [Layout<'a>]); pub struct CapturesNiche<'a>(pub(crate) &'a [Layout<'a>]);
impl CapturesNiche<'_> { impl CapturesNiche<'_> {
pub fn no_niche() -> Self { pub fn no_niche() -> Self {

View file

@ -21,3 +21,5 @@ pub mod tail_recursion;
// For now, following this warning's advice will lead to nasty type inference errors. // For now, following this warning's advice will lead to nasty type inference errors.
//#[allow(clippy::ptr_arg)] //#[allow(clippy::ptr_arg)]
pub mod decision_tree; pub mod decision_tree;
pub mod debug;

View file

@ -7,6 +7,8 @@ use roc_parse::pattern::PatternType;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::types::AliasKind; use roc_types::types::AliasKind;
use crate::Severity;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CycleEntry { pub struct CycleEntry {
pub symbol: Symbol, pub symbol: Symbol,
@ -40,6 +42,7 @@ pub enum Problem {
/// Second symbol is the name of the argument that is unused /// Second symbol is the name of the argument that is unused
UnusedArgument(Symbol, bool, Symbol, Region), UnusedArgument(Symbol, bool, Symbol, Region),
UnusedBranchDef(Symbol, Region), UnusedBranchDef(Symbol, Region),
DefsOnlyUsedInRecursion(usize, Region),
PrecedenceProblem(PrecedenceProblem), PrecedenceProblem(PrecedenceProblem),
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments! // Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(BadPattern, Region), UnsupportedPattern(BadPattern, Region),
@ -204,6 +207,71 @@ pub enum Problem {
} }
impl Problem { impl Problem {
pub fn severity(&self) -> Severity {
use Severity::{RuntimeError, Warning};
match self {
Problem::UnusedDef(_, _) => Warning,
Problem::UnusedImport(_, _) => Warning,
Problem::UnusedModuleImport(_, _) => Warning,
Problem::ExposedButNotDefined(_) => RuntimeError,
Problem::UnknownGeneratesWith(_) => RuntimeError,
Problem::UnusedArgument(_, _, _, _) => Warning,
Problem::UnusedBranchDef(_, _) => Warning,
Problem::PrecedenceProblem(_) => RuntimeError,
Problem::UnsupportedPattern(_, _) => RuntimeError,
Problem::Shadowing { .. } => RuntimeError,
Problem::CyclicAlias(..) => RuntimeError,
Problem::BadRecursion(_) => RuntimeError,
Problem::PhantomTypeArgument { .. } => Warning,
Problem::UnboundTypeVariable { .. } => RuntimeError,
Problem::DuplicateRecordFieldValue { .. } => Warning,
Problem::DuplicateRecordFieldType { .. } => RuntimeError,
Problem::InvalidOptionalValue { .. } => RuntimeError,
Problem::DuplicateTag { .. } => RuntimeError,
Problem::RuntimeError(_) => RuntimeError,
Problem::SignatureDefMismatch { .. } => RuntimeError,
Problem::InvalidAliasRigid { .. } => RuntimeError,
Problem::InvalidInterpolation(_) => RuntimeError,
Problem::InvalidHexadecimal(_) => RuntimeError,
Problem::InvalidUnicodeCodePt(_) => RuntimeError,
Problem::NestedDatatype { .. } => RuntimeError,
Problem::InvalidExtensionType { .. } => RuntimeError,
Problem::AbilityHasTypeVariables { .. } => RuntimeError,
Problem::HasClauseIsNotAbility { .. } => RuntimeError,
Problem::IllegalHasClause { .. } => RuntimeError,
Problem::DuplicateHasAbility { .. } => Warning,
Problem::AbilityMemberMissingHasClause { .. } => RuntimeError,
Problem::AbilityMemberMultipleBoundVars { .. } => RuntimeError,
Problem::AbilityNotOnToplevel { .. } => RuntimeError, // Ideally, could be compiled
Problem::AbilityUsedAsType(_, _, _) => RuntimeError,
Problem::NestedSpecialization(_, _) => RuntimeError, // Ideally, could be compiled
Problem::IllegalDerivedAbility(_) => RuntimeError,
Problem::ImplementationNotFound { .. } => RuntimeError,
Problem::NotAnAbilityMember { .. } => RuntimeError,
Problem::OptionalAbilityImpl { .. } => RuntimeError,
Problem::QualifiedAbilityImpl { .. } => RuntimeError,
Problem::AbilityImplNotIdent { .. } => RuntimeError,
Problem::DuplicateImpl { .. } => Warning, // First impl is used at runtime
Problem::NotAnAbility(_) => Warning,
Problem::ImplementsNonRequired { .. } => Warning,
Problem::DoesNotImplementAbility { .. } => RuntimeError,
Problem::NotBoundInAllPatterns { .. } => RuntimeError,
Problem::NoIdentifiersIntroduced(_) => Warning,
Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile
Problem::UnnecessaryOutputWildcard { .. } => Warning,
// TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we
// don't catch that yet.
Problem::MultipleListRestPattern { .. } => RuntimeError,
Problem::BadTypeArguments { .. } => RuntimeError,
// TODO: this can be a warning instead if we recover the program by
// injecting a crash message
Problem::UnappliedCrash { .. } => RuntimeError,
Problem::OverAppliedCrash { .. } => RuntimeError,
Problem::DefsOnlyUsedInRecursion(_, _) => Warning,
}
}
/// Returns a Region value from the Problem, if possible. /// Returns a Region value from the Problem, if possible.
/// Some problems have more than one region; in those cases, /// Some problems have more than one region; in those cases,
/// this tries to pick the one that's closest to the original /// this tries to pick the one that's closest to the original
@ -333,7 +401,8 @@ impl Problem {
| Problem::BadTypeArguments { region, .. } | Problem::BadTypeArguments { region, .. }
| Problem::UnnecessaryOutputWildcard { region } | Problem::UnnecessaryOutputWildcard { region }
| Problem::OverAppliedCrash { region } | Problem::OverAppliedCrash { region }
| Problem::UnappliedCrash { region } => Some(*region), | Problem::UnappliedCrash { region }
| Problem::DefsOnlyUsedInRecursion(_, region) => Some(*region),
Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries))
| Problem::BadRecursion(cycle_entries) => { | Problem::BadRecursion(cycle_entries) => {
cycle_entries.first().map(|entry| entry.expr_region) cycle_entries.first().map(|entry| entry.expr_region)

View file

@ -3,3 +3,15 @@
// See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check. // See github.com/roc-lang/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
pub mod can; pub mod can;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Severity {
/// This will cause a runtime error if some code get srun
/// (e.g. type mismatch, naming error)
RuntimeError,
/// This will never cause the code to misbehave,
/// but should be cleaned up
/// (e.g. unused def, unused import)
Warning,
}

View file

@ -1,6 +1,9 @@
use roc_can::abilities::AbilitiesStore; use roc_can::abilities::AbilitiesStore;
use roc_can::expr::PendingDerives; use roc_can::expr::PendingDerives;
use roc_collections::{VecMap, VecSet}; use roc_collections::{VecMap, VecSet};
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_UNDERIVABLE;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -309,12 +312,18 @@ impl ObligationCache {
Some(Err(NotDerivable { Some(Err(NotDerivable {
var: failure_var, var: failure_var,
context, context,
})) => Some(if failure_var == var { })) => {
dbg_do!(ROC_PRINT_UNDERIVABLE, {
eprintln!("❌ derived {:?} of {:?}", ability, subs.dbg(failure_var));
});
Some(if failure_var == var {
UnderivableReason::SurfaceNotDerivable(context) UnderivableReason::SurfaceNotDerivable(context)
} else { } else {
let error_type = subs.var_to_error_type(failure_var, Polarity::OF_VALUE); let error_type = subs.var_to_error_type(failure_var, Polarity::OF_VALUE);
UnderivableReason::NestedNotDerivable(error_type, context) UnderivableReason::NestedNotDerivable(error_type, context)
}), })
}
None => Some(UnderivableReason::NotABuiltin), None => Some(UnderivableReason::NotABuiltin),
}; };
@ -497,18 +506,6 @@ trait DerivableVisitor {
false false
} }
#[inline(always)]
fn visit_rigid_able(var: Variable, abilities: &[Symbol]) -> Result<(), NotDerivable> {
if abilities != [Self::ABILITY] {
Err(NotDerivable {
var,
context: NotDerivableContext::UnboundVar,
})
} else {
Ok(())
}
}
#[inline(always)] #[inline(always)]
fn visit_recursion(var: Variable) -> Result<Descend, NotDerivable> { fn visit_recursion(var: Variable) -> Result<Descend, NotDerivable> {
Err(NotDerivable { Err(NotDerivable {
@ -659,7 +656,12 @@ trait DerivableVisitor {
subs.set_content(var, Content::FlexAbleVar(opt_name, merged_abilites)); subs.set_content(var, Content::FlexAbleVar(opt_name, merged_abilites));
} }
RigidAbleVar(_, abilities) => { RigidAbleVar(_, abilities) => {
Self::visit_rigid_able(var, subs.get_subs_slice(abilities))? if !subs.get_subs_slice(abilities).contains(&Self::ABILITY) {
return Err(NotDerivable {
var,
context: NotDerivableContext::NoContext,
});
}
} }
RecursionVar { RecursionVar {
structure, structure,

View file

@ -3473,7 +3473,7 @@ mod solve_expr {
Dict.insert Dict.insert
"# "#
), ),
"Dict k v, k, v -> Dict k v | k has Eq", "Dict k v, k, v -> Dict k v | k has Hash & Eq",
); );
} }
@ -3734,7 +3734,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
reconstructPath : Dict position position, position -> List position | position has Eq reconstructPath : Dict position position, position -> List position | position has Hash & Eq
reconstructPath = \cameFrom, goal -> reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is when Dict.get cameFrom goal is
Err KeyNotFound -> Err KeyNotFound ->
@ -3746,7 +3746,7 @@ mod solve_expr {
reconstructPath reconstructPath
"# "#
), ),
"Dict position position, position -> List position | position has Eq", "Dict position position, position -> List position | position has Hash & Eq",
); );
} }
@ -3781,7 +3781,7 @@ mod solve_expr {
Model position : { openSet : Set position } Model position : { openSet : Set position }
cheapestOpen : Model position -> Result position [KeyNotFound] | position has Eq cheapestOpen : Model position -> Result position [KeyNotFound] | position has Hash & Eq
cheapestOpen = \model -> cheapestOpen = \model ->
folder = \resSmallestSoFar, position -> folder = \resSmallestSoFar, position ->
@ -3796,14 +3796,14 @@ mod solve_expr {
Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder
|> Result.map (\x -> x.position) |> Result.map (\x -> x.position)
astar : Model position -> Result position [KeyNotFound] | position has Eq astar : Model position -> Result position [KeyNotFound] | position has Hash & Eq
astar = \model -> cheapestOpen model astar = \model -> cheapestOpen model
main = main =
astar astar
"# "#
), ),
"Model position -> Result position [KeyNotFound] | position has Eq", "Model position -> Result position [KeyNotFound] | position has Hash & Eq",
); );
} }
@ -4445,7 +4445,7 @@ mod solve_expr {
Key k : Num k Key k : Num k
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Eq removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
removeHelpEQGT = \targetKey, dict -> removeHelpEQGT = \targetKey, dict ->
when dict is when dict is
Node color key value left right -> Node color key value left right ->
@ -4559,7 +4559,7 @@ mod solve_expr {
_ -> _ ->
Empty Empty
removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Eq removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
removeHelp = \targetKey, dict -> removeHelp = \targetKey, dict ->
when dict is when dict is
Empty -> Empty ->
@ -4647,7 +4647,7 @@ mod solve_expr {
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty] 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 Eq removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v | k has Hash & Eq
removeHelp = \targetKey, dict -> removeHelp = \targetKey, dict ->
when dict is when dict is
Empty -> Empty ->
@ -4682,7 +4682,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 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 Eq removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq
removeHelpEQGT = \targetKey, dict -> removeHelpEQGT = \targetKey, dict ->
when dict is when dict is
Node color key value left right -> Node color key value left right ->
@ -8270,7 +8270,7 @@ mod solve_expr {
r#" r#"
app "test" provides [top] to "./platform" app "test" provides [top] to "./platform"
MDict u := (List u) | u has Eq MDict u := (List u) | u has Hash & Eq
bot : MDict k -> MDict k bot : MDict k -> MDict k
bot = \@MDict data -> bot = \@MDict data ->
@ -8281,7 +8281,7 @@ mod solve_expr {
top = \x -> bot x top = \x -> bot x
"# "#
), ),
"MDict v -> MDict v | v has Eq", "MDict v -> MDict v | v has Hash & Eq",
); );
} }
@ -8431,4 +8431,29 @@ mod solve_expr {
@"n : Dec" @"n : Dec"
); );
} }
#[test]
fn resolve_set_eq_issue_4671() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
s1 : Set U8
s1 = Set.empty
s2 : Set Str
s2 = Set.empty
Bool.isEq s1 s1 && Bool.isEq s2 s2
# ^^^^^^^^^ ^^^^^^^^^
"#
),
@r###"
Set#Bool.isEq(17) : Set U8, Set U8 -[[Set.isEq(17)]]-> Bool
Set#Bool.isEq(17) : Set Str, Set Str -[[Set.isEq(17)]]-> Bool
"###
);
}
} }

View file

@ -1,7 +1,7 @@
//! Provides types to describe problems that can occur during solving. //! Provides types to describe problems that can occur during solving.
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_module::{ident::Lowercase, symbol::Symbol}; use roc_module::{ident::Lowercase, symbol::Symbol};
use roc_problem::can::CycleEntry; use roc_problem::{can::CycleEntry, Severity};
use roc_region::all::Region; use roc_region::all::Region;
use roc_types::types::{Category, ErrorType, PatternCategory}; use roc_types::types::{Category, ErrorType, PatternCategory};
@ -31,6 +31,27 @@ pub enum TypeError {
}, },
} }
impl TypeError {
pub fn severity(&self) -> Severity {
use Severity::*;
match self {
TypeError::BadExpr(..) => RuntimeError,
TypeError::BadPattern(..) => RuntimeError,
TypeError::CircularType(..) => RuntimeError,
TypeError::CircularDef(_) => RuntimeError,
TypeError::UnexposedLookup(_) => RuntimeError,
TypeError::UnfulfilledAbility(_) => RuntimeError,
TypeError::BadExprMissingAbility(_, _, _, _) => RuntimeError,
TypeError::BadPatternMissingAbility(_, _, _, _) => RuntimeError,
// NB: if bidirectional exhaustiveness checking is implemented, the other direction
// is also not a runtime error.
TypeError::Exhaustive(exhtv) => exhtv.severity(),
TypeError::StructuralSpecialization { .. } => RuntimeError,
TypeError::WrongSpecialization { .. } => RuntimeError,
}
}
}
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub enum Unfulfilled { pub enum Unfulfilled {
/// No claimed implementation of an ability for an opaque type. /// No claimed implementation of an ability for an opaque type.

View file

@ -378,6 +378,7 @@ fn check_derived_typechecks_and_golden(
); );
let mut def_types = Default::default(); let mut def_types = Default::default();
let mut rigid_vars = Default::default(); let mut rigid_vars = Default::default();
let mut flex_vars = Default::default();
let (import_variables, abilities_store) = add_imports( let (import_variables, abilities_store) = add_imports(
test_module, test_module,
&mut constraints, &mut constraints,
@ -386,9 +387,15 @@ fn check_derived_typechecks_and_golden(
&exposed_for_module, &exposed_for_module,
&mut def_types, &mut def_types,
&mut rigid_vars, &mut rigid_vars,
&mut flex_vars,
);
let constr = constraints.let_import_constraint(
rigid_vars,
flex_vars,
def_types,
constr,
&import_variables,
); );
let constr =
constraints.let_import_constraint(rigid_vars, def_types, constr, &import_variables);
// run the solver, print and fail if we have errors // run the solver, print and fail if we have errors
dbg_do!( dbg_do!(

View file

@ -465,7 +465,7 @@ fn eq_rosetree() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn eq_different_rosetrees() { fn eq_different_rosetrees() {
// Requires two different equality procedures for `List (Rose I64)` and `List (Rose Str)` // Requires two different equality procedures for `List (Rose I64)` and `List (Rose Str)`
// even though both appear in the mono Layout as `List(RecursivePointer)` // even though both appear in the mono Layout as `List(RecursivePointer)`

View file

@ -1,4 +1,7 @@
#![cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #![cfg(all(
any(feature = "gen-llvm", feature = "gen-wasm"),
not(debug_assertions) // https://github.com/roc-lang/roc/issues/3898
))]
#[cfg(feature = "gen-llvm")] #[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to; use crate::helpers::llvm::assert_evals_to;
@ -77,6 +80,7 @@ fn dict_nonempty_contains() {
} }
#[test] #[test]
#[ignore = "TODO figure out why this is broken with llvm wasm tests"]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn dict_empty_remove() { fn dict_empty_remove() {
assert_evals_to!( assert_evals_to!(
@ -252,7 +256,11 @@ fn from_list_with_fold_reallocates() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm"))]
// TODO: Re-enable this test for wasm.
// Currently it causes "[trap] out of bounds memory access" due to the small strings.
// I was unable to find the root cause and with llvm and valgrind it passes with no issues.
// #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn small_str_keys() { fn small_str_keys() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
@ -384,7 +392,7 @@ fn insert_all() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn insert_all_prefer_first() { fn insert_all_prefer_second() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -396,7 +404,7 @@ fn insert_all_prefer_first() {
Dict.values myDict Dict.values myDict
"# "#
), ),
RocList::from_slice(&[100]), RocList::from_slice(&[200]),
RocList<i64> RocList<i64>
); );
} }

View file

@ -2808,22 +2808,6 @@ fn cleanup_because_exception() {
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_range() {
assert_evals_to!(
"List.range 0 -1",
RocList::<i64>::from_slice(&[]),
RocList<i64>
);
assert_evals_to!("List.range 0 0", RocList::from_slice(&[0]), RocList<i64>);
assert_evals_to!(
"List.range 0 5",
RocList::from_slice(&[0, 1, 2, 3, 4]),
RocList<i64>
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_sort_with() { fn list_sort_with() {
@ -3561,6 +3545,27 @@ fn list_walk_from_until_sum() {
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn concat_unique_to_nonunique_overlapping_issue_4697() {
assert_evals_to!(
r#"
# originalList is shared, but others is unique.
# When we concat originalList with others, others should be re-used.
originalList = [1u8]
others = [2u8, 3u8, 4u8]
new = List.concat originalList others
{a: originalList, b: new}
"#,
(
RocList::from_slice(&[1u8]),
RocList::from_slice(&[1u8, 2, 3, 4]),
),
(RocList<u8>, RocList<u8>)
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_walk_from_even_prefix_sum() { fn list_walk_from_even_prefix_sum() {

View file

@ -1,4 +1,7 @@
#![cfg(feature = "gen-llvm")] #![cfg(all(
any(feature = "gen-llvm"),
not(debug_assertions) // https://github.com/roc-lang/roc/issues/3898
))]
#[cfg(feature = "gen-llvm")] #[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to; use crate::helpers::llvm::assert_evals_to;
@ -62,16 +65,6 @@ fn single_to_list() {
RocList::from_slice(&[1]), RocList::from_slice(&[1]),
RocList<i64> RocList<i64>
); );
assert_evals_to!(
indoc!(
r#"
Set.toList (Set.single 1.0)
"#
),
RocList::from_slice(&[1.0]),
RocList<f64>
);
} }
#[test] #[test]
@ -260,6 +253,20 @@ fn from_list_void() {
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn to_list_empty() {
assert_evals_to!(
indoc!(
r#"
Set.toList Set.empty
"#
),
RocList::<std::convert::Infallible>::default(),
RocList<std::convert::Infallible>
);
}
#[test] #[test]
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn from_list_result() { fn from_list_result() {
@ -280,3 +287,26 @@ fn from_list_result() {
i64 i64
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn resolve_set_eq_issue_4671() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [main] to "./platform"
main =
s1 : Set U8
s1 = Set.fromList [1, 2, 3]
s2 : Set U8
s2 = Set.fromList [3, 2, 1]
s1 == s2
"#
),
true,
bool
);
}

View file

@ -7,7 +7,7 @@ use roc_build::link::llvm_module_to_dylib;
use roc_collections::all::MutSet; use roc_collections::all::MutSet;
use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult}; use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult};
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading}; use roc_load::{EntryPoint, ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
use roc_mono::ir::{CrashTag, OptLevel}; use roc_mono::ir::{CrashTag, OptLevel};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo; use roc_region::all::LineInfo;
@ -87,7 +87,9 @@ fn create_llvm_module<'a>(
let mut loaded = match loaded { let mut loaded = match loaded {
Ok(x) => x, Ok(x) => x,
Err(roc_load::LoadingProblem::FormattedReport(report)) => { Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
report,
))) => {
println!("{}", report); println!("{}", report);
panic!(); panic!();
} }

View file

@ -1,7 +1,7 @@
procedure List.5 (#Attr.2, #Attr.3): procedure List.5 (#Attr.2, #Attr.3):
let List.409 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; let List.478 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
decref #Attr.2; decref #Attr.2;
ret List.409; ret List.478;
procedure Test.2 (Test.3): procedure Test.2 (Test.3):
let Test.7 : {} = Struct {}; let Test.7 : {} = Struct {};

View file

@ -1,7 +1,7 @@
procedure List.5 (#Attr.2, #Attr.3): procedure List.5 (#Attr.2, #Attr.3):
let List.409 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; let List.478 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
decref #Attr.2; decref #Attr.2;
ret List.409; ret List.478;
procedure Test.2 (Test.3): procedure Test.2 (Test.3):
let Test.7 : {} = Struct {}; let Test.7 : {} = Struct {};

View file

@ -1,6 +1,6 @@
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure Test.1 (Test.5): procedure Test.1 (Test.5):
let Test.2 : I64 = 41i64; let Test.2 : I64 = 41i64;

View file

@ -1,17 +1,87 @@
procedure Dict.1 (): procedure Dict.1 ():
let Dict.318 : List {[], []} = Array []; let Dict.520 : List {[], []} = Array [];
ret Dict.318; let Dict.527 : U64 = 0i64;
let Dict.528 : U64 = 8i64;
let Dict.521 : List U64 = CallByName List.11 Dict.527 Dict.528;
let Dict.524 : I8 = CallByName Dict.34;
let Dict.525 : U64 = 8i64;
let Dict.522 : List I8 = CallByName List.11 Dict.524 Dict.525;
let Dict.523 : U64 = 0i64;
let Dict.519 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.520, Dict.521, Dict.522, Dict.523};
ret Dict.519;
procedure Dict.7 (Dict.312): procedure Dict.34 ():
let Dict.317 : U64 = CallByName List.6 Dict.312; let Dict.526 : I8 = -128i64;
ret Dict.317; ret Dict.526;
procedure List.6 (#Attr.2): procedure Dict.4 (Dict.507):
let List.409 : U64 = lowlevel ListLen #Attr.2; let Dict.85 : U64 = StructAtIndex 3 Dict.507;
ret List.409; dec Dict.507;
ret Dict.85;
procedure List.11 (List.114, List.115):
let List.479 : List I8 = CallByName List.68 List.115;
let List.478 : List I8 = CallByName List.80 List.114 List.115 List.479;
ret List.478;
procedure List.11 (List.114, List.115):
let List.491 : List U64 = CallByName List.68 List.115;
let List.490 : List U64 = CallByName List.80 List.114 List.115 List.491;
ret List.490;
procedure List.68 (#Attr.2):
let List.489 : List I8 = lowlevel ListWithCapacity #Attr.2;
ret List.489;
procedure List.68 (#Attr.2):
let List.501 : List U64 = lowlevel ListWithCapacity #Attr.2;
ret List.501;
procedure List.71 (#Attr.2, #Attr.3):
let List.486 : List I8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.486;
procedure List.71 (#Attr.2, #Attr.3):
let List.498 : List U64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.498;
procedure List.80 (List.502, List.503, List.504):
joinpoint List.480 List.116 List.117 List.118:
let List.488 : U64 = 0i64;
let List.482 : Int1 = CallByName Num.24 List.117 List.488;
if List.482 then
let List.487 : U64 = 1i64;
let List.484 : U64 = CallByName Num.20 List.117 List.487;
let List.485 : List I8 = CallByName List.71 List.118 List.116;
jump List.480 List.116 List.484 List.485;
else
ret List.118;
in
jump List.480 List.502 List.503 List.504;
procedure List.80 (List.510, List.511, List.512):
joinpoint List.492 List.116 List.117 List.118:
let List.500 : U64 = 0i64;
let List.494 : Int1 = CallByName Num.24 List.117 List.500;
if List.494 then
let List.499 : U64 = 1i64;
let List.496 : U64 = CallByName Num.20 List.117 List.499;
let List.497 : List U64 = CallByName List.71 List.118 List.116;
jump List.492 List.116 List.496 List.497;
else
ret List.118;
in
jump List.492 List.510 List.511 List.512;
procedure Num.20 (#Attr.2, #Attr.3):
let Num.257 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
ret Num.257;
procedure Num.24 (#Attr.2, #Attr.3):
let Num.259 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
ret Num.259;
procedure Test.0 (): procedure Test.0 ():
let Test.2 : List {[], []} = CallByName Dict.1; let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1;
let Test.1 : U64 = CallByName Dict.7 Test.2; let Test.1 : U64 = CallByName Dict.4 Test.2;
dec Test.2;
ret Test.1; ret Test.1;

View file

@ -2,25 +2,25 @@ procedure Bool.1 ():
let Bool.23 : Int1 = false; let Bool.23 : Int1 = false;
ret Bool.23; ret Bool.23;
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.415 : U64 = CallByName List.6 List.94; let List.484 : U64 = CallByName List.6 List.95;
let List.411 : Int1 = CallByName Num.22 List.95 List.415; let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.411 then if List.480 then
let List.413 : {} = CallByName List.66 List.94 List.95; let List.482 : {} = CallByName List.66 List.95 List.96;
let List.412 : [C {}, C {}] = TagId(1) List.413; let List.481 : [C {}, C {}] = TagId(1) List.482;
ret List.412; ret List.481;
else else
let List.410 : {} = Struct {}; let List.479 : {} = Struct {};
let List.409 : [C {}, C {}] = TagId(0) List.410; let List.478 : [C {}, C {}] = TagId(0) List.479;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.414 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.483 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,16 +1,16 @@
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.412 : U64 = 1i64; let List.481 : U64 = 1i64;
let List.410 : List U8 = CallByName List.70 List.105 List.412; let List.479 : List U8 = CallByName List.70 List.106 List.481;
let List.409 : List U8 = CallByName List.71 List.410 List.106; let List.478 : List U8 = CallByName List.71 List.479 List.107;
ret List.409; ret List.478;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.411 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.480 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.411; ret List.480;
procedure Test.23 (Test.24, Test.35, Test.22): procedure Test.23 (Test.24, Test.35, Test.22):
let Test.37 : List U8 = CallByName List.4 Test.24 Test.22; let Test.37 : List U8 = CallByName List.4 Test.24 Test.22;

View file

@ -66,237 +66,237 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.112 (Json.113, Json.397, Json.111): procedure Json.112 (Json.113, Json.399, Json.111):
let Json.430 : I64 = 123i64; let Json.432 : I64 = 123i64;
let Json.429 : U8 = CallByName Num.125 Json.430; let Json.431 : U8 = CallByName Num.125 Json.432;
let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; let Json.115 : List U8 = CallByName List.4 Json.113 Json.431;
let Json.428 : U64 = CallByName List.6 Json.111; let Json.430 : U64 = CallByName List.6 Json.111;
let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; let Json.407 : {List U8, U64} = Struct {Json.115, Json.430};
let Json.406 : {} = Struct {}; let Json.408 : {} = Struct {};
let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; let Json.406 : {List U8, U64} = CallByName List.18 Json.111 Json.407 Json.408;
dec Json.111; dec Json.111;
let Json.117 : List U8 = StructAtIndex 0 Json.404; let Json.117 : List U8 = StructAtIndex 0 Json.406;
inc Json.117; inc Json.117;
dec Json.404; dec Json.406;
let Json.403 : I64 = 125i64; let Json.405 : I64 = 125i64;
let Json.402 : U8 = CallByName Num.125 Json.403; let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; let Json.403 : List U8 = CallByName List.4 Json.117 Json.404;
ret Json.401; ret Json.403;
procedure Json.112 (Json.113, Json.397, Json.111): procedure Json.112 (Json.113, Json.399, Json.111):
let Json.470 : I64 = 123i64; let Json.472 : I64 = 123i64;
let Json.469 : U8 = CallByName Num.125 Json.470; let Json.471 : U8 = CallByName Num.125 Json.472;
let Json.115 : List U8 = CallByName List.4 Json.113 Json.469; let Json.115 : List U8 = CallByName List.4 Json.113 Json.471;
let Json.468 : U64 = CallByName List.6 Json.111; let Json.470 : U64 = CallByName List.6 Json.111;
let Json.445 : {List U8, U64} = Struct {Json.115, Json.468}; let Json.447 : {List U8, U64} = Struct {Json.115, Json.470};
let Json.446 : {} = Struct {}; let Json.448 : {} = Struct {};
let Json.444 : {List U8, U64} = CallByName List.18 Json.111 Json.445 Json.446; let Json.446 : {List U8, U64} = CallByName List.18 Json.111 Json.447 Json.448;
dec Json.111; dec Json.111;
let Json.117 : List U8 = StructAtIndex 0 Json.444; let Json.117 : List U8 = StructAtIndex 0 Json.446;
inc Json.117; inc Json.117;
dec Json.444; dec Json.446;
let Json.443 : I64 = 125i64; let Json.445 : I64 = 125i64;
let Json.442 : U8 = CallByName Num.125 Json.443; let Json.444 : U8 = CallByName Num.125 Json.445;
let Json.441 : List U8 = CallByName List.4 Json.117 Json.442; let Json.443 : List U8 = CallByName List.4 Json.117 Json.444;
ret Json.441; ret Json.443;
procedure Json.114 (Json.399, Json.400): procedure Json.114 (Json.401, Json.402):
let Json.120 : Str = StructAtIndex 0 Json.400; let Json.120 : Str = StructAtIndex 0 Json.402;
inc Json.120; inc Json.120;
let Json.121 : Str = StructAtIndex 1 Json.400; let Json.121 : Str = StructAtIndex 1 Json.402;
inc Json.121; inc Json.121;
dec Json.400; dec Json.402;
let Json.118 : List U8 = StructAtIndex 0 Json.399; let Json.118 : List U8 = StructAtIndex 0 Json.401;
inc Json.118; inc Json.118;
let Json.119 : U64 = StructAtIndex 1 Json.399; let Json.119 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.427 : I64 = 34i64; let Json.429 : I64 = 34i64;
let Json.426 : U8 = CallByName Num.125 Json.427; let Json.428 : U8 = CallByName Num.125 Json.429;
let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; let Json.426 : List U8 = CallByName List.4 Json.118 Json.428;
let Json.425 : List U8 = CallByName Str.12 Json.120; let Json.427 : List U8 = CallByName Str.12 Json.120;
let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; let Json.423 : List U8 = CallByName List.8 Json.426 Json.427;
let Json.423 : I64 = 34i64; let Json.425 : I64 = 34i64;
let Json.422 : U8 = CallByName Num.125 Json.423; let Json.424 : U8 = CallByName Num.125 Json.425;
let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; let Json.420 : List U8 = CallByName List.4 Json.423 Json.424;
let Json.420 : I64 = 58i64; let Json.422 : I64 = 58i64;
let Json.419 : U8 = CallByName Num.125 Json.420; let Json.421 : U8 = CallByName Num.125 Json.422;
let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; let Json.418 : List U8 = CallByName List.4 Json.420 Json.421;
let Json.417 : {} = Struct {}; let Json.419 : {} = Struct {};
let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; let Json.122 : List U8 = CallByName Encode.23 Json.418 Json.121 Json.419;
joinpoint Json.411 Json.123: joinpoint Json.413 Json.123:
let Json.409 : U64 = 1i64; let Json.411 : U64 = 1i64;
let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; let Json.410 : U64 = CallByName Num.20 Json.119 Json.411;
let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; let Json.409 : {List U8, U64} = Struct {Json.123, Json.410};
ret Json.407; ret Json.409;
in in
let Json.415 : U64 = 1i64; let Json.417 : U64 = 1i64;
let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; let Json.414 : Int1 = CallByName Num.24 Json.119 Json.417;
if Json.412 then if Json.414 then
let Json.414 : I64 = 44i64; let Json.416 : I64 = 44i64;
let Json.413 : U8 = CallByName Num.125 Json.414; let Json.415 : U8 = CallByName Num.125 Json.416;
let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; let Json.412 : List U8 = CallByName List.4 Json.122 Json.415;
jump Json.411 Json.410; jump Json.413 Json.412;
else else
jump Json.411 Json.122; jump Json.413 Json.122;
procedure Json.114 (Json.399, Json.400): procedure Json.114 (Json.401, Json.402):
let Json.120 : Str = StructAtIndex 0 Json.400; let Json.120 : Str = StructAtIndex 0 Json.402;
inc Json.120; inc Json.120;
let Json.121 : Str = StructAtIndex 1 Json.400; let Json.121 : Str = StructAtIndex 1 Json.402;
inc Json.121; inc Json.121;
dec Json.400; dec Json.402;
let Json.118 : List U8 = StructAtIndex 0 Json.399; let Json.118 : List U8 = StructAtIndex 0 Json.401;
inc Json.118; inc Json.118;
let Json.119 : U64 = StructAtIndex 1 Json.399; let Json.119 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.467 : I64 = 34i64; let Json.469 : I64 = 34i64;
let Json.466 : U8 = CallByName Num.125 Json.467; let Json.468 : U8 = CallByName Num.125 Json.469;
let Json.464 : List U8 = CallByName List.4 Json.118 Json.466; let Json.466 : List U8 = CallByName List.4 Json.118 Json.468;
let Json.465 : List U8 = CallByName Str.12 Json.120; let Json.467 : List U8 = CallByName Str.12 Json.120;
let Json.461 : List U8 = CallByName List.8 Json.464 Json.465; let Json.463 : List U8 = CallByName List.8 Json.466 Json.467;
let Json.463 : I64 = 34i64; let Json.465 : I64 = 34i64;
let Json.462 : U8 = CallByName Num.125 Json.463; let Json.464 : U8 = CallByName Num.125 Json.465;
let Json.458 : List U8 = CallByName List.4 Json.461 Json.462; let Json.460 : List U8 = CallByName List.4 Json.463 Json.464;
let Json.460 : I64 = 58i64; let Json.462 : I64 = 58i64;
let Json.459 : U8 = CallByName Num.125 Json.460; let Json.461 : U8 = CallByName Num.125 Json.462;
let Json.456 : List U8 = CallByName List.4 Json.458 Json.459; let Json.458 : List U8 = CallByName List.4 Json.460 Json.461;
let Json.457 : {} = Struct {}; let Json.459 : {} = Struct {};
let Json.122 : List U8 = CallByName Encode.23 Json.456 Json.121 Json.457; let Json.122 : List U8 = CallByName Encode.23 Json.458 Json.121 Json.459;
joinpoint Json.451 Json.123: joinpoint Json.453 Json.123:
let Json.449 : U64 = 1i64; let Json.451 : U64 = 1i64;
let Json.448 : U64 = CallByName Num.20 Json.119 Json.449; let Json.450 : U64 = CallByName Num.20 Json.119 Json.451;
let Json.447 : {List U8, U64} = Struct {Json.123, Json.448}; let Json.449 : {List U8, U64} = Struct {Json.123, Json.450};
ret Json.447; ret Json.449;
in in
let Json.455 : U64 = 1i64; let Json.457 : U64 = 1i64;
let Json.452 : Int1 = CallByName Num.24 Json.119 Json.455; let Json.454 : Int1 = CallByName Num.24 Json.119 Json.457;
if Json.452 then if Json.454 then
let Json.454 : I64 = 44i64; let Json.456 : I64 = 44i64;
let Json.453 : U8 = CallByName Num.125 Json.454; let Json.455 : U8 = CallByName Num.125 Json.456;
let Json.450 : List U8 = CallByName List.4 Json.122 Json.453; let Json.452 : List U8 = CallByName List.4 Json.122 Json.455;
jump Json.451 Json.450; jump Json.453 Json.452;
else else
jump Json.451 Json.122; jump Json.453 Json.122;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.471 : Str = CallByName Encode.22 Json.95; let Json.473 : Str = CallByName Encode.22 Json.95;
ret Json.471; ret Json.473;
procedure Json.20 (Json.111): procedure Json.20 (Json.111):
let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; let Json.397 : List {Str, Str} = CallByName Encode.22 Json.111;
ret Json.395; ret Json.397;
procedure Json.20 (Json.111): procedure Json.20 (Json.111):
let Json.437 : List {Str, Str} = CallByName Encode.22 Json.111; let Json.439 : List {Str, Str} = CallByName Encode.22 Json.111;
ret Json.437; ret Json.439;
procedure Json.96 (Json.97, Json.473, Json.95): procedure Json.96 (Json.97, Json.475, Json.95):
let Json.482 : I64 = 34i64; let Json.484 : I64 = 34i64;
let Json.481 : U8 = CallByName Num.125 Json.482; let Json.483 : U8 = CallByName Num.125 Json.484;
let Json.479 : List U8 = CallByName List.4 Json.97 Json.481; let Json.481 : List U8 = CallByName List.4 Json.97 Json.483;
let Json.480 : List U8 = CallByName Str.12 Json.95; let Json.482 : List U8 = CallByName Str.12 Json.95;
let Json.476 : List U8 = CallByName List.8 Json.479 Json.480; let Json.478 : List U8 = CallByName List.8 Json.481 Json.482;
let Json.478 : I64 = 34i64; let Json.480 : I64 = 34i64;
let Json.477 : U8 = CallByName Num.125 Json.478; let Json.479 : U8 = CallByName Num.125 Json.480;
let Json.475 : List U8 = CallByName List.4 Json.476 Json.477; let Json.477 : List U8 = CallByName List.4 Json.478 Json.479;
ret Json.475; ret Json.477;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.450 : {List U8, U64} = CallByName Json.114 List.138 List.139; let List.519 : {List U8, U64} = CallByName Json.114 List.139 List.140;
ret List.450; ret List.519;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.523 : {List U8, U64} = CallByName Json.114 List.138 List.139; let List.592 : {List U8, U64} = CallByName Json.114 List.139 List.140;
ret List.523; ret List.592;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.431 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.500 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.431; ret List.500;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.504 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.573 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.504; ret List.573;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.503 : U64 = 1i64; let List.572 : U64 = 1i64;
let List.502 : List U8 = CallByName List.70 List.105 List.503; let List.571 : List U8 = CallByName List.70 List.106 List.572;
let List.501 : List U8 = CallByName List.71 List.502 List.106; let List.570 : List U8 = CallByName List.71 List.571 List.107;
ret List.501; ret List.570;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.452 : U64 = lowlevel ListLen #Attr.2; let List.521 : U64 = lowlevel ListLen #Attr.2;
ret List.452; ret List.521;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.526 : U64 = lowlevel ListLen #Attr.2; let List.595 : U64 = lowlevel ListLen #Attr.2;
ret List.526; ret List.595;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.447 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.516 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.447; ret List.516;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.520 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.589 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.520; ret List.589;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.482 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.551 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.482; ret List.551;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.480 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.549 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.480; ret List.549;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.525 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.594 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.525; ret List.594;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.435 : U64 = 0i64; let List.504 : U64 = 0i64;
let List.436 : U64 = CallByName List.6 List.385; let List.505 : U64 = CallByName List.6 List.426;
let List.434 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.435 List.436; let List.503 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.504 List.505;
ret List.434; ret List.503;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.508 : U64 = 0i64; let List.577 : U64 = 0i64;
let List.509 : U64 = CallByName List.6 List.385; let List.578 : U64 = CallByName List.6 List.426;
let List.507 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.508 List.509; let List.576 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.577 List.578;
ret List.507; ret List.576;
procedure List.90 (List.462, List.463, List.464, List.465, List.466): procedure List.91 (List.531, List.532, List.533, List.534, List.535):
joinpoint List.437 List.388 List.389 List.390 List.391 List.392: joinpoint List.506 List.429 List.430 List.431 List.432 List.433:
let List.439 : Int1 = CallByName Num.22 List.391 List.392; let List.508 : Int1 = CallByName Num.22 List.432 List.433;
if List.439 then if List.508 then
let List.446 : {Str, Str} = CallByName List.66 List.388 List.391; let List.515 : {Str, Str} = CallByName List.66 List.429 List.432;
let List.440 : {List U8, U64} = CallByName List.137 List.389 List.446 List.390; let List.509 : {List U8, U64} = CallByName List.138 List.430 List.515 List.431;
let List.443 : U64 = 1i64; let List.512 : U64 = 1i64;
let List.442 : U64 = CallByName Num.19 List.391 List.443; let List.511 : U64 = CallByName Num.19 List.432 List.512;
jump List.437 List.388 List.440 List.390 List.442 List.392; jump List.506 List.429 List.509 List.431 List.511 List.433;
else else
ret List.389; ret List.430;
in in
jump List.437 List.462 List.463 List.464 List.465 List.466; jump List.506 List.531 List.532 List.533 List.534 List.535;
procedure List.90 (List.536, List.537, List.538, List.539, List.540): procedure List.91 (List.605, List.606, List.607, List.608, List.609):
joinpoint List.510 List.388 List.389 List.390 List.391 List.392: joinpoint List.579 List.429 List.430 List.431 List.432 List.433:
let List.512 : Int1 = CallByName Num.22 List.391 List.392; let List.581 : Int1 = CallByName Num.22 List.432 List.433;
if List.512 then if List.581 then
let List.519 : {Str, Str} = CallByName List.66 List.388 List.391; let List.588 : {Str, Str} = CallByName List.66 List.429 List.432;
let List.513 : {List U8, U64} = CallByName List.137 List.389 List.519 List.390; let List.582 : {List U8, U64} = CallByName List.138 List.430 List.588 List.431;
let List.516 : U64 = 1i64; let List.585 : U64 = 1i64;
let List.515 : U64 = CallByName Num.19 List.391 List.516; let List.584 : U64 = CallByName Num.19 List.432 List.585;
jump List.510 List.388 List.513 List.390 List.515 List.392; jump List.579 List.429 List.582 List.431 List.584 List.433;
else else
ret List.389; ret List.430;
in in
jump List.510 List.536 List.537 List.538 List.539 List.540; jump List.579 List.605 List.606 List.607 List.608 List.609;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.282 : U8 = lowlevel NumIntCast #Attr.2; let Num.282 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -39,141 +39,141 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.112 (Json.113, Json.397, Json.111): procedure Json.112 (Json.113, Json.399, Json.111):
let Json.430 : I64 = 123i64; let Json.432 : I64 = 123i64;
let Json.429 : U8 = CallByName Num.125 Json.430; let Json.431 : U8 = CallByName Num.125 Json.432;
let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; let Json.115 : List U8 = CallByName List.4 Json.113 Json.431;
let Json.428 : U64 = CallByName List.6 Json.111; let Json.430 : U64 = CallByName List.6 Json.111;
let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; let Json.407 : {List U8, U64} = Struct {Json.115, Json.430};
let Json.406 : {} = Struct {}; let Json.408 : {} = Struct {};
let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; let Json.406 : {List U8, U64} = CallByName List.18 Json.111 Json.407 Json.408;
dec Json.111; dec Json.111;
let Json.117 : List U8 = StructAtIndex 0 Json.404; let Json.117 : List U8 = StructAtIndex 0 Json.406;
inc Json.117; inc Json.117;
dec Json.404; dec Json.406;
let Json.403 : I64 = 125i64; let Json.405 : I64 = 125i64;
let Json.402 : U8 = CallByName Num.125 Json.403; let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; let Json.403 : List U8 = CallByName List.4 Json.117 Json.404;
ret Json.401; ret Json.403;
procedure Json.114 (Json.399, Json.400): procedure Json.114 (Json.401, Json.402):
let Json.120 : Str = StructAtIndex 0 Json.400; let Json.120 : Str = StructAtIndex 0 Json.402;
inc Json.120; inc Json.120;
let Json.121 : Str = StructAtIndex 1 Json.400; let Json.121 : Str = StructAtIndex 1 Json.402;
inc Json.121; inc Json.121;
dec Json.400; dec Json.402;
let Json.118 : List U8 = StructAtIndex 0 Json.399; let Json.118 : List U8 = StructAtIndex 0 Json.401;
inc Json.118; inc Json.118;
let Json.119 : U64 = StructAtIndex 1 Json.399; let Json.119 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.427 : I64 = 34i64; let Json.429 : I64 = 34i64;
let Json.426 : U8 = CallByName Num.125 Json.427; let Json.428 : U8 = CallByName Num.125 Json.429;
let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; let Json.426 : List U8 = CallByName List.4 Json.118 Json.428;
let Json.425 : List U8 = CallByName Str.12 Json.120; let Json.427 : List U8 = CallByName Str.12 Json.120;
let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; let Json.423 : List U8 = CallByName List.8 Json.426 Json.427;
let Json.423 : I64 = 34i64; let Json.425 : I64 = 34i64;
let Json.422 : U8 = CallByName Num.125 Json.423; let Json.424 : U8 = CallByName Num.125 Json.425;
let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; let Json.420 : List U8 = CallByName List.4 Json.423 Json.424;
let Json.420 : I64 = 58i64; let Json.422 : I64 = 58i64;
let Json.419 : U8 = CallByName Num.125 Json.420; let Json.421 : U8 = CallByName Num.125 Json.422;
let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; let Json.418 : List U8 = CallByName List.4 Json.420 Json.421;
let Json.417 : {} = Struct {}; let Json.419 : {} = Struct {};
let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; let Json.122 : List U8 = CallByName Encode.23 Json.418 Json.121 Json.419;
joinpoint Json.411 Json.123: joinpoint Json.413 Json.123:
let Json.409 : U64 = 1i64; let Json.411 : U64 = 1i64;
let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; let Json.410 : U64 = CallByName Num.20 Json.119 Json.411;
let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; let Json.409 : {List U8, U64} = Struct {Json.123, Json.410};
ret Json.407; ret Json.409;
in in
let Json.415 : U64 = 1i64; let Json.417 : U64 = 1i64;
let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; let Json.414 : Int1 = CallByName Num.24 Json.119 Json.417;
if Json.412 then if Json.414 then
let Json.414 : I64 = 44i64; let Json.416 : I64 = 44i64;
let Json.413 : U8 = CallByName Num.125 Json.414; let Json.415 : U8 = CallByName Num.125 Json.416;
let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; let Json.412 : List U8 = CallByName List.4 Json.122 Json.415;
jump Json.411 Json.410; jump Json.413 Json.412;
else else
jump Json.411 Json.122; jump Json.413 Json.122;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.431 : Str = CallByName Encode.22 Json.95; let Json.433 : Str = CallByName Encode.22 Json.95;
ret Json.431; ret Json.433;
procedure Json.20 (Json.111): procedure Json.20 (Json.111):
let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; let Json.397 : List {Str, Str} = CallByName Encode.22 Json.111;
ret Json.395; ret Json.397;
procedure Json.96 (Json.97, Json.433, Json.95): procedure Json.96 (Json.97, Json.435, Json.95):
let Json.442 : I64 = 34i64; let Json.444 : I64 = 34i64;
let Json.441 : U8 = CallByName Num.125 Json.442; let Json.443 : U8 = CallByName Num.125 Json.444;
let Json.439 : List U8 = CallByName List.4 Json.97 Json.441; let Json.441 : List U8 = CallByName List.4 Json.97 Json.443;
let Json.440 : List U8 = CallByName Str.12 Json.95; let Json.442 : List U8 = CallByName Str.12 Json.95;
let Json.436 : List U8 = CallByName List.8 Json.439 Json.440; let Json.438 : List U8 = CallByName List.8 Json.441 Json.442;
let Json.438 : I64 = 34i64; let Json.440 : I64 = 34i64;
let Json.437 : U8 = CallByName Num.125 Json.438; let Json.439 : U8 = CallByName Num.125 Json.440;
let Json.435 : List U8 = CallByName List.4 Json.436 Json.437; let Json.437 : List U8 = CallByName List.4 Json.438 Json.439;
ret Json.435; ret Json.437;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.456 : {List U8, U64} = CallByName Json.114 List.138 List.139; let List.525 : {List U8, U64} = CallByName Json.114 List.139 List.140;
ret List.456; ret List.525;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.506 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.437; ret List.506;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.436 : U64 = 1i64; let List.505 : U64 = 1i64;
let List.435 : List U8 = CallByName List.70 List.105 List.436; let List.504 : List U8 = CallByName List.70 List.106 List.505;
let List.434 : List U8 = CallByName List.71 List.435 List.106; let List.503 : List U8 = CallByName List.71 List.504 List.107;
ret List.434; ret List.503;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.459 : U64 = lowlevel ListLen #Attr.2; let List.528 : U64 = lowlevel ListLen #Attr.2;
ret List.459; ret List.528;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.453 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.522 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.453; ret List.522;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.484 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.415; ret List.484;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.458 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.458; ret List.527;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.441 : U64 = 0i64; let List.510 : U64 = 0i64;
let List.442 : U64 = CallByName List.6 List.385; let List.511 : U64 = CallByName List.6 List.426;
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442; let List.509 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.510 List.511;
ret List.440; ret List.509;
procedure List.90 (List.469, List.470, List.471, List.472, List.473): procedure List.91 (List.538, List.539, List.540, List.541, List.542):
joinpoint List.443 List.388 List.389 List.390 List.391 List.392: joinpoint List.512 List.429 List.430 List.431 List.432 List.433:
let List.445 : Int1 = CallByName Num.22 List.391 List.392; let List.514 : Int1 = CallByName Num.22 List.432 List.433;
if List.445 then if List.514 then
let List.452 : {Str, Str} = CallByName List.66 List.388 List.391; let List.521 : {Str, Str} = CallByName List.66 List.429 List.432;
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.452 List.390; let List.515 : {List U8, U64} = CallByName List.138 List.430 List.521 List.431;
let List.449 : U64 = 1i64; let List.518 : U64 = 1i64;
let List.448 : U64 = CallByName Num.19 List.391 List.449; let List.517 : U64 = CallByName Num.19 List.432 List.518;
jump List.443 List.388 List.446 List.390 List.448 List.392; jump List.512 List.429 List.515 List.431 List.517 List.433;
else else
ret List.389; ret List.430;
in in
jump List.443 List.469 List.470 List.471 List.472 List.473; jump List.512 List.538 List.539 List.540 List.541 List.542;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.263 : U8 = lowlevel NumIntCast #Attr.2; let Num.263 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -47,141 +47,141 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.112 (Json.113, Json.397, Json.111): procedure Json.112 (Json.113, Json.399, Json.111):
let Json.430 : I64 = 123i64; let Json.432 : I64 = 123i64;
let Json.429 : U8 = CallByName Num.125 Json.430; let Json.431 : U8 = CallByName Num.125 Json.432;
let Json.115 : List U8 = CallByName List.4 Json.113 Json.429; let Json.115 : List U8 = CallByName List.4 Json.113 Json.431;
let Json.428 : U64 = CallByName List.6 Json.111; let Json.430 : U64 = CallByName List.6 Json.111;
let Json.405 : {List U8, U64} = Struct {Json.115, Json.428}; let Json.407 : {List U8, U64} = Struct {Json.115, Json.430};
let Json.406 : {} = Struct {}; let Json.408 : {} = Struct {};
let Json.404 : {List U8, U64} = CallByName List.18 Json.111 Json.405 Json.406; let Json.406 : {List U8, U64} = CallByName List.18 Json.111 Json.407 Json.408;
dec Json.111; dec Json.111;
let Json.117 : List U8 = StructAtIndex 0 Json.404; let Json.117 : List U8 = StructAtIndex 0 Json.406;
inc Json.117; inc Json.117;
dec Json.404; dec Json.406;
let Json.403 : I64 = 125i64; let Json.405 : I64 = 125i64;
let Json.402 : U8 = CallByName Num.125 Json.403; let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.401 : List U8 = CallByName List.4 Json.117 Json.402; let Json.403 : List U8 = CallByName List.4 Json.117 Json.404;
ret Json.401; ret Json.403;
procedure Json.114 (Json.399, Json.400): procedure Json.114 (Json.401, Json.402):
let Json.120 : Str = StructAtIndex 0 Json.400; let Json.120 : Str = StructAtIndex 0 Json.402;
inc Json.120; inc Json.120;
let Json.121 : Str = StructAtIndex 1 Json.400; let Json.121 : Str = StructAtIndex 1 Json.402;
inc Json.121; inc Json.121;
dec Json.400; dec Json.402;
let Json.118 : List U8 = StructAtIndex 0 Json.399; let Json.118 : List U8 = StructAtIndex 0 Json.401;
inc Json.118; inc Json.118;
let Json.119 : U64 = StructAtIndex 1 Json.399; let Json.119 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.427 : I64 = 34i64; let Json.429 : I64 = 34i64;
let Json.426 : U8 = CallByName Num.125 Json.427; let Json.428 : U8 = CallByName Num.125 Json.429;
let Json.424 : List U8 = CallByName List.4 Json.118 Json.426; let Json.426 : List U8 = CallByName List.4 Json.118 Json.428;
let Json.425 : List U8 = CallByName Str.12 Json.120; let Json.427 : List U8 = CallByName Str.12 Json.120;
let Json.421 : List U8 = CallByName List.8 Json.424 Json.425; let Json.423 : List U8 = CallByName List.8 Json.426 Json.427;
let Json.423 : I64 = 34i64; let Json.425 : I64 = 34i64;
let Json.422 : U8 = CallByName Num.125 Json.423; let Json.424 : U8 = CallByName Num.125 Json.425;
let Json.418 : List U8 = CallByName List.4 Json.421 Json.422; let Json.420 : List U8 = CallByName List.4 Json.423 Json.424;
let Json.420 : I64 = 58i64; let Json.422 : I64 = 58i64;
let Json.419 : U8 = CallByName Num.125 Json.420; let Json.421 : U8 = CallByName Num.125 Json.422;
let Json.416 : List U8 = CallByName List.4 Json.418 Json.419; let Json.418 : List U8 = CallByName List.4 Json.420 Json.421;
let Json.417 : {} = Struct {}; let Json.419 : {} = Struct {};
let Json.122 : List U8 = CallByName Encode.23 Json.416 Json.121 Json.417; let Json.122 : List U8 = CallByName Encode.23 Json.418 Json.121 Json.419;
joinpoint Json.411 Json.123: joinpoint Json.413 Json.123:
let Json.409 : U64 = 1i64; let Json.411 : U64 = 1i64;
let Json.408 : U64 = CallByName Num.20 Json.119 Json.409; let Json.410 : U64 = CallByName Num.20 Json.119 Json.411;
let Json.407 : {List U8, U64} = Struct {Json.123, Json.408}; let Json.409 : {List U8, U64} = Struct {Json.123, Json.410};
ret Json.407; ret Json.409;
in in
let Json.415 : U64 = 1i64; let Json.417 : U64 = 1i64;
let Json.412 : Int1 = CallByName Num.24 Json.119 Json.415; let Json.414 : Int1 = CallByName Num.24 Json.119 Json.417;
if Json.412 then if Json.414 then
let Json.414 : I64 = 44i64; let Json.416 : I64 = 44i64;
let Json.413 : U8 = CallByName Num.125 Json.414; let Json.415 : U8 = CallByName Num.125 Json.416;
let Json.410 : List U8 = CallByName List.4 Json.122 Json.413; let Json.412 : List U8 = CallByName List.4 Json.122 Json.415;
jump Json.411 Json.410; jump Json.413 Json.412;
else else
jump Json.411 Json.122; jump Json.413 Json.122;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.443 : Str = CallByName Encode.22 Json.95; let Json.445 : Str = CallByName Encode.22 Json.95;
ret Json.443; ret Json.445;
procedure Json.20 (Json.111): procedure Json.20 (Json.111):
let Json.395 : List {Str, Str} = CallByName Encode.22 Json.111; let Json.397 : List {Str, Str} = CallByName Encode.22 Json.111;
ret Json.395; ret Json.397;
procedure Json.96 (Json.97, Json.433, Json.95): procedure Json.96 (Json.97, Json.435, Json.95):
let Json.442 : I64 = 34i64; let Json.444 : I64 = 34i64;
let Json.441 : U8 = CallByName Num.125 Json.442; let Json.443 : U8 = CallByName Num.125 Json.444;
let Json.439 : List U8 = CallByName List.4 Json.97 Json.441; let Json.441 : List U8 = CallByName List.4 Json.97 Json.443;
let Json.440 : List U8 = CallByName Str.12 Json.95; let Json.442 : List U8 = CallByName Str.12 Json.95;
let Json.436 : List U8 = CallByName List.8 Json.439 Json.440; let Json.438 : List U8 = CallByName List.8 Json.441 Json.442;
let Json.438 : I64 = 34i64; let Json.440 : I64 = 34i64;
let Json.437 : U8 = CallByName Num.125 Json.438; let Json.439 : U8 = CallByName Num.125 Json.440;
let Json.435 : List U8 = CallByName List.4 Json.436 Json.437; let Json.437 : List U8 = CallByName List.4 Json.438 Json.439;
ret Json.435; ret Json.437;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.456 : {List U8, U64} = CallByName Json.114 List.138 List.139; let List.525 : {List U8, U64} = CallByName Json.114 List.139 List.140;
ret List.456; ret List.525;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.437 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.506 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.437; ret List.506;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.436 : U64 = 1i64; let List.505 : U64 = 1i64;
let List.435 : List U8 = CallByName List.70 List.105 List.436; let List.504 : List U8 = CallByName List.70 List.106 List.505;
let List.434 : List U8 = CallByName List.71 List.435 List.106; let List.503 : List U8 = CallByName List.71 List.504 List.107;
ret List.434; ret List.503;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.459 : U64 = lowlevel ListLen #Attr.2; let List.528 : U64 = lowlevel ListLen #Attr.2;
ret List.459; ret List.528;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.453 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.522 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.453; ret List.522;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.484 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.415; ret List.484;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.458 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.527 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.458; ret List.527;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.441 : U64 = 0i64; let List.510 : U64 = 0i64;
let List.442 : U64 = CallByName List.6 List.385; let List.511 : U64 = CallByName List.6 List.426;
let List.440 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.441 List.442; let List.509 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.510 List.511;
ret List.440; ret List.509;
procedure List.90 (List.469, List.470, List.471, List.472, List.473): procedure List.91 (List.538, List.539, List.540, List.541, List.542):
joinpoint List.443 List.388 List.389 List.390 List.391 List.392: joinpoint List.512 List.429 List.430 List.431 List.432 List.433:
let List.445 : Int1 = CallByName Num.22 List.391 List.392; let List.514 : Int1 = CallByName Num.22 List.432 List.433;
if List.445 then if List.514 then
let List.452 : {Str, Str} = CallByName List.66 List.388 List.391; let List.521 : {Str, Str} = CallByName List.66 List.429 List.432;
let List.446 : {List U8, U64} = CallByName List.137 List.389 List.452 List.390; let List.515 : {List U8, U64} = CallByName List.138 List.430 List.521 List.431;
let List.449 : U64 = 1i64; let List.518 : U64 = 1i64;
let List.448 : U64 = CallByName Num.19 List.391 List.449; let List.517 : U64 = CallByName Num.19 List.432 List.518;
jump List.443 List.388 List.446 List.390 List.448 List.392; jump List.512 List.429 List.515 List.431 List.517 List.433;
else else
ret List.389; ret List.430;
in in
jump List.443 List.469 List.470 List.471 List.472 List.473; jump List.512 List.538 List.539 List.540 List.541 List.542;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.263 : U8 = lowlevel NumIntCast #Attr.2; let Num.263 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -12,45 +12,45 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.395 : Str = CallByName Encode.22 Json.95; let Json.397 : Str = CallByName Encode.22 Json.95;
ret Json.395; ret Json.397;
procedure Json.96 (Json.97, Json.397, Json.95): procedure Json.96 (Json.97, Json.399, Json.95):
let Json.406 : I64 = 34i64; let Json.408 : I64 = 34i64;
let Json.405 : U8 = CallByName Num.125 Json.406; let Json.407 : U8 = CallByName Num.125 Json.408;
let Json.403 : List U8 = CallByName List.4 Json.97 Json.405; let Json.405 : List U8 = CallByName List.4 Json.97 Json.407;
let Json.404 : List U8 = CallByName Str.12 Json.95; let Json.406 : List U8 = CallByName Str.12 Json.95;
let Json.400 : List U8 = CallByName List.8 Json.403 Json.404; let Json.402 : List U8 = CallByName List.8 Json.405 Json.406;
let Json.402 : I64 = 34i64; let Json.404 : I64 = 34i64;
let Json.401 : U8 = CallByName Num.125 Json.402; let Json.403 : U8 = CallByName Num.125 Json.404;
let Json.399 : List U8 = CallByName List.4 Json.400 Json.401; let Json.401 : List U8 = CallByName List.4 Json.402 Json.403;
ret Json.399; ret Json.401;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.418 : U64 = 1i64; let List.487 : U64 = 1i64;
let List.417 : List U8 = CallByName List.70 List.105 List.418; let List.486 : List U8 = CallByName List.70 List.106 List.487;
let List.416 : List U8 = CallByName List.71 List.417 List.106; let List.485 : List U8 = CallByName List.71 List.486 List.107;
ret List.416; ret List.485;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.484 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.415; ret List.484;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.419 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.488 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.419; ret List.488;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.257 : U8 = lowlevel NumIntCast #Attr.2; let Num.257 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -41,148 +41,148 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.126 (Json.127, Json.397, #Attr.12): procedure Json.126 (Json.127, Json.399, #Attr.12):
let Json.125 : List Str = StructAtIndex 1 #Attr.12; let Json.125 : List Str = StructAtIndex 1 #Attr.12;
inc Json.125; inc Json.125;
let Json.124 : Str = StructAtIndex 0 #Attr.12; let Json.124 : Str = StructAtIndex 0 #Attr.12;
inc Json.124; inc Json.124;
dec #Attr.12; dec #Attr.12;
let Json.435 : I64 = 123i64; let Json.437 : I64 = 123i64;
let Json.436 : U8 = CallByName Num.125 Json.437;
let Json.433 : List U8 = CallByName List.4 Json.127 Json.436;
let Json.435 : I64 = 34i64;
let Json.434 : U8 = CallByName Num.125 Json.435; let Json.434 : U8 = CallByName Num.125 Json.435;
let Json.431 : List U8 = CallByName List.4 Json.127 Json.434; let Json.431 : List U8 = CallByName List.4 Json.433 Json.434;
let Json.433 : I64 = 34i64; let Json.432 : List U8 = CallByName Str.12 Json.124;
let Json.432 : U8 = CallByName Num.125 Json.433; let Json.428 : List U8 = CallByName List.8 Json.431 Json.432;
let Json.429 : List U8 = CallByName List.4 Json.431 Json.432; let Json.430 : I64 = 34i64;
let Json.430 : List U8 = CallByName Str.12 Json.124; let Json.429 : U8 = CallByName Num.125 Json.430;
let Json.426 : List U8 = CallByName List.8 Json.429 Json.430; let Json.425 : List U8 = CallByName List.4 Json.428 Json.429;
let Json.428 : I64 = 34i64; let Json.427 : I64 = 58i64;
let Json.427 : U8 = CallByName Num.125 Json.428; let Json.426 : U8 = CallByName Num.125 Json.427;
let Json.423 : List U8 = CallByName List.4 Json.426 Json.427; let Json.422 : List U8 = CallByName List.4 Json.425 Json.426;
let Json.425 : I64 = 58i64; let Json.424 : I64 = 91i64;
let Json.424 : U8 = CallByName Num.125 Json.425; let Json.423 : U8 = CallByName Num.125 Json.424;
let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; let Json.129 : List U8 = CallByName List.4 Json.422 Json.423;
let Json.422 : I64 = 91i64; let Json.421 : U64 = CallByName List.6 Json.125;
let Json.421 : U8 = CallByName Num.125 Json.422; let Json.409 : {List U8, U64} = Struct {Json.129, Json.421};
let Json.129 : List U8 = CallByName List.4 Json.420 Json.421; let Json.410 : {} = Struct {};
let Json.419 : U64 = CallByName List.6 Json.125; let Json.408 : {List U8, U64} = CallByName List.18 Json.125 Json.409 Json.410;
let Json.407 : {List U8, U64} = Struct {Json.129, Json.419};
let Json.408 : {} = Struct {};
let Json.406 : {List U8, U64} = CallByName List.18 Json.125 Json.407 Json.408;
dec Json.125; dec Json.125;
let Json.131 : List U8 = StructAtIndex 0 Json.406; let Json.131 : List U8 = StructAtIndex 0 Json.408;
inc Json.131; inc Json.131;
dec Json.406; dec Json.408;
let Json.405 : I64 = 93i64; let Json.407 : I64 = 93i64;
let Json.406 : U8 = CallByName Num.125 Json.407;
let Json.403 : List U8 = CallByName List.4 Json.131 Json.406;
let Json.405 : I64 = 125i64;
let Json.404 : U8 = CallByName Num.125 Json.405; let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.401 : List U8 = CallByName List.4 Json.131 Json.404; let Json.402 : List U8 = CallByName List.4 Json.403 Json.404;
let Json.403 : I64 = 125i64; ret Json.402;
let Json.402 : U8 = CallByName Num.125 Json.403;
let Json.400 : List U8 = CallByName List.4 Json.401 Json.402;
ret Json.400;
procedure Json.128 (Json.399, Json.134): procedure Json.128 (Json.401, Json.134):
let Json.132 : List U8 = StructAtIndex 0 Json.399; let Json.132 : List U8 = StructAtIndex 0 Json.401;
inc Json.132; inc Json.132;
let Json.133 : U64 = StructAtIndex 1 Json.399; let Json.133 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.418 : {} = Struct {}; let Json.420 : {} = Struct {};
let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.418; let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.420;
joinpoint Json.413 Json.136: joinpoint Json.415 Json.136:
let Json.411 : U64 = 1i64; let Json.413 : U64 = 1i64;
let Json.410 : U64 = CallByName Num.20 Json.133 Json.411; let Json.412 : U64 = CallByName Num.20 Json.133 Json.413;
let Json.409 : {List U8, U64} = Struct {Json.136, Json.410}; let Json.411 : {List U8, U64} = Struct {Json.136, Json.412};
ret Json.409; ret Json.411;
in in
let Json.417 : U64 = 1i64; let Json.419 : U64 = 1i64;
let Json.414 : Int1 = CallByName Num.24 Json.133 Json.417; let Json.416 : Int1 = CallByName Num.24 Json.133 Json.419;
if Json.414 then if Json.416 then
let Json.416 : I64 = 44i64; let Json.418 : I64 = 44i64;
let Json.415 : U8 = CallByName Num.125 Json.416; let Json.417 : U8 = CallByName Num.125 Json.418;
let Json.412 : List U8 = CallByName List.4 Json.135 Json.415; let Json.414 : List U8 = CallByName List.4 Json.135 Json.417;
jump Json.413 Json.412; jump Json.415 Json.414;
else else
jump Json.413 Json.135; jump Json.415 Json.135;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.436 : Str = CallByName Encode.22 Json.95; let Json.438 : Str = CallByName Encode.22 Json.95;
ret Json.436; ret Json.438;
procedure Json.21 (Json.124, Json.125): procedure Json.21 (Json.124, Json.125):
let Json.396 : {Str, List Str} = Struct {Json.124, Json.125}; let Json.398 : {Str, List Str} = Struct {Json.124, Json.125};
let Json.395 : {Str, List Str} = CallByName Encode.22 Json.396; let Json.397 : {Str, List Str} = CallByName Encode.22 Json.398;
ret Json.395; ret Json.397;
procedure Json.96 (Json.97, Json.438, Json.95): procedure Json.96 (Json.97, Json.440, Json.95):
let Json.447 : I64 = 34i64; let Json.449 : I64 = 34i64;
let Json.446 : U8 = CallByName Num.125 Json.447; let Json.448 : U8 = CallByName Num.125 Json.449;
let Json.444 : List U8 = CallByName List.4 Json.97 Json.446; let Json.446 : List U8 = CallByName List.4 Json.97 Json.448;
let Json.445 : List U8 = CallByName Str.12 Json.95; let Json.447 : List U8 = CallByName Str.12 Json.95;
let Json.441 : List U8 = CallByName List.8 Json.444 Json.445; let Json.443 : List U8 = CallByName List.8 Json.446 Json.447;
let Json.443 : I64 = 34i64; let Json.445 : I64 = 34i64;
let Json.442 : U8 = CallByName Num.125 Json.443; let Json.444 : U8 = CallByName Num.125 Json.445;
let Json.440 : List U8 = CallByName List.4 Json.441 Json.442; let Json.442 : List U8 = CallByName List.4 Json.443 Json.444;
ret Json.440; ret Json.442;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.462 : {List U8, U64} = CallByName Json.128 List.138 List.139; let List.531 : {List U8, U64} = CallByName Json.128 List.139 List.140;
ret List.462; ret List.531;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.512 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.443; ret List.512;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.442 : U64 = 1i64; let List.511 : U64 = 1i64;
let List.441 : List U8 = CallByName List.70 List.105 List.442; let List.510 : List U8 = CallByName List.70 List.106 List.511;
let List.440 : List U8 = CallByName List.71 List.441 List.106; let List.509 : List U8 = CallByName List.71 List.510 List.107;
ret List.440; ret List.509;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.463 : U64 = lowlevel ListLen #Attr.2; let List.532 : U64 = lowlevel ListLen #Attr.2;
ret List.463; ret List.532;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.459 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.528 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.459; ret List.528;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.484 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.415; ret List.484;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.465 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.465; ret List.534;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.447 : U64 = 0i64; let List.516 : U64 = 0i64;
let List.448 : U64 = CallByName List.6 List.385; let List.517 : U64 = CallByName List.6 List.426;
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448; let List.515 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.516 List.517;
ret List.446; ret List.515;
procedure List.90 (List.475, List.476, List.477, List.478, List.479): procedure List.91 (List.544, List.545, List.546, List.547, List.548):
joinpoint List.449 List.388 List.389 List.390 List.391 List.392: joinpoint List.518 List.429 List.430 List.431 List.432 List.433:
let List.451 : Int1 = CallByName Num.22 List.391 List.392; let List.520 : Int1 = CallByName Num.22 List.432 List.433;
if List.451 then if List.520 then
let List.458 : Str = CallByName List.66 List.388 List.391; let List.527 : Str = CallByName List.66 List.429 List.432;
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.458 List.390; let List.521 : {List U8, U64} = CallByName List.138 List.430 List.527 List.431;
let List.455 : U64 = 1i64; let List.524 : U64 = 1i64;
let List.454 : U64 = CallByName Num.19 List.391 List.455; let List.523 : U64 = CallByName Num.19 List.432 List.524;
jump List.449 List.388 List.452 List.390 List.454 List.392; jump List.518 List.429 List.521 List.431 List.523 List.433;
else else
ret List.389; ret List.430;
in in
jump List.449 List.475 List.476 List.477 List.478 List.479; jump List.518 List.544 List.545 List.546 List.547 List.548;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.265 : U8 = lowlevel NumIntCast #Attr.2; let Num.265 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -47,148 +47,148 @@ procedure Encode.25 (Encode.100, Encode.101):
ret Encode.103; ret Encode.103;
procedure Json.1 (): procedure Json.1 ():
let Json.394 : {} = Struct {}; let Json.396 : {} = Struct {};
ret Json.394; ret Json.396;
procedure Json.126 (Json.127, Json.397, #Attr.12): procedure Json.126 (Json.127, Json.399, #Attr.12):
let Json.125 : List Str = StructAtIndex 1 #Attr.12; let Json.125 : List Str = StructAtIndex 1 #Attr.12;
inc Json.125; inc Json.125;
let Json.124 : Str = StructAtIndex 0 #Attr.12; let Json.124 : Str = StructAtIndex 0 #Attr.12;
inc Json.124; inc Json.124;
dec #Attr.12; dec #Attr.12;
let Json.435 : I64 = 123i64; let Json.437 : I64 = 123i64;
let Json.436 : U8 = CallByName Num.125 Json.437;
let Json.433 : List U8 = CallByName List.4 Json.127 Json.436;
let Json.435 : I64 = 34i64;
let Json.434 : U8 = CallByName Num.125 Json.435; let Json.434 : U8 = CallByName Num.125 Json.435;
let Json.431 : List U8 = CallByName List.4 Json.127 Json.434; let Json.431 : List U8 = CallByName List.4 Json.433 Json.434;
let Json.433 : I64 = 34i64; let Json.432 : List U8 = CallByName Str.12 Json.124;
let Json.432 : U8 = CallByName Num.125 Json.433; let Json.428 : List U8 = CallByName List.8 Json.431 Json.432;
let Json.429 : List U8 = CallByName List.4 Json.431 Json.432; let Json.430 : I64 = 34i64;
let Json.430 : List U8 = CallByName Str.12 Json.124; let Json.429 : U8 = CallByName Num.125 Json.430;
let Json.426 : List U8 = CallByName List.8 Json.429 Json.430; let Json.425 : List U8 = CallByName List.4 Json.428 Json.429;
let Json.428 : I64 = 34i64; let Json.427 : I64 = 58i64;
let Json.427 : U8 = CallByName Num.125 Json.428; let Json.426 : U8 = CallByName Num.125 Json.427;
let Json.423 : List U8 = CallByName List.4 Json.426 Json.427; let Json.422 : List U8 = CallByName List.4 Json.425 Json.426;
let Json.425 : I64 = 58i64; let Json.424 : I64 = 91i64;
let Json.424 : U8 = CallByName Num.125 Json.425; let Json.423 : U8 = CallByName Num.125 Json.424;
let Json.420 : List U8 = CallByName List.4 Json.423 Json.424; let Json.129 : List U8 = CallByName List.4 Json.422 Json.423;
let Json.422 : I64 = 91i64; let Json.421 : U64 = CallByName List.6 Json.125;
let Json.421 : U8 = CallByName Num.125 Json.422; let Json.409 : {List U8, U64} = Struct {Json.129, Json.421};
let Json.129 : List U8 = CallByName List.4 Json.420 Json.421; let Json.410 : {} = Struct {};
let Json.419 : U64 = CallByName List.6 Json.125; let Json.408 : {List U8, U64} = CallByName List.18 Json.125 Json.409 Json.410;
let Json.407 : {List U8, U64} = Struct {Json.129, Json.419};
let Json.408 : {} = Struct {};
let Json.406 : {List U8, U64} = CallByName List.18 Json.125 Json.407 Json.408;
dec Json.125; dec Json.125;
let Json.131 : List U8 = StructAtIndex 0 Json.406; let Json.131 : List U8 = StructAtIndex 0 Json.408;
inc Json.131; inc Json.131;
dec Json.406; dec Json.408;
let Json.405 : I64 = 93i64; let Json.407 : I64 = 93i64;
let Json.406 : U8 = CallByName Num.125 Json.407;
let Json.403 : List U8 = CallByName List.4 Json.131 Json.406;
let Json.405 : I64 = 125i64;
let Json.404 : U8 = CallByName Num.125 Json.405; let Json.404 : U8 = CallByName Num.125 Json.405;
let Json.401 : List U8 = CallByName List.4 Json.131 Json.404; let Json.402 : List U8 = CallByName List.4 Json.403 Json.404;
let Json.403 : I64 = 125i64; ret Json.402;
let Json.402 : U8 = CallByName Num.125 Json.403;
let Json.400 : List U8 = CallByName List.4 Json.401 Json.402;
ret Json.400;
procedure Json.128 (Json.399, Json.134): procedure Json.128 (Json.401, Json.134):
let Json.132 : List U8 = StructAtIndex 0 Json.399; let Json.132 : List U8 = StructAtIndex 0 Json.401;
inc Json.132; inc Json.132;
let Json.133 : U64 = StructAtIndex 1 Json.399; let Json.133 : U64 = StructAtIndex 1 Json.401;
dec Json.399; dec Json.401;
let Json.418 : {} = Struct {}; let Json.420 : {} = Struct {};
let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.418; let Json.135 : List U8 = CallByName Encode.23 Json.132 Json.134 Json.420;
joinpoint Json.413 Json.136: joinpoint Json.415 Json.136:
let Json.411 : U64 = 1i64; let Json.413 : U64 = 1i64;
let Json.410 : U64 = CallByName Num.20 Json.133 Json.411; let Json.412 : U64 = CallByName Num.20 Json.133 Json.413;
let Json.409 : {List U8, U64} = Struct {Json.136, Json.410}; let Json.411 : {List U8, U64} = Struct {Json.136, Json.412};
ret Json.409; ret Json.411;
in in
let Json.417 : U64 = 1i64; let Json.419 : U64 = 1i64;
let Json.414 : Int1 = CallByName Num.24 Json.133 Json.417; let Json.416 : Int1 = CallByName Num.24 Json.133 Json.419;
if Json.414 then if Json.416 then
let Json.416 : I64 = 44i64; let Json.418 : I64 = 44i64;
let Json.415 : U8 = CallByName Num.125 Json.416; let Json.417 : U8 = CallByName Num.125 Json.418;
let Json.412 : List U8 = CallByName List.4 Json.135 Json.415; let Json.414 : List U8 = CallByName List.4 Json.135 Json.417;
jump Json.413 Json.412; jump Json.415 Json.414;
else else
jump Json.413 Json.135; jump Json.415 Json.135;
procedure Json.18 (Json.95): procedure Json.18 (Json.95):
let Json.448 : Str = CallByName Encode.22 Json.95; let Json.450 : Str = CallByName Encode.22 Json.95;
ret Json.448; ret Json.450;
procedure Json.21 (Json.124, Json.125): procedure Json.21 (Json.124, Json.125):
let Json.396 : {Str, List Str} = Struct {Json.124, Json.125}; let Json.398 : {Str, List Str} = Struct {Json.124, Json.125};
let Json.395 : {Str, List Str} = CallByName Encode.22 Json.396; let Json.397 : {Str, List Str} = CallByName Encode.22 Json.398;
ret Json.395; ret Json.397;
procedure Json.96 (Json.97, Json.438, Json.95): procedure Json.96 (Json.97, Json.440, Json.95):
let Json.447 : I64 = 34i64; let Json.449 : I64 = 34i64;
let Json.446 : U8 = CallByName Num.125 Json.447; let Json.448 : U8 = CallByName Num.125 Json.449;
let Json.444 : List U8 = CallByName List.4 Json.97 Json.446; let Json.446 : List U8 = CallByName List.4 Json.97 Json.448;
let Json.445 : List U8 = CallByName Str.12 Json.95; let Json.447 : List U8 = CallByName Str.12 Json.95;
let Json.441 : List U8 = CallByName List.8 Json.444 Json.445; let Json.443 : List U8 = CallByName List.8 Json.446 Json.447;
let Json.443 : I64 = 34i64; let Json.445 : I64 = 34i64;
let Json.442 : U8 = CallByName Num.125 Json.443; let Json.444 : U8 = CallByName Num.125 Json.445;
let Json.440 : List U8 = CallByName List.4 Json.441 Json.442; let Json.442 : List U8 = CallByName List.4 Json.443 Json.444;
ret Json.440; ret Json.442;
procedure List.137 (List.138, List.139, List.136): procedure List.138 (List.139, List.140, List.137):
let List.462 : {List U8, U64} = CallByName Json.128 List.138 List.139; let List.531 : {List U8, U64} = CallByName Json.128 List.139 List.140;
ret List.462; ret List.531;
procedure List.18 (List.134, List.135, List.136): procedure List.18 (List.135, List.136, List.137):
let List.443 : {List U8, U64} = CallByName List.89 List.134 List.135 List.136; let List.512 : {List U8, U64} = CallByName List.90 List.135 List.136 List.137;
ret List.443; ret List.512;
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.442 : U64 = 1i64; let List.511 : U64 = 1i64;
let List.441 : List U8 = CallByName List.70 List.105 List.442; let List.510 : List U8 = CallByName List.70 List.106 List.511;
let List.440 : List U8 = CallByName List.71 List.441 List.106; let List.509 : List U8 = CallByName List.71 List.510 List.107;
ret List.440; ret List.509;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.463 : U64 = lowlevel ListLen #Attr.2; let List.532 : U64 = lowlevel ListLen #Attr.2;
ret List.463; ret List.532;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.459 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.528 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.459; ret List.528;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.415 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; let List.484 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.415; ret List.484;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.413 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.482 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.8 (#Attr.2, #Attr.3): procedure List.8 (#Attr.2, #Attr.3):
let List.465 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; let List.534 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3;
ret List.465; ret List.534;
procedure List.89 (List.385, List.386, List.387): procedure List.90 (List.426, List.427, List.428):
let List.447 : U64 = 0i64; let List.516 : U64 = 0i64;
let List.448 : U64 = CallByName List.6 List.385; let List.517 : U64 = CallByName List.6 List.426;
let List.446 : {List U8, U64} = CallByName List.90 List.385 List.386 List.387 List.447 List.448; let List.515 : {List U8, U64} = CallByName List.91 List.426 List.427 List.428 List.516 List.517;
ret List.446; ret List.515;
procedure List.90 (List.475, List.476, List.477, List.478, List.479): procedure List.91 (List.544, List.545, List.546, List.547, List.548):
joinpoint List.449 List.388 List.389 List.390 List.391 List.392: joinpoint List.518 List.429 List.430 List.431 List.432 List.433:
let List.451 : Int1 = CallByName Num.22 List.391 List.392; let List.520 : Int1 = CallByName Num.22 List.432 List.433;
if List.451 then if List.520 then
let List.458 : Str = CallByName List.66 List.388 List.391; let List.527 : Str = CallByName List.66 List.429 List.432;
let List.452 : {List U8, U64} = CallByName List.137 List.389 List.458 List.390; let List.521 : {List U8, U64} = CallByName List.138 List.430 List.527 List.431;
let List.455 : U64 = 1i64; let List.524 : U64 = 1i64;
let List.454 : U64 = CallByName Num.19 List.391 List.455; let List.523 : U64 = CallByName Num.19 List.432 List.524;
jump List.449 List.388 List.452 List.390 List.454 List.392; jump List.518 List.429 List.521 List.431 List.523 List.433;
else else
ret List.389; ret List.430;
in in
jump List.449 List.475 List.476 List.477 List.478 List.479; jump List.518 List.544 List.545 List.546 List.547 List.548;
procedure Num.125 (#Attr.2): procedure Num.125 (#Attr.2):
let Num.265 : U8 = lowlevel NumIntCast #Attr.2; let Num.265 : U8 = lowlevel NumIntCast #Attr.2;

View file

@ -1,6 +1,6 @@
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure Num.19 (#Attr.2, #Attr.3): procedure Num.19 (#Attr.2, #Attr.3):
let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; let Num.258 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -6,40 +6,40 @@ procedure Bool.2 ():
let Bool.23 : Int1 = true; let Bool.23 : Int1 = true;
ret Bool.23; ret Bool.23;
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.423 : U64 = CallByName List.6 List.94; let List.492 : U64 = CallByName List.6 List.95;
let List.419 : Int1 = CallByName Num.22 List.95 List.423; let List.488 : Int1 = CallByName Num.22 List.96 List.492;
if List.419 then if List.488 then
let List.421 : I64 = CallByName List.66 List.94 List.95; let List.490 : I64 = CallByName List.66 List.95 List.96;
let List.420 : [C {}, C I64] = TagId(1) List.421; let List.489 : [C {}, C I64] = TagId(1) List.490;
ret List.420; ret List.489;
else else
let List.418 : {} = Struct {}; let List.487 : {} = Struct {};
let List.417 : [C {}, C I64] = TagId(0) List.418; let List.486 : [C {}, C I64] = TagId(0) List.487;
ret List.417; ret List.486;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.424 : U64 = lowlevel ListLen #Attr.2; let List.493 : U64 = lowlevel ListLen #Attr.2;
ret List.424; ret List.493;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.422 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.491 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.422; ret List.491;
procedure List.9 (List.242): procedure List.9 (List.283):
let List.416 : U64 = 0i64; let List.485 : U64 = 0i64;
let List.409 : [C {}, C I64] = CallByName List.2 List.242 List.416; let List.478 : [C {}, C I64] = CallByName List.2 List.283 List.485;
let List.413 : U8 = 1i64; let List.482 : U8 = 1i64;
let List.414 : U8 = GetTagId List.409; let List.483 : U8 = GetTagId List.478;
let List.415 : Int1 = lowlevel Eq List.413 List.414; let List.484 : Int1 = lowlevel Eq List.482 List.483;
if List.415 then if List.484 then
let List.243 : I64 = UnionAtIndex (Id 1) (Index 0) List.409; let List.284 : I64 = UnionAtIndex (Id 1) (Index 0) List.478;
let List.410 : [C Int1, C I64] = TagId(1) List.243; let List.479 : [C Int1, C I64] = TagId(1) List.284;
ret List.410; ret List.479;
else else
let List.412 : Int1 = true; let List.481 : Int1 = true;
let List.411 : [C Int1, C I64] = TagId(0) List.412; let List.480 : [C Int1, C I64] = TagId(0) List.481;
ret List.411; ret List.480;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -0,0 +1,14 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
procedure Test.0 (Test.4):
let Test.7 : Int1 = CallByName Bool.2;
ret Test.7;
procedure Test.3 ():
let Test.1 : {} = Struct {};
let Test.2 : Int1 = CallByName Test.0 Test.1;
expect Test.2;
let Test.5 : {} = Struct {};
ret Test.5;

View file

@ -1,16 +1,16 @@
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.412 : U64 = 1i64; let List.481 : U64 = 1i64;
let List.410 : List I64 = CallByName List.70 List.105 List.412; let List.479 : List I64 = CallByName List.70 List.106 List.481;
let List.409 : List I64 = CallByName List.71 List.410 List.106; let List.478 : List I64 = CallByName List.71 List.479 List.107;
ret List.409; ret List.478;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.413 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; let List.482 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.411 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.480 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.411; ret List.480;
procedure Test.0 (): procedure Test.0 ():
let Test.2 : List I64 = Array [1i64]; let Test.2 : List I64 = Array [1i64];

View file

@ -1,16 +1,16 @@
procedure List.4 (List.105, List.106): procedure List.4 (List.106, List.107):
let List.412 : U64 = 1i64; let List.481 : U64 = 1i64;
let List.410 : List I64 = CallByName List.70 List.105 List.412; let List.479 : List I64 = CallByName List.70 List.106 List.481;
let List.409 : List I64 = CallByName List.71 List.410 List.106; let List.478 : List I64 = CallByName List.71 List.479 List.107;
ret List.409; ret List.478;
procedure List.70 (#Attr.2, #Attr.3): procedure List.70 (#Attr.2, #Attr.3):
let List.413 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; let List.482 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
ret List.413; ret List.482;
procedure List.71 (#Attr.2, #Attr.3): procedure List.71 (#Attr.2, #Attr.3):
let List.411 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; let List.480 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
ret List.411; ret List.480;
procedure Test.1 (Test.2): procedure Test.1 (Test.2):
let Test.6 : I64 = 42i64; let Test.6 : I64 = 42i64;

View file

@ -1,27 +1,27 @@
procedure List.3 (List.102, List.103, List.104): procedure List.3 (List.103, List.104, List.105):
let List.412 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; let List.481 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.411 : List I64 = StructAtIndex 0 List.412; let List.480 : List I64 = StructAtIndex 0 List.481;
inc List.411; inc List.480;
dec List.412; dec List.481;
ret List.411; ret List.480;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.410 : U64 = lowlevel ListLen #Attr.2; let List.479 : U64 = lowlevel ListLen #Attr.2;
ret List.410; ret List.479;
procedure List.64 (List.99, List.100, List.101): procedure List.64 (List.100, List.101, List.102):
let List.417 : U64 = CallByName List.6 List.99; let List.486 : U64 = CallByName List.6 List.100;
let List.414 : Int1 = CallByName Num.22 List.100 List.417; let List.483 : Int1 = CallByName Num.22 List.101 List.486;
if List.414 then if List.483 then
let List.415 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; let List.484 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.415; ret List.484;
else else
let List.413 : {List I64, I64} = Struct {List.99, List.101}; let List.482 : {List I64, I64} = Struct {List.100, List.102};
ret List.413; ret List.482;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4): procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.416 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; let List.485 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.416; ret List.485;
procedure Num.19 (#Attr.2, #Attr.3): procedure Num.19 (#Attr.2, #Attr.3):
let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,22 +1,22 @@
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.415 : U64 = CallByName List.6 List.94; let List.484 : U64 = CallByName List.6 List.95;
let List.411 : Int1 = CallByName Num.22 List.95 List.415; let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.411 then if List.480 then
let List.413 : I64 = CallByName List.66 List.94 List.95; let List.482 : I64 = CallByName List.66 List.95 List.96;
let List.412 : [C {}, C I64] = TagId(1) List.413; let List.481 : [C {}, C I64] = TagId(1) List.482;
ret List.412; ret List.481;
else else
let List.410 : {} = Struct {}; let List.479 : {} = Struct {};
let List.409 : [C {}, C I64] = TagId(0) List.410; let List.478 : [C {}, C I64] = TagId(0) List.479;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.414 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.483 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,10 +1,10 @@
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2; let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.410 : U64 = lowlevel ListLen #Attr.2; let List.479 : U64 = lowlevel ListLen #Attr.2;
ret List.410; ret List.479;
procedure Num.19 (#Attr.2, #Attr.3): procedure Num.19 (#Attr.2, #Attr.3):
let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,26 +1,26 @@
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.415 : U64 = CallByName List.6 List.94; let List.484 : U64 = CallByName List.6 List.95;
let List.411 : Int1 = CallByName Num.22 List.95 List.415; let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.411 then if List.480 then
let List.413 : Str = CallByName List.66 List.94 List.95; let List.482 : Str = CallByName List.66 List.95 List.96;
let List.412 : [C {}, C Str] = TagId(1) List.413; let List.481 : [C {}, C Str] = TagId(1) List.482;
ret List.412; ret List.481;
else else
let List.410 : {} = Struct {}; let List.479 : {} = Struct {};
let List.409 : [C {}, C Str] = TagId(0) List.410; let List.478 : [C {}, C Str] = TagId(0) List.479;
ret List.409; ret List.478;
procedure List.5 (#Attr.2, #Attr.3): procedure List.5 (#Attr.2, #Attr.3):
let List.417 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; let List.486 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.417; ret List.486;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.483 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,27 +1,27 @@
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.415 : U64 = CallByName List.6 List.94; let List.484 : U64 = CallByName List.6 List.95;
let List.411 : Int1 = CallByName Num.22 List.95 List.415; let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.411 then if List.480 then
let List.413 : Str = CallByName List.66 List.94 List.95; let List.482 : Str = CallByName List.66 List.95 List.96;
let List.412 : [C {}, C Str] = TagId(1) List.413; let List.481 : [C {}, C Str] = TagId(1) List.482;
ret List.412; ret List.481;
else else
let List.410 : {} = Struct {}; let List.479 : {} = Struct {};
let List.409 : [C {}, C Str] = TagId(0) List.410; let List.478 : [C {}, C Str] = TagId(0) List.479;
ret List.409; ret List.478;
procedure List.5 (#Attr.2, #Attr.3): procedure List.5 (#Attr.2, #Attr.3):
let List.417 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; let List.486 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
decref #Attr.2; decref #Attr.2;
ret List.417; ret List.486;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.483 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -0,0 +1,18 @@
procedure Test.0 ():
let Test.6 : Str = "";
let Test.1 : List Str = Array [Test.6];
let Test.5 : U64 = lowlevel ListLen Test.1;
dec Test.1;
switch Test.5:
case 0:
let Test.2 : Str = "A";
ret Test.2;
case 1:
let Test.3 : Str = "B";
ret Test.3;
default:
let Test.4 : Str = "C";
ret Test.4;

View file

@ -1,27 +1,27 @@
procedure List.3 (List.102, List.103, List.104): procedure List.3 (List.103, List.104, List.105):
let List.410 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; let List.479 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.409 : List I64 = StructAtIndex 0 List.410; let List.478 : List I64 = StructAtIndex 0 List.479;
inc List.409; inc List.478;
dec List.410; dec List.479;
ret List.409; ret List.478;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.64 (List.99, List.100, List.101): procedure List.64 (List.100, List.101, List.102):
let List.415 : U64 = CallByName List.6 List.99; let List.484 : U64 = CallByName List.6 List.100;
let List.412 : Int1 = CallByName Num.22 List.100 List.415; let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.412 then if List.481 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.413; ret List.482;
else else
let List.411 : {List I64, I64} = Struct {List.99, List.101}; let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.411; ret List.480;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4): procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.256 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,16 +1,16 @@
procedure List.28 (#Attr.2, #Attr.3): procedure List.28 (#Attr.2, #Attr.3):
let List.411 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; let List.480 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2; let #Derived_gen.0 : Int1 = lowlevel ListIsUnique #Attr.2;
if #Derived_gen.0 then if #Derived_gen.0 then
ret List.411; ret List.480;
else else
decref #Attr.2; decref #Attr.2;
ret List.411; ret List.480;
procedure List.59 (List.237): procedure List.59 (List.278):
let List.410 : {} = Struct {}; let List.479 : {} = Struct {};
let List.409 : List I64 = CallByName List.28 List.237 List.410; let List.478 : List I64 = CallByName List.28 List.278 List.479;
ret List.409; ret List.478;
procedure Num.46 (#Attr.2, #Attr.3): procedure Num.46 (#Attr.2, #Attr.3):
let Num.256 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; let Num.256 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;

View file

@ -1,52 +1,49 @@
procedure Test.0 (): procedure Test.0 ():
let Test.36 : Int1 = false; let Test.31 : Int1 = false;
let Test.37 : Int1 = true; let Test.32 : Int1 = true;
let Test.1 : List Int1 = Array [Test.36, Test.37]; let Test.1 : List Int1 = Array [Test.31, Test.32];
joinpoint Test.10: joinpoint Test.9:
let Test.8 : Str = "E"; let Test.8 : Str = "E";
ret Test.8; ret Test.8;
in in
joinpoint Test.9: let Test.28 : U64 = lowlevel ListLen Test.1;
let Test.5 : Str = "B"; let Test.29 : U64 = 0i64;
ret Test.5; let Test.30 : Int1 = lowlevel Eq Test.28 Test.29;
in if Test.30 then
let Test.33 : U64 = lowlevel ListLen Test.1;
let Test.34 : U64 = 0i64;
let Test.35 : Int1 = lowlevel Eq Test.33 Test.34;
if Test.35 then
dec Test.1; dec Test.1;
let Test.4 : Str = "A"; let Test.4 : Str = "A";
ret Test.4; ret Test.4;
else else
let Test.30 : U64 = lowlevel ListLen Test.1; let Test.25 : U64 = lowlevel ListLen Test.1;
let Test.31 : U64 = 1i64; let Test.26 : U64 = 1i64;
let Test.32 : Int1 = lowlevel Eq Test.30 Test.31; let Test.27 : Int1 = lowlevel Eq Test.25 Test.26;
if Test.32 then if Test.27 then
let Test.11 : U64 = 0i64; let Test.10 : U64 = 0i64;
let Test.12 : Int1 = lowlevel ListGetUnsafe Test.1 Test.11; let Test.11 : Int1 = lowlevel ListGetUnsafe Test.1 Test.10;
dec Test.1; dec Test.1;
let Test.13 : Int1 = false; let Test.12 : Int1 = false;
let Test.14 : Int1 = lowlevel Eq Test.13 Test.12; let Test.13 : Int1 = lowlevel Eq Test.12 Test.11;
if Test.14 then if Test.13 then
let Test.5 : Str = "B";
ret Test.5;
else
jump Test.9; jump Test.9;
else else
jump Test.10; let Test.22 : U64 = lowlevel ListLen Test.1;
else let Test.23 : U64 = 2i64;
let Test.27 : U64 = lowlevel ListLen Test.1; let Test.24 : Int1 = lowlevel NumGte Test.22 Test.23;
let Test.28 : U64 = 2i64; if Test.24 then
let Test.29 : Int1 = lowlevel NumGte Test.27 Test.28; let Test.18 : U64 = 0i64;
if Test.29 then let Test.19 : Int1 = lowlevel ListGetUnsafe Test.1 Test.18;
let Test.19 : U64 = 0i64; let Test.20 : Int1 = false;
let Test.20 : Int1 = lowlevel ListGetUnsafe Test.1 Test.19; let Test.21 : Int1 = lowlevel Eq Test.20 Test.19;
let Test.21 : Int1 = false; if Test.21 then
let Test.22 : Int1 = lowlevel Eq Test.21 Test.20; let Test.14 : U64 = 1i64;
if Test.22 then let Test.15 : Int1 = lowlevel ListGetUnsafe Test.1 Test.14;
let Test.15 : U64 = 1i64;
let Test.16 : Int1 = lowlevel ListGetUnsafe Test.1 Test.15;
dec Test.1; dec Test.1;
let Test.17 : Int1 = false; let Test.16 : Int1 = false;
let Test.18 : Int1 = lowlevel Eq Test.17 Test.16; let Test.17 : Int1 = lowlevel Eq Test.16 Test.15;
if Test.18 then if Test.17 then
let Test.6 : Str = "C"; let Test.6 : Str = "C";
ret Test.6; ret Test.6;
else else
@ -54,14 +51,7 @@ procedure Test.0 ():
ret Test.7; ret Test.7;
else else
dec Test.1; dec Test.1;
jump Test.10;
else
let Test.23 : U64 = 0i64;
let Test.24 : Int1 = lowlevel ListGetUnsafe Test.1 Test.23;
dec Test.1;
let Test.25 : Int1 = false;
let Test.26 : Int1 = lowlevel Eq Test.25 Test.24;
if Test.26 then
jump Test.9; jump Test.9;
else else
jump Test.10; dec Test.1;
jump Test.9;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.431 : U64 = CallByName List.6 List.94; let List.500 : U64 = CallByName List.6 List.95;
let List.428 : Int1 = CallByName Num.22 List.95 List.431; let List.497 : Int1 = CallByName Num.22 List.96 List.500;
if List.428 then if List.497 then
let List.430 : I64 = CallByName List.66 List.94 List.95; let List.499 : I64 = CallByName List.66 List.95 List.96;
let List.429 : [C {}, C I64] = TagId(1) List.430; let List.498 : [C {}, C I64] = TagId(1) List.499;
ret List.429; ret List.498;
else else
let List.427 : {} = Struct {}; let List.496 : {} = Struct {};
let List.426 : [C {}, C I64] = TagId(0) List.427; let List.495 : [C {}, C I64] = TagId(0) List.496;
ret List.426; ret List.495;
procedure List.3 (List.102, List.103, List.104): procedure List.3 (List.103, List.104, List.105):
let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; let List.487 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.417 : List I64 = StructAtIndex 0 List.418; let List.486 : List I64 = StructAtIndex 0 List.487;
inc List.417; inc List.486;
dec List.418; dec List.487;
ret List.417; ret List.486;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.64 (List.99, List.100, List.101): procedure List.64 (List.100, List.101, List.102):
let List.415 : U64 = CallByName List.6 List.99; let List.484 : U64 = CallByName List.6 List.100;
let List.412 : Int1 = CallByName Num.22 List.100 List.415; let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.412 then if List.481 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.413; ret List.482;
else else
let List.411 : {List I64, I64} = Struct {List.99, List.101}; let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.411; ret List.480;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.493 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.424; ret List.493;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4): procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.94, List.95): procedure List.2 (List.95, List.96):
let List.431 : U64 = CallByName List.6 List.94; let List.500 : U64 = CallByName List.6 List.95;
let List.428 : Int1 = CallByName Num.22 List.95 List.431; let List.497 : Int1 = CallByName Num.22 List.96 List.500;
if List.428 then if List.497 then
let List.430 : I64 = CallByName List.66 List.94 List.95; let List.499 : I64 = CallByName List.66 List.95 List.96;
let List.429 : [C {}, C I64] = TagId(1) List.430; let List.498 : [C {}, C I64] = TagId(1) List.499;
ret List.429; ret List.498;
else else
let List.427 : {} = Struct {}; let List.496 : {} = Struct {};
let List.426 : [C {}, C I64] = TagId(0) List.427; let List.495 : [C {}, C I64] = TagId(0) List.496;
ret List.426; ret List.495;
procedure List.3 (List.102, List.103, List.104): procedure List.3 (List.103, List.104, List.105):
let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104; let List.487 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.417 : List I64 = StructAtIndex 0 List.418; let List.486 : List I64 = StructAtIndex 0 List.487;
inc List.417; inc List.486;
dec List.418; dec List.487;
ret List.417; ret List.486;
procedure List.6 (#Attr.2): procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2; let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.416; ret List.485;
procedure List.64 (List.99, List.100, List.101): procedure List.64 (List.100, List.101, List.102):
let List.415 : U64 = CallByName List.6 List.99; let List.484 : U64 = CallByName List.6 List.100;
let List.412 : Int1 = CallByName Num.22 List.100 List.415; let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.412 then if List.481 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101; let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.413; ret List.482;
else else
let List.411 : {List I64, I64} = Struct {List.99, List.101}; let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.411; ret List.480;
procedure List.66 (#Attr.2, #Attr.3): procedure List.66 (#Attr.2, #Attr.3):
let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; let List.493 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.424; ret List.493;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4): procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414; ret List.483;
procedure Num.22 (#Attr.2, #Attr.3): procedure Num.22 (#Attr.2, #Attr.3):
let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -13,10 +13,13 @@ extern crate indoc;
#[allow(dead_code)] #[allow(dead_code)]
const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024; const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
use bumpalo::Bump;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_load::ExecutionMode; use roc_load::ExecutionMode;
use roc_load::LoadConfig; use roc_load::LoadConfig;
use roc_load::LoadMonomorphizedError;
use roc_load::Threading; use roc_load::Threading;
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::Proc; use roc_mono::ir::Proc;
use roc_mono::ir::ProcLayout; use roc_mono::ir::ProcLayout;
@ -73,11 +76,16 @@ fn promote_expr_to_module(src: &str) -> String {
buffer buffer
} }
fn compiles_to_ir(test_name: &str, src: &str) { fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
use bumpalo::Bump;
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use std::path::PathBuf; use std::path::PathBuf;
let exec_mode = match mode {
"exec" => ExecutionMode::Executable,
"test" => ExecutionMode::Test,
_ => panic!("Invalid test_mono exec mode {mode}"),
};
let arena = &Bump::new(); let arena = &Bump::new();
let filename = PathBuf::from("Test.roc"); let filename = PathBuf::from("Test.roc");
@ -85,7 +93,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
let module_src; let module_src;
let temp; let temp;
if src.starts_with("app") { if src.starts_with("app") || src.starts_with("interface") {
// this is already a module // this is already a module
module_src = src; module_src = src;
} else { } else {
@ -99,7 +107,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
threading: Threading::Single, threading: Threading::Single,
render: roc_reporting::report::RenderTarget::Generic, render: roc_reporting::report::RenderTarget::Generic,
palette: roc_reporting::report::DEFAULT_PALETTE, palette: roc_reporting::report::DEFAULT_PALETTE,
exec_mode: ExecutionMode::Executable, exec_mode,
}; };
let loaded = roc_load::load_and_monomorphize_from_str( let loaded = roc_load::load_and_monomorphize_from_str(
arena, arena,
@ -113,7 +121,9 @@ fn compiles_to_ir(test_name: &str, src: &str) {
let mut loaded = match loaded { let mut loaded = match loaded {
Ok(x) => x, Ok(x) => x,
Err(roc_load::LoadingProblem::FormattedReport(report)) => { Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
report,
))) => {
println!("{}", report); println!("{}", report);
panic!(); panic!();
} }
@ -126,6 +136,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
procedures, procedures,
exposed_to_host, exposed_to_host,
layout_interner, layout_interner,
interns,
.. ..
} = loaded; } = loaded;
@ -138,33 +149,54 @@ fn compiles_to_ir(test_name: &str, src: &str) {
assert!(type_problems.is_empty()); assert!(type_problems.is_empty());
debug_assert_eq!(exposed_to_host.values.len(), 1); let main_fn_symbol = exposed_to_host.values.keys().copied().next();
let main_fn_symbol = exposed_to_host.values.keys().copied().next().unwrap(); if !no_check {
check_procedures(arena, &interns, &layout_interner, &procedures);
}
verify_procedures(test_name, layout_interner, procedures, main_fn_symbol); verify_procedures(test_name, layout_interner, procedures, main_fn_symbol);
} }
fn check_procedures<'a>(
arena: &'a Bump,
interns: &Interns,
interner: &STLayoutInterner<'a>,
procedures: &MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
) {
use roc_mono::debug::{check_procs, format_problems};
let problems = check_procs(arena, interner, procedures);
if problems.is_empty() {
return;
}
let formatted = format_problems(interns, interner, problems);
panic!("IR problems found:\n{formatted}");
}
fn verify_procedures<'a>( fn verify_procedures<'a>(
test_name: &str, test_name: &str,
interner: STLayoutInterner<'a>, interner: STLayoutInterner<'a>,
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>, procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
main_fn_symbol: Symbol, opt_main_fn_symbol: Option<Symbol>,
) { ) {
let mut procs_string = procedures
.values()
.map(|proc| proc.to_pretty(&interner, 200, false))
.collect::<Vec<_>>();
let opt_main_fn = opt_main_fn_symbol.map(|main_fn_symbol| {
let index = procedures let index = procedures
.keys() .keys()
.position(|(s, _)| *s == main_fn_symbol) .position(|(s, _)| *s == main_fn_symbol)
.unwrap(); .unwrap();
procs_string.swap_remove(index)
let mut procs_string = procedures });
.values()
.map(|proc| proc.to_pretty(&interner, 200))
.collect::<Vec<_>>();
let main_fn = procs_string.swap_remove(index);
procs_string.sort(); procs_string.sort();
if let Some(main_fn) = opt_main_fn {
procs_string.push(main_fn); procs_string.push(main_fn);
}
let result = procs_string.join("\n"); let result = procs_string.join("\n");
@ -561,7 +593,7 @@ fn record_optional_field_function_use_default() {
"# "#
} }
#[mono_test] #[mono_test(no_check)]
fn quicksort_help() { fn quicksort_help() {
// do we still need with_larger_debug_stack? // do we still need with_larger_debug_stack?
r#" r#"
@ -1279,7 +1311,7 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
) )
} }
#[mono_test] #[mono_test(no_check)]
fn issue_2810() { fn issue_2810() {
indoc!( indoc!(
r#" r#"
@ -2096,3 +2128,34 @@ fn toplevel_accessor_fn_thunk() {
"# "#
) )
} }
#[mono_test]
fn list_one_vs_one_spread_issue_4685() {
indoc!(
r#"
app "test" provides [main] to "./platform"
main = when [""] is
[] -> "A"
[_] -> "B"
[_, ..] -> "C"
"#
)
}
#[mono_test(mode = "test")]
fn issue_4705() {
indoc!(
r###"
interface Test exposes [] imports []
go : {} -> Bool
go = \{} -> Bool.true
expect
input = {}
x = go input
x
"###
)
}

View file

@ -5,7 +5,26 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn mono_test(_args: TokenStream, item: TokenStream) -> TokenStream { pub fn mono_test(args: TokenStream, item: TokenStream) -> TokenStream {
let mut no_check = false;
let mut mode = "exec".to_owned();
for arg in syn::parse_macro_input!(args as syn::AttributeArgs) {
use syn::{Lit, Meta, MetaNameValue, NestedMeta};
if matches!(&arg, NestedMeta::Meta(Meta::Path(p)) if p.is_ident("no_check")) {
no_check = true;
}
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path,
eq_token: _,
lit: Lit::Str(s),
})) = arg
{
if path.is_ident("mode") {
mode = s.value();
}
}
}
let task_fn = syn::parse_macro_input!(item as syn::ItemFn); let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
let args = task_fn.sig.inputs.clone(); let args = task_fn.sig.inputs.clone();
@ -21,7 +40,7 @@ pub fn mono_test(_args: TokenStream, item: TokenStream) -> TokenStream {
#[test] #[test]
#(#attributes)* #(#attributes)*
#visibility fn #name(#args) { #visibility fn #name(#args) {
compiles_to_ir(#name_str, #body); compiles_to_ir(#name_str, #body, &#mode, #no_check);
} }
}; };

View file

@ -4339,8 +4339,12 @@ impl StorageSubs {
match content { match content {
FlexVar(opt_name) => FlexVar(*opt_name), FlexVar(opt_name) => FlexVar(*opt_name),
RigidVar(name) => RigidVar(*name), RigidVar(name) => RigidVar(*name),
FlexAbleVar(opt_name, ability) => FlexAbleVar(*opt_name, *ability), FlexAbleVar(opt_name, abilities) => {
RigidAbleVar(name, ability) => RigidAbleVar(*name, *ability), FlexAbleVar(*opt_name, Self::offset_ability_slice(offsets, *abilities))
}
RigidAbleVar(name, abilities) => {
RigidAbleVar(*name, Self::offset_ability_slice(offsets, *abilities))
}
RecursionVar { RecursionVar {
structure, structure,
opt_name, opt_name,
@ -4387,6 +4391,15 @@ impl StorageSubs {
union_tags union_tags
} }
fn offset_ability_slice(
offsets: &StorageSubsOffsets,
mut ability_names: SubsSlice<Symbol>,
) -> SubsSlice<Symbol> {
ability_names.start += offsets.symbol_names;
ability_names
}
fn offset_lambda_set( fn offset_lambda_set(
offsets: &StorageSubsOffsets, offsets: &StorageSubsOffsets,
mut union_lambdas: UnionLambdas, mut union_lambdas: UnionLambdas,

View file

@ -933,6 +933,50 @@ fn add_type_help<'a>(
} }
} }
} }
Layout::Struct { .. } if *name == Symbol::DICT_DICT => {
let type_vars = env.subs.get_subs_slice(alias_vars.type_variables());
let key_var = type_vars[0];
let key_layout =
env.layout_cache.from_var(env.arena, key_var, subs).unwrap();
let key_id = add_type_help(env, key_layout, key_var, None, types);
let value_var = type_vars[1];
let value_layout = env
.layout_cache
.from_var(env.arena, value_var, subs)
.unwrap();
let value_id = add_type_help(env, value_layout, value_var, None, types);
let type_id = types.add_anonymous(
&env.layout_cache.interner,
RocType::RocDict(key_id, value_id),
layout,
);
types.depends(type_id, key_id);
types.depends(type_id, value_id);
type_id
}
Layout::Struct { .. } if *name == Symbol::SET_SET => {
let type_vars = env.subs.get_subs_slice(alias_vars.type_variables());
let key_var = type_vars[0];
let key_layout =
env.layout_cache.from_var(env.arena, key_var, subs).unwrap();
let key_id = add_type_help(env, key_layout, key_var, None, types);
let type_id = types.add_anonymous(
&env.layout_cache.interner,
RocType::RocSet(key_id),
layout,
);
types.depends(type_id, key_id);
type_id
}
_ => { _ => {
unreachable!() unreachable!()
} }

View file

@ -18,8 +18,8 @@ pub fn create_dylib_elf64(custom_names: &[String]) -> object::read::Result<Vec<u
writer.reserve_shstrtab_section_index(), writer.reserve_shstrtab_section_index(),
]; ];
// we need this later, but must allocate it here
let soname = writer.add_dynamic_string(b"libapp.so"); let soname = writer.add_dynamic_string(b"libapp.so");
let out_dynamic = [(elf::DT_SONAME, 1, Some(soname)), (elf::DT_NULL, 0, None)];
// Assign dynamic symbol indices. // Assign dynamic symbol indices.
let out_dynsyms: Vec<_> = custom_names let out_dynsyms: Vec<_> = custom_names
@ -34,26 +34,57 @@ pub fn create_dylib_elf64(custom_names: &[String]) -> object::read::Result<Vec<u
writer.reserve_file_header(); writer.reserve_file_header();
const PLACEHOLDER: u64 = 0xAAAAAAAAAAAAAAAA;
let mut program_headers = [ let mut program_headers = [
object::write::elf::ProgramHeader {
p_type: object::elf::PT_PHDR,
p_flags: object::elf::PF_R,
p_offset: 0x40,
p_vaddr: 0x40,
p_paddr: 0x40,
p_filesz: 0x188,
p_memsz: 0x188,
p_align: 1 << 3,
},
object::write::elf::ProgramHeader { object::write::elf::ProgramHeader {
p_type: object::elf::PT_LOAD, p_type: object::elf::PT_LOAD,
p_flags: object::elf::PF_R | object::elf::PF_W, p_flags: object::elf::PF_R | object::elf::PF_W,
p_offset: 0, p_offset: PLACEHOLDER,
p_vaddr: 0, p_vaddr: PLACEHOLDER,
p_paddr: 0, p_paddr: PLACEHOLDER,
p_filesz: 0, p_filesz: PLACEHOLDER,
p_memsz: 0, p_memsz: PLACEHOLDER,
p_align: 1 << 12, p_align: 1 << 12,
}, },
object::write::elf::ProgramHeader { object::write::elf::ProgramHeader {
p_type: object::elf::PT_DYNAMIC, p_type: object::elf::PT_DYNAMIC,
p_flags: object::elf::PF_R | object::elf::PF_W, p_flags: object::elf::PF_R | object::elf::PF_W,
p_offset: PLACEHOLDER,
p_vaddr: PLACEHOLDER,
p_paddr: PLACEHOLDER,
p_filesz: PLACEHOLDER,
p_memsz: PLACEHOLDER,
p_align: 1 << 3,
},
object::write::elf::ProgramHeader {
p_type: object::elf::PT_GNU_RELRO,
p_flags: object::elf::PF_R,
p_offset: PLACEHOLDER,
p_vaddr: PLACEHOLDER,
p_paddr: PLACEHOLDER,
p_filesz: PLACEHOLDER,
p_memsz: PLACEHOLDER,
p_align: 1 << 1,
},
object::write::elf::ProgramHeader {
p_type: object::elf::PT_GNU_STACK,
p_flags: object::elf::PF_R,
p_offset: 0, p_offset: 0,
p_vaddr: 0, p_vaddr: 0,
p_paddr: 0, p_paddr: 0,
p_filesz: 0, p_filesz: 0,
p_memsz: 0, p_memsz: 0,
p_align: 1 << 3, p_align: 0,
}, },
]; ];
@ -65,6 +96,15 @@ pub fn create_dylib_elf64(custom_names: &[String]) -> object::read::Result<Vec<u
let dynstr_address = writer.reserved_len(); let dynstr_address = writer.reserved_len();
writer.reserve_dynstr(); writer.reserve_dynstr();
let out_dynamic = [
(elf::DT_SONAME, 1, Some(soname)),
(elf::DT_SYMTAB, dynsym_address as u64, None),
(elf::DT_STRTAB, dynstr_address as u64, None),
(elf::DT_NULL, 0, None),
];
let dyn_size = std::mem::size_of::<elf::Dyn64<Endianness>>() * out_dynamic.len();
// aligned to the next multiple of 8 // aligned to the next multiple of 8
let dynamic_address = next_multiple_of(writer.reserved_len(), 8); let dynamic_address = next_multiple_of(writer.reserved_len(), 8);
writer.reserve_dynamic(out_dynamic.len()); writer.reserve_dynamic(out_dynamic.len());
@ -77,12 +117,22 @@ pub fn create_dylib_elf64(custom_names: &[String]) -> object::read::Result<Vec<u
// just enough program header info to satisfy the dynamic loader // just enough program header info to satisfy the dynamic loader
for program_header in program_headers.iter_mut() { for program_header in program_headers.iter_mut() {
match program_header.p_type { match program_header.p_type {
object::elf::PT_LOAD | object::elf::PT_DYNAMIC => { elf::PT_LOAD | elf::PT_DYNAMIC | elf::PT_GNU_RELRO => {
program_header.p_offset = dynamic_address as u64; program_header.p_offset = dynamic_address as u64;
program_header.p_vaddr = dynamic_address as u64 + 0x1000; program_header.p_vaddr = dynamic_address as u64 + 0x1000;
program_header.p_paddr = dynamic_address as u64 + 0x1000; program_header.p_paddr = dynamic_address as u64 + 0x1000;
program_header.p_filesz = 0x8;
program_header.p_memsz = 0x8; // this puts the dynamic section inside of the segments, so
//
// Section to Segment mapping:
// Segment Sections...
// 00
// 01 .dynamic
// 02 .dynamic
// 03 .dynamic
// 04
program_header.p_filesz = dyn_size as u64;
program_header.p_memsz = dyn_size as u64;
} }
_ => {} _ => {}
} }

View file

@ -20,6 +20,7 @@ roc_load = {path = "../compiler/load"}
roc_module = {path = "../compiler/module"} roc_module = {path = "../compiler/module"}
roc_mono = {path = "../compiler/mono"} roc_mono = {path = "../compiler/mono"}
roc_parse = {path = "../compiler/parse"} roc_parse = {path = "../compiler/parse"}
roc_problem = {path = "../compiler/problem"}
roc_region = {path = "../compiler/region"} roc_region = {path = "../compiler/region"}
roc_packaging = {path = "../packaging"} roc_packaging = {path = "../packaging"}
roc_reporting = {path = "../reporting"} roc_reporting = {path = "../reporting"}

View file

@ -1,7 +1,8 @@
use bumpalo::Bump; use bumpalo::Bump;
use roc_load::{ExecutionMode, LoadConfig, Threading}; use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
use roc_packaging::cache::{self, RocCacheDir}; use roc_packaging::cache::{self, RocCacheDir};
use roc_reporting::report::{Palette, Severity}; use roc_problem::Severity;
use roc_reporting::report::Palette;
use std::path::PathBuf; use std::path::PathBuf;
use roc_fmt::annotation::Formattable; use roc_fmt::annotation::Formattable;
@ -72,7 +73,13 @@ pub fn compile_to_mono<'a, 'i, I: Iterator<Item = &'i str>>(
let mut loaded = match loaded { let mut loaded = match loaded {
Ok(v) => v, Ok(v) => v,
Err(LoadingProblem::FormattedReport(report)) => { Err(LoadMonomorphizedError::ErrorModule(m)) => {
todo!(
"error while loading module: {:?}",
(m.can_problems, m.type_problems)
);
}
Err(LoadMonomorphizedError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
return ( return (
None, None,
Problems { Problems {

View file

@ -87,7 +87,7 @@ mod test {
use indoc::indoc; use indoc::indoc;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib}; use roc_gen_llvm::{llvm::build::LlvmBackendMode, run_roc::RocCallResult, run_roc_dylib};
use roc_load::{ExecutionMode, LoadConfig, Threading}; use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
use roc_packaging::cache::RocCacheDir; use roc_packaging::cache::RocCacheDir;
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE}; use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
use target_lexicon::Triple; use target_lexicon::Triple;
@ -119,7 +119,7 @@ mod test {
threading: Threading::Single, threading: Threading::Single,
exec_mode: ExecutionMode::Test, exec_mode: ExecutionMode::Test,
}; };
let loaded = roc_load::load_and_monomorphize_from_str( let loaded = match roc_load::load_and_monomorphize_from_str(
arena, arena,
filename, filename,
source, source,
@ -127,8 +127,13 @@ mod test {
Default::default(), Default::default(),
RocCacheDir::Disallowed, RocCacheDir::Disallowed,
load_config, load_config,
) ) {
.unwrap(); Ok(m) => m,
Err(LoadMonomorphizedError::ErrorModule(m)) => {
panic!("{:?}", (m.can_problems, m.type_problems))
}
Err(e) => panic!("{e:?}"),
};
let mut loaded = loaded; let mut loaded = loaded;
let mut expectations = std::mem::take(&mut loaded.expectations); let mut expectations = std::mem::take(&mut loaded.expectations);
@ -442,8 +447,8 @@ mod test {
main = 0 main = 0
expect expect
vec1 = { x: 1.0f64, y: 2.0f64 } vec1 = { x: 1u8, y: 2u8 }
vec2 = { x: 4.0f64, y: 8.0f64 } vec2 = { x: 4u8, y: 8u8 }
vec1 == vec2 vec1 == vec2
"# "#
@ -453,17 +458,17 @@ mod test {
This expectation failed: This expectation failed:
5> expect 5> expect
6> vec1 = { x: 1.0f64, y: 2.0f64 } 6> vec1 = { x: 1u8, y: 2u8 }
7> vec2 = { x: 4.0f64, y: 8.0f64 } 7> vec2 = { x: 4u8, y: 8u8 }
8> 8>
9> vec1 == vec2 9> vec1 == vec2
When it failed, these variables had these values: When it failed, these variables had these values:
vec1 : { x : F64, y : F64 } vec1 : { x : U8, y : U8 }
vec1 = { x: 1, y: 2 } vec1 = { x: 1, y: 2 }
vec2 : { x : F64, y : F64 } vec2 : { x : U8, y : U8 }
vec2 = { x: 4, y: 8 } vec2 = { x: 4, y: 8 }
"# "#
), ),

View file

@ -1204,6 +1204,8 @@ fn opaque_wrap_function() {
} }
#[test] #[test]
#[ignore]
// I think this is picking the wrong integer type on wasm I64 vs I32.
fn dict_get_single() { fn dict_get_single() {
expect_success( expect_success(
indoc!( indoc!(

View file

@ -29,9 +29,8 @@ pub fn report_problems(
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>, can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
type_problems: &mut MutMap<ModuleId, Vec<TypeError>>, type_problems: &mut MutMap<ModuleId, Vec<TypeError>>,
) -> Problems { ) -> Problems {
use crate::report::{ use crate::report::{can_problem, type_problem, Report, RocDocAllocator, DEFAULT_PALETTE};
can_problem, type_problem, Report, RocDocAllocator, Severity::*, DEFAULT_PALETTE, use roc_problem::Severity::*;
};
let palette = DEFAULT_PALETTE; let palette = DEFAULT_PALETTE;
// This will often over-allocate total memory, but it means we definitely // This will often over-allocate total memory, but it means we definitely

View file

@ -6,12 +6,13 @@ use roc_problem::can::{
BadPattern, CycleEntry, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError, BadPattern, CycleEntry, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
ShadowKind, ShadowKind,
}; };
use roc_problem::Severity;
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region}; use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
use roc_types::types::AliasKind; use roc_types::types::AliasKind;
use std::path::PathBuf; use std::path::PathBuf;
use crate::error::r#type::suggest; use crate::error::r#type::suggest;
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity}; use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
use ven_pretty::DocAllocator; use ven_pretty::DocAllocator;
const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM"; const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM";
@ -67,7 +68,7 @@ pub fn can_problem<'b>(
) -> Report<'b> { ) -> Report<'b> {
let doc; let doc;
let title; let title;
let severity; let severity = problem.severity();
match problem { match problem {
Problem::UnusedDef(symbol, region) => { Problem::UnusedDef(symbol, region) => {
@ -86,7 +87,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_DEF.to_string(); title = UNUSED_DEF.to_string();
severity = Severity::Warning;
} }
Problem::UnusedImport(symbol, region) => { Problem::UnusedImport(symbol, region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -103,7 +103,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_IMPORT.to_string(); title = UNUSED_IMPORT.to_string();
severity = Severity::Warning;
} }
Problem::UnusedModuleImport(module_id, region) => { Problem::UnusedModuleImport(module_id, region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -121,7 +120,32 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_IMPORT.to_string(); title = UNUSED_IMPORT.to_string();
severity = Severity::Warning; }
Problem::DefsOnlyUsedInRecursion(1, region) => {
doc = alloc.stack([
alloc.reflow("This definition is only used in recursion with itself:"),
alloc.region(lines.convert_region(region)),
alloc.reflow(
"If you don't intend to use or export this definition, it should be removed!",
),
]);
title = "DEFINITION ONLY USED IN RECURSION".to_string();
}
Problem::DefsOnlyUsedInRecursion(n, region) => {
doc = alloc.stack([
alloc.concat([
alloc.reflow("These "),
alloc.string(n.to_string()),
alloc.reflow(" definitions are only used in mutual recursion with themselves:"),
]),
alloc.region(lines.convert_region(region)),
alloc.reflow(
"If you don't intend to use or export any of them, they should all be removed!",
),
]);
title = "DEFINITIONs ONLY USED IN RECURSION".to_string();
} }
Problem::ExposedButNotDefined(symbol) => { Problem::ExposedButNotDefined(symbol) => {
doc = alloc.stack([ doc = alloc.stack([
@ -137,7 +161,6 @@ pub fn can_problem<'b>(
]); ]);
title = MISSING_DEFINITION.to_string(); title = MISSING_DEFINITION.to_string();
severity = Severity::RuntimeError;
} }
Problem::UnknownGeneratesWith(loc_ident) => { Problem::UnknownGeneratesWith(loc_ident) => {
doc = alloc.stack([ doc = alloc.stack([
@ -152,7 +175,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNKNOWN_GENERATES_WITH.to_string(); title = UNKNOWN_GENERATES_WITH.to_string();
severity = Severity::RuntimeError;
} }
Problem::UnusedArgument(closure_symbol, is_anonymous, argument_symbol, region) => { Problem::UnusedArgument(closure_symbol, is_anonymous, argument_symbol, region) => {
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used."; let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
@ -187,7 +209,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_ARG.to_string(); title = UNUSED_ARG.to_string();
severity = Severity::Warning;
} }
Problem::UnusedBranchDef(symbol, region) => { Problem::UnusedBranchDef(symbol, region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -208,7 +229,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_DEF.to_string(); title = UNUSED_DEF.to_string();
severity = Severity::Warning;
} }
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => { Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
doc = alloc.stack([ doc = alloc.stack([
@ -237,7 +257,6 @@ pub fn can_problem<'b>(
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => { Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => {
use roc_parse::pattern::PatternType::*; use roc_parse::pattern::PatternType::*;
@ -268,7 +287,6 @@ pub fn can_problem<'b>(
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::Shadowing { Problem::Shadowing {
original_region, original_region,
@ -280,7 +298,6 @@ pub fn can_problem<'b>(
doc = res_doc; doc = res_doc;
title = res_title.to_string(); title = res_title.to_string();
severity = Severity::RuntimeError;
} }
Problem::CyclicAlias(symbol, region, others, alias_kind) => { Problem::CyclicAlias(symbol, region, others, alias_kind) => {
let answer = crate::error::r#type::cyclic_alias( let answer = crate::error::r#type::cyclic_alias(
@ -289,7 +306,6 @@ pub fn can_problem<'b>(
doc = answer.0; doc = answer.0;
title = answer.1; title = answer.1;
severity = Severity::RuntimeError;
} }
Problem::PhantomTypeArgument { Problem::PhantomTypeArgument {
typ: alias, typ: alias,
@ -317,7 +333,6 @@ pub fn can_problem<'b>(
]); ]);
title = UNUSED_ALIAS_PARAM.to_string(); title = UNUSED_ALIAS_PARAM.to_string();
severity = Severity::RuntimeError;
} }
Problem::UnboundTypeVariable { Problem::UnboundTypeVariable {
typ: alias, typ: alias,
@ -354,12 +369,10 @@ pub fn can_problem<'b>(
doc = alloc.stack(stack); doc = alloc.stack(stack);
title = UNBOUND_TYPE_VARIABLE.to_string(); title = UNBOUND_TYPE_VARIABLE.to_string();
severity = Severity::RuntimeError;
} }
Problem::BadRecursion(entries) => { Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, lines, &entries); doc = to_circular_def_doc(alloc, lines, &entries);
title = CIRCULAR_DEF.to_string(); title = CIRCULAR_DEF.to_string();
severity = Severity::RuntimeError;
} }
Problem::DuplicateRecordFieldValue { Problem::DuplicateRecordFieldValue {
field_name, field_name,
@ -394,7 +407,6 @@ pub fn can_problem<'b>(
]); ]);
title = DUPLICATE_FIELD_NAME.to_string(); title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
} }
Problem::InvalidOptionalValue { Problem::InvalidOptionalValue {
field_name, field_name,
@ -443,7 +455,6 @@ pub fn can_problem<'b>(
]); ]);
title = DUPLICATE_FIELD_NAME.to_string(); title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
} }
Problem::DuplicateTag { Problem::DuplicateTag {
tag_name, tag_name,
@ -478,7 +489,6 @@ pub fn can_problem<'b>(
]); ]);
title = DUPLICATE_TAG_NAME.to_string(); title = DUPLICATE_TAG_NAME.to_string();
severity = Severity::Warning;
} }
Problem::SignatureDefMismatch { Problem::SignatureDefMismatch {
ref annotation_pattern, ref annotation_pattern,
@ -495,7 +505,6 @@ pub fn can_problem<'b>(
]); ]);
title = NAMING_PROBLEM.to_string(); title = NAMING_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::InvalidAliasRigid { Problem::InvalidAliasRigid {
alias_name: type_name, alias_name: type_name,
@ -518,7 +527,6 @@ pub fn can_problem<'b>(
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::InvalidHexadecimal(region) => { Problem::InvalidHexadecimal(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -535,7 +543,6 @@ pub fn can_problem<'b>(
]); ]);
title = INVALID_UNICODE.to_string(); title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
} }
Problem::InvalidUnicodeCodePt(region) => { Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -545,7 +552,6 @@ pub fn can_problem<'b>(
]); ]);
title = INVALID_UNICODE.to_string(); title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
} }
Problem::InvalidInterpolation(region) => { Problem::InvalidInterpolation(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -562,14 +568,12 @@ pub fn can_problem<'b>(
]); ]);
title = SYNTAX_PROBLEM.to_string(); title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
} }
Problem::RuntimeError(runtime_error) => { Problem::RuntimeError(runtime_error) => {
let answer = pretty_runtime_error(alloc, lines, runtime_error); let answer = pretty_runtime_error(alloc, lines, runtime_error);
doc = answer.0; doc = answer.0;
title = answer.1.to_string(); title = answer.1.to_string();
severity = Severity::RuntimeError;
} }
Problem::NestedDatatype { Problem::NestedDatatype {
alias, alias,
@ -597,7 +601,6 @@ pub fn can_problem<'b>(
]); ]);
title = NESTED_DATATYPE.to_string(); title = NESTED_DATATYPE.to_string();
severity = Severity::RuntimeError;
} }
Problem::InvalidExtensionType { region, kind } => { Problem::InvalidExtensionType { region, kind } => {
@ -625,7 +628,6 @@ pub fn can_problem<'b>(
]); ]);
title = INVALID_EXTENSION_TYPE.to_string(); title = INVALID_EXTENSION_TYPE.to_string();
severity = Severity::RuntimeError;
} }
Problem::AbilityHasTypeVariables { Problem::AbilityHasTypeVariables {
@ -644,7 +646,6 @@ pub fn can_problem<'b>(
), ),
]); ]);
title = ABILITY_HAS_TYPE_VARIABLES.to_string(); title = ABILITY_HAS_TYPE_VARIABLES.to_string();
severity = Severity::RuntimeError;
} }
Problem::HasClauseIsNotAbility { Problem::HasClauseIsNotAbility {
@ -655,7 +656,6 @@ pub fn can_problem<'b>(
alloc.region(lines.convert_region(clause_region)), alloc.region(lines.convert_region(clause_region)),
]); ]);
title = HAS_CLAUSE_IS_NOT_AN_ABILITY.to_string(); title = HAS_CLAUSE_IS_NOT_AN_ABILITY.to_string();
severity = Severity::RuntimeError;
} }
Problem::IllegalHasClause { region } => { Problem::IllegalHasClause { region } => {
@ -674,7 +674,6 @@ pub fn can_problem<'b>(
]), ]),
]); ]);
title = ILLEGAL_HAS_CLAUSE.to_string(); title = ILLEGAL_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
} }
Problem::DuplicateHasAbility { ability, region } => { Problem::DuplicateHasAbility { ability, region } => {
@ -692,7 +691,6 @@ pub fn can_problem<'b>(
]), ]),
]); ]);
title = "DUPLICATE BOUND ABILITY".to_string(); title = "DUPLICATE BOUND ABILITY".to_string();
severity = Severity::Warning;
} }
Problem::AbilityMemberMissingHasClause { Problem::AbilityMemberMissingHasClause {
@ -727,7 +725,6 @@ pub fn can_problem<'b>(
.reflow("Otherwise, the function does not need to be part of the ability!")]), .reflow("Otherwise, the function does not need to be part of the ability!")]),
]); ]);
title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string(); title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
} }
Problem::AbilityMemberMultipleBoundVars { Problem::AbilityMemberMultipleBoundVars {
@ -755,7 +752,6 @@ pub fn can_problem<'b>(
]) ])
]); ]);
title = ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES.to_string(); title = ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES.to_string();
severity = Severity::RuntimeError;
} }
Problem::AbilityNotOnToplevel { region } => { Problem::AbilityNotOnToplevel { region } => {
@ -767,7 +763,6 @@ pub fn can_problem<'b>(
alloc.reflow("Abilities can only be defined on the top-level of a Roc module."), alloc.reflow("Abilities can only be defined on the top-level of a Roc module."),
]); ]);
title = ABILITY_NOT_ON_TOPLEVEL.to_string(); title = ABILITY_NOT_ON_TOPLEVEL.to_string();
severity = Severity::RuntimeError;
} }
Problem::AbilityUsedAsType(suggested_var_name, ability, region) => { Problem::AbilityUsedAsType(suggested_var_name, ability, region) => {
@ -795,7 +790,6 @@ pub fn can_problem<'b>(
])), ])),
]); ]);
title = ABILITY_USED_AS_TYPE.to_string(); title = ABILITY_USED_AS_TYPE.to_string();
severity = Severity::RuntimeError;
} }
Problem::NestedSpecialization(member, region) => { Problem::NestedSpecialization(member, region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -808,7 +802,6 @@ pub fn can_problem<'b>(
alloc.reflow("Specializations can only be defined on the top-level of a module."), alloc.reflow("Specializations can only be defined on the top-level of a module."),
]); ]);
title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string(); title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string();
severity = Severity::Warning;
} }
Problem::IllegalDerivedAbility(region) => { Problem::IllegalDerivedAbility(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -820,7 +813,6 @@ pub fn can_problem<'b>(
.append(list_builtin_abilities(alloc)), .append(list_builtin_abilities(alloc)),
]); ]);
title = ILLEGAL_DERIVE.to_string(); title = ILLEGAL_DERIVE.to_string();
severity = Severity::Warning;
} }
Problem::NotAnAbility(region) => { Problem::NotAnAbility(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -829,7 +821,6 @@ pub fn can_problem<'b>(
alloc.reflow("Only abilities can be implemented."), alloc.reflow("Only abilities can be implemented."),
]); ]);
title = NOT_AN_ABILITY.to_string(); title = NOT_AN_ABILITY.to_string();
severity = Severity::Warning;
} }
Problem::NotAnAbilityMember { Problem::NotAnAbilityMember {
ability, ability,
@ -844,7 +835,6 @@ pub fn can_problem<'b>(
alloc.reflow("Only implementations for members an ability has can be specified in this location.") alloc.reflow("Only implementations for members an ability has can be specified in this location.")
]); ]);
title = NOT_AN_ABILITY_MEMBER.to_string(); title = NOT_AN_ABILITY_MEMBER.to_string();
severity = Severity::RuntimeError;
} }
Problem::ImplementationNotFound { member, region } => { Problem::ImplementationNotFound { member, region } => {
let member_str = member.as_str(alloc.interns); let member_str = member.as_str(alloc.interns);
@ -856,7 +846,6 @@ pub fn can_problem<'b>(
alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {}: my{} }}", member_str, member_str))])) alloc.tip().append(alloc.concat([alloc.reflow("consider adding a value of name "), alloc.symbol_unqualified(member), alloc.reflow(" in this scope, or using another variable that implements this ability member, like "), alloc.type_str(&format!("{{ {}: my{} }}", member_str, member_str))]))
]); ]);
title = IMPLEMENTATION_NOT_FOUND.to_string(); title = IMPLEMENTATION_NOT_FOUND.to_string();
severity = Severity::RuntimeError;
} }
Problem::OptionalAbilityImpl { ability, region } => { Problem::OptionalAbilityImpl { ability, region } => {
let hint = if ability.is_builtin() { let hint = if ability.is_builtin() {
@ -875,7 +864,6 @@ pub fn can_problem<'b>(
hint, hint,
]); ]);
title = OPTIONAL_ABILITY_IMPLEMENTATION.to_string(); title = OPTIONAL_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
} }
Problem::QualifiedAbilityImpl { region } => { Problem::QualifiedAbilityImpl { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -886,7 +874,6 @@ pub fn can_problem<'b>(
), ),
]); ]);
title = QUALIFIED_ABILITY_IMPLEMENTATION.to_string(); title = QUALIFIED_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
} }
Problem::AbilityImplNotIdent { region } => { Problem::AbilityImplNotIdent { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -898,7 +885,6 @@ pub fn can_problem<'b>(
alloc.tip().append(alloc.reflow("consider defining this expression as a variable.")) alloc.tip().append(alloc.reflow("consider defining this expression as a variable."))
]); ]);
title = ABILITY_IMPLEMENTATION_NOT_IDENTIFIER.to_string(); title = ABILITY_IMPLEMENTATION_NOT_IDENTIFIER.to_string();
severity = Severity::RuntimeError;
} }
Problem::DuplicateImpl { Problem::DuplicateImpl {
original, original,
@ -913,7 +899,6 @@ pub fn can_problem<'b>(
.reflow("Only one custom implementation can be defined for an ability member."), .reflow("Only one custom implementation can be defined for an ability member."),
]); ]);
title = DUPLICATE_IMPLEMENTATION.to_string(); title = DUPLICATE_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
} }
Problem::ImplementsNonRequired { Problem::ImplementsNonRequired {
region, region,
@ -938,7 +923,6 @@ pub fn can_problem<'b>(
), ),
]); ]);
title = UNNECESSARY_IMPLEMENTATIONS.to_string(); title = UNNECESSARY_IMPLEMENTATIONS.to_string();
severity = Severity::Warning;
} }
Problem::DoesNotImplementAbility { Problem::DoesNotImplementAbility {
region, region,
@ -963,7 +947,6 @@ pub fn can_problem<'b>(
), ),
]); ]);
title = INCOMPLETE_ABILITY_IMPLEMENTATION.to_string(); title = INCOMPLETE_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
} }
Problem::NotBoundInAllPatterns { Problem::NotBoundInAllPatterns {
unbound_symbol, unbound_symbol,
@ -984,7 +967,6 @@ pub fn can_problem<'b>(
]), ]),
]); ]);
title = "NAME NOT BOUND IN ALL PATTERNS".to_string(); title = "NAME NOT BOUND IN ALL PATTERNS".to_string();
severity = Severity::RuntimeError;
} }
Problem::NoIdentifiersIntroduced(region) => { Problem::NoIdentifiersIntroduced(region) => {
doc = alloc.stack([ doc = alloc.stack([
@ -993,7 +975,6 @@ pub fn can_problem<'b>(
alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"), alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"),
]); ]);
title = "UNNECESSARY DEFINITION".to_string(); title = "UNNECESSARY DEFINITION".to_string();
severity = Severity::Warning;
} }
Problem::OverloadedSpecialization { Problem::OverloadedSpecialization {
ability_member, ability_member,
@ -1013,7 +994,6 @@ pub fn can_problem<'b>(
alloc.reflow("Ability specializations can only provide implementations for one opaque type, since all opaque types are different!"), alloc.reflow("Ability specializations can only provide implementations for one opaque type, since all opaque types are different!"),
]); ]);
title = "OVERLOADED SPECIALIZATION".to_string(); title = "OVERLOADED SPECIALIZATION".to_string();
severity = Severity::Warning;
} }
Problem::UnnecessaryOutputWildcard { region } => { Problem::UnnecessaryOutputWildcard { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -1033,7 +1013,6 @@ pub fn can_problem<'b>(
alloc.reflow("You can safely remove this to make the code more concise without changing what it means."), alloc.reflow("You can safely remove this to make the code more concise without changing what it means."),
]); ]);
title = "UNNECESSARY WILDCARD".to_string(); title = "UNNECESSARY WILDCARD".to_string();
severity = Severity::Warning;
} }
Problem::MultipleListRestPattern { region } => { Problem::MultipleListRestPattern { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -1046,7 +1025,6 @@ pub fn can_problem<'b>(
]), ]),
]); ]);
title = "MULTIPLE LIST REST PATTERNS".to_string(); title = "MULTIPLE LIST REST PATTERNS".to_string();
severity = Severity::RuntimeError;
} }
Problem::BadTypeArguments { Problem::BadTypeArguments {
symbol, symbol,
@ -1086,7 +1064,6 @@ pub fn can_problem<'b>(
} else { } else {
"TOO FEW TYPE ARGUMENTS".to_string() "TOO FEW TYPE ARGUMENTS".to_string()
}; };
severity = Severity::RuntimeError;
} }
Problem::UnappliedCrash { region } => { Problem::UnappliedCrash { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -1100,7 +1077,6 @@ pub fn can_problem<'b>(
]) ])
]); ]);
title = "UNAPPLIED CRASH".to_string(); title = "UNAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
} }
Problem::OverAppliedCrash { region } => { Problem::OverAppliedCrash { region } => {
doc = alloc.stack([ doc = alloc.stack([
@ -1116,7 +1092,6 @@ pub fn can_problem<'b>(
]), ]),
]); ]);
title = "OVERAPPLIED CRASH".to_string(); title = "OVERAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
} }
}; };

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