Merge branch 'refactor-platform-info' into packages

This commit is contained in:
Richard Feldman 2022-12-11 03:42:24 -05:00
commit 3227f95d45
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
137 changed files with 6178 additions and 2855 deletions

View file

@ -10,11 +10,12 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_mono::ir::{
Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal,
ModifyRc, OptLevel, Proc, Stmt,
Call, CallType, EntryPoint, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement,
Literal, ModifyRc, OptLevel, Proc, ProcLayout, SingleEntryPoint, Stmt,
};
use roc_mono::layout::{
Builtin, CapturesNiche, Layout, RawFunctionLayout, STLayoutInterner, UnionLayout,
Builtin, CapturesNiche, FieldOrderHash, Layout, RawFunctionLayout, STLayoutInterner,
UnionLayout,
};
// just using one module for now
@ -136,7 +137,7 @@ pub fn spec_program<'a, I>(
arena: &'a Bump,
interner: &STLayoutInterner<'a>,
opt_level: OptLevel,
opt_entry_point: Option<roc_mono::ir::EntryPoint<'a>>,
entry_point: roc_mono::ir::EntryPoint<'a>,
procs: I,
) -> Result<morphic_lib::Solutions>
where
@ -226,30 +227,70 @@ where
m.add_func(func_name, spec)?;
}
if let Some(entry_point) = opt_entry_point {
// the entry point wrapper
let roc_main_bytes = func_name_bytes_help(
entry_point.symbol,
entry_point.layout.arguments.iter().copied(),
CapturesNiche::no_niche(),
&entry_point.layout.result,
);
let roc_main = FuncName(&roc_main_bytes);
match entry_point {
EntryPoint::Single(SingleEntryPoint {
symbol: entry_point_symbol,
layout: entry_point_layout,
}) => {
// the entry point wrapper
let roc_main_bytes = func_name_bytes_help(
entry_point_symbol,
entry_point_layout.arguments.iter().copied(),
CapturesNiche::no_niche(),
&entry_point_layout.result,
);
let roc_main = FuncName(&roc_main_bytes);
let mut env = Env::new(arena);
let mut env = Env::new(arena);
let entry_point_function = build_entry_point(
&mut env,
interner,
entry_point.layout,
roc_main,
&host_exposed_functions,
)?;
let entry_point_function = build_entry_point(
&mut env,
interner,
entry_point_layout,
Some(roc_main),
&host_exposed_functions,
)?;
type_definitions.extend(env.type_names);
type_definitions.extend(env.type_names);
let entry_point_name = FuncName(ENTRY_POINT_NAME);
m.add_func(entry_point_name, entry_point_function)?;
let entry_point_name = FuncName(ENTRY_POINT_NAME);
m.add_func(entry_point_name, entry_point_function)?;
}
EntryPoint::Expects { symbols } => {
// construct a big pattern match picking one of the expects at random
let layout: ProcLayout<'a> = ProcLayout {
arguments: &[],
result: Layout::Struct {
field_order_hash: FieldOrderHash::from_ordered_fields(&[]),
field_layouts: &[],
},
captures_niche: CapturesNiche::no_niche(),
};
let host_exposed: Vec<_> = symbols
.iter()
.map(|symbol| {
(
func_name_bytes_help(
*symbol,
[],
CapturesNiche::no_niche(),
&layout.result,
),
[].as_slice(),
)
})
.collect();
let mut env = Env::new(arena);
let entry_point_function =
build_entry_point(&mut env, interner, layout, None, &host_exposed)?;
type_definitions.extend(env.type_names);
let entry_point_name = FuncName(ENTRY_POINT_NAME);
m.add_func(entry_point_name, entry_point_function)?;
}
}
for union_layout in type_definitions {
@ -286,10 +327,11 @@ where
let mut p = ProgramBuilder::new();
p.add_mod(MOD_APP, main_module)?;
if opt_entry_point.is_some() {
let entry_point_name = FuncName(ENTRY_POINT_NAME);
p.add_entry_point(EntryPointName(ENTRY_POINT_NAME), MOD_APP, entry_point_name)?;
}
p.add_entry_point(
EntryPointName(ENTRY_POINT_NAME),
MOD_APP,
FuncName(ENTRY_POINT_NAME),
)?;
p.build()?
};
@ -324,7 +366,7 @@ fn build_entry_point<'a>(
env: &mut Env<'a>,
interner: &STLayoutInterner<'a>,
layout: roc_mono::ir::ProcLayout<'a>,
func_name: FuncName,
entry_point_function: Option<FuncName>,
host_exposed_functions: &[([u8; SIZE], &'a [Layout<'a>])],
) -> Result<FuncDef> {
let mut builder = FuncDefBuilder::new();
@ -332,7 +374,7 @@ fn build_entry_point<'a>(
let mut cases = Vec::new();
{
if let Some(entry_point_function) = entry_point_function {
let block = builder.add_block();
// to the modelling language, the arguments appear out of thin air
@ -352,7 +394,7 @@ fn build_entry_point<'a>(
let name_bytes = [0; 16];
let spec_var = CalleeSpecVar(&name_bytes);
let result = builder.add_call(block, spec_var, MOD_APP, func_name, argument)?;
let result = builder.add_call(block, spec_var, MOD_APP, entry_point_function, argument)?;
// to the modelling language, the result disappears into the void
let unit_type = builder.add_tuple_type(&[])?;
@ -365,7 +407,7 @@ fn build_entry_point<'a>(
for (name_bytes, layouts) in host_exposed_functions {
let host_exposed_func_name = FuncName(name_bytes);
if host_exposed_func_name == func_name {
if Some(host_exposed_func_name) == entry_point_function {
continue;
}
@ -392,7 +434,11 @@ fn build_entry_point<'a>(
}
let unit_type = builder.add_tuple_type(&[])?;
let unit_value = builder.add_choice(outer_block, &cases)?;
let unit_value = if cases.is_empty() {
builder.add_make_tuple(outer_block, &[])?
} else {
builder.add_choice(outer_block, &cases)?
};
let root = BlockExpr(outer_block, unit_value);
let spec = builder.build(unit_type, unit_type, root)?;

View file

@ -3,7 +3,7 @@ use roc_error_macros::internal_error;
use roc_gen_llvm::llvm::build::{module_from_builtins, LlvmBackendMode};
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
use roc_load::{EntryPoint, ExpectMetadata, LoadedModule, MonomorphizedModule};
use roc_mono::ir::OptLevel;
use roc_mono::ir::{OptLevel, SingleEntryPoint};
use roc_reporting::cli::{report_problems, Problems};
use std::ops::Deref;
use std::path::{Path, PathBuf};
@ -188,18 +188,25 @@ fn gen_from_mono_module_llvm<'a>(
// expects that would confuse the surgical linker
add_default_roc_externs(&env);
let opt_entry_point = match loaded.entry_point {
EntryPoint::Executable { symbol, layout, .. } => {
Some(roc_mono::ir::EntryPoint { symbol, layout })
let entry_point = match loaded.entry_point {
EntryPoint::Executable {
exposed_to_host,
platform_path: _,
} => {
// TODO support multiple of these!
debug_assert_eq!(exposed_to_host.len(), 1);
let (symbol, layout) = exposed_to_host[0];
roc_mono::ir::EntryPoint::Single(SingleEntryPoint { symbol, layout })
}
EntryPoint::Test => None,
EntryPoint::Test => roc_mono::ir::EntryPoint::Expects { symbols: &[] },
};
roc_gen_llvm::llvm::build::build_procedures(
&env,
opt_level,
loaded.procedures,
opt_entry_point,
entry_point,
Some(&app_ll_file),
);

View file

@ -1,8 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");
const SIGUSR1: c_int = if (builtin.os.tag.isDarwin()) 30 else 10;
const SIGUSR2: c_int = if (builtin.os.tag.isDarwin()) 31 else 12;
const Atomic = std.atomic.Atomic;
const O_RDWR: c_int = 2;
const O_CREAT: c_int = 64;
@ -51,7 +50,6 @@ pub fn expectFailedStartSharedFile() callconv(.C) [*]u8 {
}
}
extern fn roc_send_signal(pid: c_int, sig: c_int) c_int;
extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
extern fn roc_getppid() c_int;
@ -81,18 +79,24 @@ pub fn readSharedBufferEnv() callconv(.C) void {
}
}
pub fn expectFailedFinalize() callconv(.C) void {
pub fn notifyParent(shared_buffer: [*]u8, tag: u32) callconv(.C) void {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
const parent_pid = roc_getppid();
const usize_ptr = @ptrCast([*]u32, @alignCast(@alignOf(usize), shared_buffer));
const atomic_ptr = @ptrCast(*Atomic(u32), &usize_ptr[5]);
atomic_ptr.storeUnchecked(tag);
_ = roc_send_signal(parent_pid, SIGUSR1);
// wait till the parent is done before proceeding
const Ordering = std.atomic.Ordering;
while (atomic_ptr.load(Ordering.Acquire) != 0) {
std.atomic.spinLoopHint();
}
}
}
pub fn sendDbg() callconv(.C) void {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
const parent_pid = roc_getppid();
_ = roc_send_signal(parent_pid, SIGUSR2);
}
pub fn notifyParentExpect(shared_buffer: [*]u8) callconv(.C) void {
notifyParent(shared_buffer, 1);
}
pub fn notifyParentDbg(shared_buffer: [*]u8) callconv(.C) void {
notifyParent(shared_buffer, 2);
}

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

@ -172,8 +172,8 @@ comptime {
if (builtin.target.cpu.arch != .wasm32) {
exportUtilsFn(expect.expectFailedStartSharedBuffer, "expect_failed_start_shared_buffer");
exportUtilsFn(expect.expectFailedStartSharedFile, "expect_failed_start_shared_file");
exportUtilsFn(expect.expectFailedFinalize, "expect_failed_finalize");
exportUtilsFn(expect.sendDbg, "send_dbg");
exportUtilsFn(expect.notifyParentExpect, "notify_parent_expect");
exportUtilsFn(expect.notifyParentDbg, "notify_parent_dbg");
// sets the buffer used for expect failures
@export(expect.setSharedBuffer, .{ .name = "set_shared_buffer", .linkage = .Weak });

View file

@ -32,9 +32,6 @@ fn roc_getppid_windows_stub() callconv(.C) c_int {
return 0;
}
fn testing_roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int {
return kill(pid, sig);
}
fn testing_roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int {
return shm_open(name, oflag, mode);
}
@ -55,7 +52,6 @@ comptime {
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
@export(testing_roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong });
@export(testing_roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong });
@export(testing_roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong });
@export(testing_roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong });
}
}

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,122 @@ 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 0, end: Before 9, step: 3 } # returns [3, 6]
##
## 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.
range : _
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

@ -424,9 +424,9 @@ pub const UTILS_EXPECT_FAILED_START_SHARED_BUFFER: &str =
"roc_builtins.utils.expect_failed_start_shared_buffer";
pub const UTILS_EXPECT_FAILED_START_SHARED_FILE: &str =
"roc_builtins.utils.expect_failed_start_shared_file";
pub const UTILS_EXPECT_FAILED_FINALIZE: &str = "roc_builtins.utils.expect_failed_finalize";
pub const UTILS_EXPECT_READ_ENV_SHARED_BUFFER: &str = "roc_builtins.utils.read_env_shared_buffer";
pub const UTILS_SEND_DBG: &str = "roc_builtins.utils.send_dbg";
pub const NOTIFY_PARENT_EXPECT: &str = "roc_builtins.utils.notify_parent_expect";
pub const NOTIFY_PARENT_DBG: &str = "roc_builtins.utils.notify_parent_dbg";
pub const UTILS_LONGJMP: &str = "longjmp";
pub const UTILS_SETJMP: &str = "setjmp";

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

@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables;
use crate::annotation::OwnedNamedOrAble;
use crate::derive;
use crate::env::Env;
use crate::expr::get_lookup_symbols;
use crate::expr::AnnotatedMark;
use crate::expr::ClosureData;
use crate::expr::Declarations;
@ -2414,10 +2415,12 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
for ((expect_region, condition_region), condition) in it {
let region = Region::span_across(&expect_region, &loc_ret.region);
let lookups_in_cond = get_lookup_symbols(&condition);
let expr = Expr::Expect {
loc_condition: Box::new(Loc::at(condition_region, condition)),
loc_continuation: Box::new(loc_ret),
lookups_in_cond: vec![],
lookups_in_cond,
};
loc_ret = Loc::at(region, expr);

View file

@ -2778,7 +2778,7 @@ pub struct DestructureDef {
pub pattern_vars: VecMap<Symbol, Variable>,
}
fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
let mut stack: Vec<&Expr> = vec![expr];
let mut lookups: Vec<ExpectLookup> = Vec::new();
@ -2840,8 +2840,16 @@ fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
stack.push(&final_else.value);
}
Expr::LetRec(_, _, _) => todo!(),
Expr::LetNonRec { .. } => todo!(),
Expr::LetRec(defs, expr, _illegal_cycle_mark) => {
for def in defs {
stack.push(&def.loc_expr.value);
}
stack.push(&expr.value);
}
Expr::LetNonRec(def, expr) => {
stack.push(&def.loc_expr.value);
stack.push(&expr.value);
}
Expr::Call(boxed_expr, args, _called_via) => {
stack.reserve(1 + args.len());
@ -2995,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

@ -15,7 +15,7 @@ use roc_module::ident::Ident;
use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, ModuleIds, Symbol};
use roc_parse::ast::{Defs, TypeAnnotation};
use roc_parse::header::HeaderFor;
use roc_parse::header::HeaderType;
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
use roc_region::all::{Loc, Region};
@ -194,16 +194,17 @@ enum GeneratedInfo {
}
impl GeneratedInfo {
fn from_header_for<'a>(
fn from_header_type<'a>(
env: &mut Env,
scope: &mut Scope,
var_store: &mut VarStore,
header_for: &HeaderFor<'a>,
header_type: &HeaderType<'a>,
) -> Self {
match header_for {
HeaderFor::Hosted {
match header_type {
HeaderType::Hosted {
generates,
generates_with,
name: _,
} => {
let name: &str = generates.into();
let (generated_functions, unknown_generated) =
@ -236,7 +237,10 @@ impl GeneratedInfo {
generated_functions,
}
}
HeaderFor::Builtin { generates_with } => {
HeaderType::Builtin {
generates_with,
name: _,
} => {
debug_assert!(generates_with.is_empty());
GeneratedInfo::Builtin
}
@ -266,7 +270,7 @@ fn has_no_implementation(expr: &Expr) -> bool {
pub fn canonicalize_module_defs<'a>(
arena: &'a Bump,
loc_defs: &'a mut Defs<'a>,
header_for: &roc_parse::header::HeaderFor,
header_type: &roc_parse::header::HeaderType,
home: ModuleId,
module_ids: &'a ModuleIds,
exposed_ident_ids: IdentIds,
@ -294,7 +298,7 @@ pub fn canonicalize_module_defs<'a>(
}
let generated_info =
GeneratedInfo::from_header_for(&mut env, &mut scope, var_store, header_for);
GeneratedInfo::from_header_type(&mut env, &mut scope, var_store, header_type);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.

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

@ -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

@ -3,7 +3,7 @@ use crate::llvm::build_list::{self, allocate_list, empty_polymorphic_list};
use crate::llvm::convert::{
argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout, zig_str_type,
};
use crate::llvm::expect::clone_to_shared_memory;
use crate::llvm::expect::{clone_to_shared_memory, SharedMemoryPointer};
use crate::llvm::refcounting::{
build_reset, decrement_refcount_layout, increment_refcount_layout, PointerToRefcount,
};
@ -41,7 +41,7 @@ use roc_error_macros::internal_error;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_mono::ir::{
BranchInfo, CallType, CrashTag, EntryPoint, JoinPointId, ListLiteralElement, ModifyRc,
OptLevel, ProcLayout,
OptLevel, ProcLayout, SingleEntryPoint,
};
use roc_mono::layout::{
Builtin, CapturesNiche, LambdaName, LambdaSet, Layout, LayoutIds, RawFunctionLayout,
@ -2611,17 +2611,20 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes8 => {
let shared_memory = SharedMemoryPointer::get(env);
clone_to_shared_memory(
env,
scope,
layout_ids,
&shared_memory,
*cond_symbol,
*region,
lookups,
);
if let LlvmBackendMode::BinaryDev = env.mode {
crate::llvm::expect::finalize(env);
crate::llvm::expect::notify_parent_expect(env, &shared_memory);
}
bd.build_unconditional_branch(then_block);
@ -2677,10 +2680,13 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes8 => {
let shared_memory = SharedMemoryPointer::get(env);
clone_to_shared_memory(
env,
scope,
layout_ids,
&shared_memory,
*cond_symbol,
*region,
lookups,
@ -4164,29 +4170,23 @@ pub fn build_procedures<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>,
entry_point: EntryPoint<'a>,
debug_output_file: Option<&Path>,
) {
build_procedures_help(
env,
opt_level,
procedures,
opt_entry_point,
debug_output_file,
);
build_procedures_help(env, opt_level, procedures, entry_point, debug_output_file);
}
pub fn build_wasm_test_wrapper<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>,
entry_point: SingleEntryPoint<'a>,
) -> (&'static str, FunctionValue<'ctx>) {
let mod_solutions = build_procedures_help(
env,
opt_level,
procedures,
Some(entry_point),
EntryPoint::Single(entry_point),
Some(&std::env::temp_dir().join("test.ll")),
);
@ -4197,13 +4197,13 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
entry_point: EntryPoint<'a>,
entry_point: SingleEntryPoint<'a>,
) -> (&'static str, FunctionValue<'ctx>) {
let mod_solutions = build_procedures_help(
env,
opt_level,
procedures,
Some(entry_point),
EntryPoint::Single(entry_point),
Some(&std::env::temp_dir().join("test.ll")),
);
@ -4215,13 +4215,14 @@ pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
opt_level: OptLevel,
expects: &[Symbol],
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>,
) -> Vec<'a, &'a str> {
let entry_point = EntryPoint::Expects { symbols: expects };
let mod_solutions = build_procedures_help(
env,
opt_level,
procedures,
opt_entry_point,
entry_point,
Some(&std::env::temp_dir().join("test.ll")),
);
@ -4243,7 +4244,11 @@ pub fn build_procedures_expose_expects<'a, 'ctx, 'env>(
let func_solutions = mod_solutions.func_solutions(func_name).unwrap();
let mut it = func_solutions.specs();
let func_spec = it.next().unwrap();
let func_spec = match it.next() {
Some(spec) => spec,
None => panic!("no specialization for expect {}", symbol),
};
debug_assert!(
it.next().is_none(),
"we expect only one specialization of this symbol"
@ -4283,7 +4288,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
opt_level: OptLevel,
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
opt_entry_point: Option<EntryPoint<'a>>,
entry_point: EntryPoint<'a>,
debug_output_file: Option<&Path>,
) -> &'a ModSolutions {
let mut layout_ids = roc_mono::layout::LayoutIds::default();
@ -4295,7 +4300,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
env.arena,
env.layout_interner,
opt_level,
opt_entry_point,
entry_point,
it,
) {
Err(e) => panic!("Error in alias analysis: {}", e),

View file

@ -18,6 +18,32 @@ use super::build::{
Scope,
};
pub(crate) struct SharedMemoryPointer<'ctx>(PointerValue<'ctx>);
impl<'ctx> SharedMemoryPointer<'ctx> {
pub(crate) fn get<'a, 'env>(env: &Env<'a, 'ctx, 'env>) -> Self {
let start_function = if let LlvmBackendMode::BinaryDev = env.mode {
bitcode::UTILS_EXPECT_FAILED_START_SHARED_FILE
} else {
bitcode::UTILS_EXPECT_FAILED_START_SHARED_BUFFER
};
let func = env.module.get_function(start_function).unwrap();
let call_result = env
.builder
.build_call(func, &[], "call_expect_start_failed");
let ptr = call_result
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
Self(ptr)
}
}
#[derive(Debug, Clone, Copy)]
struct Cursors<'ctx> {
offset: IntValue<'ctx>,
@ -94,48 +120,39 @@ fn write_state<'a, 'ctx, 'env>(
env.builder.build_store(offset_ptr, offset);
}
pub(crate) fn finalize(env: &Env) {
pub(crate) fn notify_parent_expect(env: &Env, shared_memory: &SharedMemoryPointer) {
let func = env
.module
.get_function(bitcode::UTILS_EXPECT_FAILED_FINALIZE)
.get_function(bitcode::NOTIFY_PARENT_EXPECT)
.unwrap();
env.builder
.build_call(func, &[], "call_expect_failed_finalize");
env.builder.build_call(
func,
&[shared_memory.0.into()],
"call_expect_failed_finalize",
);
}
pub(crate) fn send_dbg(env: &Env) {
let func = env.module.get_function(bitcode::UTILS_SEND_DBG).unwrap();
pub(crate) fn notify_parent_dbg(env: &Env, shared_memory: &SharedMemoryPointer) {
let func = env.module.get_function(bitcode::NOTIFY_PARENT_DBG).unwrap();
env.builder
.build_call(func, &[], "call_expect_failed_finalize");
env.builder.build_call(
func,
&[shared_memory.0.into()],
"call_expect_failed_finalize",
);
}
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
scope: &Scope<'a, 'ctx>,
layout_ids: &mut LayoutIds<'a>,
shared_memory: &SharedMemoryPointer<'ctx>,
condition: Symbol,
region: Region,
lookups: &[Symbol],
) {
let start_function = if let LlvmBackendMode::BinaryDev = env.mode {
bitcode::UTILS_EXPECT_FAILED_START_SHARED_FILE
} else {
bitcode::UTILS_EXPECT_FAILED_START_SHARED_BUFFER
};
let func = env.module.get_function(start_function).unwrap();
let call_result = env
.builder
.build_call(func, &[], "call_expect_start_failed");
let original_ptr = call_result
.try_as_basic_value()
.left()
.unwrap()
.into_pointer_value();
let original_ptr = shared_memory.0;
let (count, mut offset) = read_state(env, original_ptr);

View file

@ -158,7 +158,6 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
unreachable_function(env, "roc_getppid");
unreachable_function(env, "roc_mmap");
unreachable_function(env, "roc_send_signal");
unreachable_function(env, "roc_shm_open");
add_sjlj_roc_panic(env)
@ -168,7 +167,10 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
fn unreachable_function(env: &Env, name: &str) {
// The type of this function (but not the implementation) should have
// already been defined by the builtins, which rely on it.
let fn_val = env.module.get_function(name).unwrap();
let fn_val = match env.module.get_function(name) {
Some(f) => f,
None => panic!("extern function {name} is not defined by the builtins"),
};
// Add a basic block for the entry point
let entry = env.context.append_basic_block(fn_val, "entry");

View file

@ -1126,16 +1126,19 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
if env.mode.runs_expects() {
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
let shared_memory = crate::llvm::expect::SharedMemoryPointer::get(env);
crate::llvm::expect::clone_to_shared_memory(
env,
scope,
layout_ids,
&shared_memory,
args[0],
region,
&[args[0]],
);
crate::llvm::expect::send_dbg(env);
crate::llvm::expect::notify_parent_dbg(env, &shared_memory);
}
condition

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);
}
}

File diff suppressed because it is too large Load diff

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

@ -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

@ -119,11 +119,17 @@ pub enum OptLevel {
}
#[derive(Debug, Clone, Copy)]
pub struct EntryPoint<'a> {
pub struct SingleEntryPoint<'a> {
pub symbol: Symbol,
pub layout: ProcLayout<'a>,
}
#[derive(Debug, Clone, Copy)]
pub enum EntryPoint<'a> {
Single(SingleEntryPoint<'a>),
Expects { symbols: &'a [Symbol] },
}
#[derive(Clone, Copy, Debug)]
pub struct PartialProcId(usize);
@ -326,6 +332,7 @@ impl<'a> Proc<'a> {
&'b self,
alloc: &'b D,
interner: &'b I,
pretty: bool,
_parens: Parens,
) -> DocBuilder<'b, D, A>
where
@ -335,7 +342,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 +357,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 +1682,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 +1709,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 +1720,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 +1793,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 +1807,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 +1828,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 +2019,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 +2036,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 +2070,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 +2080,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 +2098,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 +2119,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 +2129,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 +2143,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 +2166,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 +2193,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 +2210,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 +2230,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 +2241,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 +2262,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 +2287,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 +2306,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 +2316,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 +2348,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

@ -1,22 +1,43 @@
use std::path::PathBuf;
use bumpalo::Bump;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use roc_parse::{module, module::module_defs, parser::Parser, state::State};
pub fn criterion_benchmark(c: &mut Criterion) {
let mut path = std::env::current_dir()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.to_owned();
path.push("examples");
path.push("false-interpreter");
path.push("False.roc");
let src = std::fs::read_to_string(&path).unwrap();
pub fn parse_benchmark(c: &mut Criterion) {
c.bench_function("parse false-interpreter", |b| {
let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"));
path.push("examples");
path.push("cli");
path.push("false-interpreter");
path.push("False.roc");
let src = std::fs::read_to_string(&path).unwrap();
b.iter(|| {
let arena = Bump::new();
let (_actual, state) =
module::parse_header(&arena, State::new(src.as_bytes())).unwrap();
let min_indent = 0;
let res = module_defs()
.parse(&arena, state, min_indent)
.map(|tuple| tuple.1)
.unwrap();
black_box(res.len());
})
});
c.bench_function("parse Num builtin", |b| {
let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"));
path.push("crates");
path.push("compiler");
path.push("builtins");
path.push("roc");
path.push("Num.roc");
let src = std::fs::read_to_string(&path).unwrap();
b.iter(|| {
let arena = Bump::new();
@ -34,5 +55,5 @@ pub fn criterion_benchmark(c: &mut Criterion) {
});
}
criterion_group!(benches, criterion_benchmark);
criterion_group!(benches, parse_benchmark);
criterion_main!(benches);

View file

@ -4,34 +4,40 @@ use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::{optional, then};
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
use crate::string_literal;
use roc_module::symbol::Symbol;
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::Loc;
use std::fmt::Debug;
#[derive(Debug)]
pub enum HeaderFor<'a> {
pub enum HeaderType<'a> {
App {
output_name: StrLiteral<'a>,
to_platform: To<'a>,
},
Hosted {
name: ModuleName<'a>,
generates: UppercaseIdent<'a>,
generates_with: &'a [Loc<ExposedName<'a>>],
},
/// Only created during canonicalization, never actually parsed from source
Builtin {
name: ModuleName<'a>,
generates_with: &'a [Symbol],
},
Platform {
opt_app_module_id: Option<ModuleId>,
/// the name and type scheme of the main function (required by the platform)
/// (type scheme is currently unused)
provides: &'a [(Loc<ExposedName<'a>>, Loc<TypedIdent<'a>>)],
requires: &'a [Loc<TypedIdent<'a>>],
requires_types: &'a [Loc<UppercaseIdent<'a>>],
/// usually `pf`
config_shorthand: &'a str,
/// the type scheme of the main function (required by the platform)
/// (currently unused)
#[allow(dead_code)]
platform_main_type: TypedIdent<'a>,
/// provided symbol to host (commonly `mainForHost`)
main_for_host: roc_module::symbol::Symbol,
},
Interface,
Interface {
name: ModuleName<'a>,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@ -95,15 +101,6 @@ impl<'a> ModuleName<'a> {
}
}
#[derive(Debug)]
pub enum ModuleNameEnum<'a> {
/// A filename
App(StrLiteral<'a>),
Interface(ModuleName<'a>),
Hosted(ModuleName<'a>),
Platform,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct ExposedName<'a>(&'a str);

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

@ -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

@ -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

@ -2,6 +2,7 @@ use libloading::Library;
use roc_build::link::{link, LinkType};
use roc_builtins::bitcode;
use roc_load::{EntryPoint, ExecutionMode, LoadConfig, Threading};
use roc_mono::ir::SingleEntryPoint;
use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
use tempfile::tempdir;
@ -105,8 +106,15 @@ pub fn helper(
debug_assert_eq!(exposed_to_host.values.len(), 1);
let entry_point = match loaded.entry_point {
EntryPoint::Executable { symbol, layout, .. } => {
roc_mono::ir::EntryPoint { symbol, layout }
EntryPoint::Executable {
exposed_to_host,
platform_path: _,
} => {
// TODO support multiple of these!
debug_assert_eq!(exposed_to_host.len(), 1);
let (symbol, layout) = exposed_to_host[0];
SingleEntryPoint { symbol, layout }
}
EntryPoint::Test => {
unreachable!()

View file

@ -8,7 +8,7 @@ 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, LoadMonomorphizedError, Threading};
use roc_mono::ir::{CrashTag, OptLevel};
use roc_mono::ir::{CrashTag, OptLevel, SingleEntryPoint};
use roc_packaging::cache::RocCacheDir;
use roc_region::all::LineInfo;
use roc_reporting::report::{RenderTarget, DEFAULT_PALETTE};
@ -99,7 +99,6 @@ fn create_llvm_module<'a>(
use roc_load::MonomorphizedModule;
let MonomorphizedModule {
procedures,
entry_point,
interns,
layout_interner,
..
@ -238,9 +237,16 @@ fn create_llvm_module<'a>(
// platform to provide them.
add_default_roc_externs(&env);
let entry_point = match entry_point {
EntryPoint::Executable { symbol, layout, .. } => {
roc_mono::ir::EntryPoint { symbol, layout }
let entry_point = match loaded.entry_point {
EntryPoint::Executable {
exposed_to_host,
platform_path: _,
} => {
// TODO support multiple of these!
debug_assert_eq!(exposed_to_host.len(), 1);
let (symbol, layout) = exposed_to_host[0];
SingleEntryPoint { symbol, layout }
}
EntryPoint::Test => {
unreachable!()

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,11 +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;
@ -74,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");
@ -86,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 {
@ -100,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,
@ -129,6 +136,7 @@ fn compiles_to_ir(test_name: &str, src: &str) {
procedures,
exposed_to_host,
layout_interner,
interns,
..
} = loaded;
@ -141,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");
@ -564,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#"
@ -1282,7 +1311,7 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
)
}
#[mono_test]
#[mono_test(no_check)]
fn issue_2810() {
indoc!(
r#"
@ -2099,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,