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.
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
ROC_PRINT_UNIFICATIONS = "0"
ROC_PRINT_UNDERIVABLE = "0"
ROC_TRACE_COMPACTION = "0"
ROC_PRINT_UNIFICATIONS_DERIVED = "0"
ROC_PRINT_MISMATCHES = "0"
ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
ROC_CHECK_MONO_IR = "0"
ROC_PRINT_IR_AFTER_SPECIALIZATION = "0"
ROC_PRINT_IR_AFTER_RESET_REUSE = "0"
ROC_PRINT_IR_AFTER_REFCOUNT = "0"

View file

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

View file

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

View file

@ -31,6 +31,12 @@ jobs:
- name: execute tests with --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
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
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:
name: test nightly macos 11, macos 12, ubu 20.04, ubu 22.04
strategy:
fail-fast: false
matrix:
os: [ macos-11, macos-12, ubuntu-20.04, ubuntu-22.04 ]
runs-on: ${{ matrix.os }}

View file

@ -34,7 +34,10 @@ jobs:
run: cd crates/compiler/builtins/bitcode && ./run-wasm-tests.sh
- 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
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-ast-after
*.roc-format-failed-ast-before
# nix
result

14
Cargo.lock generated
View file

@ -279,9 +279,9 @@ dependencies = [
[[package]]
name = "blake3"
version = "1.3.1"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f"
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
dependencies = [
"arrayref",
"arrayvec 0.7.2",
@ -719,9 +719,9 @@ dependencies = [
[[package]]
name = "constant_time_eq"
version = "0.1.5"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
[[package]]
name = "copyless"
@ -1982,7 +1982,7 @@ checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
[[package]]
name = "inkwell"
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 = [
"either",
"inkwell_internals",
@ -1995,7 +1995,7 @@ dependencies = [
[[package]]
name = "inkwell_internals"
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 = [
"proc-macro2",
"quote",
@ -3880,6 +3880,7 @@ dependencies = [
"roc_collections",
"roc_error_macros",
"roc_module",
"roc_problem",
"roc_region",
]
@ -4247,6 +4248,7 @@ dependencies = [
"roc_mono",
"roc_packaging",
"roc_parse",
"roc_problem",
"roc_region",
"roc_reporting",
"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.
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
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`.
@ -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
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?
@ -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
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
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.
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.
### 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.
- 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 `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
## 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/
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
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))]
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_load::{ExecutionMode, LoadConfig};
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError};
use roc_packaging::cache;
use roc_target::TargetInfo;
@ -425,18 +426,26 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
threading,
exec_mode: ExecutionMode::Test,
};
let loaded = roc_load::load_and_monomorphize(
let load_result = roc_load::load_and_monomorphize(
arena,
path.to_path_buf(),
subs_by_module,
RocCacheDir::Persistent(cache::roc_cache_dir().as_path()),
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 loaded = loaded;
let interns = loaded.interns.clone();
@ -449,6 +458,19 @@ pub fn test(matches: &ArgMatches, triple: Triple) -> io::Result<i32> {
)
.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 interns = arena.alloc(interns);
@ -733,35 +755,51 @@ pub fn build(
}
}
}
Err(BuildFileError::ErrorModule {
mut module,
total_time,
}) => {
debug_assert!(module.total_problems() > 0);
let problems = roc_build::program::report_problems_typechecked(&mut module);
print_problems(problems, total_time);
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
// If you're running "main.roc" then you can just do `roc run`
// to re-run the program.
if filename != DEFAULT_ROC_FILENAME {
print!(" {}", &filename.to_string_lossy());
}
println!("\x1B[39m");
Ok(problems.exit_code())
Err(BuildFileError::ErrorModule { module, total_time }) => {
handle_error_module(module, total_time, filename, true)
}
Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
print!("{}", report);
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);
let problems = roc_build::program::report_problems_typechecked(&mut module);
print_problems(problems, total_time);
if print_run_anyway_hint {
// If you're running "main.roc" then you can just do `roc run`
// to re-run the program.
print!(".\n\nYou can run the program anyway with \x1B[32mroc run");
if filename != DEFAULT_ROC_FILENAME {
print!(" {}", &filename.to_string_lossy());
}
println!("\x1B[39m");
}
Ok(problems.exit_code())
}
fn handle_loading_problem(problem: LoadingProblem) -> io::Result<i32> {
match problem {
LoadingProblem::FormattedReport(report) => {
print!("{}", report);
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,12 +840,24 @@ mod cli_run {
let file_name = cli_testing_dir("benchmarks").join(roc_filename);
// TODO fix QuicksortApp and then remove this!
if roc_filename == "QuicksortApp.roc" {
eprintln!(
match roc_filename {
"QuicksortApp.roc" => {
eprintln!(
"WARNING: skipping testing benchmark {} because the test is broken right now!",
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")))]

View file

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

View file

@ -4,6 +4,8 @@ const UpdateMode = utils.UpdateMode;
const mem = std.mem;
const math = std.math;
const expect = std.testing.expect;
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) 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.
const byte_count_a = list_a.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);
// decrement list a.
@ -854,3 +856,21 @@ pub fn listIsUnique(
) callconv(.C) bool {
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,
withCapacity,
single,
get,
walk,
insert,
clear,
capacity,
len,
get,
contains,
insert,
remove,
update,
contains,
walk,
walkUntil,
toList,
fromList,
keys,
values,
insertAll,
@ -22,8 +27,8 @@ interface Dict
Result.{ Result },
List,
Str,
Num.{ Nat, U64, U8 },
Hash.{ Hasher },
Num.{ Nat, U64, U8, I8 },
Hash.{ Hasher, Hash },
]
## 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
## vacated spot.
##
## This move is done as a performance optimization, and it lets [Dict.remove]
## have [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time).
## This move is done as a performance optimization, and it lets [remove] have
## [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time). ##
##
## ### Equality
##
## Two dictionaries are equal when their contents and orderings match. This
## means that when `dict1 == dict2`, the expression `fn dict1 == fn dict2` will
## also evaluate to `Bool.true`. The function `fn` can count on the ordering of
## values in the dictionary to also match.
Dict k v := List [Pair k v] has [Eq]
## 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).
## It has a list of keys value pairs that is ordered based on insertion.
## It uses a list of indices into the data as the backing of a hash map.
Dict k v := {
# TODO: Add hashflooding ordered map fall back.
# 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.
empty : Dict k v
empty = @Dict []
empty : Dict k v | k has Hash & Eq
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
## may provide a performance optimisation if you know how many entries will be
## inserted.
withCapacity : Nat -> Dict k v
withCapacity = \n -> @Dict (List.withCapacity n)
withCapacity : Nat -> Dict k v | k has Hash & Eq
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
## will return [Ok value], otherwise return [Err KeyNotFound].
## Returns a dictionary containing the key and value provided as input.
##
## 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.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 Eq
get = \@Dict list, needle ->
when List.findFirst list (\Pair key _ -> key == needle) is
Ok (Pair _ v) ->
Ok v
## |> Dict.insert "One" "A Song"
## |> Dict.insert "Two" "Candy Canes"
## |> Dict.insert "Three" "Boughs of Holly"
## |> Dict.len
## |> Bool.isEq 3
len : Dict k v -> Nat | k has Hash & Eq
len = \@Dict { size } ->
size
Err NotFound ->
Err KeyNotFound
## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
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
## 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.walk 0 (\count, _, qty -> count + qty)
## |> Bool.isEq 36
walk : Dict k v, state, (state, k, v -> state) -> state
walk = \@Dict list, initialState, transform ->
List.walk list initialState (\state, Pair k v -> transform state k v)
walk : Dict k v, state, (state, k, v -> state) -> state | k has Hash & Eq
walk = \@Dict { data }, initialState, transform ->
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.
##
@ -135,29 +267,42 @@ walk = \@Dict list, initialState, transform ->
## |> Dict.insert "Apples" 12
## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12)
insert : Dict k v, k, v -> Dict k v | k has Eq
insert = \@Dict list, k, v ->
when List.findFirstIndex list (\Pair key _ -> key == k) is
Err NotFound ->
insertFresh (@Dict list) k v
insert : Dict k v, k, v -> Dict k v | k has Hash & Eq
insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
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 ->
list
|> List.set index (Pair k v)
|> @Dict
dataIndex = listGetUnsafe dataIndices index
## Returns the number of values in the dictionary.
##
## expect
## Dict.empty
## |> Dict.insert "One" "A Song"
## |> Dict.insert "Two" "Candy Canes"
## |> Dict.insert "Three" "Boughs of Holly"
## |> Dict.len
## |> Bool.isEq 3
len : Dict k v -> Nat
len = \@Dict list ->
List.len list
@Dict {
metadata,
dataIndices,
data: List.set data dataIndex (T key value),
size,
}
Err NotFound ->
# The dictionary has grown, it might need to rehash.
rehashedDict =
maybeRehash
(
@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.
##
@ -167,19 +312,34 @@ len = \@Dict list ->
## |> Dict.remove "Some"
## |> Dict.len
## |> Bool.isEq 0
remove : Dict k v, k -> Dict k v | k has Eq
remove = \@Dict list, key ->
when List.findFirstIndex list (\Pair k _ -> k == key) is
Err NotFound ->
@Dict list
remove : Dict k v, k -> Dict k v | k has Hash & Eq
remove = \@Dict { metadata, dataIndices, data, size }, key ->
# TODO: change this from swap remove to tombstone and test is performance is still good.
hashKey =
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 ->
lastIndex = List.len list - 1
last = List.len data - 1
dataIndex = listGetUnsafe dataIndices index
list
|> List.swap index lastIndex
|> List.dropLast
|> @Dict
if dataIndex == last then
@Dict {
metadata: List.set metadata index deletedSlot,
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
## 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.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Eq
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq
update = \dict, key, alter ->
# TODO: look into optimizing by merging substeps and reducing lookups.
possibleValue =
get dict key
|> Result.map Present
@ -206,46 +367,22 @@ update = \dict, key, alter ->
Present value -> insert dict key value
Missing -> remove dict key
# Internal for testing only
alterValue : [Present Bool, Missing] -> [Present Bool, Missing]
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.
## Returns the keys and values of a dictionary as a [List].
## This requires allocating a temporary list, prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.empty
## |> Dict.insert 1234 "5678"
## |> Dict.contains 1234
contains : Dict k v, k -> Bool | k has Eq
contains = \@Dict list, needle ->
List.any list \Pair key _val -> key == needle
expect contains empty "a" == Bool.false
expect contains (single "a" {}) "a" == Bool.true
expect contains (single "b" {}) "a" == Bool.false
expect
Dict.empty
|> Dict.insert 1234 "5678"
|> Dict.contains 1234
|> Bool.isEq Bool.true
## Returns a dictionary containing the key and value provided as input.
##
## 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]
## Dict.single 1 "One"
## |> Dict.insert 2 "Two"
## |> Dict.insert 3 "Three"
## |> Dict.insert 4 "Four"
## |> Dict.toList
## |> 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
toList = \@Dict { data } ->
data
## Returns the keys of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.single 1 "One"
@ -254,11 +391,12 @@ single = \key, value ->
## |> Dict.insert 4 "Four"
## |> Dict.keys
## |> Bool.isEq [1,2,3,4]
keys : Dict k v -> List k
keys = \@Dict list ->
List.map list (\Pair k _ -> k)
keys : Dict k v -> List k | k has Hash & Eq
keys = \@Dict { data } ->
List.map data (\T k _ -> k)
## Returns the values of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
##
## expect
## Dict.single 1 "One"
@ -267,22 +405,22 @@ keys = \@Dict list ->
## |> Dict.insert 4 "Four"
## |> Dict.values
## |> Bool.isEq ["One","Two","Three","Four"]
values : Dict k v -> List v
values = \@Dict list ->
List.map list (\Pair _ v -> v)
values : Dict k v -> List v | k has Hash & Eq
values = \@Dict { data } ->
List.map data (\T _ v -> v)
## 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
## both dictionaries will be combined. Note that where there are pairs
## with the same key, the value contained in the first input will be
## retained, and the value in the second input will be removed.
## with the same key, the value contained in the second input will be
## retained, and the value in the first input will be removed.
##
## first =
## Dict.single 1 "Keep Me"
## Dict.single 1 "Not Me"
## |> Dict.insert 2 "And Me"
##
## second =
## Dict.single 1 "Not Me"
## Dict.single 1 "Keep Me"
## |> Dict.insert 3 "Me Too"
## |> Dict.insert 4 "And Also Me"
##
@ -294,9 +432,9 @@ values = \@Dict list ->
##
## expect
## Dict.insertAll first second == expected
insertAll : Dict k v, Dict k v -> Dict k v | k has Eq
insertAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k v -> Dict.insertIfVacant state k v)
insertAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
insertAll = \xs, ys ->
walk ys xs insert
## 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
@ -315,10 +453,17 @@ insertAll = \xs, @Dict ys ->
## |> Dict.insert 4 "Or Me"
##
## expect Dict.keepShared first second == first
keepShared : Dict k v, Dict k v -> Dict k v | k has Eq
keepShared = \@Dict xs, ys ->
List.keepIf xs (\Pair k _ -> Dict.contains ys k)
|> @Dict
keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
keepShared = \xs, ys ->
walk
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
## 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"
##
## expect Dict.removeAll first second == expected
removeAll : Dict k v, Dict k v -> Dict k v | k has Eq
removeAll = \xs, @Dict ys ->
List.walk ys xs (\state, Pair k _ -> Dict.remove state k)
removeAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq
removeAll = \xs, ys ->
walk ys xs (\state, k, _ -> remove state k)
## Internal helper function to insert a new association
##
## Precondition: `k` should not exist in the Dict yet.
insertFresh : Dict k v, k, v -> Dict k v
insertFresh = \@Dict list, k, v ->
list
|> List.append (Pair k v)
|> @Dict
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k has Hash & Eq
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
(T key _) = listGetUnsafe data lastIndex
hashKey =
createLowLevelHasher {}
|> Hash.hash key
|> complete
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
insertIfVacant = \dict, key, value ->
if Dict.contains dict key then
dict
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
Ok index ->
dataIndex = listGetUnsafe dataIndices removedIndex
# 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
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.
# 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 = \a, b ->
r = Num.toU128 a * Num.toU128 b
lower = Num.toU64 r
upper = Num.shiftRightZfBy r 64 |> Num.toU64
# uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
# uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
# 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.
# { lower: Num.bitwiseXor a lower, upper: Num.bitwiseXor b upper }
{ lower, upper }

View file

@ -14,14 +14,16 @@ interface Hash
hashI32,
hashI64,
hashI128,
hashNat,
complete,
hashStrBytes,
hashList,
hashUnordered,
] imports [
Bool.{ isEq },
List,
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.
@ -88,6 +90,21 @@ hashI64 = \hasher, n -> addU64 hasher (Num.toU64 n)
hashI128 : a, I128 -> a | a has Hasher
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.
## The container is iterated using the walk method passed in.
## The order of the elements does not affect the final hash.

View file

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

View file

@ -637,26 +637,130 @@ mapWithIndexHelp = \src, dest, func, index, length ->
else
dest
## Returns a list of all the integers between one and another,
## including both of the given numbers.
## Returns a list of all the integers between `start` and `end`.
##
## >>> List.range 2 8
range : Int a, Int a -> List (Int a)
range = \start, end ->
when Num.compare start end is
GT -> []
EQ -> [start]
LT ->
length = Num.intCast (end - start)
## To include the `start` and `end` integers themselves, use `At` like so:
##
## List.range { start: At 2, end: At 5 } # returns [2, 3, 4, 5]
##
## To exclude them, use `After` and `Before`, like so:
##
## List.range { start: After 2, end: Before 5 } # returns [3, 4]
##
## 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)
rangeHelp = \accum, start, end ->
if end <= start then
inclusiveStart =
when start is
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
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
sortWith : List a, (a, a -> [LT, EQ, GT]) -> List a

View file

@ -14,99 +14,192 @@ interface Set
intersection,
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
fromDict = \dict -> @Set dict
toDict : Set k -> Dict k {}
toDict = \@Set dict -> dict
isEq : Set k, Set k -> Bool | k has Hash & Eq
isEq = \xs, ys ->
if len xs != len ys then
Bool.false
else
walkUntil xs Bool.true \_, elem ->
if contains ys elem then
Continue Bool.true
else
Break Bool.false
## An empty set.
empty : Set k
empty = fromDict Dict.empty
empty : Set k | k has Hash & Eq
empty = @Set Dict.empty
single : k -> Set k
single : k -> Set k | k has Hash & Eq
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
## 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 k, k -> Set k | k has Hash & Eq
insert = \@Set dict, key ->
dict
|> Dict.insert key {}
|> @Set
Dict.insert dict key {} |> @Set
# Inserting a duplicate key has no effect.
expect
actual =
Set.empty
|> Set.insert "foo"
|> Set.insert "bar"
|> Set.insert "foo"
|> Set.insert "baz"
empty
|> insert "foo"
|> insert "bar"
|> insert "foo"
|> insert "baz"
expected =
Set.empty
|> Set.insert "foo"
|> Set.insert "bar"
|> Set.insert "baz"
empty
|> insert "foo"
|> insert "bar"
|> insert "baz"
expected == actual
len : Set k -> Nat
len : Set k -> Nat | k has Hash & Eq
len = \@Set dict ->
Dict.len dict
# Inserting a duplicate key has no effect on length.
expect
actual =
Set.empty
|> Set.insert "foo"
|> Set.insert "bar"
|> Set.insert "foo"
|> Set.insert "baz"
|> Set.len
empty
|> insert "foo"
|> insert "bar"
|> insert "foo"
|> insert "baz"
|> len
actual == 3
## 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 ->
@Set (Dict.remove dict key)
Dict.remove dict key |> @Set
contains : Set k, k -> Bool | k has Eq
contains = \set, key ->
set
|> Set.toDict
|> Dict.contains key
contains : Set k, k -> Bool | k has Hash & Eq
contains = \@Set dict, key ->
Dict.contains dict key
toList : Set k -> List k
toList : Set k -> List k | k has Hash & Eq
toList = \@Set dict ->
Dict.keys dict
fromList : List k -> Set k | k has Eq
fromList : List k -> Set k | k has Hash & Eq
fromList = \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 ->
@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 ->
@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 ->
@Set (Dict.removeAll dict1 dict2)
Dict.removeAll dict1 dict2 |> @Set
walk : Set k, state, (state, k -> state) -> state
walk = \set, state, step ->
Dict.walk (Set.toDict set) state (\s, k, _ -> step s k)
walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq
walk = \@Set dict, state, step ->
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
/// tag them onto the `Let` that we used to add the imported values.
#[inline(always)]
pub fn let_import_constraint<I1, I2>(
pub fn let_import_constraint<I1, I2, I3>(
&mut self,
rigid_vars: I1,
def_types: I2,
flex_vars: I2,
def_types: I3,
module_constraint: Constraint,
pool_variables: &[Variable],
) -> Constraint
where
I1: IntoIterator<Item = Variable>,
I2: IntoIterator<Item = (Symbol, Loc<TypeOrVar>)>,
I2::IntoIter: ExactSizeIterator,
I2: IntoIterator<Item = Variable>,
I3: IntoIterator<Item = (Symbol, Loc<TypeOrVar>)>,
I3::IntoIter: ExactSizeIterator,
{
// 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 _);
@ -508,7 +510,7 @@ impl Constraints {
let let_contraint = LetConstraint {
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),
defs_and_ret_constraint,
};

View file

@ -302,7 +302,7 @@ fn sort_type_defs_before_introduction(
matrix
.strongly_connected_components_all()
.groups()
.flat_map(|group| group.iter_ones())
.flat_map(|(group, _)| group.iter_ones())
.map(|index| symbols[index])
.collect()
}
@ -1547,10 +1547,12 @@ impl DefOrdering {
#[inline(always)]
pub(crate) fn sort_can_defs_new(
env: &mut Env<'_>,
scope: &mut Scope,
var_store: &mut VarStore,
defs: CanDefs,
mut output: Output,
exposed_symbols: &VecSet<Symbol>,
) -> (Declarations, Output) {
let CanDefs {
defs,
@ -1611,7 +1613,7 @@ pub(crate) fn sort_can_defs_new(
sccs.reorder(&mut defs);
for group in sccs.groups().rev() {
for (group, is_initial) in sccs.groups().rev() {
match group.count_ones() {
1 => {
// 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 {
Closure(closure_data) => {
declarations.push_recursive_def(
@ -1720,6 +1726,9 @@ pub(crate) fn sort_can_defs_new(
let cycle_mark = IllegalCycleMark::new(var_store);
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
for def in group_defs {
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 {
Closure(closure_data) => {
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());
for group in sccs.groups() {
for (group, is_initial) in sccs.groups() {
if group.count_ones() == 1 {
// a group with a single Def, nice and simple
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) {
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
Declaration::DeclareRec(vec![mark_def_recursive(def)], IllegalCycleMark::empty())
} else {
@ -1862,11 +1894,26 @@ pub(crate) fn sort_can_defs(
Declaration::InvalidCycle(entries)
} else {
let rec_defs = group
let rec_defs: Vec<Def> = group
.iter_ones()
.map(|index| mark_def_recursive(take_def!(index)))
.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))
};
@ -2312,10 +2359,15 @@ pub fn can_defs_with_return<'a>(
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);
// 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 {
if !output.references.has_type_or_value_lookup(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;
for declaration in declarations.into_iter().rev() {
@ -2738,12 +2788,12 @@ fn correct_mutual_recursive_type_alias<'a>(
// this is needed.
let scratchpad_capacity = sccs
.groups()
.map(|r| r.count_ones())
.map(|(r, _)| r.count_ones())
.max()
.unwrap_or_default();
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);
// 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);
for stored in stack {
for stored in stack.into_iter().rev() {
match stored {
StoredDef::NonRecursive(region, boxed_def) => {
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.
// 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 {
if !output.references.has_type_or_value_lookup(symbol)
&& !exposed_symbols.contains(&symbol)
@ -427,8 +430,14 @@ pub fn canonicalize_module_defs<'a>(
..Default::default()
};
let (mut declarations, mut output) =
crate::def::sort_can_defs_new(&mut scope, var_store, defs, new_output);
let (mut declarations, mut output) = crate::def::sort_can_defs_new(
&mut env,
&mut scope,
var_store,
defs,
new_output,
exposed_symbols,
);
debug_assert!(
output.pending_derives.is_empty(),

View file

@ -181,7 +181,7 @@ struct Params {
p: Vec<u32>,
s: Vec<u32>,
scc: Sccs,
scca: BitVec,
scca: Vec<u32>,
}
impl Params {
@ -200,8 +200,10 @@ impl Params {
scc: Sccs {
matrix: ReferenceMatrix::new(length),
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);
for w in bitvec[v * length..][..length].iter_ones() {
if !params.scca[w] {
if params.scca[w] == u32::MAX {
match params.preorders[w] {
Preorder::Filled(pw) => loop {
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::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
.matrix
.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;
if node as usize == v {
break;
}
}
if !params.s.is_empty() {
params.scc.not_initial.set(params.scc.components, true);
}
params.scc.components += 1;
}
}
@ -261,6 +269,7 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
pub struct Sccs {
components: usize,
matrix: ReferenceMatrix,
not_initial: BitVec,
}
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
/// 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
let length = if self.matrix.length == 0 {
// the `.take(self.components)` ensures the resulting iterator will be empty
@ -286,13 +295,15 @@ impl Sccs {
.bitvec
.chunks_exact(length)
.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
pub fn reorder<T>(&self, slice: &mut [T]) {
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() {
let mut index = indices[i];

View file

@ -79,6 +79,9 @@ flags! {
/// Only use this in single-threaded mode!
ROC_PRINT_UNIFICATIONS
/// Prints types whose ability impls failed to be derived.
ROC_PRINT_UNDERIVABLE
/// Prints traces of unspecialized lambda set compaction
ROC_TRACE_COMPACTION
@ -116,6 +119,9 @@ flags! {
// ===Mono===
/// Type-checks the mono IR after specialization.
ROC_CHECK_MONO_IR
/// Writes a pretty-printed mono IR to stderr after function 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 => {
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_I128))
}
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_NAT))
}
_ => None,
}
}

View file

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

View file

@ -7,6 +7,7 @@ use roc_module::{
ident::{Lowercase, TagIdIntType, TagName},
symbol::Symbol,
};
use roc_problem::Severity;
use roc_region::all::Region;
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)]
pub enum Context {
BadArg,

View file

@ -298,17 +298,7 @@ impl<'a> Formattable for Expr<'a> {
}
SingleQuote(string) => {
buf.indent(indent);
buf.push('\'');
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('\'');
format_sq_literal(buf, string);
}
&NonBase10Int {
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 {
use roc_parse::ast::Expr::*;

View file

@ -1,5 +1,5 @@
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::Buf;
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),
SingleQuote(string) => {
buf.indent(indent);
buf.push('\'');
buf.push_str(string);
buf.push('\'');
format_sq_literal(buf, string);
}
Underscore(name) => {
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]
fn format_nested_pipeline() {
expr_formats_same(indoc!(

View file

@ -37,6 +37,7 @@ use roc_collections::all::{ImMap, MutMap, MutSet};
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_PRINT_LLVM_FN_VERIFICATION;
use roc_error_macros::internal_error;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
@ -5610,3 +5611,23 @@ pub fn add_func<'ctx>(
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_str::str_equal;
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::lowlevel::dec_binop_with_unchecked;
#[derive(Clone, Debug)]
enum WhenRecursive<'a> {
Unreachable,
Loop(UnionLayout<'a>),
}
pub fn generic_eq<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
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 symbol = Symbol::LIST_EQ;
let element_layout = when_recursive.unwrap_recursive_pointer(*element_layout);
let fn_name = layout_ids
.get(symbol, element_layout)
.get(symbol, &element_layout)
.to_symbol_string(symbol, &env.interns);
let function = match env.module.get_function(fn_name.as_str()) {
@ -427,7 +424,7 @@ fn build_list_eq<'a, 'ctx, 'env>(
layout_ids,
when_recursive,
function_value,
element_layout,
&element_layout,
);
function_value

View file

@ -2,7 +2,7 @@ use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{
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::convert::{basic_type_from_layout, RocUnion};
@ -399,14 +399,8 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
match builtin {
List(element_layout) => {
let function = modify_refcount_list(
env,
layout_ids,
mode,
when_recursive,
layout,
element_layout,
);
let function =
modify_refcount_list(env, layout_ids, mode, when_recursive, element_layout);
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>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
@ -609,25 +597,26 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
layout_ids: &mut LayoutIds<'a>,
mode: Mode,
when_recursive: &WhenRecursive<'a>,
layout: &Layout<'a>,
element_layout: &Layout<'a>,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
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(
layout_ids,
&env.interns,
"increment_list",
"decrement_list",
layout,
list_layout,
mode,
);
let function = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
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);
modify_refcount_list_help(
@ -635,8 +624,8 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
layout_ids,
mode,
when_recursive,
layout,
element_layout,
list_layout,
&element_layout,
function_value,
);

View file

@ -744,7 +744,7 @@ impl<'a> WasmBackend<'a> {
let mut current_stmt = stmt;
while let Stmt::Let(sym, expr, layout, following) = current_stmt {
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 {
@ -974,7 +974,7 @@ impl<'a> WasmBackend<'a> {
self.register_symbol_debug_names();
println!(
"## 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
);
}
@ -1078,7 +1078,7 @@ impl<'a> WasmBackend<'a> {
Expr::Reset { symbol: arg, .. } => self.expr_reset(*arg, sym, storage),
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 {
println!("## procs");
for proc in procs.iter() {
println!("{}", proc.to_pretty(env.layout_interner, 200));
println!("{}", proc.to_pretty(env.layout_interner, 200, true));
// println!("{:?}", proc);
}
}
@ -161,7 +161,7 @@ pub fn build_app_module<'a>(
if DEBUG_SETTINGS.helper_procs_ir {
println!("## helper_procs");
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);
}
}

View file

@ -101,14 +101,14 @@ pub fn load_and_monomorphize_from_str<'a>(
exposed_types: ExposedByModule,
roc_cache_dir: RocCacheDir<'_>,
load_config: LoadConfig,
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
) -> Result<MonomorphizedModule<'a>, LoadMonomorphizedError<'a>> {
use LoadResult::*;
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)? {
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 bumpalo::Bump;
use crossbeam::channel::{bounded, Sender};
@ -17,8 +19,8 @@ use roc_constrain::module::constrain_module;
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::{
ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION,
ROC_PRINT_LOAD_LOG,
ROC_CHECK_MONO_IR, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG,
};
use roc_derive::SharedDerivedModule;
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::module::module_defs;
use roc_parse::parser::{FileError, Parser, SourceError, SyntaxError};
use roc_problem::Severity;
use roc_region::all::{LineInfo, Loc, Region};
use roc_reporting::report::{Annotation, Palette, RenderTarget};
use roc_solve::module::{extract_module_owned_implementations, Solved, SolvedModule};
use roc_solve_problem::TypeError;
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 std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
@ -97,20 +100,28 @@ pub struct LoadConfig {
#[derive(Debug, Clone, Copy)]
pub enum ExecutionMode {
Test,
Check,
Executable,
/// Like [`ExecutionMode::Executable`], but stops in the presence of type errors.
ExecutableIfCheck,
/// Test is like [`ExecutionMode::ExecutableIfCheck`], but rather than producing a proper
/// executable, run tests.
Test,
}
impl ExecutionMode {
fn goal_phase(&self) -> Phase {
match self {
ExecutionMode::Test | ExecutionMode::Executable => Phase::MakeSpecializations,
ExecutionMode::Check | ExecutionMode::ExecutableIfCheck => Phase::SolveTypes,
ExecutionMode::Executable => Phase::MakeSpecializations,
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
@ -141,18 +152,22 @@ struct ModuleCache<'a> {
}
impl<'a> ModuleCache<'a> {
pub fn total_problems(&self) -> usize {
let mut total = 0;
fn has_can_errors(&self) -> bool {
self.can_problems
.values()
.flatten()
.any(|problem| problem.severity() == Severity::RuntimeError)
}
for problems in self.can_problems.values() {
total += problems.len();
}
fn has_type_errors(&self) -> bool {
self.type_problems
.values()
.flatten()
.any(|problem| problem.severity() == Severity::RuntimeError)
}
for problems in self.type_problems.values() {
total += problems.len();
}
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()
}
#[allow(clippy::too_many_arguments)]
fn new(
root_id: ModuleId,
target_info: TargetInfo,
@ -1208,7 +1222,6 @@ fn enqueue_task<'a>(
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn load_and_typecheck_str<'a>(
arena: &'a Bump,
filename: PathBuf,
@ -1495,7 +1508,6 @@ pub enum Threading {
/// 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
/// to rebuild the module and can link in the cached one directly.)
#[allow(clippy::too_many_arguments)]
pub fn load<'a>(
arena: &'a Bump,
load_start: LoadStart<'a>,
@ -1556,7 +1568,6 @@ pub fn load<'a>(
}
/// Load using only a single thread; used when compiling to webassembly
#[allow(clippy::too_many_arguments)]
pub fn load_single_threaded<'a>(
arena: &'a Bump,
load_start: LoadStart<'a>,
@ -1821,7 +1832,6 @@ fn state_thread_step<'a>(
}
}
#[allow(clippy::too_many_arguments)]
fn load_multi_threaded<'a>(
arena: &'a Bump,
load_start: LoadStart<'a>,
@ -1997,7 +2007,6 @@ fn load_multi_threaded<'a>(
.unwrap()
}
#[allow(clippy::too_many_arguments)]
fn worker_task_step<'a>(
worker_arena: &'a Bump,
worker: &Worker<BuildTask<'a>>,
@ -2072,7 +2081,6 @@ fn worker_task_step<'a>(
}
}
#[allow(clippy::too_many_arguments)]
fn worker_task<'a>(
worker_arena: &'a Bump,
worker: Worker<BuildTask<'a>>,
@ -2164,7 +2172,7 @@ macro_rules! debug_print_ir {
let procs_string = $state
.procedures
.values()
.map(|proc| proc.to_pretty($interner, 200))
.map(|proc| proc.to_pretty($interner, 200, true))
.collect::<Vec<_>>();
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
fn report_unused_imported_modules<'a>(
state: &mut State<'a>,
@ -2615,7 +2644,7 @@ fn update<'a>(
let finish_type_checking = is_host_exposed &&
(state.goal_phase() == Phase::SolveTypes)
// 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 {
debug_assert!(work.is_empty());
@ -2623,7 +2652,7 @@ fn update<'a>(
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
// aren't registered; transfer all of their idents over to the state, since
// we're now done and ready to report errors.
@ -2677,9 +2706,7 @@ fn update<'a>(
},
);
if state.goal_phase() > Phase::SolveTypes
|| matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
if state.goal_phase() > Phase::SolveTypes || state.exec_mode.build_if_checks() {
let layout_cache = state.layout_caches.pop().unwrap_or_else(|| {
LayoutCache::new(state.layout_interner.fork(), state.target_info)
});
@ -2703,17 +2730,12 @@ fn update<'a>(
state.timings.insert(module_id, module_timing);
}
let work = if is_host_exposed
&& matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck)
{
let work = if is_host_exposed && state.exec_mode.build_if_checks() {
debug_assert!(
work.is_empty(),
"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.
state
.dependencies
@ -2785,7 +2807,10 @@ fn update<'a>(
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);
@ -2896,6 +2921,7 @@ fn update<'a>(
log!("specializations complete from {:?}", module_id);
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();
@ -3205,7 +3231,6 @@ fn finish_specialization<'a>(
})
}
#[allow(clippy::too_many_arguments)]
fn finish(
mut state: State,
solved: Solved<Subs>,
@ -3644,7 +3669,6 @@ fn verify_interface_matches_file_path<'a>(
Err(problem)
}
#[allow(clippy::too_many_arguments)]
fn parse_header<'a>(
arena: &'a Bump,
read_file_duration: Duration,
@ -3931,7 +3955,6 @@ fn parse_header<'a>(
}
/// Load a module by its filename
#[allow(clippy::too_many_arguments)]
fn load_filename<'a>(
arena: &'a Bump,
filename: PathBuf,
@ -3970,7 +3993,6 @@ fn load_filename<'a>(
/// Load a module from a str
/// the `filename` is never read, but used for the module name
#[allow(clippy::too_many_arguments)]
fn load_from_str<'a>(
arena: &'a Bump,
filename: PathBuf,
@ -4010,7 +4032,6 @@ struct HeaderInfo<'a> {
extra: HeaderFor<'a>,
}
#[allow(clippy::too_many_arguments)]
fn build_header<'a>(
info: HeaderInfo<'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`
#[allow(clippy::too_many_arguments)]
fn send_header_two<'a>(
info: PlatformHeaderInfo<'a>,
parse_state: roc_parse::state::State<'a>,
@ -4488,7 +4508,6 @@ fn send_header_two<'a>(
impl<'a> BuildTask<'a> {
// TODO trim down these arguments - possibly by moving Constraint into Module
#[allow(clippy::too_many_arguments)]
fn solve_module(
module: Module,
ident_ids: IdentIds,
@ -4575,61 +4594,26 @@ pub fn add_imports(
mut pending_abilities: PendingAbilitiesStore,
exposed_for_module: &ExposedForModule,
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) {
let mut import_variables = Vec::new();
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 {
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.
@ -4656,6 +4640,9 @@ pub fn add_imports(
struct Ctx<'a> {
subs: &'a mut Subs,
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(
@ -4663,16 +4650,26 @@ pub fn add_imports(
&mut Ctx {
subs,
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() {
Some(var) => var,
None => {
import_var_for_symbol!(
import_variable_for_symbol(
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,
internal_error!("Import ability member {:?} not available", symbol)
OnSymbolNotFound::AbilityMemberMustBeAvailable,
);
*cached_symbol_vars.get(&symbol).unwrap()
}
},
@ -4690,7 +4687,15 @@ pub fn add_imports(
.storage_subs
.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),
},
@ -4699,6 +4704,95 @@ pub fn add_imports(
(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)]
fn run_solve_solve(
exposed_for_module: ExposedForModule,
@ -4724,7 +4818,8 @@ fn run_solve_solve(
..
} = 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 subs = Subs::new_from_varstore(var_store);
@ -4736,11 +4831,17 @@ fn run_solve_solve(
pending_abilities,
&exposed_for_module,
&mut def_types,
&mut rigid_vars,
&mut imported_rigid_vars,
&mut imported_flex_vars,
);
let actual_constraint =
constraints.let_import_constraint(rigid_vars, def_types, constraint, &import_variables);
let actual_constraint = constraints.let_import_constraint(
imported_rigid_vars,
imported_flex_vars,
def_types,
constraint,
&import_variables,
);
let mut solve_aliases = roc_solve::solve::Aliases::with_capacity(aliases.len());
for (name, (_, alias)) in aliases.iter() {
@ -4805,7 +4906,6 @@ fn run_solve_solve(
)
}
#[allow(clippy::too_many_arguments)]
fn run_solve<'a>(
module: Module,
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()
}
#[allow(clippy::too_many_arguments)]
fn fabricate_platform_module<'a>(
arena: &'a Bump,
opt_shorthand: Option<&'a str>,
@ -4959,7 +5058,6 @@ fn fabricate_platform_module<'a>(
)
}
#[allow(clippy::too_many_arguments)]
#[allow(clippy::unnecessary_wraps)]
fn canonicalize_and_constrain<'a>(
arena: &'a Bump,
@ -5233,7 +5331,6 @@ fn ident_from_exposed(entry: &Spaced<'_, ExposedName<'_>>) -> Ident {
entry.extract_spaces().item.as_str().into()
}
#[allow(clippy::too_many_arguments)]
fn make_specializations<'a>(
arena: &'a Bump,
home: ModuleId,
@ -5310,7 +5407,6 @@ fn make_specializations<'a>(
}
}
#[allow(clippy::too_many_arguments)]
fn build_pending_specializations<'a>(
arena: &'a Bump,
solved_subs: Solved<Subs>,
@ -5741,7 +5837,6 @@ fn build_pending_specializations<'a>(
/// their specializations.
// TODO: right now, this runs sequentially, and no other modules are mono'd in parallel to the
// derived module.
#[allow(clippy::too_many_arguments)]
fn load_derived_partial_procs<'a>(
home: ModuleId,
arena: &'a Bump,
@ -5987,7 +6082,7 @@ fn run_task<'a>(
}
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;
let src_lines: Vec<&str> = Vec::new();
@ -6074,7 +6169,7 @@ fn to_import_cycle_report(
filename: PathBuf,
render: RenderTarget,
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE};
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
// import_cycle looks like CycleModule, Import1, ..., ImportN, CycleModule
@ -6134,7 +6229,7 @@ fn to_incorrect_module_name_report<'a>(
src: &'a [u8],
render: RenderTarget,
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, Severity, DEFAULT_PALETTE};
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
let IncorrectModuleName {
@ -6220,7 +6315,7 @@ fn to_parse_problem_report<'a>(
}
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 PlatformPath::*;

View file

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

View file

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

View file

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

View file

@ -68,7 +68,7 @@ pub fn infer_borrow<'a>(
let sccs = matrix.strongly_connected_components_all();
for group in sccs.groups() {
for (group, _) in sccs.groups() {
// This is a fixed-point analysis
//
// 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
/// us something like ("x" => value.0.0)
#[derive(Debug)]
enum Match {
Exact(Label),
GuardOnly,
@ -795,7 +796,22 @@ fn to_relevant_branch_help<'a>(
elements,
element_layout: _,
} => 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 mut new_path = path.to_vec();

View file

@ -326,6 +326,7 @@ impl<'a> Proc<'a> {
&'b self,
alloc: &'b D,
interner: &'b I,
pretty: bool,
_parens: Parens,
) -> DocBuilder<'b, D, A>
where
@ -335,7 +336,7 @@ impl<'a> Proc<'a> {
I: Interner<'a, Layout<'a>>,
{
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() {
arg_doc.append(alloc.reflow(": ")).append(layout.to_doc(
alloc,
@ -350,36 +351,36 @@ impl<'a> Proc<'a> {
if pretty_print_ir_symbols() {
alloc
.text("procedure : ")
.append(symbol_to_doc(alloc, self.name.name()))
.append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" ")
.append(self.ret_layout.to_doc(alloc, interner, Parens::NotNeeded))
.append(alloc.hardline())
.append(alloc.text("procedure = "))
.append(symbol_to_doc(alloc, self.name.name()))
.append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" (")
.append(alloc.intersperse(args_doc, ", "))
.append("):")
.append(alloc.hardline())
.append(self.body.to_doc(alloc, interner).indent(4))
.append(self.body.to_doc(alloc, interner, pretty).indent(4))
} else {
alloc
.text("procedure ")
.append(symbol_to_doc(alloc, self.name.name()))
.append(symbol_to_doc(alloc, self.name.name(), pretty))
.append(" (")
.append(alloc.intersperse(args_doc, ", "))
.append("):")
.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
I: Interner<'a, Layout<'a>>,
{
let allocator = BoxAllocator;
let mut w = std::vec::Vec::new();
self.to_doc::<_, (), _>(&allocator, interner, Parens::NotNeeded)
self.to_doc::<_, (), _>(&allocator, interner, pretty, Parens::NotNeeded)
.1
.render(width, &mut w)
.unwrap();
@ -1675,35 +1676,13 @@ pub enum 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
D: DocAllocator<'b, A>,
D::Doc: 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("")
}
}
@ -1724,7 +1703,7 @@ pub enum 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
D: DocAllocator<'a, A>,
D::Doc: Clone,
@ -1735,20 +1714,20 @@ impl ModifyRc {
match self {
Inc(symbol, 1) => alloc
.text("inc ")
.append(symbol_to_doc(alloc, symbol))
.append(symbol_to_doc(alloc, symbol, pretty))
.append(";"),
Inc(symbol, n) => alloc
.text("inc ")
.append(alloc.text(format!("{} ", n)))
.append(symbol_to_doc(alloc, symbol))
.append(symbol_to_doc(alloc, symbol, pretty))
.append(";"),
Dec(symbol) => alloc
.text("dec ")
.append(symbol_to_doc(alloc, symbol))
.append(symbol_to_doc(alloc, symbol, pretty))
.append(";"),
DecRef(symbol) => alloc
.text("decref ")
.append(symbol_to_doc(alloc, symbol))
.append(symbol_to_doc(alloc, symbol, pretty))
.append(";"),
}
}
@ -1808,7 +1787,7 @@ pub struct 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
D: DocAllocator<'b, A>,
D::Doc: Clone,
@ -1822,19 +1801,19 @@ impl<'a> Call<'a> {
CallType::ByName { name, .. } => {
let it = std::iter::once(name.name())
.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, " "))
}
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
.text(format!("lowlevel {:?} ", lowlevel))
.append(alloc.intersperse(it, " "))
}
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
.text(format!("lowlevel {:?} ", higher_order.op))
@ -1843,7 +1822,7 @@ impl<'a> Call<'a> {
Foreign {
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
.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;
if pretty_print_ir_symbols() {
if pretty_print_ir_symbols() || force_pretty {
format!("{:?}", symbol)
} else {
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
D: DocAllocator<'b, A>,
D::Doc: 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
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
symbol_to_doc(alloc, symbol.0)
symbol_to_doc(alloc, symbol.0, pretty)
}
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
D: DocAllocator<'b, A>,
D::Doc: Clone,
@ -2081,7 +2064,7 @@ impl<'a> Expr<'a> {
match self {
Literal(lit) => lit.to_doc(alloc),
Call(call) => call.to_doc(alloc),
Call(call) => call.to_doc(alloc, pretty),
Tag {
tag_id, arguments, ..
@ -2091,7 +2074,7 @@ impl<'a> Expr<'a> {
.append(alloc.text(tag_id.to_string()))
.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
.append(alloc.space())
@ -2109,11 +2092,11 @@ impl<'a> Expr<'a> {
.append(alloc.text(tag_id.to_string()))
.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
.text("Reuse ")
.append(symbol_to_doc(alloc, *symbol))
.append(symbol_to_doc(alloc, *symbol, pretty))
.append(alloc.space())
.append(format!("{:?}", update_mode))
.append(alloc.space())
@ -2130,7 +2113,7 @@ impl<'a> Expr<'a> {
)),
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
.text("Struct {")
@ -2140,7 +2123,7 @@ impl<'a> Expr<'a> {
Array { elems, .. } => {
let it = elems.iter().map(|e| match e {
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
@ -2154,17 +2137,21 @@ impl<'a> Expr<'a> {
index, structure, ..
} => alloc
.text(format!("StructAtIndex {} ", index))
.append(symbol_to_doc(alloc, *structure)),
.append(symbol_to_doc(alloc, *structure, pretty)),
RuntimeErrorFunction(s) => alloc.text(format!("ErrorFunction {}", s)),
GetTagId { structure, .. } => alloc
.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 {
tag_id,
@ -2173,14 +2160,14 @@ impl<'a> Expr<'a> {
..
} => alloc
.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 mut w = std::vec::Vec::new();
self.to_doc::<_, ()>(&allocator)
self.to_doc::<_, ()>(&allocator, pretty)
.1
.render(width, &mut w)
.unwrap();
@ -2200,7 +2187,12 @@ impl<'a> Stmt<'a> {
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
D: DocAllocator<'b, A>,
D::Doc: Clone,
@ -2212,19 +2204,19 @@ impl<'a> Stmt<'a> {
match self {
Let(symbol, expr, layout, cont) => alloc
.text("let ")
.append(symbol_to_doc(alloc, *symbol))
.append(symbol_to_doc(alloc, *symbol, pretty))
.append(" : ")
.append(layout.to_doc(alloc, interner, Parens::NotNeeded))
.append(" = ")
.append(expr.to_doc(alloc))
.append(expr.to_doc(alloc, pretty))
.append(";")
.append(alloc.hardline())
.append(cont.to_doc(alloc, interner)),
.append(cont.to_doc(alloc, interner, pretty)),
Refcounting(modify, cont) => modify
.to_doc(alloc)
.to_doc(alloc, pretty)
.append(alloc.hardline())
.append(cont.to_doc(alloc, interner)),
.append(cont.to_doc(alloc, interner, pretty)),
Expect {
condition,
@ -2232,10 +2224,10 @@ impl<'a> Stmt<'a> {
..
} => alloc
.text("expect ")
.append(symbol_to_doc(alloc, *condition))
.append(symbol_to_doc(alloc, *condition, pretty))
.append(";")
.append(alloc.hardline())
.append(remainder.to_doc(alloc, interner)),
.append(remainder.to_doc(alloc, interner, pretty)),
ExpectFx {
condition,
@ -2243,14 +2235,14 @@ impl<'a> Stmt<'a> {
..
} => alloc
.text("expect-fx ")
.append(symbol_to_doc(alloc, *condition))
.append(symbol_to_doc(alloc, *condition, pretty))
.append(";")
.append(alloc.hardline())
.append(remainder.to_doc(alloc, interner)),
.append(remainder.to_doc(alloc, interner, pretty)),
Ret(symbol) => alloc
.text("ret ")
.append(symbol_to_doc(alloc, *symbol))
.append(symbol_to_doc(alloc, *symbol, pretty))
.append(";"),
Switch {
@ -2264,23 +2256,23 @@ impl<'a> Stmt<'a> {
let fail = default_branch.1;
alloc
.text("if ")
.append(symbol_to_doc(alloc, *cond_symbol))
.append(symbol_to_doc(alloc, *cond_symbol, pretty))
.append(" then")
.append(info.to_doc(alloc))
.append(info.to_doc(alloc, pretty))
.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.text("else"))
.append(default_branch.0.to_doc(alloc))
.append(default_branch.0.to_doc(alloc, pretty))
.append(alloc.hardline())
.append(fail.to_doc(alloc, interner).indent(4))
.append(fail.to_doc(alloc, interner, pretty).indent(4))
}
_ => {
let default_doc = alloc
.text("default:")
.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);
let branches_docs = branches
@ -2289,14 +2281,14 @@ impl<'a> Stmt<'a> {
alloc
.text(format!("case {}:", tag))
.append(alloc.hardline())
.append(expr.to_doc(alloc, interner).indent(4))
.append(expr.to_doc(alloc, interner, pretty).indent(4))
.indent(4)
})
.chain(std::iter::once(default_doc));
//
alloc
.text("switch ")
.append(symbol_to_doc(alloc, *cond_symbol))
.append(symbol_to_doc(alloc, *cond_symbol, pretty))
.append(":")
.append(alloc.hardline())
.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 {
id,
@ -2316,29 +2310,31 @@ impl<'a> Stmt<'a> {
body: continuation,
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(
vec![
alloc
.text("joinpoint ")
.append(join_point_to_doc(alloc, *id))
.append(join_point_to_doc(alloc, *id, pretty))
.append(" ".repeat(parameters.len().min(1)))
.append(alloc.intersperse(it, alloc.space()))
.append(":"),
continuation.to_doc(alloc, interner).indent(4),
continuation.to_doc(alloc, interner, pretty).indent(4),
alloc.text("in"),
remainder.to_doc(alloc, interner),
remainder.to_doc(alloc, interner, pretty),
],
alloc.hardline(),
)
}
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
.text("jump ")
.append(join_point_to_doc(alloc, *id))
.append(join_point_to_doc(alloc, *id, pretty))
.append(" ".repeat(arguments.len().min(1)))
.append(alloc.intersperse(it, alloc.space()))
.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
I: Interner<'a, Layout<'a>>,
{
let allocator = BoxAllocator;
let mut w = std::vec::Vec::new();
self.to_doc::<_, (), _>(&allocator, interner)
self.to_doc::<_, (), _>(&allocator, interner, pretty)
.1
.render(width, &mut w)
.unwrap();

View file

@ -1154,7 +1154,7 @@ struct SetElement<'a> {
impl std::fmt::Debug for SetElement<'_> {
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)
}
@ -1204,7 +1204,7 @@ impl std::fmt::Debug for LambdaSet<'_> {
///
/// See also https://github.com/roc-lang/roc/issues/3336.
#[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<'_> {
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.
//#[allow(clippy::ptr_arg)]
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_types::types::AliasKind;
use crate::Severity;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CycleEntry {
pub symbol: Symbol,
@ -40,6 +42,7 @@ pub enum Problem {
/// Second symbol is the name of the argument that is unused
UnusedArgument(Symbol, bool, Symbol, Region),
UnusedBranchDef(Symbol, Region),
DefsOnlyUsedInRecursion(usize, Region),
PrecedenceProblem(PrecedenceProblem),
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
UnsupportedPattern(BadPattern, Region),
@ -204,6 +207,71 @@ pub enum 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.
/// Some problems have more than one region; in those cases,
/// this tries to pick the one that's closest to the original
@ -333,7 +401,8 @@ impl Problem {
| Problem::BadTypeArguments { region, .. }
| Problem::UnnecessaryOutputWildcard { region }
| Problem::OverAppliedCrash { region }
| Problem::UnappliedCrash { region } => Some(*region),
| Problem::UnappliedCrash { region }
| Problem::DefsOnlyUsedInRecursion(_, region) => Some(*region),
Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries))
| Problem::BadRecursion(cycle_entries) => {
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.
#![allow(clippy::large_enum_variant)]
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::expr::PendingDerives;
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_module::symbol::Symbol;
use roc_region::all::{Loc, Region};
@ -309,12 +312,18 @@ impl ObligationCache {
Some(Err(NotDerivable {
var: failure_var,
context,
})) => Some(if failure_var == var {
UnderivableReason::SurfaceNotDerivable(context)
} else {
let error_type = subs.var_to_error_type(failure_var, Polarity::OF_VALUE);
UnderivableReason::NestedNotDerivable(error_type, context)
}),
})) => {
dbg_do!(ROC_PRINT_UNDERIVABLE, {
eprintln!("❌ derived {:?} of {:?}", ability, subs.dbg(failure_var));
});
Some(if failure_var == var {
UnderivableReason::SurfaceNotDerivable(context)
} else {
let error_type = subs.var_to_error_type(failure_var, Polarity::OF_VALUE);
UnderivableReason::NestedNotDerivable(error_type, context)
})
}
None => Some(UnderivableReason::NotABuiltin),
};
@ -497,18 +506,6 @@ trait DerivableVisitor {
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)]
fn visit_recursion(var: Variable) -> Result<Descend, NotDerivable> {
Err(NotDerivable {
@ -659,7 +656,12 @@ trait DerivableVisitor {
subs.set_content(var, Content::FlexAbleVar(opt_name, merged_abilites));
}
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 {
structure,

View file

@ -3473,7 +3473,7 @@ mod solve_expr {
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(
indoc!(
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 ->
when Dict.get cameFrom goal is
Err KeyNotFound ->
@ -3746,7 +3746,7 @@ mod solve_expr {
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 }
cheapestOpen : Model position -> Result position [KeyNotFound] | position has Eq
cheapestOpen : Model position -> Result position [KeyNotFound] | position has Hash & Eq
cheapestOpen = \model ->
folder = \resSmallestSoFar, position ->
@ -3796,14 +3796,14 @@ mod solve_expr {
Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder
|> Result.map (\x -> x.position)
astar : Model position -> Result position [KeyNotFound] | position has Eq
astar : Model position -> Result position [KeyNotFound] | position has Hash & Eq
astar = \model -> cheapestOpen model
main =
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
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 ->
when dict is
Node color key value left right ->
@ -4559,7 +4559,7 @@ mod solve_expr {
_ ->
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 ->
when dict is
Empty ->
@ -4647,7 +4647,7 @@ mod solve_expr {
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v | k has Eq
removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v | k has Hash & Eq
removeHelp = \targetKey, dict ->
when dict is
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
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 ->
when dict is
Node color key value left right ->
@ -8270,7 +8270,7 @@ mod solve_expr {
r#"
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 data ->
@ -8281,7 +8281,7 @@ mod solve_expr {
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"
);
}
#[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.
use roc_can::expected::{Expected, PExpected};
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_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)]
pub enum Unfulfilled {
/// 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 rigid_vars = Default::default();
let mut flex_vars = Default::default();
let (import_variables, abilities_store) = add_imports(
test_module,
&mut constraints,
@ -386,9 +387,15 @@ fn check_derived_typechecks_and_golden(
&exposed_for_module,
&mut def_types,
&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
dbg_do!(

View file

@ -465,7 +465,7 @@ fn eq_rosetree() {
}
#[test]
#[cfg(any(feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn eq_different_rosetrees() {
// Requires two different equality procedures for `List (Rose I64)` and `List (Rose Str)`
// 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")]
use crate::helpers::llvm::assert_evals_to;
@ -77,6 +80,7 @@ fn dict_nonempty_contains() {
}
#[test]
#[ignore = "TODO figure out why this is broken with llvm wasm tests"]
#[cfg(any(feature = "gen-llvm"))]
fn dict_empty_remove() {
assert_evals_to!(
@ -252,7 +256,11 @@ fn from_list_with_fold_reallocates() {
}
#[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() {
assert_evals_to!(
indoc!(
@ -384,7 +392,7 @@ fn insert_all() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn insert_all_prefer_first() {
fn insert_all_prefer_second() {
assert_evals_to!(
indoc!(
r#"
@ -396,7 +404,7 @@ fn insert_all_prefer_first() {
Dict.values myDict
"#
),
RocList::from_slice(&[100]),
RocList::from_slice(&[200]),
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]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
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]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
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")]
use crate::helpers::llvm::assert_evals_to;
@ -62,16 +65,6 @@ fn single_to_list() {
RocList::from_slice(&[1]),
RocList<i64>
);
assert_evals_to!(
indoc!(
r#"
Set.toList (Set.single 1.0)
"#
),
RocList::from_slice(&[1.0]),
RocList<f64>
);
}
#[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]
#[cfg(any(feature = "gen-llvm"))]
fn from_list_result() {
@ -280,3 +287,26 @@ fn from_list_result() {
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_gen_llvm::llvm::externs::add_default_roc_externs;
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_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
@ -87,7 +87,9 @@ fn create_llvm_module<'a>(
let mut loaded = match loaded {
Ok(x) => x,
Err(roc_load::LoadingProblem::FormattedReport(report)) => {
Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
report,
))) => {
println!("{}", report);
panic!();
}

View file

@ -1,7 +1,7 @@
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;
ret List.409;
ret List.478;
procedure Test.2 (Test.3):
let Test.7 : {} = Struct {};

View file

@ -1,7 +1,7 @@
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;
ret List.409;
ret List.478;
procedure Test.2 (Test.3):
let Test.7 : {} = Struct {};

View file

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

View file

@ -1,17 +1,87 @@
procedure Dict.1 ():
let Dict.318 : List {[], []} = Array [];
ret Dict.318;
let Dict.520 : List {[], []} = Array [];
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):
let Dict.317 : U64 = CallByName List.6 Dict.312;
ret Dict.317;
procedure Dict.34 ():
let Dict.526 : I8 = -128i64;
ret Dict.526;
procedure List.6 (#Attr.2):
let List.409 : U64 = lowlevel ListLen #Attr.2;
ret List.409;
procedure Dict.4 (Dict.507):
let Dict.85 : U64 = StructAtIndex 3 Dict.507;
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 ():
let Test.2 : List {[], []} = CallByName Dict.1;
let Test.1 : U64 = CallByName Dict.7 Test.2;
dec Test.2;
let Test.2 : {List {[], []}, List U64, List I8, U64} = CallByName Dict.1;
let Test.1 : U64 = CallByName Dict.4 Test.2;
ret Test.1;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,27 +1,27 @@
procedure List.3 (List.102, List.103, List.104):
let List.412 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104;
let List.411 : List I64 = StructAtIndex 0 List.412;
inc List.411;
dec List.412;
ret List.411;
procedure List.3 (List.103, List.104, List.105):
let List.481 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.480 : List I64 = StructAtIndex 0 List.481;
inc List.480;
dec List.481;
ret List.480;
procedure List.6 (#Attr.2):
let List.410 : U64 = lowlevel ListLen #Attr.2;
ret List.410;
let List.479 : U64 = lowlevel ListLen #Attr.2;
ret List.479;
procedure List.64 (List.99, List.100, List.101):
let List.417 : U64 = CallByName List.6 List.99;
let List.414 : Int1 = CallByName Num.22 List.100 List.417;
if List.414 then
let List.415 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101;
ret List.415;
procedure List.64 (List.100, List.101, List.102):
let List.486 : U64 = CallByName List.6 List.100;
let List.483 : Int1 = CallByName Num.22 List.101 List.486;
if List.483 then
let List.484 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.484;
else
let List.413 : {List I64, I64} = Struct {List.99, List.101};
ret List.413;
let List.482 : {List I64, I64} = Struct {List.100, List.102};
ret List.482;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.416 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.416;
let List.485 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.485;
procedure Num.19 (#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):
let List.415 : U64 = CallByName List.6 List.94;
let List.411 : Int1 = CallByName Num.22 List.95 List.415;
if List.411 then
let List.413 : I64 = CallByName List.66 List.94 List.95;
let List.412 : [C {}, C I64] = TagId(1) List.413;
ret List.412;
procedure List.2 (List.95, List.96):
let List.484 : U64 = CallByName List.6 List.95;
let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.480 then
let List.482 : I64 = CallByName List.66 List.95 List.96;
let List.481 : [C {}, C I64] = TagId(1) List.482;
ret List.481;
else
let List.410 : {} = Struct {};
let List.409 : [C {}, C I64] = TagId(0) List.410;
ret List.409;
let List.479 : {} = Struct {};
let List.478 : [C {}, C I64] = TagId(0) List.479;
ret List.478;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.66 (#Attr.2, #Attr.3):
let List.414 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414;
let List.483 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.483;
procedure Num.22 (#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):
let List.409 : U64 = lowlevel ListLen #Attr.2;
ret List.409;
let List.478 : U64 = lowlevel ListLen #Attr.2;
ret List.478;
procedure List.6 (#Attr.2):
let List.410 : U64 = lowlevel ListLen #Attr.2;
ret List.410;
let List.479 : U64 = lowlevel ListLen #Attr.2;
ret List.479;
procedure Num.19 (#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):
let List.415 : U64 = CallByName List.6 List.94;
let List.411 : Int1 = CallByName Num.22 List.95 List.415;
if List.411 then
let List.413 : Str = CallByName List.66 List.94 List.95;
let List.412 : [C {}, C Str] = TagId(1) List.413;
ret List.412;
procedure List.2 (List.95, List.96):
let List.484 : U64 = CallByName List.6 List.95;
let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.480 then
let List.482 : Str = CallByName List.66 List.95 List.96;
let List.481 : [C {}, C Str] = TagId(1) List.482;
ret List.481;
else
let List.410 : {} = Struct {};
let List.409 : [C {}, C Str] = TagId(0) List.410;
ret List.409;
let List.479 : {} = Struct {};
let List.478 : [C {}, C Str] = TagId(0) List.479;
ret List.478;
procedure List.5 (#Attr.2, #Attr.3):
let List.417 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.417;
let List.486 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.486;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.66 (#Attr.2, #Attr.3):
let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414;
let List.483 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.483;
procedure Num.22 (#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):
let List.415 : U64 = CallByName List.6 List.94;
let List.411 : Int1 = CallByName Num.22 List.95 List.415;
if List.411 then
let List.413 : Str = CallByName List.66 List.94 List.95;
let List.412 : [C {}, C Str] = TagId(1) List.413;
ret List.412;
procedure List.2 (List.95, List.96):
let List.484 : U64 = CallByName List.6 List.95;
let List.480 : Int1 = CallByName Num.22 List.96 List.484;
if List.480 then
let List.482 : Str = CallByName List.66 List.95 List.96;
let List.481 : [C {}, C Str] = TagId(1) List.482;
ret List.481;
else
let List.410 : {} = Struct {};
let List.409 : [C {}, C Str] = TagId(0) List.410;
ret List.409;
let List.479 : {} = Struct {};
let List.478 : [C {}, C Str] = TagId(0) List.479;
ret List.478;
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;
ret List.417;
ret List.486;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.66 (#Attr.2, #Attr.3):
let List.414 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.414;
let List.483 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.483;
procedure Num.22 (#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):
let List.410 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104;
let List.409 : List I64 = StructAtIndex 0 List.410;
inc List.409;
dec List.410;
ret List.409;
procedure List.3 (List.103, List.104, List.105):
let List.479 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.478 : List I64 = StructAtIndex 0 List.479;
inc List.478;
dec List.479;
ret List.478;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.64 (List.99, List.100, List.101):
let List.415 : U64 = CallByName List.6 List.99;
let List.412 : Int1 = CallByName Num.22 List.100 List.415;
if List.412 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101;
ret List.413;
procedure List.64 (List.100, List.101, List.102):
let List.484 : U64 = CallByName List.6 List.100;
let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.481 then
let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.482;
else
let List.411 : {List I64, I64} = Struct {List.99, List.101};
ret List.411;
let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.480;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414;
let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.483;
procedure Num.22 (#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):
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;
if #Derived_gen.0 then
ret List.411;
ret List.480;
else
decref #Attr.2;
ret List.411;
ret List.480;
procedure List.59 (List.237):
let List.410 : {} = Struct {};
let List.409 : List I64 = CallByName List.28 List.237 List.410;
ret List.409;
procedure List.59 (List.278):
let List.479 : {} = Struct {};
let List.478 : List I64 = CallByName List.28 List.278 List.479;
ret List.478;
procedure Num.46 (#Attr.2, #Attr.3):
let Num.256 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;

View file

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

View file

@ -1,43 +1,43 @@
procedure List.2 (List.94, List.95):
let List.431 : U64 = CallByName List.6 List.94;
let List.428 : Int1 = CallByName Num.22 List.95 List.431;
if List.428 then
let List.430 : I64 = CallByName List.66 List.94 List.95;
let List.429 : [C {}, C I64] = TagId(1) List.430;
ret List.429;
procedure List.2 (List.95, List.96):
let List.500 : U64 = CallByName List.6 List.95;
let List.497 : Int1 = CallByName Num.22 List.96 List.500;
if List.497 then
let List.499 : I64 = CallByName List.66 List.95 List.96;
let List.498 : [C {}, C I64] = TagId(1) List.499;
ret List.498;
else
let List.427 : {} = Struct {};
let List.426 : [C {}, C I64] = TagId(0) List.427;
ret List.426;
let List.496 : {} = Struct {};
let List.495 : [C {}, C I64] = TagId(0) List.496;
ret List.495;
procedure List.3 (List.102, List.103, List.104):
let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104;
let List.417 : List I64 = StructAtIndex 0 List.418;
inc List.417;
dec List.418;
ret List.417;
procedure List.3 (List.103, List.104, List.105):
let List.487 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.486 : List I64 = StructAtIndex 0 List.487;
inc List.486;
dec List.487;
ret List.486;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.64 (List.99, List.100, List.101):
let List.415 : U64 = CallByName List.6 List.99;
let List.412 : Int1 = CallByName Num.22 List.100 List.415;
if List.412 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101;
ret List.413;
procedure List.64 (List.100, List.101, List.102):
let List.484 : U64 = CallByName List.6 List.100;
let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.481 then
let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.482;
else
let List.411 : {List I64, I64} = Struct {List.99, List.101};
ret List.411;
let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.480;
procedure List.66 (#Attr.2, #Attr.3):
let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.424;
let List.493 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.493;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414;
let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.483;
procedure Num.22 (#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):
let List.431 : U64 = CallByName List.6 List.94;
let List.428 : Int1 = CallByName Num.22 List.95 List.431;
if List.428 then
let List.430 : I64 = CallByName List.66 List.94 List.95;
let List.429 : [C {}, C I64] = TagId(1) List.430;
ret List.429;
procedure List.2 (List.95, List.96):
let List.500 : U64 = CallByName List.6 List.95;
let List.497 : Int1 = CallByName Num.22 List.96 List.500;
if List.497 then
let List.499 : I64 = CallByName List.66 List.95 List.96;
let List.498 : [C {}, C I64] = TagId(1) List.499;
ret List.498;
else
let List.427 : {} = Struct {};
let List.426 : [C {}, C I64] = TagId(0) List.427;
ret List.426;
let List.496 : {} = Struct {};
let List.495 : [C {}, C I64] = TagId(0) List.496;
ret List.495;
procedure List.3 (List.102, List.103, List.104):
let List.418 : {List I64, I64} = CallByName List.64 List.102 List.103 List.104;
let List.417 : List I64 = StructAtIndex 0 List.418;
inc List.417;
dec List.418;
ret List.417;
procedure List.3 (List.103, List.104, List.105):
let List.487 : {List I64, I64} = CallByName List.64 List.103 List.104 List.105;
let List.486 : List I64 = StructAtIndex 0 List.487;
inc List.486;
dec List.487;
ret List.486;
procedure List.6 (#Attr.2):
let List.416 : U64 = lowlevel ListLen #Attr.2;
ret List.416;
let List.485 : U64 = lowlevel ListLen #Attr.2;
ret List.485;
procedure List.64 (List.99, List.100, List.101):
let List.415 : U64 = CallByName List.6 List.99;
let List.412 : Int1 = CallByName Num.22 List.100 List.415;
if List.412 then
let List.413 : {List I64, I64} = CallByName List.67 List.99 List.100 List.101;
ret List.413;
procedure List.64 (List.100, List.101, List.102):
let List.484 : U64 = CallByName List.6 List.100;
let List.481 : Int1 = CallByName Num.22 List.101 List.484;
if List.481 then
let List.482 : {List I64, I64} = CallByName List.67 List.100 List.101 List.102;
ret List.482;
else
let List.411 : {List I64, I64} = Struct {List.99, List.101};
ret List.411;
let List.480 : {List I64, I64} = Struct {List.100, List.102};
ret List.480;
procedure List.66 (#Attr.2, #Attr.3):
let List.424 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.424;
let List.493 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.493;
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
let List.414 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.414;
let List.483 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.483;
procedure Num.22 (#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)]
const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
use bumpalo::Bump;
use roc_collections::all::MutMap;
use roc_load::ExecutionMode;
use roc_load::LoadConfig;
use roc_load::LoadMonomorphizedError;
use roc_load::Threading;
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol;
use roc_mono::ir::Proc;
use roc_mono::ir::ProcLayout;
@ -73,11 +76,16 @@ fn promote_expr_to_module(src: &str) -> String {
buffer
}
fn compiles_to_ir(test_name: &str, src: &str) {
use bumpalo::Bump;
fn compiles_to_ir(test_name: &str, src: &str, mode: &str, no_check: bool) {
use roc_packaging::cache::RocCacheDir;
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 filename = PathBuf::from("Test.roc");
@ -85,7 +93,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
let module_src;
let temp;
if src.starts_with("app") {
if src.starts_with("app") || src.starts_with("interface") {
// this is already a module
module_src = src;
} else {
@ -99,7 +107,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
threading: Threading::Single,
render: roc_reporting::report::RenderTarget::Generic,
palette: roc_reporting::report::DEFAULT_PALETTE,
exec_mode: ExecutionMode::Executable,
exec_mode,
};
let loaded = roc_load::load_and_monomorphize_from_str(
arena,
@ -113,7 +121,9 @@ fn compiles_to_ir(test_name: &str, src: &str) {
let mut loaded = match loaded {
Ok(x) => x,
Err(roc_load::LoadingProblem::FormattedReport(report)) => {
Err(LoadMonomorphizedError::LoadingProblem(roc_load::LoadingProblem::FormattedReport(
report,
))) => {
println!("{}", report);
panic!();
}
@ -126,6 +136,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
procedures,
exposed_to_host,
layout_interner,
interns,
..
} = loaded;
@ -138,33 +149,54 @@ fn compiles_to_ir(test_name: &str, src: &str) {
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);
}
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>(
test_name: &str,
interner: STLayoutInterner<'a>,
procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
main_fn_symbol: Symbol,
opt_main_fn_symbol: Option<Symbol>,
) {
let index = procedures
.keys()
.position(|(s, _)| *s == main_fn_symbol)
.unwrap();
let mut procs_string = procedures
.values()
.map(|proc| proc.to_pretty(&interner, 200))
.map(|proc| proc.to_pretty(&interner, 200, false))
.collect::<Vec<_>>();
let main_fn = procs_string.swap_remove(index);
let opt_main_fn = opt_main_fn_symbol.map(|main_fn_symbol| {
let index = procedures
.keys()
.position(|(s, _)| *s == main_fn_symbol)
.unwrap();
procs_string.swap_remove(index)
});
procs_string.sort();
procs_string.push(main_fn);
if let Some(main_fn) = opt_main_fn {
procs_string.push(main_fn);
}
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() {
// do we still need with_larger_debug_stack?
r#"
@ -1279,7 +1311,7 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
)
}
#[mono_test]
#[mono_test(no_check)]
fn issue_2810() {
indoc!(
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;
#[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 args = task_fn.sig.inputs.clone();
@ -21,7 +40,7 @@ pub fn mono_test(_args: TokenStream, item: TokenStream) -> TokenStream {
#[test]
#(#attributes)*
#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 {
FlexVar(opt_name) => FlexVar(*opt_name),
RigidVar(name) => RigidVar(*name),
FlexAbleVar(opt_name, ability) => FlexAbleVar(*opt_name, *ability),
RigidAbleVar(name, ability) => RigidAbleVar(*name, *ability),
FlexAbleVar(opt_name, abilities) => {
FlexAbleVar(*opt_name, Self::offset_ability_slice(offsets, *abilities))
}
RigidAbleVar(name, abilities) => {
RigidAbleVar(*name, Self::offset_ability_slice(offsets, *abilities))
}
RecursionVar {
structure,
opt_name,
@ -4387,6 +4391,15 @@ impl StorageSubs {
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(
offsets: &StorageSubsOffsets,
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!()
}

View file

@ -18,8 +18,8 @@ pub fn create_dylib_elf64(custom_names: &[String]) -> object::read::Result<Vec<u
writer.reserve_shstrtab_section_index(),
];
// we need this later, but must allocate it here
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.
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();
const PLACEHOLDER: u64 = 0xAAAAAAAAAAAAAAAA;
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 {
p_type: object::elf::PT_LOAD,
p_flags: object::elf::PF_R | object::elf::PF_W,
p_offset: 0,
p_vaddr: 0,
p_paddr: 0,
p_filesz: 0,
p_memsz: 0,
p_offset: PLACEHOLDER,
p_vaddr: PLACEHOLDER,
p_paddr: PLACEHOLDER,
p_filesz: PLACEHOLDER,
p_memsz: PLACEHOLDER,
p_align: 1 << 12,
},
object::write::elf::ProgramHeader {
p_type: object::elf::PT_DYNAMIC,
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_vaddr: 0,
p_paddr: 0,
p_filesz: 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();
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
let dynamic_address = next_multiple_of(writer.reserved_len(), 8);
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
for program_header in program_headers.iter_mut() {
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_vaddr = 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_mono = {path = "../compiler/mono"}
roc_parse = {path = "../compiler/parse"}
roc_problem = {path = "../compiler/problem"}
roc_region = {path = "../compiler/region"}
roc_packaging = {path = "../packaging"}
roc_reporting = {path = "../reporting"}

View file

@ -1,7 +1,8 @@
use bumpalo::Bump;
use roc_load::{ExecutionMode, LoadConfig, Threading};
use roc_load::{ExecutionMode, LoadConfig, LoadMonomorphizedError, Threading};
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 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 {
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 (
None,
Problems {

View file

@ -87,7 +87,7 @@ mod test {
use indoc::indoc;
use pretty_assertions::assert_eq;
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_reporting::report::{RenderTarget, DEFAULT_PALETTE};
use target_lexicon::Triple;
@ -119,7 +119,7 @@ mod test {
threading: Threading::Single,
exec_mode: ExecutionMode::Test,
};
let loaded = roc_load::load_and_monomorphize_from_str(
let loaded = match roc_load::load_and_monomorphize_from_str(
arena,
filename,
source,
@ -127,8 +127,13 @@ mod test {
Default::default(),
RocCacheDir::Disallowed,
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 expectations = std::mem::take(&mut loaded.expectations);
@ -442,8 +447,8 @@ mod test {
main = 0
expect
vec1 = { x: 1.0f64, y: 2.0f64 }
vec2 = { x: 4.0f64, y: 8.0f64 }
vec1 = { x: 1u8, y: 2u8 }
vec2 = { x: 4u8, y: 8u8 }
vec1 == vec2
"#
@ -453,17 +458,17 @@ mod test {
This expectation failed:
5> expect
6> vec1 = { x: 1.0f64, y: 2.0f64 }
7> vec2 = { x: 4.0f64, y: 8.0f64 }
6> vec1 = { x: 1u8, y: 2u8 }
7> vec2 = { x: 4u8, y: 8u8 }
8>
9> vec1 == vec2
When it failed, these variables had these values:
vec1 : { x : F64, y : F64 }
vec1 : { x : U8, y : U8 }
vec1 = { x: 1, y: 2 }
vec2 : { x : F64, y : F64 }
vec2 : { x : U8, y : U8 }
vec2 = { x: 4, y: 8 }
"#
),

View file

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

View file

@ -29,9 +29,8 @@ pub fn report_problems(
can_problems: &mut MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
type_problems: &mut MutMap<ModuleId, Vec<TypeError>>,
) -> Problems {
use crate::report::{
can_problem, type_problem, Report, RocDocAllocator, Severity::*, DEFAULT_PALETTE,
};
use crate::report::{can_problem, type_problem, Report, RocDocAllocator, DEFAULT_PALETTE};
use roc_problem::Severity::*;
let palette = DEFAULT_PALETTE;
// 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,
ShadowKind,
};
use roc_problem::Severity;
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
use roc_types::types::AliasKind;
use std::path::PathBuf;
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;
const SYNTAX_PROBLEM: &str = "SYNTAX PROBLEM";
@ -67,7 +68,7 @@ pub fn can_problem<'b>(
) -> Report<'b> {
let doc;
let title;
let severity;
let severity = problem.severity();
match problem {
Problem::UnusedDef(symbol, region) => {
@ -86,7 +87,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_DEF.to_string();
severity = Severity::Warning;
}
Problem::UnusedImport(symbol, region) => {
doc = alloc.stack([
@ -103,7 +103,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_IMPORT.to_string();
severity = Severity::Warning;
}
Problem::UnusedModuleImport(module_id, region) => {
doc = alloc.stack([
@ -121,7 +120,32 @@ pub fn can_problem<'b>(
]);
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) => {
doc = alloc.stack([
@ -137,7 +161,6 @@ pub fn can_problem<'b>(
]);
title = MISSING_DEFINITION.to_string();
severity = Severity::RuntimeError;
}
Problem::UnknownGeneratesWith(loc_ident) => {
doc = alloc.stack([
@ -152,7 +175,6 @@ pub fn can_problem<'b>(
]);
title = UNKNOWN_GENERATES_WITH.to_string();
severity = Severity::RuntimeError;
}
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.";
@ -187,7 +209,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_ARG.to_string();
severity = Severity::Warning;
}
Problem::UnusedBranchDef(symbol, region) => {
doc = alloc.stack([
@ -208,7 +229,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_DEF.to_string();
severity = Severity::Warning;
}
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
doc = alloc.stack([
@ -237,7 +257,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::UnsupportedPattern(BadPattern::Unsupported(pattern_type), region) => {
use roc_parse::pattern::PatternType::*;
@ -268,7 +287,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::Shadowing {
original_region,
@ -280,7 +298,6 @@ pub fn can_problem<'b>(
doc = res_doc;
title = res_title.to_string();
severity = Severity::RuntimeError;
}
Problem::CyclicAlias(symbol, region, others, alias_kind) => {
let answer = crate::error::r#type::cyclic_alias(
@ -289,7 +306,6 @@ pub fn can_problem<'b>(
doc = answer.0;
title = answer.1;
severity = Severity::RuntimeError;
}
Problem::PhantomTypeArgument {
typ: alias,
@ -317,7 +333,6 @@ pub fn can_problem<'b>(
]);
title = UNUSED_ALIAS_PARAM.to_string();
severity = Severity::RuntimeError;
}
Problem::UnboundTypeVariable {
typ: alias,
@ -354,12 +369,10 @@ pub fn can_problem<'b>(
doc = alloc.stack(stack);
title = UNBOUND_TYPE_VARIABLE.to_string();
severity = Severity::RuntimeError;
}
Problem::BadRecursion(entries) => {
doc = to_circular_def_doc(alloc, lines, &entries);
title = CIRCULAR_DEF.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateRecordFieldValue {
field_name,
@ -394,7 +407,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::InvalidOptionalValue {
field_name,
@ -443,7 +455,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_FIELD_NAME.to_string();
severity = Severity::Warning;
}
Problem::DuplicateTag {
tag_name,
@ -478,7 +489,6 @@ pub fn can_problem<'b>(
]);
title = DUPLICATE_TAG_NAME.to_string();
severity = Severity::Warning;
}
Problem::SignatureDefMismatch {
ref annotation_pattern,
@ -495,7 +505,6 @@ pub fn can_problem<'b>(
]);
title = NAMING_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidAliasRigid {
alias_name: type_name,
@ -518,7 +527,6 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidHexadecimal(region) => {
doc = alloc.stack([
@ -535,7 +543,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidUnicodeCodePt(region) => {
doc = alloc.stack([
@ -545,7 +552,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_UNICODE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidInterpolation(region) => {
doc = alloc.stack([
@ -562,14 +568,12 @@ pub fn can_problem<'b>(
]);
title = SYNTAX_PROBLEM.to_string();
severity = Severity::RuntimeError;
}
Problem::RuntimeError(runtime_error) => {
let answer = pretty_runtime_error(alloc, lines, runtime_error);
doc = answer.0;
title = answer.1.to_string();
severity = Severity::RuntimeError;
}
Problem::NestedDatatype {
alias,
@ -597,7 +601,6 @@ pub fn can_problem<'b>(
]);
title = NESTED_DATATYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::InvalidExtensionType { region, kind } => {
@ -625,7 +628,6 @@ pub fn can_problem<'b>(
]);
title = INVALID_EXTENSION_TYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityHasTypeVariables {
@ -644,7 +646,6 @@ pub fn can_problem<'b>(
),
]);
title = ABILITY_HAS_TYPE_VARIABLES.to_string();
severity = Severity::RuntimeError;
}
Problem::HasClauseIsNotAbility {
@ -655,7 +656,6 @@ pub fn can_problem<'b>(
alloc.region(lines.convert_region(clause_region)),
]);
title = HAS_CLAUSE_IS_NOT_AN_ABILITY.to_string();
severity = Severity::RuntimeError;
}
Problem::IllegalHasClause { region } => {
@ -674,7 +674,6 @@ pub fn can_problem<'b>(
]),
]);
title = ILLEGAL_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateHasAbility { ability, region } => {
@ -692,7 +691,6 @@ pub fn can_problem<'b>(
]),
]);
title = "DUPLICATE BOUND ABILITY".to_string();
severity = Severity::Warning;
}
Problem::AbilityMemberMissingHasClause {
@ -727,7 +725,6 @@ pub fn can_problem<'b>(
.reflow("Otherwise, the function does not need to be part of the ability!")]),
]);
title = ABILITY_MEMBER_MISSING_HAS_CLAUSE.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityMemberMultipleBoundVars {
@ -755,7 +752,6 @@ pub fn can_problem<'b>(
])
]);
title = ABILITY_MEMBER_BINDS_MULTIPLE_VARIABLES.to_string();
severity = Severity::RuntimeError;
}
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."),
]);
title = ABILITY_NOT_ON_TOPLEVEL.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityUsedAsType(suggested_var_name, ability, region) => {
@ -795,7 +790,6 @@ pub fn can_problem<'b>(
])),
]);
title = ABILITY_USED_AS_TYPE.to_string();
severity = Severity::RuntimeError;
}
Problem::NestedSpecialization(member, region) => {
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."),
]);
title = SPECIALIZATION_NOT_ON_TOPLEVEL.to_string();
severity = Severity::Warning;
}
Problem::IllegalDerivedAbility(region) => {
doc = alloc.stack([
@ -820,7 +813,6 @@ pub fn can_problem<'b>(
.append(list_builtin_abilities(alloc)),
]);
title = ILLEGAL_DERIVE.to_string();
severity = Severity::Warning;
}
Problem::NotAnAbility(region) => {
doc = alloc.stack([
@ -829,7 +821,6 @@ pub fn can_problem<'b>(
alloc.reflow("Only abilities can be implemented."),
]);
title = NOT_AN_ABILITY.to_string();
severity = Severity::Warning;
}
Problem::NotAnAbilityMember {
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.")
]);
title = NOT_AN_ABILITY_MEMBER.to_string();
severity = Severity::RuntimeError;
}
Problem::ImplementationNotFound { member, region } => {
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))]))
]);
title = IMPLEMENTATION_NOT_FOUND.to_string();
severity = Severity::RuntimeError;
}
Problem::OptionalAbilityImpl { ability, region } => {
let hint = if ability.is_builtin() {
@ -875,7 +864,6 @@ pub fn can_problem<'b>(
hint,
]);
title = OPTIONAL_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::QualifiedAbilityImpl { region } => {
doc = alloc.stack([
@ -886,7 +874,6 @@ pub fn can_problem<'b>(
),
]);
title = QUALIFIED_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::AbilityImplNotIdent { region } => {
doc = alloc.stack([
@ -898,7 +885,6 @@ pub fn can_problem<'b>(
alloc.tip().append(alloc.reflow("consider defining this expression as a variable."))
]);
title = ABILITY_IMPLEMENTATION_NOT_IDENTIFIER.to_string();
severity = Severity::RuntimeError;
}
Problem::DuplicateImpl {
original,
@ -913,7 +899,6 @@ pub fn can_problem<'b>(
.reflow("Only one custom implementation can be defined for an ability member."),
]);
title = DUPLICATE_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::ImplementsNonRequired {
region,
@ -938,7 +923,6 @@ pub fn can_problem<'b>(
),
]);
title = UNNECESSARY_IMPLEMENTATIONS.to_string();
severity = Severity::Warning;
}
Problem::DoesNotImplementAbility {
region,
@ -963,7 +947,6 @@ pub fn can_problem<'b>(
),
]);
title = INCOMPLETE_ABILITY_IMPLEMENTATION.to_string();
severity = Severity::RuntimeError;
}
Problem::NotBoundInAllPatterns {
unbound_symbol,
@ -984,7 +967,6 @@ pub fn can_problem<'b>(
]),
]);
title = "NAME NOT BOUND IN ALL PATTERNS".to_string();
severity = Severity::RuntimeError;
}
Problem::NoIdentifiersIntroduced(region) => {
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!"),
]);
title = "UNNECESSARY DEFINITION".to_string();
severity = Severity::Warning;
}
Problem::OverloadedSpecialization {
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!"),
]);
title = "OVERLOADED SPECIALIZATION".to_string();
severity = Severity::Warning;
}
Problem::UnnecessaryOutputWildcard { region } => {
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."),
]);
title = "UNNECESSARY WILDCARD".to_string();
severity = Severity::Warning;
}
Problem::MultipleListRestPattern { region } => {
doc = alloc.stack([
@ -1046,7 +1025,6 @@ pub fn can_problem<'b>(
]),
]);
title = "MULTIPLE LIST REST PATTERNS".to_string();
severity = Severity::RuntimeError;
}
Problem::BadTypeArguments {
symbol,
@ -1086,7 +1064,6 @@ pub fn can_problem<'b>(
} else {
"TOO FEW TYPE ARGUMENTS".to_string()
};
severity = Severity::RuntimeError;
}
Problem::UnappliedCrash { region } => {
doc = alloc.stack([
@ -1100,7 +1077,6 @@ pub fn can_problem<'b>(
])
]);
title = "UNAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
}
Problem::OverAppliedCrash { region } => {
doc = alloc.stack([
@ -1116,7 +1092,6 @@ pub fn can_problem<'b>(
]),
]);
title = "OVERAPPLIED CRASH".to_string();
severity = Severity::RuntimeError;
}
};

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