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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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