mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge remote-tracking branch 'origin/trunk' into fix-nested-imports
This commit is contained in:
commit
c45e3ec4b4
93 changed files with 3258 additions and 4416 deletions
|
@ -23,6 +23,7 @@ ROC_WORKSPACE_DIR = { value = "", relative = true }
|
||||||
# Set = "1" to turn a debug flag on.
|
# Set = "1" to turn a debug flag on.
|
||||||
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
|
ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0"
|
||||||
ROC_PRINT_UNIFICATIONS = "0"
|
ROC_PRINT_UNIFICATIONS = "0"
|
||||||
|
ROC_TRACE_COMPACTION = "0"
|
||||||
ROC_PRINT_UNIFICATIONS_DERIVED = "0"
|
ROC_PRINT_UNIFICATIONS_DERIVED = "0"
|
||||||
ROC_PRINT_MISMATCHES = "0"
|
ROC_PRINT_MISMATCHES = "0"
|
||||||
ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
|
ROC_VERIFY_RIGID_LET_GENERALIZED = "0"
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -20,6 +20,9 @@ vgcore.*
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
.ignore
|
.ignore
|
||||||
|
.exrc
|
||||||
|
.vimrc
|
||||||
|
.nvimrc
|
||||||
|
|
||||||
#files too big to track in git
|
#files too big to track in git
|
||||||
editor/benches/resources/100000_lines.roc
|
editor/benches/resources/100000_lines.roc
|
||||||
|
|
|
@ -1421,6 +1421,7 @@ fn adjust_rank_content(
|
||||||
recursion_var,
|
recursion_var,
|
||||||
// TODO: handle unspecialized
|
// TODO: handle unspecialized
|
||||||
unspecialized: _,
|
unspecialized: _,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
let mut rank = group_rank;
|
let mut rank = group_rank;
|
||||||
|
|
||||||
|
@ -1623,6 +1624,7 @@ fn instantiate_rigids_help(
|
||||||
recursion_var,
|
recursion_var,
|
||||||
// TODO: handle unspecialized
|
// TODO: handle unspecialized
|
||||||
unspecialized: _,
|
unspecialized: _,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
if let Some(rec_var) = recursion_var.into_variable() {
|
if let Some(rec_var) = recursion_var.into_variable() {
|
||||||
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
instantiate_rigids_help(subs, max_rank, pools, rec_var);
|
||||||
|
@ -1903,6 +1905,7 @@ fn deep_copy_var_help(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function,
|
||||||
}) => {
|
}) => {
|
||||||
let mut new_variable_slices = Vec::with_capacity(solved.len());
|
let mut new_variable_slices = Vec::with_capacity(solved.len());
|
||||||
|
|
||||||
|
@ -1937,6 +1940,7 @@ fn deep_copy_var_help(
|
||||||
recursion_var: new_rec_var,
|
recursion_var: new_rec_var,
|
||||||
// TODO: actually copy
|
// TODO: actually copy
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function,
|
||||||
});
|
});
|
||||||
|
|
||||||
subs.set(copy, make_descriptor(new_content));
|
subs.set(copy, make_descriptor(new_content));
|
||||||
|
|
|
@ -1075,7 +1075,7 @@ fn lowlevel_spec(
|
||||||
|
|
||||||
builder.add_make_tuple(block, &[cell, bag])
|
builder.add_make_tuple(block, &[cell, bag])
|
||||||
}
|
}
|
||||||
StrFromUtf8 => {
|
StrFromUtf8Range => {
|
||||||
let list = env.symbols[&arguments[0]];
|
let list = env.symbols[&arguments[0]];
|
||||||
|
|
||||||
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?;
|
||||||
|
|
|
@ -1638,18 +1638,45 @@ inline fn fromUtf8(arg: RocList, update_mode: UpdateMode) FromUtf8Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromUtf8RangeC(output: *FromUtf8Result, arg: RocList, countAndStart: CountAndStart) callconv(.C) void {
|
pub fn fromUtf8RangeC(
|
||||||
output.* = @call(.{ .modifier = always_inline }, fromUtf8Range, .{ arg, countAndStart });
|
output: *FromUtf8Result,
|
||||||
|
list: RocList,
|
||||||
|
start: usize,
|
||||||
|
count: usize,
|
||||||
|
update_mode: UpdateMode,
|
||||||
|
) callconv(.C) void {
|
||||||
|
output.* = @call(.{ .modifier = always_inline }, fromUtf8Range, .{ list, start, count, update_mode });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fromUtf8Range(arg: RocList, countAndStart: CountAndStart) FromUtf8Result {
|
pub fn fromUtf8Range(arg: RocList, start: usize, count: usize, update_mode: UpdateMode) FromUtf8Result {
|
||||||
const bytes = @ptrCast([*]const u8, arg.bytes)[countAndStart.start..countAndStart.count];
|
const bytes = @ptrCast([*]const u8, arg.bytes)[start..count];
|
||||||
|
|
||||||
if (unicode.utf8ValidateSlice(bytes)) {
|
if (unicode.utf8ValidateSlice(bytes)) {
|
||||||
// the output will be correct. Now we need to clone the input
|
// the output will be correct. Now we need to clone the input
|
||||||
const string = RocStr.init(@ptrCast([*]const u8, bytes), countAndStart.count);
|
|
||||||
|
|
||||||
return FromUtf8Result{ .is_ok = true, .string = string, .byte_index = 0, .problem_code = Utf8ByteProblem.InvalidStartByte };
|
if (count == arg.len() and count > SMALL_STR_MAX_LENGTH) {
|
||||||
|
const byte_list = arg.makeUniqueExtra(RocStr.alignment, @sizeOf(u8), update_mode);
|
||||||
|
|
||||||
|
const string = RocStr{
|
||||||
|
.str_bytes = byte_list.bytes,
|
||||||
|
.str_len = byte_list.length,
|
||||||
|
.str_capacity = byte_list.capacity,
|
||||||
|
};
|
||||||
|
|
||||||
|
return FromUtf8Result{
|
||||||
|
.is_ok = true,
|
||||||
|
.string = string,
|
||||||
|
.byte_index = 0,
|
||||||
|
.problem_code = Utf8ByteProblem.InvalidStartByte,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return FromUtf8Result{
|
||||||
|
.is_ok = true,
|
||||||
|
.string = RocStr.init(@ptrCast([*]const u8, bytes), count),
|
||||||
|
.byte_index = 0,
|
||||||
|
.problem_code = Utf8ByteProblem.InvalidStartByte,
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const temp = errorToProblem(@ptrCast([*]u8, arg.bytes), arg.length);
|
const temp = errorToProblem(@ptrCast([*]u8, arg.bytes), arg.length);
|
||||||
return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem };
|
return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem };
|
||||||
|
|
|
@ -68,14 +68,27 @@ interface Dict
|
||||||
## `fn dict1 == fn dict2` also being `True`, even if `fn` relies on the dictionary's ordering.
|
## `fn dict1 == fn dict2` also being `True`, even if `fn` relies on the dictionary's ordering.
|
||||||
## An empty dictionary.
|
## An empty dictionary.
|
||||||
empty : Dict k v
|
empty : Dict k v
|
||||||
single : k, v -> Dict k v
|
|
||||||
get : Dict k v, k -> Result v [KeyNotFound]*
|
get : Dict k v, k -> Result v [KeyNotFound]*
|
||||||
|
get = \dict, key ->
|
||||||
|
result = getLowlevel dict key
|
||||||
|
|
||||||
|
when result.flag is
|
||||||
|
True -> Ok result.value
|
||||||
|
False -> Err KeyNotFound
|
||||||
|
|
||||||
|
getLowlevel : Dict k v, k -> { flag : Bool, value : v }
|
||||||
|
|
||||||
walk : Dict k v, state, (state, k, v -> state) -> state
|
walk : Dict k v, state, (state, k, v -> state) -> state
|
||||||
insert : Dict k v, k, v -> Dict k v
|
insert : Dict k v, k, v -> Dict k v
|
||||||
len : Dict k v -> Nat
|
len : Dict k v -> Nat
|
||||||
remove : Dict k v, k -> Dict k v
|
remove : Dict k v, k -> Dict k v
|
||||||
contains : Dict k v, k -> Bool
|
contains : Dict k v, k -> Bool
|
||||||
|
|
||||||
|
single : k, v -> Dict k v
|
||||||
|
single = \key, value ->
|
||||||
|
Dict.empty
|
||||||
|
|> Dict.insert key value
|
||||||
|
|
||||||
## Returns a [List] of the dictionary's keys.
|
## Returns a [List] of the dictionary's keys.
|
||||||
keys : Dict k v -> List k
|
keys : Dict k v -> List k
|
||||||
|
|
||||||
|
|
|
@ -717,6 +717,10 @@ takeLast = \list, outputLength ->
|
||||||
|
|
||||||
## Drops n elements from the beginning of the list.
|
## Drops n elements from the beginning of the list.
|
||||||
drop : List elem, Nat -> List elem
|
drop : List elem, Nat -> List elem
|
||||||
|
drop = \list, n ->
|
||||||
|
remaining = Num.subSaturated (List.len list) n
|
||||||
|
|
||||||
|
List.takeLast list remaining
|
||||||
|
|
||||||
## Drops the element at the given index from the list.
|
## Drops the element at the given index from the list.
|
||||||
##
|
##
|
||||||
|
@ -812,6 +816,10 @@ findIndex = \list, matcher ->
|
||||||
##
|
##
|
||||||
## Some languages have a function called **`slice`** which works similarly to this.
|
## Some languages have a function called **`slice`** which works similarly to this.
|
||||||
sublist : List elem, { start : Nat, len : Nat } -> List elem
|
sublist : List elem, { start : Nat, len : Nat } -> List elem
|
||||||
|
sublist = \list, config ->
|
||||||
|
sublistLowlevel list config.start config.len
|
||||||
|
|
||||||
|
sublistLowlevel : List elem, Nat, Nat -> List elem
|
||||||
|
|
||||||
## Intersperses `sep` between the elements of `list`
|
## Intersperses `sep` between the elements of `list`
|
||||||
## >>> List.intersperse 9 [1, 2, 3] # [1, 9, 2, 9, 3]
|
## >>> List.intersperse 9 [1, 2, 3] # [1, 9, 2, 9, 3]
|
||||||
|
@ -835,6 +843,13 @@ intersperse = \list, sep ->
|
||||||
## means if you give an index of 0, the `before` list will be empty and the
|
## means if you give an index of 0, the `before` list will be empty and the
|
||||||
## `others` list will have the same elements as the original list.)
|
## `others` list will have the same elements as the original list.)
|
||||||
split : List elem, Nat -> { before : List elem, others : List elem }
|
split : List elem, Nat -> { before : List elem, others : List elem }
|
||||||
|
split = \elements, userSplitIndex ->
|
||||||
|
length = List.len elements
|
||||||
|
splitIndex = if length > userSplitIndex then userSplitIndex else length
|
||||||
|
before = List.sublist elements { start: 0, len: splitIndex }
|
||||||
|
others = List.sublist elements { start: splitIndex, len: length - splitIndex }
|
||||||
|
|
||||||
|
{ before, others }
|
||||||
|
|
||||||
## Primitive for iterating over a List, being able to decide at every element whether to continue
|
## Primitive for iterating over a List, being able to decide at every element whether to continue
|
||||||
iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
|
iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
|
||||||
|
|
|
@ -509,8 +509,28 @@ Dec : Num (FloatingPoint Decimal)
|
||||||
toStr : Num * -> Str
|
toStr : Num * -> Str
|
||||||
intCast : Int a -> Int b
|
intCast : Int a -> Int b
|
||||||
|
|
||||||
|
bytesToU16Lowlevel : List U8, Nat -> U16
|
||||||
|
bytesToU32Lowlevel : List U8, Nat -> U32
|
||||||
|
|
||||||
bytesToU16 : List U8, Nat -> Result U16 [OutOfBounds]
|
bytesToU16 : List U8, Nat -> Result U16 [OutOfBounds]
|
||||||
|
bytesToU16 = \bytes, index ->
|
||||||
|
# we need at least 1 more byte
|
||||||
|
offset = 1
|
||||||
|
|
||||||
|
if index + offset < List.len bytes then
|
||||||
|
Ok (bytesToU16Lowlevel bytes index)
|
||||||
|
else
|
||||||
|
Err OutOfBounds
|
||||||
|
|
||||||
bytesToU32 : List U8, Nat -> Result U32 [OutOfBounds]
|
bytesToU32 : List U8, Nat -> Result U32 [OutOfBounds]
|
||||||
|
bytesToU32 = \bytes, index ->
|
||||||
|
# we need at least 3 more bytes
|
||||||
|
offset = 3
|
||||||
|
|
||||||
|
if index + offset < List.len bytes then
|
||||||
|
Ok (bytesToU32Lowlevel bytes index)
|
||||||
|
else
|
||||||
|
Err OutOfBounds
|
||||||
|
|
||||||
compare : Num a, Num a -> [LT, EQ, GT]
|
compare : Num a, Num a -> [LT, EQ, GT]
|
||||||
|
|
||||||
|
@ -554,22 +574,27 @@ isGte : Num a, Num a -> Bool
|
||||||
|
|
||||||
## Returns `True` if the number is `0`, and `False` otherwise.
|
## Returns `True` if the number is `0`, and `False` otherwise.
|
||||||
isZero : Num a -> Bool
|
isZero : Num a -> Bool
|
||||||
|
isZero = \x -> x == 0
|
||||||
|
|
||||||
## A number is even if dividing it by 2 gives a remainder of 0.
|
## A number is even if dividing it by 2 gives a remainder of 0.
|
||||||
##
|
##
|
||||||
## Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8
|
## Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8
|
||||||
isEven : Int a -> Bool
|
isEven : Int a -> Bool
|
||||||
|
isEven = \x -> Num.isMultipleOf x 2
|
||||||
|
|
||||||
## A number is odd if dividing it by 2 gives a remainder of 1.
|
## A number is odd if dividing it by 2 gives a remainder of 1.
|
||||||
##
|
##
|
||||||
## Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7
|
## Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7
|
||||||
isOdd : Int a -> Bool
|
isOdd : Int a -> Bool
|
||||||
|
isOdd = \x -> Bool.not (Num.isMultipleOf x 2)
|
||||||
|
|
||||||
## Positive numbers are greater than `0`.
|
## Positive numbers are greater than `0`.
|
||||||
isPositive : Num a -> Bool
|
isPositive : Num a -> Bool
|
||||||
|
isPositive = \x -> x > 0
|
||||||
|
|
||||||
## Negative numbers are less than `0`.
|
## Negative numbers are less than `0`.
|
||||||
isNegative : Num a -> Bool
|
isNegative : Num a -> Bool
|
||||||
|
isNegative = \x -> x < 0
|
||||||
|
|
||||||
toFrac : Num * -> Frac *
|
toFrac : Num * -> Frac *
|
||||||
|
|
||||||
|
@ -682,7 +707,11 @@ mul : Num a, Num a -> Num a
|
||||||
|
|
||||||
sin : Frac a -> Frac a
|
sin : Frac a -> Frac a
|
||||||
cos : Frac a -> Frac a
|
cos : Frac a -> Frac a
|
||||||
|
|
||||||
tan : Frac a -> Frac a
|
tan : Frac a -> Frac a
|
||||||
|
tan = \x ->
|
||||||
|
# `tan` is not available as an intrinsic in LLVM
|
||||||
|
Num.div (Num.sin x) (Num.cos x)
|
||||||
|
|
||||||
asin : Frac a -> Frac a
|
asin : Frac a -> Frac a
|
||||||
acos : Frac a -> Frac a
|
acos : Frac a -> Frac a
|
||||||
|
@ -713,9 +742,22 @@ atan : Frac a -> Frac a
|
||||||
##
|
##
|
||||||
## >>> Num.sqrt -4.0f64
|
## >>> Num.sqrt -4.0f64
|
||||||
sqrt : Frac a -> Frac a
|
sqrt : Frac a -> Frac a
|
||||||
|
|
||||||
sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]*
|
sqrtChecked : Frac a -> Result (Frac a) [SqrtOfNegative]*
|
||||||
|
sqrtChecked = \x ->
|
||||||
|
if x < 0.0 then
|
||||||
|
Err SqrtOfNegative
|
||||||
|
else
|
||||||
|
Ok (Num.sqrt x)
|
||||||
|
|
||||||
log : Frac a -> Frac a
|
log : Frac a -> Frac a
|
||||||
|
|
||||||
logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]*
|
logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]*
|
||||||
|
logChecked = \x ->
|
||||||
|
if x <= 0.0 then
|
||||||
|
Err LogNeedsPositive
|
||||||
|
else
|
||||||
|
Ok (Num.log x)
|
||||||
|
|
||||||
## Divide one [Frac] by another.
|
## Divide one [Frac] by another.
|
||||||
##
|
##
|
||||||
|
@ -748,9 +790,22 @@ logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]*
|
||||||
## >>> Num.pi
|
## >>> Num.pi
|
||||||
## >>> |> Num.div 2.0
|
## >>> |> Num.div 2.0
|
||||||
div : Frac a, Frac a -> Frac a
|
div : Frac a, Frac a -> Frac a
|
||||||
|
|
||||||
divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]*
|
divChecked : Frac a, Frac a -> Result (Frac a) [DivByZero]*
|
||||||
|
divChecked = \a, b ->
|
||||||
|
if b == 0 then
|
||||||
|
Err DivByZero
|
||||||
|
else
|
||||||
|
Ok (Num.div a b)
|
||||||
|
|
||||||
divCeil : Int a, Int a -> Int a
|
divCeil : Int a, Int a -> Int a
|
||||||
|
|
||||||
divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
||||||
|
divCeilChecked = \a, b ->
|
||||||
|
if b == 0 then
|
||||||
|
Err DivByZero
|
||||||
|
else
|
||||||
|
Ok (Num.divCeil a b)
|
||||||
|
|
||||||
## Divide two integers, truncating the result towards zero.
|
## Divide two integers, truncating the result towards zero.
|
||||||
##
|
##
|
||||||
|
@ -769,7 +824,13 @@ divCeilChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
||||||
## >>> Num.divTrunc 8 -3
|
## >>> Num.divTrunc 8 -3
|
||||||
##
|
##
|
||||||
divTrunc : Int a, Int a -> Int a
|
divTrunc : Int a, Int a -> Int a
|
||||||
|
|
||||||
divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
||||||
|
divTruncChecked = \a, b ->
|
||||||
|
if b == 0 then
|
||||||
|
Err DivByZero
|
||||||
|
else
|
||||||
|
Ok (Num.divTrunc a b)
|
||||||
|
|
||||||
## Obtain the remainder (truncating modulo) from the division of two integers.
|
## Obtain the remainder (truncating modulo) from the division of two integers.
|
||||||
##
|
##
|
||||||
|
@ -783,7 +844,13 @@ divTruncChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
||||||
##
|
##
|
||||||
## >>> Num.rem -8 -3
|
## >>> Num.rem -8 -3
|
||||||
rem : Int a, Int a -> Int a
|
rem : Int a, Int a -> Int a
|
||||||
|
|
||||||
remChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
remChecked : Int a, Int a -> Result (Int a) [DivByZero]*
|
||||||
|
remChecked = \a, b ->
|
||||||
|
if b == 0 then
|
||||||
|
Err DivByZero
|
||||||
|
else
|
||||||
|
Ok (Num.rem a b)
|
||||||
|
|
||||||
isMultipleOf : Int a, Int a -> Bool
|
isMultipleOf : Int a, Int a -> Bool
|
||||||
|
|
||||||
|
@ -842,6 +909,15 @@ addSaturated : Num a, Num a -> Num a
|
||||||
## This is the same as [Num.add] except if the operation overflows, instead of
|
## This is the same as [Num.add] except if the operation overflows, instead of
|
||||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||||
addChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
addChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
||||||
|
addChecked = \a, b ->
|
||||||
|
result = addCheckedLowlevel a b
|
||||||
|
|
||||||
|
if result.b then
|
||||||
|
Err Overflow
|
||||||
|
else
|
||||||
|
Ok result.a
|
||||||
|
|
||||||
|
addCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
||||||
|
|
||||||
subWrap : Int range, Int range -> Int range
|
subWrap : Int range, Int range -> Int range
|
||||||
|
|
||||||
|
@ -859,6 +935,15 @@ subSaturated : Num a, Num a -> Num a
|
||||||
## This is the same as [Num.sub] except if the operation overflows, instead of
|
## This is the same as [Num.sub] except if the operation overflows, instead of
|
||||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||||
subChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
subChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
||||||
|
subChecked = \a, b ->
|
||||||
|
result = subCheckedLowlevel a b
|
||||||
|
|
||||||
|
if result.b then
|
||||||
|
Err Overflow
|
||||||
|
else
|
||||||
|
Ok result.a
|
||||||
|
|
||||||
|
subCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
||||||
|
|
||||||
mulWrap : Int range, Int range -> Int range
|
mulWrap : Int range, Int range -> Int range
|
||||||
|
|
||||||
|
@ -874,6 +959,15 @@ mulSaturated : Num a, Num a -> Num a
|
||||||
## This is the same as [Num.mul] except if the operation overflows, instead of
|
## This is the same as [Num.mul] except if the operation overflows, instead of
|
||||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||||
mulChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
mulChecked : Num a, Num a -> Result (Num a) [Overflow]*
|
||||||
|
mulChecked = \a, b ->
|
||||||
|
result = mulCheckedLowlevel a b
|
||||||
|
|
||||||
|
if result.b then
|
||||||
|
Err Overflow
|
||||||
|
else
|
||||||
|
Ok result.a
|
||||||
|
|
||||||
|
mulCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
||||||
|
|
||||||
## The lowest number that can be stored in an [I8] without underflowing its
|
## The lowest number that can be stored in an [I8] without underflowing its
|
||||||
## available memory and crashing.
|
## available memory and crashing.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface Result
|
interface Result
|
||||||
exposes [Result, isOk, isErr, map, mapErr, after, withDefault]
|
exposes [Result, isOk, isErr, map, mapErr, after, afterErr, withDefault]
|
||||||
imports [Bool.{ Bool }]
|
imports [Bool.{ Bool }]
|
||||||
|
|
||||||
## The result of an operation that could fail: either the operation went
|
## The result of an operation that could fail: either the operation went
|
||||||
|
@ -92,3 +92,19 @@ after = \result, transform ->
|
||||||
transform v
|
transform v
|
||||||
Err e ->
|
Err e ->
|
||||||
Err e
|
Err e
|
||||||
|
|
||||||
|
## If the result is `Err`, transform the entire result by running a conversion
|
||||||
|
## function on the value the `Err` holds. Then return that new result.
|
||||||
|
##
|
||||||
|
## (If the result is `Ok`, this has no effect. Use `after` to transform an `Ok`.)
|
||||||
|
##
|
||||||
|
## >>> Result.afterErr (Ok 10) \errorNum -> Str.toNat errorNum
|
||||||
|
##
|
||||||
|
## >>> Result.afterErr (Err "42") \errorNum -> Str.toNat errorNum
|
||||||
|
afterErr : Result a err, (err -> Result a otherErr) -> Result a otherErr
|
||||||
|
afterErr = \result, transform ->
|
||||||
|
when result is
|
||||||
|
Ok v ->
|
||||||
|
Ok v
|
||||||
|
Err e ->
|
||||||
|
transform e
|
||||||
|
|
|
@ -24,10 +24,19 @@ single : k -> Set k
|
||||||
## retrieved or removed from the [Set].
|
## retrieved or removed from the [Set].
|
||||||
insert : Set k, k -> Set k
|
insert : Set k, k -> Set k
|
||||||
len : Set k -> Nat
|
len : Set k -> Nat
|
||||||
|
len = \set ->
|
||||||
|
set
|
||||||
|
|> Set.toDict
|
||||||
|
|> Dict.len
|
||||||
|
|
||||||
## Drops the given element from the set.
|
## Drops the given element from the set.
|
||||||
remove : Set k, k -> Set k
|
remove : Set k, k -> Set k
|
||||||
|
|
||||||
contains : Set k, k -> Bool
|
contains : Set k, k -> Bool
|
||||||
|
contains = \set, key ->
|
||||||
|
set
|
||||||
|
|> Set.toDict
|
||||||
|
|> Dict.contains key
|
||||||
|
|
||||||
# toList = \set -> Dict.keys (toDict set)
|
# toList = \set -> Dict.keys (toDict set)
|
||||||
toList : Set k -> List k
|
toList : Set k -> List k
|
||||||
|
|
|
@ -199,10 +199,35 @@ toScalars : Str -> List U32
|
||||||
## >>> Str.toUtf8 "🐦"
|
## >>> Str.toUtf8 "🐦"
|
||||||
toUtf8 : Str -> List U8
|
toUtf8 : Str -> List U8
|
||||||
|
|
||||||
# fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8Problem]*
|
|
||||||
# fromUtf8Range : List U8 -> Result Str [BadUtf8 Utf8Problem Nat, OutOfBounds]*
|
|
||||||
fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]*
|
fromUtf8 : List U8 -> Result Str [BadUtf8 Utf8ByteProblem Nat]*
|
||||||
|
fromUtf8 = \bytes ->
|
||||||
|
result = fromUtf8RangeLowlevel bytes 0 (List.len bytes)
|
||||||
|
|
||||||
|
if result.cIsOk then
|
||||||
|
Ok result.bString
|
||||||
|
else
|
||||||
|
Err (BadUtf8 result.dProblemCode result.aByteIndex)
|
||||||
|
|
||||||
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]*
|
fromUtf8Range : List U8, { start : Nat, count : Nat } -> Result Str [BadUtf8 Utf8ByteProblem Nat, OutOfBounds]*
|
||||||
|
fromUtf8Range = \bytes, config ->
|
||||||
|
if config.start + config.count <= List.len bytes then
|
||||||
|
result = fromUtf8RangeLowlevel bytes config.start config.count
|
||||||
|
|
||||||
|
if result.cIsOk then
|
||||||
|
Ok result.bString
|
||||||
|
else
|
||||||
|
Err (BadUtf8 result.dProblemCode result.aByteIndex)
|
||||||
|
else
|
||||||
|
Err OutOfBounds
|
||||||
|
|
||||||
|
FromUtf8Result : {
|
||||||
|
aByteIndex : Nat,
|
||||||
|
bString : Str,
|
||||||
|
cIsOk : Bool,
|
||||||
|
dProblemCode : Utf8ByteProblem,
|
||||||
|
}
|
||||||
|
|
||||||
|
fromUtf8RangeLowlevel : List U8, Nat, Nat -> FromUtf8Result
|
||||||
|
|
||||||
startsWith : Str, Str -> Bool
|
startsWith : Str, Str -> Bool
|
||||||
endsWith : Str, Str -> Bool
|
endsWith : Str, Str -> Bool
|
||||||
|
@ -214,19 +239,33 @@ trimLeft : Str -> Str
|
||||||
trimRight : Str -> Str
|
trimRight : Str -> Str
|
||||||
|
|
||||||
toDec : Str -> Result Dec [InvalidNumStr]*
|
toDec : Str -> Result Dec [InvalidNumStr]*
|
||||||
|
toDec = \string -> strToNumHelp string
|
||||||
toF64 : Str -> Result F64 [InvalidNumStr]*
|
toF64 : Str -> Result F64 [InvalidNumStr]*
|
||||||
|
toF64 = \string -> strToNumHelp string
|
||||||
toF32 : Str -> Result F32 [InvalidNumStr]*
|
toF32 : Str -> Result F32 [InvalidNumStr]*
|
||||||
|
toF32 = \string -> strToNumHelp string
|
||||||
toNat : Str -> Result Nat [InvalidNumStr]*
|
toNat : Str -> Result Nat [InvalidNumStr]*
|
||||||
|
toNat = \string -> strToNumHelp string
|
||||||
toU128 : Str -> Result U128 [InvalidNumStr]*
|
toU128 : Str -> Result U128 [InvalidNumStr]*
|
||||||
|
toU128 = \string -> strToNumHelp string
|
||||||
toI128 : Str -> Result I128 [InvalidNumStr]*
|
toI128 : Str -> Result I128 [InvalidNumStr]*
|
||||||
|
toI128 = \string -> strToNumHelp string
|
||||||
toU64 : Str -> Result U64 [InvalidNumStr]*
|
toU64 : Str -> Result U64 [InvalidNumStr]*
|
||||||
|
toU64 = \string -> strToNumHelp string
|
||||||
toI64 : Str -> Result I64 [InvalidNumStr]*
|
toI64 : Str -> Result I64 [InvalidNumStr]*
|
||||||
|
toI64 = \string -> strToNumHelp string
|
||||||
toU32 : Str -> Result U32 [InvalidNumStr]*
|
toU32 : Str -> Result U32 [InvalidNumStr]*
|
||||||
|
toU32 = \string -> strToNumHelp string
|
||||||
toI32 : Str -> Result I32 [InvalidNumStr]*
|
toI32 : Str -> Result I32 [InvalidNumStr]*
|
||||||
|
toI32 = \string -> strToNumHelp string
|
||||||
toU16 : Str -> Result U16 [InvalidNumStr]*
|
toU16 : Str -> Result U16 [InvalidNumStr]*
|
||||||
|
toU16 = \string -> strToNumHelp string
|
||||||
toI16 : Str -> Result I16 [InvalidNumStr]*
|
toI16 : Str -> Result I16 [InvalidNumStr]*
|
||||||
|
toI16 = \string -> strToNumHelp string
|
||||||
toU8 : Str -> Result U8 [InvalidNumStr]*
|
toU8 : Str -> Result U8 [InvalidNumStr]*
|
||||||
|
toU8 = \string -> strToNumHelp string
|
||||||
toI8 : Str -> Result I8 [InvalidNumStr]*
|
toI8 : Str -> Result I8 [InvalidNumStr]*
|
||||||
|
toI8 = \string -> strToNumHelp string
|
||||||
|
|
||||||
## Gets the byte at the given index, without performing a bounds check
|
## Gets the byte at the given index, without performing a bounds check
|
||||||
getUnsafe : Str, Nat -> U8
|
getUnsafe : Str, Nat -> U8
|
||||||
|
@ -393,3 +432,15 @@ walkScalarsUntilHelp = \string, state, step, index, length ->
|
||||||
newState
|
newState
|
||||||
else
|
else
|
||||||
state
|
state
|
||||||
|
|
||||||
|
strToNum : Str -> { berrorcode : U8, aresult : Num * }
|
||||||
|
|
||||||
|
strToNumHelp : Str -> Result (Num a) [InvalidNumStr]*
|
||||||
|
strToNumHelp = \string ->
|
||||||
|
result : { berrorcode : U8, aresult : Num a }
|
||||||
|
result = strToNum string
|
||||||
|
|
||||||
|
if result.berrorcode == 0 then
|
||||||
|
Ok result.aresult
|
||||||
|
else
|
||||||
|
Err InvalidNumStr
|
||||||
|
|
|
@ -326,7 +326,6 @@ pub const STR_TO_DECIMAL: &str = "roc_builtins.str.to_decimal";
|
||||||
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
||||||
pub const STR_SUBSTRING_UNSAFE: &str = "roc_builtins.str.substring_unsafe";
|
pub const STR_SUBSTRING_UNSAFE: &str = "roc_builtins.str.substring_unsafe";
|
||||||
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
||||||
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
|
|
||||||
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
||||||
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
|
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
|
||||||
pub const STR_TRIM: &str = "roc_builtins.str.trim";
|
pub const STR_TRIM: &str = "roc_builtins.str.trim";
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,12 @@ impl<T> Default for VecSet<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> VecSet<T> {
|
||||||
|
pub fn into_vec(self) -> Vec<T> {
|
||||||
|
self.elements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: PartialEq> VecSet<T> {
|
impl<T: PartialEq> VecSet<T> {
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -143,6 +143,7 @@ fn constrain_untyped_closure(
|
||||||
constraints,
|
constraints,
|
||||||
name,
|
name,
|
||||||
region,
|
region,
|
||||||
|
fn_var,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
closure_var,
|
closure_var,
|
||||||
&mut vars,
|
&mut vars,
|
||||||
|
@ -911,6 +912,7 @@ pub fn constrain_expr(
|
||||||
let lambda_set = Type::ClosureTag {
|
let lambda_set = Type::ClosureTag {
|
||||||
name: *closure_name,
|
name: *closure_name,
|
||||||
captures: vec![],
|
captures: vec![],
|
||||||
|
ambient_function: *function_var,
|
||||||
};
|
};
|
||||||
|
|
||||||
let closure_type = Type::Variable(*closure_var);
|
let closure_type = Type::Variable(*closure_var);
|
||||||
|
@ -1390,6 +1392,7 @@ fn constrain_function_def(
|
||||||
constraints,
|
constraints,
|
||||||
loc_symbol.value,
|
loc_symbol.value,
|
||||||
region,
|
region,
|
||||||
|
expr_var,
|
||||||
&function_def.captured_symbols,
|
&function_def.captured_symbols,
|
||||||
closure_var,
|
closure_var,
|
||||||
&mut vars,
|
&mut vars,
|
||||||
|
@ -1981,13 +1984,13 @@ fn constrain_typed_def(
|
||||||
);
|
);
|
||||||
|
|
||||||
def_pattern_state.constraints.push(constraints.equal_types(
|
def_pattern_state.constraints.push(constraints.equal_types(
|
||||||
expr_type,
|
expr_type.clone(),
|
||||||
annotation_expected,
|
annotation_expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
Region::span_across(&annotation.region, &def.loc_expr.region),
|
Region::span_across(&annotation.region, &def.loc_expr.region),
|
||||||
));
|
));
|
||||||
|
|
||||||
// when a def is annotated, and it's body is a closure, treat this
|
// when a def is annotated, and its body is a closure, treat this
|
||||||
// as a named function (in elm terms) for error messages.
|
// as a named function (in elm terms) for error messages.
|
||||||
//
|
//
|
||||||
// This means we get errors like "the first argument of `f` is weird"
|
// This means we get errors like "the first argument of `f` is weird"
|
||||||
|
@ -2040,6 +2043,7 @@ fn constrain_typed_def(
|
||||||
constraints,
|
constraints,
|
||||||
*name,
|
*name,
|
||||||
region,
|
region,
|
||||||
|
*fn_var,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
closure_var,
|
closure_var,
|
||||||
&mut vars,
|
&mut vars,
|
||||||
|
@ -2114,7 +2118,7 @@ fn constrain_typed_def(
|
||||||
AnnotationSource::TypedBody {
|
AnnotationSource::TypedBody {
|
||||||
region: annotation.region,
|
region: annotation.region,
|
||||||
},
|
},
|
||||||
signature.clone(),
|
expr_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
let ret_constraint = constrain_expr(
|
let ret_constraint = constrain_expr(
|
||||||
|
@ -2124,14 +2128,7 @@ fn constrain_typed_def(
|
||||||
&def.loc_expr.value,
|
&def.loc_expr.value,
|
||||||
annotation_expected,
|
annotation_expected,
|
||||||
);
|
);
|
||||||
let ret_constraint = attach_resolution_constraints(constraints, env, ret_constraint);
|
let expr_con = attach_resolution_constraints(constraints, env, ret_constraint);
|
||||||
|
|
||||||
let cons = [
|
|
||||||
ret_constraint,
|
|
||||||
// Store type into AST vars. We use Store so errors aren't reported twice
|
|
||||||
constraints.store(signature, expr_var, std::file!(), std::line!()),
|
|
||||||
];
|
|
||||||
let expr_con = constraints.and_constraint(cons);
|
|
||||||
|
|
||||||
constrain_def_make_constraint(
|
constrain_def_make_constraint(
|
||||||
constraints,
|
constraints,
|
||||||
|
@ -2514,6 +2511,7 @@ fn constrain_closure_size(
|
||||||
constraints: &mut Constraints,
|
constraints: &mut Constraints,
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
ambient_function: Variable,
|
||||||
captured_symbols: &[(Symbol, Variable)],
|
captured_symbols: &[(Symbol, Variable)],
|
||||||
closure_var: Variable,
|
closure_var: Variable,
|
||||||
variables: &mut Vec<Variable>,
|
variables: &mut Vec<Variable>,
|
||||||
|
@ -2542,6 +2540,7 @@ fn constrain_closure_size(
|
||||||
let closure_type = Type::ClosureTag {
|
let closure_type = Type::ClosureTag {
|
||||||
name,
|
name,
|
||||||
captures: captured_types,
|
captures: captured_types,
|
||||||
|
ambient_function,
|
||||||
};
|
};
|
||||||
|
|
||||||
let finalizer = constraints.equal_types_var(
|
let finalizer = constraints.equal_types_var(
|
||||||
|
@ -2811,6 +2810,7 @@ fn constraint_recursive_function(
|
||||||
constraints,
|
constraints,
|
||||||
loc_symbol.value,
|
loc_symbol.value,
|
||||||
region,
|
region,
|
||||||
|
expr_var,
|
||||||
&function_def.captured_symbols,
|
&function_def.captured_symbols,
|
||||||
closure_var,
|
closure_var,
|
||||||
&mut vars,
|
&mut vars,
|
||||||
|
@ -3220,6 +3220,7 @@ fn rec_defs_help(
|
||||||
constraints,
|
constraints,
|
||||||
*name,
|
*name,
|
||||||
region,
|
region,
|
||||||
|
*fn_var,
|
||||||
captured_symbols,
|
captured_symbols,
|
||||||
closure_var,
|
closure_var,
|
||||||
&mut vars,
|
&mut vars,
|
||||||
|
|
|
@ -65,6 +65,9 @@ flags! {
|
||||||
/// Only use this in single-threaded mode!
|
/// Only use this in single-threaded mode!
|
||||||
ROC_PRINT_UNIFICATIONS
|
ROC_PRINT_UNIFICATIONS
|
||||||
|
|
||||||
|
/// Prints traces of unspecialized lambda set compaction
|
||||||
|
ROC_TRACE_COMPACTION
|
||||||
|
|
||||||
/// Like ROC_PRINT_UNIFICATIONS, in the context of typechecking derived implementations.
|
/// Like ROC_PRINT_UNIFICATIONS, in the context of typechecking derived implementations.
|
||||||
/// Only use this in single-threaded mode!
|
/// Only use this in single-threaded mode!
|
||||||
ROC_PRINT_UNIFICATIONS_DERIVED
|
ROC_PRINT_UNIFICATIONS_DERIVED
|
||||||
|
|
|
@ -325,6 +325,9 @@ fn to_encoder_string(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
||||||
let (body, this_encoder_var) =
|
let (body, this_encoder_var) =
|
||||||
wrap_in_encode_custom(env, encode_string_call, encoder_var, s_sym, Variable::STR);
|
wrap_in_encode_custom(env, encode_string_call, encoder_var, s_sym, Variable::STR);
|
||||||
|
|
||||||
|
// Create fn_var for ambient capture; we fix it up below.
|
||||||
|
let fn_var = synth_var(env.subs, Content::Error);
|
||||||
|
|
||||||
// -[fn_name]->
|
// -[fn_name]->
|
||||||
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
||||||
let fn_clos_var = synth_var(
|
let fn_clos_var = synth_var(
|
||||||
|
@ -333,11 +336,12 @@ fn to_encoder_string(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
|
||||||
solved: fn_name_labels,
|
solved: fn_name_labels,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: fn_var,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// Str -[fn_name]-> (typeof Encode.record [ .. ] = Encoder fmt)
|
// Str -[fn_name]-> (typeof Encode.record [ .. ] = Encoder fmt)
|
||||||
let fn_var = synth_var(
|
env.subs.set_content(
|
||||||
env.subs,
|
fn_var,
|
||||||
Content::Structure(FlatType::Func(
|
Content::Structure(FlatType::Func(
|
||||||
string_var_slice,
|
string_var_slice,
|
||||||
fn_clos_var,
|
fn_clos_var,
|
||||||
|
@ -527,6 +531,9 @@ fn to_encoder_record(
|
||||||
let (body, this_encoder_var) =
|
let (body, this_encoder_var) =
|
||||||
wrap_in_encode_custom(env, encode_record_call, encoder_var, rcd_sym, record_var);
|
wrap_in_encode_custom(env, encode_record_call, encoder_var, rcd_sym, record_var);
|
||||||
|
|
||||||
|
// Create fn_var for ambient capture; we fix it up below.
|
||||||
|
let fn_var = synth_var(env.subs, Content::Error);
|
||||||
|
|
||||||
// -[fn_name]->
|
// -[fn_name]->
|
||||||
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
||||||
let fn_clos_var = synth_var(
|
let fn_clos_var = synth_var(
|
||||||
|
@ -535,12 +542,13 @@ fn to_encoder_record(
|
||||||
solved: fn_name_labels,
|
solved: fn_name_labels,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: fn_var,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// typeof rcd -[fn_name]-> (typeof Encode.record [ .. ] = Encoder fmt)
|
// typeof rcd -[fn_name]-> (typeof Encode.record [ .. ] = Encoder fmt)
|
||||||
let record_var_slice = SubsSlice::insert_into_subs(env.subs, once(record_var));
|
let record_var_slice = SubsSlice::insert_into_subs(env.subs, once(record_var));
|
||||||
let fn_var = synth_var(
|
env.subs.set_content(
|
||||||
env.subs,
|
fn_var,
|
||||||
Content::Structure(FlatType::Func(
|
Content::Structure(FlatType::Func(
|
||||||
record_var_slice,
|
record_var_slice,
|
||||||
fn_clos_var,
|
fn_clos_var,
|
||||||
|
@ -758,6 +766,9 @@ fn to_encoder_tag_union(
|
||||||
tag_union_var,
|
tag_union_var,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create fn_var for ambient capture; we fix it up below.
|
||||||
|
let fn_var = synth_var(env.subs, Content::Error);
|
||||||
|
|
||||||
// -[fn_name]->
|
// -[fn_name]->
|
||||||
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
let fn_name_labels = UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![])));
|
||||||
let fn_clos_var = synth_var(
|
let fn_clos_var = synth_var(
|
||||||
|
@ -766,12 +777,13 @@ fn to_encoder_tag_union(
|
||||||
solved: fn_name_labels,
|
solved: fn_name_labels,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: fn_var,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
// tag_union_var -[fn_name]-> whole_tag_encoders_var
|
// tag_union_var -[fn_name]-> whole_tag_encoders_var
|
||||||
let tag_union_var_slice = SubsSlice::insert_into_subs(env.subs, once(tag_union_var));
|
let tag_union_var_slice = SubsSlice::insert_into_subs(env.subs, once(tag_union_var));
|
||||||
let fn_var = synth_var(
|
env.subs.set_content(
|
||||||
env.subs,
|
fn_var,
|
||||||
Content::Structure(FlatType::Func(
|
Content::Structure(FlatType::Func(
|
||||||
tag_union_var_slice,
|
tag_union_var_slice,
|
||||||
fn_clos_var,
|
fn_clos_var,
|
||||||
|
@ -871,6 +883,9 @@ fn wrap_in_encode_custom(
|
||||||
CalledVia::Space,
|
CalledVia::Space,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create fn_var for ambient capture; we fix it up below.
|
||||||
|
let fn_var = synth_var(env.subs, Content::Error);
|
||||||
|
|
||||||
// -[[FN_name captured_var]]->
|
// -[[FN_name captured_var]]->
|
||||||
let fn_name_labels =
|
let fn_name_labels =
|
||||||
UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![captured_var])));
|
UnionLambdas::insert_into_subs(env.subs, once((fn_name, vec![captured_var])));
|
||||||
|
@ -880,13 +895,14 @@ fn wrap_in_encode_custom(
|
||||||
solved: fn_name_labels,
|
solved: fn_name_labels,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: fn_var,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// bytes, fmt -[[FN_name captured_var]]-> Encode.appendWith bytes encoder fmt
|
// bytes, fmt -[[FN_name captured_var]]-> Encode.appendWith bytes encoder fmt
|
||||||
let args_slice = SubsSlice::insert_into_subs(env.subs, vec![bytes_var, fmt_var]);
|
let args_slice = SubsSlice::insert_into_subs(env.subs, vec![bytes_var, fmt_var]);
|
||||||
let fn_var = synth_var(
|
env.subs.set_content(
|
||||||
env.subs,
|
fn_var,
|
||||||
Content::Structure(FlatType::Func(args_slice, fn_clos_var, Variable::LIST_U8)),
|
Content::Structure(FlatType::Func(args_slice, fn_clos_var, Variable::LIST_U8)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ use crate::llvm::build_list::{
|
||||||
self, allocate_list, empty_polymorphic_list, list_append_unsafe, list_concat, list_drop_at,
|
self, allocate_list, empty_polymorphic_list, list_append_unsafe, list_concat, list_drop_at,
|
||||||
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_prepend,
|
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_prepend,
|
||||||
list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
|
list_replace_unsafe, list_reserve, list_sort_with, list_sublist, list_swap,
|
||||||
list_symbol_to_c_abi, list_to_c_abi, list_with_capacity,
|
list_symbol_to_c_abi, list_to_c_abi, list_with_capacity, pass_update_mode,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{str_from_float, str_from_int, str_from_utf8, str_from_utf8_range};
|
use crate::llvm::build_str::{str_from_float, str_from_int};
|
||||||
use crate::llvm::compare::{generic_eq, generic_neq};
|
use crate::llvm::compare::{generic_eq, generic_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
|
self, argument_type_from_layout, basic_type_from_builtin, basic_type_from_layout,
|
||||||
|
@ -3629,7 +3629,11 @@ fn expose_function_to_host_help_c_abi_v2<'a, 'ctx, 'env>(
|
||||||
let (params, param_types) = match (&roc_return, &cc_return) {
|
let (params, param_types) = match (&roc_return, &cc_return) {
|
||||||
// Drop the "return pointer" if it exists on the roc function
|
// Drop the "return pointer" if it exists on the roc function
|
||||||
// and the c function does not return via pointer
|
// and the c function does not return via pointer
|
||||||
(RocReturn::ByPointer, CCReturn::Return) => (¶ms[..], ¶m_types[1..]),
|
(RocReturn::ByPointer, CCReturn::Return) => {
|
||||||
|
// Roc currently puts the return pointer at the end of the argument list.
|
||||||
|
// As such, we drop the last element here instead of the first.
|
||||||
|
(¶ms[..], ¶m_types[..param_types.len() - 1])
|
||||||
|
}
|
||||||
// Drop the return pointer the other way, if the C function returns by pointer but Roc
|
// Drop the return pointer the other way, if the C function returns by pointer but Roc
|
||||||
// doesn't
|
// doesn't
|
||||||
(RocReturn::Return, CCReturn::ByPointer) => (¶ms[1..], ¶m_types[..]),
|
(RocReturn::Return, CCReturn::ByPointer) => (¶ms[1..], ¶m_types[..]),
|
||||||
|
@ -5354,18 +5358,31 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
str_from_float(env, scope, args[0])
|
str_from_float(env, scope, args[0])
|
||||||
}
|
}
|
||||||
StrFromUtf8 => {
|
|
||||||
// Str.fromUtf8 : List U8 -> Result Str Utf8Problem
|
|
||||||
debug_assert_eq!(args.len(), 1);
|
|
||||||
|
|
||||||
str_from_utf8(env, scope, args[0], update_mode)
|
|
||||||
}
|
|
||||||
StrFromUtf8Range => {
|
StrFromUtf8Range => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
let count_and_start = load_symbol(scope, &args[1]).into_struct_value();
|
let list = args[0];
|
||||||
|
let start = load_symbol(scope, &args[1]);
|
||||||
|
let count = load_symbol(scope, &args[2]);
|
||||||
|
|
||||||
str_from_utf8_range(env, scope, args[0], count_and_start)
|
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
||||||
|
let result_ptr = env
|
||||||
|
.builder
|
||||||
|
.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
||||||
|
|
||||||
|
call_void_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
result_ptr.into(),
|
||||||
|
list_symbol_to_c_abi(env, scope, list).into(),
|
||||||
|
start,
|
||||||
|
count,
|
||||||
|
pass_update_mode(env, update_mode),
|
||||||
|
],
|
||||||
|
bitcode::STR_FROM_UTF8_RANGE,
|
||||||
|
);
|
||||||
|
|
||||||
|
crate::llvm::build_str::decode_from_utf8_result(env, result_ptr).into()
|
||||||
}
|
}
|
||||||
StrToUtf8 => {
|
StrToUtf8 => {
|
||||||
// Str.fromInt : Str -> List U8
|
// Str.fromInt : Str -> List U8
|
||||||
|
@ -5549,10 +5566,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ListSublist => {
|
ListSublist => {
|
||||||
// List.sublist : List elem, { start : Nat, len : Nat } -> List elem
|
|
||||||
//
|
|
||||||
// As a low-level, record is destructed
|
|
||||||
// List.sublist : List elem, start : Nat, len : Nat -> List elem
|
|
||||||
debug_assert_eq!(args.len(), 3);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use crate::llvm::bitcode::{call_bitcode_fn, call_str_bitcode_fn, call_void_bitcode_fn};
|
use crate::llvm::bitcode::{call_bitcode_fn, call_str_bitcode_fn};
|
||||||
use crate::llvm::build::{Env, Scope};
|
use crate::llvm::build::{Env, Scope};
|
||||||
use crate::llvm::build_list::pass_update_mode;
|
|
||||||
use inkwell::builder::Builder;
|
use inkwell::builder::Builder;
|
||||||
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, IntValue, PointerValue, StructValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use morphic_lib::UpdateMode;
|
|
||||||
use roc_builtins::bitcode::{self, IntWidth};
|
use roc_builtins::bitcode::{self, IntWidth};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
use roc_target::PtrWidth;
|
use roc_target::PtrWidth;
|
||||||
|
|
||||||
use super::build::{create_entry_block_alloca, load_symbol};
|
use super::build::{create_entry_block_alloca, load_symbol};
|
||||||
use super::build_list::list_symbol_to_c_abi;
|
|
||||||
|
|
||||||
pub static CHAR_LAYOUT: Layout = Layout::u8();
|
pub static CHAR_LAYOUT: Layout = Layout::u8();
|
||||||
|
|
||||||
|
@ -70,7 +67,7 @@ pub fn str_from_int<'a, 'ctx, 'env>(
|
||||||
call_str_bitcode_fn(env, &[value.into()], &bitcode::STR_FROM_INT[int_width])
|
call_str_bitcode_fn(env, &[value.into()], &bitcode::STR_FROM_INT[int_width])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
pub fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
pointer: PointerValue<'ctx>,
|
pointer: PointerValue<'ctx>,
|
||||||
) -> StructValue<'ctx> {
|
) -> StructValue<'ctx> {
|
||||||
|
@ -106,67 +103,6 @@ fn decode_from_utf8_result<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
|
|
||||||
pub fn str_from_utf8_range<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
scope: &Scope<'a, 'ctx>,
|
|
||||||
list: Symbol,
|
|
||||||
count_and_start: StructValue<'ctx>,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
|
||||||
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
|
||||||
|
|
||||||
let count = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(count_and_start, 0, "get_count")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let start = env
|
|
||||||
.builder
|
|
||||||
.build_extract_value(count_and_start, 1, "get_start")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
result_ptr.into(),
|
|
||||||
list_symbol_to_c_abi(env, scope, list).into(),
|
|
||||||
count,
|
|
||||||
start,
|
|
||||||
],
|
|
||||||
bitcode::STR_FROM_UTF8_RANGE,
|
|
||||||
);
|
|
||||||
|
|
||||||
decode_from_utf8_result(env, result_ptr).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Str.fromUtf8 : List U8 -> { a : Bool, b : Str, c : Nat, d : I8 }
|
|
||||||
pub fn str_from_utf8<'a, 'ctx, 'env>(
|
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
|
||||||
scope: &Scope<'a, 'ctx>,
|
|
||||||
list: Symbol,
|
|
||||||
update_mode: UpdateMode,
|
|
||||||
) -> BasicValueEnum<'ctx> {
|
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
|
|
||||||
let result_ptr = builder.build_alloca(result_type, "alloca_utf8_validate_bytes_result");
|
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
|
||||||
env,
|
|
||||||
&[
|
|
||||||
result_ptr.into(),
|
|
||||||
list_symbol_to_c_abi(env, scope, list).into(),
|
|
||||||
pass_update_mode(env, update_mode),
|
|
||||||
],
|
|
||||||
bitcode::STR_FROM_UTF8,
|
|
||||||
);
|
|
||||||
|
|
||||||
decode_from_utf8_result(env, result_ptr).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Str.fromFloat : Int -> Str
|
/// Str.fromFloat : Int -> Str
|
||||||
pub fn str_from_float<'a, 'ctx, 'env>(
|
pub fn str_from_float<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -1595,7 +1595,8 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
// Store the tag ID (if any)
|
// Store the tag ID (if any)
|
||||||
if stores_tag_id_as_data {
|
if stores_tag_id_as_data {
|
||||||
let id_offset = data_offset + data_size - data_alignment;
|
let id_offset =
|
||||||
|
data_offset + union_layout.data_size_without_tag_id(TARGET_INFO).unwrap();
|
||||||
|
|
||||||
let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO);
|
let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO);
|
||||||
let id_align = Align::from(id_align);
|
let id_align = Align::from(id_align);
|
||||||
|
@ -1678,8 +1679,7 @@ impl<'a> WasmBackend<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if union_layout.stores_tag_id_as_data(TARGET_INFO) {
|
if union_layout.stores_tag_id_as_data(TARGET_INFO) {
|
||||||
let (data_size, data_alignment) = union_layout.data_size_and_alignment(TARGET_INFO);
|
let id_offset = union_layout.data_size_without_tag_id(TARGET_INFO).unwrap();
|
||||||
let id_offset = data_size - data_alignment;
|
|
||||||
|
|
||||||
let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO);
|
let id_align = union_layout.tag_id_builtin().alignment_bytes(TARGET_INFO);
|
||||||
let id_align = Align::from(id_align);
|
let id_align = Align::from(id_align);
|
||||||
|
|
|
@ -264,16 +264,19 @@ impl<'a> LowLevelCall<'a> {
|
||||||
}
|
}
|
||||||
StrFromInt => self.num_to_str(backend),
|
StrFromInt => self.num_to_str(backend),
|
||||||
StrFromFloat => self.num_to_str(backend),
|
StrFromFloat => self.num_to_str(backend),
|
||||||
StrFromUtf8 => {
|
StrFromUtf8Range => {
|
||||||
/*
|
/*
|
||||||
Low-level op returns a struct with all the data for both Ok and Err.
|
Low-level op returns a struct with all the data for both Ok and Err.
|
||||||
Roc AST wrapper converts this to a tag union, with app-dependent tag IDs.
|
Roc AST wrapper converts this to a tag union, with app-dependent tag IDs.
|
||||||
|
|
||||||
fromUtf8C(output: *FromUtf8Result, arg: RocList, update_mode: UpdateMode) callconv(.C) void
|
|
||||||
output: *FromUtf8Result i32
|
output: *FromUtf8Result i32
|
||||||
arg: RocList i64, i32
|
arg: RocList i64, i32
|
||||||
|
start i32
|
||||||
|
count i32
|
||||||
update_mode: UpdateMode i32
|
update_mode: UpdateMode i32
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// loads arg, start, count
|
||||||
backend.storage.load_symbols_for_call(
|
backend.storage.load_symbols_for_call(
|
||||||
backend.env.arena,
|
backend.env.arena,
|
||||||
&mut backend.code_builder,
|
&mut backend.code_builder,
|
||||||
|
@ -283,9 +286,8 @@ impl<'a> LowLevelCall<'a> {
|
||||||
CallConv::Zig,
|
CallConv::Zig,
|
||||||
);
|
);
|
||||||
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
|
||||||
backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8, 4, false);
|
backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8_RANGE, 6, false);
|
||||||
}
|
}
|
||||||
StrFromUtf8Range => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_RANGE),
|
|
||||||
StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT),
|
StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT),
|
||||||
StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT),
|
StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT),
|
||||||
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
|
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
|
||||||
|
|
|
@ -9,7 +9,7 @@ use roc_collections::MutMap;
|
||||||
use roc_derive_key::GlobalDerivedSymbols;
|
use roc_derive_key::GlobalDerivedSymbols;
|
||||||
use roc_module::symbol::ModuleId;
|
use roc_module::symbol::ModuleId;
|
||||||
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
|
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
|
||||||
use roc_types::subs::Content;
|
use roc_types::subs::{Content, FlatType, LambdaSet};
|
||||||
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
||||||
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
|
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ impl Phase for LatePhase<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn copy_lambda_set_var_to_home_subs(
|
fn copy_lambda_set_ambient_function_to_home_subs(
|
||||||
&self,
|
&self,
|
||||||
external_lambda_set_var: Variable,
|
external_lambda_set_var: Variable,
|
||||||
external_module_id: ModuleId,
|
external_module_id: ModuleId,
|
||||||
|
@ -99,11 +99,12 @@ impl Phase for LatePhase<'_> {
|
||||||
) -> Variable {
|
) -> Variable {
|
||||||
match (external_module_id == self.home, self.abilities) {
|
match (external_module_id == self.home, self.abilities) {
|
||||||
(true, _) | (false, AbilitiesView::Module(_)) => {
|
(true, _) | (false, AbilitiesView::Module(_)) => {
|
||||||
debug_assert!(matches!(
|
// The lambda set (and hence its ambient function) should be available in the
|
||||||
target_subs.get_content_without_compacting(external_lambda_set_var),
|
// current subs.
|
||||||
Content::LambdaSet(..)
|
let LambdaSet {
|
||||||
));
|
ambient_function, ..
|
||||||
external_lambda_set_var
|
} = target_subs.get_lambda_set(external_lambda_set_var);
|
||||||
|
ambient_function
|
||||||
}
|
}
|
||||||
(false, AbilitiesView::World(wa)) => {
|
(false, AbilitiesView::World(wa)) => {
|
||||||
let mut world = wa.world.write().unwrap();
|
let mut world = wa.world.write().unwrap();
|
||||||
|
@ -113,17 +114,27 @@ impl Phase for LatePhase<'_> {
|
||||||
.stored_specialization_lambda_set_vars
|
.stored_specialization_lambda_set_vars
|
||||||
.get(&external_lambda_set_var)
|
.get(&external_lambda_set_var)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let LambdaSet {
|
||||||
|
ambient_function, ..
|
||||||
|
} = module_types
|
||||||
|
.storage_subs
|
||||||
|
.as_inner()
|
||||||
|
.get_lambda_set(storage_lambda_set_var);
|
||||||
|
|
||||||
let copied = module_types
|
let copied = module_types
|
||||||
.storage_subs
|
.storage_subs
|
||||||
.export_variable_to(target_subs, storage_lambda_set_var);
|
// TODO: I think this is always okay, but revisit later when we're in a more
|
||||||
let our_lambda_set_var = copied.variable;
|
// stable position to see if we can get rid of the bookkeeping done as a result
|
||||||
|
// of this.
|
||||||
|
.export_variable_to_directly_to_use_site(target_subs, ambient_function);
|
||||||
|
let our_ambient_function_var = copied.variable;
|
||||||
|
|
||||||
debug_assert!(matches!(
|
debug_assert!(matches!(
|
||||||
target_subs.get_content_without_compacting(our_lambda_set_var),
|
target_subs.get_content_without_compacting(our_ambient_function_var),
|
||||||
Content::LambdaSet(..)
|
Content::Structure(FlatType::Func(..))
|
||||||
));
|
));
|
||||||
|
|
||||||
our_lambda_set_var
|
our_ambient_function_var
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +163,7 @@ pub fn unify(
|
||||||
|
|
||||||
let late_phase = LatePhase { home, abilities };
|
let late_phase = LatePhase { home, abilities };
|
||||||
|
|
||||||
compact_lambda_sets_of_vars(
|
let must_implement_constraints = compact_lambda_sets_of_vars(
|
||||||
subs,
|
subs,
|
||||||
arena,
|
arena,
|
||||||
&mut pools,
|
&mut pools,
|
||||||
|
@ -160,6 +171,11 @@ pub fn unify(
|
||||||
&late_phase,
|
&late_phase,
|
||||||
derived_symbols,
|
derived_symbols,
|
||||||
);
|
);
|
||||||
|
// At this point we can't do anything with must-implement constraints, since we're no
|
||||||
|
// longer solving. We must assume that they were totally caught during solving.
|
||||||
|
// After we land https://github.com/rtfeldman/roc/issues/3207 this concern should totally
|
||||||
|
// go away.
|
||||||
|
let _ = must_implement_constraints;
|
||||||
// Pools are only used to keep track of variable ranks for generalization purposes.
|
// Pools are only used to keep track of variable ranks for generalization purposes.
|
||||||
// Since we break generalization during monomorphization, `pools` is irrelevant
|
// Since we break generalization during monomorphization, `pools` is irrelevant
|
||||||
// here. We only need it for `compact_lambda_sets_of_vars`, which is also used in a
|
// here. We only need it for `compact_lambda_sets_of_vars`, which is also used in a
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_load_internal::file::Threading;
|
use roc_load_internal::file::{LoadingProblem, Threading};
|
||||||
use roc_module::symbol::ModuleId;
|
use roc_module::symbol::ModuleId;
|
||||||
|
|
||||||
const MODULES: &[(ModuleId, &str)] = &[
|
const MODULES: &[(ModuleId, &str)] = &[
|
||||||
|
@ -47,7 +47,16 @@ fn write_subs_for_module(module_id: ModuleId, filename: &str) {
|
||||||
Threading::AllAvailable,
|
Threading::AllAvailable,
|
||||||
);
|
);
|
||||||
|
|
||||||
let module = res_module.unwrap();
|
let module = match res_module {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(LoadingProblem::FormattedReport(report)) => {
|
||||||
|
panic!("{}", report);
|
||||||
|
}
|
||||||
|
Err(other) => {
|
||||||
|
panic!("build_file failed with error:\n{:?}", other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let subs = module.solved.inner();
|
let subs = module.solved.inner();
|
||||||
let exposed_vars_by_symbol: Vec<_> = module.exposed_to_host.into_iter().collect();
|
let exposed_vars_by_symbol: Vec<_> = module.exposed_to_host.into_iter().collect();
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,9 @@ fn generate_entry_docs<'a>(
|
||||||
|
|
||||||
ValueDef::Body(_, _) => (),
|
ValueDef::Body(_, _) => (),
|
||||||
|
|
||||||
ValueDef::Expect(c) => todo!("documentation for tests {:?}", c),
|
ValueDef::Expect(_) => {
|
||||||
|
// Don't generate docs for `expect`s
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Ok(type_index) => match &defs.type_defs[type_index.index()] {
|
Ok(type_index) => match &defs.type_defs[type_index.index()] {
|
||||||
TypeDef::Alias {
|
TypeDef::Alias {
|
||||||
|
|
|
@ -5125,12 +5125,12 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
||||||
}
|
}
|
||||||
RootIsInterface => {
|
RootIsInterface => {
|
||||||
let doc = alloc.stack([
|
let doc = alloc.stack([
|
||||||
alloc.reflow(r"The input file is an interface module, but only app modules can be ran."),
|
alloc.reflow(r"The input file is an interface module, but only app modules can be run."),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
||||||
alloc.reflow(r"but won't output any executable."),
|
alloc.reflow(r"but won't output any executable."),
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
filename: "UNKNOWN.roc".into(),
|
filename: "UNKNOWN.roc".into(),
|
||||||
|
@ -5141,12 +5141,12 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
||||||
}
|
}
|
||||||
RootIsHosted => {
|
RootIsHosted => {
|
||||||
let doc = alloc.stack([
|
let doc = alloc.stack([
|
||||||
alloc.reflow(r"The input file is a hosted module, but only app modules can be ran."),
|
alloc.reflow(r"The input file is a hosted module, but only app modules can be run."),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
||||||
alloc.reflow(r"but won't output any executable."),
|
alloc.reflow(r"but won't output any executable."),
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
filename: "UNKNOWN.roc".into(),
|
filename: "UNKNOWN.roc".into(),
|
||||||
|
@ -5157,12 +5157,12 @@ fn to_missing_platform_report(module_id: ModuleId, other: PlatformPath) -> Strin
|
||||||
}
|
}
|
||||||
RootIsPlatformModule => {
|
RootIsPlatformModule => {
|
||||||
let doc = alloc.stack([
|
let doc = alloc.stack([
|
||||||
alloc.reflow(r"The input file is a package config file, but only app modules can be ran."),
|
alloc.reflow(r"The input file is a package config file, but only app modules can be run."),
|
||||||
alloc.concat([
|
alloc.concat([
|
||||||
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
alloc.reflow(r"I will still parse and typecheck the input file and its dependencies, "),
|
||||||
alloc.reflow(r"but won't output any executable."),
|
alloc.reflow(r"but won't output any executable."),
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
filename: "UNKNOWN.roc".into(),
|
filename: "UNKNOWN.roc".into(),
|
||||||
|
|
|
@ -15,7 +15,6 @@ pub enum LowLevel {
|
||||||
StrCountGraphemes,
|
StrCountGraphemes,
|
||||||
StrCountUtf8Bytes,
|
StrCountUtf8Bytes,
|
||||||
StrFromInt,
|
StrFromInt,
|
||||||
StrFromUtf8,
|
|
||||||
StrFromUtf8Range,
|
StrFromUtf8Range,
|
||||||
StrToUtf8,
|
StrToUtf8,
|
||||||
StrRepeat,
|
StrRepeat,
|
||||||
|
@ -161,141 +160,181 @@ impl LowLevel {
|
||||||
pub enum LowLevelWrapperType {
|
pub enum LowLevelWrapperType {
|
||||||
/// This wrapper function contains no logic and we can remove it in code gen
|
/// This wrapper function contains no logic and we can remove it in code gen
|
||||||
CanBeReplacedBy(LowLevel),
|
CanBeReplacedBy(LowLevel),
|
||||||
/// This wrapper function contains important logic and we cannot remove it in code gen
|
|
||||||
WrapperIsRequired,
|
|
||||||
NotALowLevelWrapper,
|
NotALowLevelWrapper,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LowLevelWrapperType {
|
impl LowLevelWrapperType {
|
||||||
pub fn from_symbol(symbol: Symbol) -> LowLevelWrapperType {
|
pub fn from_symbol(symbol: Symbol) -> LowLevelWrapperType {
|
||||||
use LowLevel::*;
|
for_symbol_help(symbol)
|
||||||
use LowLevelWrapperType::*;
|
|
||||||
|
|
||||||
match symbol {
|
|
||||||
Symbol::STR_CONCAT => CanBeReplacedBy(StrConcat),
|
|
||||||
Symbol::STR_GET_UNSAFE => CanBeReplacedBy(StrGetUnsafe),
|
|
||||||
Symbol::STR_TO_SCALARS => CanBeReplacedBy(StrToScalars),
|
|
||||||
Symbol::STR_JOIN_WITH => CanBeReplacedBy(StrJoinWith),
|
|
||||||
Symbol::STR_IS_EMPTY => CanBeReplacedBy(StrIsEmpty),
|
|
||||||
Symbol::STR_STARTS_WITH => CanBeReplacedBy(StrStartsWith),
|
|
||||||
Symbol::STR_STARTS_WITH_SCALAR => CanBeReplacedBy(StrStartsWithScalar),
|
|
||||||
Symbol::STR_ENDS_WITH => CanBeReplacedBy(StrEndsWith),
|
|
||||||
Symbol::STR_SPLIT => CanBeReplacedBy(StrSplit),
|
|
||||||
Symbol::STR_COUNT_GRAPHEMES => CanBeReplacedBy(StrCountGraphemes),
|
|
||||||
Symbol::STR_COUNT_UTF8_BYTES => CanBeReplacedBy(StrCountUtf8Bytes),
|
|
||||||
Symbol::STR_FROM_UTF8 => WrapperIsRequired,
|
|
||||||
Symbol::STR_FROM_UTF8_RANGE => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_UTF8 => CanBeReplacedBy(StrToUtf8),
|
|
||||||
Symbol::STR_REPEAT => CanBeReplacedBy(StrRepeat),
|
|
||||||
Symbol::STR_RESERVE => CanBeReplacedBy(StrReserve),
|
|
||||||
Symbol::STR_APPEND_SCALAR_UNSAFE => CanBeReplacedBy(StrAppendScalar),
|
|
||||||
Symbol::STR_TRIM => CanBeReplacedBy(StrTrim),
|
|
||||||
Symbol::STR_TRIM_LEFT => CanBeReplacedBy(StrTrimLeft),
|
|
||||||
Symbol::STR_TRIM_RIGHT => CanBeReplacedBy(StrTrimRight),
|
|
||||||
Symbol::STR_TO_DEC => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_F64 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_F32 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_NAT => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_U128 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_I128 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_U64 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_I64 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_U32 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_I32 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_U16 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_I16 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_U8 => WrapperIsRequired,
|
|
||||||
Symbol::STR_TO_I8 => WrapperIsRequired,
|
|
||||||
Symbol::LIST_LEN => CanBeReplacedBy(ListLen),
|
|
||||||
Symbol::LIST_GET => WrapperIsRequired,
|
|
||||||
Symbol::LIST_REPLACE => WrapperIsRequired,
|
|
||||||
Symbol::LIST_CONCAT => CanBeReplacedBy(ListConcat),
|
|
||||||
Symbol::LIST_APPEND_UNSAFE => CanBeReplacedBy(ListAppendUnsafe),
|
|
||||||
Symbol::LIST_PREPEND => CanBeReplacedBy(ListPrepend),
|
|
||||||
Symbol::LIST_MAP => WrapperIsRequired,
|
|
||||||
Symbol::LIST_MAP2 => WrapperIsRequired,
|
|
||||||
Symbol::LIST_MAP3 => WrapperIsRequired,
|
|
||||||
Symbol::LIST_MAP4 => WrapperIsRequired,
|
|
||||||
Symbol::LIST_SORT_WITH => WrapperIsRequired,
|
|
||||||
Symbol::LIST_SUBLIST => WrapperIsRequired,
|
|
||||||
Symbol::LIST_DROP_AT => CanBeReplacedBy(ListDropAt),
|
|
||||||
Symbol::LIST_SWAP => CanBeReplacedBy(ListSwap),
|
|
||||||
Symbol::LIST_ANY => WrapperIsRequired,
|
|
||||||
Symbol::LIST_ALL => WrapperIsRequired,
|
|
||||||
Symbol::LIST_FIND => WrapperIsRequired,
|
|
||||||
Symbol::DICT_LEN => CanBeReplacedBy(DictSize),
|
|
||||||
Symbol::DICT_EMPTY => CanBeReplacedBy(DictEmpty),
|
|
||||||
Symbol::DICT_INSERT => CanBeReplacedBy(DictInsert),
|
|
||||||
Symbol::DICT_REMOVE => CanBeReplacedBy(DictRemove),
|
|
||||||
Symbol::DICT_CONTAINS => CanBeReplacedBy(DictContains),
|
|
||||||
Symbol::DICT_GET => WrapperIsRequired,
|
|
||||||
Symbol::DICT_KEYS => CanBeReplacedBy(DictKeys),
|
|
||||||
Symbol::DICT_VALUES => CanBeReplacedBy(DictValues),
|
|
||||||
Symbol::DICT_UNION => CanBeReplacedBy(DictUnion),
|
|
||||||
Symbol::DICT_INTERSECTION => CanBeReplacedBy(DictIntersection),
|
|
||||||
Symbol::DICT_DIFFERENCE => CanBeReplacedBy(DictDifference),
|
|
||||||
Symbol::DICT_WALK => WrapperIsRequired,
|
|
||||||
Symbol::SET_FROM_LIST => CanBeReplacedBy(SetFromList),
|
|
||||||
Symbol::NUM_ADD => CanBeReplacedBy(NumAdd),
|
|
||||||
Symbol::NUM_ADD_WRAP => CanBeReplacedBy(NumAddWrap),
|
|
||||||
Symbol::NUM_ADD_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_ADD_SATURATED => CanBeReplacedBy(NumAddSaturated),
|
|
||||||
Symbol::NUM_SUB => CanBeReplacedBy(NumSub),
|
|
||||||
Symbol::NUM_SUB_WRAP => CanBeReplacedBy(NumSubWrap),
|
|
||||||
Symbol::NUM_SUB_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_SUB_SATURATED => CanBeReplacedBy(NumSubSaturated),
|
|
||||||
Symbol::NUM_MUL => CanBeReplacedBy(NumMul),
|
|
||||||
Symbol::NUM_MUL_WRAP => CanBeReplacedBy(NumMulWrap),
|
|
||||||
Symbol::NUM_MUL_SATURATED => CanBeReplacedBy(NumMulSaturated),
|
|
||||||
Symbol::NUM_MUL_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_GT => CanBeReplacedBy(NumGt),
|
|
||||||
Symbol::NUM_GTE => CanBeReplacedBy(NumGte),
|
|
||||||
Symbol::NUM_LT => CanBeReplacedBy(NumLt),
|
|
||||||
Symbol::NUM_LTE => CanBeReplacedBy(NumLte),
|
|
||||||
Symbol::NUM_COMPARE => CanBeReplacedBy(NumCompare),
|
|
||||||
Symbol::NUM_DIV_FRAC => CanBeReplacedBy(NumDivUnchecked),
|
|
||||||
Symbol::NUM_DIV_FRAC_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_DIV_CEIL => CanBeReplacedBy(NumDivCeilUnchecked),
|
|
||||||
Symbol::NUM_DIV_CEIL_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_REM => CanBeReplacedBy(NumRemUnchecked),
|
|
||||||
Symbol::NUM_REM_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_IS_MULTIPLE_OF => CanBeReplacedBy(NumIsMultipleOf),
|
|
||||||
Symbol::NUM_ABS => CanBeReplacedBy(NumAbs),
|
|
||||||
Symbol::NUM_NEG => CanBeReplacedBy(NumNeg),
|
|
||||||
Symbol::NUM_SIN => CanBeReplacedBy(NumSin),
|
|
||||||
Symbol::NUM_COS => CanBeReplacedBy(NumCos),
|
|
||||||
Symbol::NUM_SQRT => CanBeReplacedBy(NumSqrtUnchecked),
|
|
||||||
Symbol::NUM_SQRT_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_LOG => CanBeReplacedBy(NumLogUnchecked),
|
|
||||||
Symbol::NUM_LOG_CHECKED => WrapperIsRequired,
|
|
||||||
Symbol::NUM_ROUND => CanBeReplacedBy(NumRound),
|
|
||||||
Symbol::NUM_TO_FRAC => CanBeReplacedBy(NumToFrac),
|
|
||||||
Symbol::NUM_POW => CanBeReplacedBy(NumPow),
|
|
||||||
Symbol::NUM_CEILING => CanBeReplacedBy(NumCeiling),
|
|
||||||
Symbol::NUM_POW_INT => CanBeReplacedBy(NumPowInt),
|
|
||||||
Symbol::NUM_FLOOR => CanBeReplacedBy(NumFloor),
|
|
||||||
Symbol::NUM_TO_STR => CanBeReplacedBy(NumToStr),
|
|
||||||
// => CanBeReplacedBy(NumIsFinite),
|
|
||||||
Symbol::NUM_ATAN => CanBeReplacedBy(NumAtan),
|
|
||||||
Symbol::NUM_ACOS => CanBeReplacedBy(NumAcos),
|
|
||||||
Symbol::NUM_ASIN => CanBeReplacedBy(NumAsin),
|
|
||||||
Symbol::NUM_BYTES_TO_U16 => WrapperIsRequired,
|
|
||||||
Symbol::NUM_BYTES_TO_U32 => WrapperIsRequired,
|
|
||||||
Symbol::NUM_BITWISE_AND => CanBeReplacedBy(NumBitwiseAnd),
|
|
||||||
Symbol::NUM_BITWISE_XOR => CanBeReplacedBy(NumBitwiseXor),
|
|
||||||
Symbol::NUM_BITWISE_OR => CanBeReplacedBy(NumBitwiseOr),
|
|
||||||
Symbol::NUM_SHIFT_LEFT => CanBeReplacedBy(NumShiftLeftBy),
|
|
||||||
Symbol::NUM_SHIFT_RIGHT => CanBeReplacedBy(NumShiftRightBy),
|
|
||||||
Symbol::NUM_SHIFT_RIGHT_ZERO_FILL => CanBeReplacedBy(NumShiftRightZfBy),
|
|
||||||
Symbol::NUM_INT_CAST => CanBeReplacedBy(NumIntCast),
|
|
||||||
Symbol::BOOL_EQ => CanBeReplacedBy(Eq),
|
|
||||||
Symbol::BOOL_NEQ => CanBeReplacedBy(NotEq),
|
|
||||||
Symbol::BOOL_AND => CanBeReplacedBy(And),
|
|
||||||
Symbol::BOOL_OR => CanBeReplacedBy(Or),
|
|
||||||
Symbol::BOOL_NOT => CanBeReplacedBy(Not),
|
|
||||||
// => CanBeReplacedBy(Hash),
|
|
||||||
// => CanBeReplacedBy(ExpectTrue),
|
|
||||||
_ => NotALowLevelWrapper,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We use a rust macro to ensure that every LowLevel gets handled
|
||||||
|
macro_rules! map_symbol_to_lowlevel {
|
||||||
|
($($lowlevel:ident <= $symbol:ident),* $(,)?) => {
|
||||||
|
|
||||||
|
fn for_symbol_help(symbol: Symbol) -> LowLevelWrapperType {
|
||||||
|
use $crate::low_level::LowLevelWrapperType::*;
|
||||||
|
|
||||||
|
// expands to a big (but non-exhaustive) match on symbols and maps them to a lowlevel
|
||||||
|
match symbol {
|
||||||
|
$(
|
||||||
|
Symbol::$symbol => CanBeReplacedBy(LowLevel::$lowlevel),
|
||||||
|
)*
|
||||||
|
|
||||||
|
_ => NotALowLevelWrapper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _enforce_exhaustiveness(lowlevel: LowLevel) -> Symbol {
|
||||||
|
// when adding a new lowlevel, this match will stop being exhaustive, and give a
|
||||||
|
// compiler error. Most likely, you are adding a new lowlevel that maps directly to a
|
||||||
|
// symbol. For instance, you want to have `List.foo` to stand for the `ListFoo`
|
||||||
|
// lowlevel. In that case, see below in the invocation of `map_symbol_to_lowlevel!`
|
||||||
|
//
|
||||||
|
// Below, we explicitly handle some exceptions to the pattern where a lowlevel maps
|
||||||
|
// directly to a symbol. If you are unsure if your lowlevel is an exception, assume
|
||||||
|
// that it isn't and just see if that works.
|
||||||
|
match lowlevel {
|
||||||
|
$(
|
||||||
|
LowLevel::$lowlevel => Symbol::$symbol,
|
||||||
|
)*
|
||||||
|
|
||||||
|
// these are higher-order lowlevels. these need the surrounding
|
||||||
|
// function to provide enough type information for code generation
|
||||||
|
LowLevel::ListMap => unreachable!(),
|
||||||
|
LowLevel::ListMap2 => unreachable!(),
|
||||||
|
LowLevel::ListMap3 => unreachable!(),
|
||||||
|
LowLevel::ListMap4 => unreachable!(),
|
||||||
|
LowLevel::ListSortWith => unreachable!(),
|
||||||
|
LowLevel::DictWalk => unreachable!(),
|
||||||
|
|
||||||
|
// (un)boxing is handled in a custom way
|
||||||
|
LowLevel::BoxExpr => unreachable!(),
|
||||||
|
LowLevel::UnboxExpr => unreachable!(),
|
||||||
|
|
||||||
|
// these functions return polymorphic values
|
||||||
|
LowLevel::NumIntCast => unreachable!(),
|
||||||
|
LowLevel::NumToFloatCast => unreachable!(),
|
||||||
|
LowLevel::NumToIntChecked => unreachable!(),
|
||||||
|
LowLevel::NumToFloatChecked => unreachable!(),
|
||||||
|
LowLevel::NumDivUnchecked => unreachable!(),
|
||||||
|
LowLevel::DictEmpty => unreachable!(),
|
||||||
|
|
||||||
|
// these are used internally and not tied to a symbol
|
||||||
|
LowLevel::Hash => unimplemented!(),
|
||||||
|
LowLevel::PtrCast => unimplemented!(),
|
||||||
|
LowLevel::RefCountInc => unimplemented!(),
|
||||||
|
LowLevel::RefCountDec => unimplemented!(),
|
||||||
|
|
||||||
|
// these are not implemented, not sure why
|
||||||
|
LowLevel::StrFromInt => unimplemented!(),
|
||||||
|
LowLevel::StrFromFloat => unimplemented!(),
|
||||||
|
LowLevel::NumIsFinite => unimplemented!(),
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// here is where we actually specify the mapping for the fast majority of cases that follow the
|
||||||
|
// pattern of a symbol mapping directly to a lowlevel. In other words, most lowlevels (left) are generated
|
||||||
|
// by only one specific symbol (right)
|
||||||
|
map_symbol_to_lowlevel! {
|
||||||
|
StrConcat <= STR_CONCAT,
|
||||||
|
StrJoinWith <= STR_JOIN_WITH,
|
||||||
|
StrIsEmpty <= STR_IS_EMPTY,
|
||||||
|
StrStartsWith <= STR_STARTS_WITH,
|
||||||
|
StrStartsWithScalar <= STR_STARTS_WITH_SCALAR,
|
||||||
|
StrEndsWith <= STR_ENDS_WITH,
|
||||||
|
StrSplit <= STR_SPLIT,
|
||||||
|
StrCountGraphemes <= STR_COUNT_GRAPHEMES,
|
||||||
|
StrCountUtf8Bytes <= STR_COUNT_UTF8_BYTES,
|
||||||
|
StrFromUtf8Range <= STR_FROM_UTF8_RANGE_LOWLEVEL,
|
||||||
|
StrToUtf8 <= STR_TO_UTF8,
|
||||||
|
StrRepeat <= STR_REPEAT,
|
||||||
|
StrTrim <= STR_TRIM,
|
||||||
|
StrTrimLeft <= STR_TRIM_LEFT,
|
||||||
|
StrTrimRight <= STR_TRIM_RIGHT,
|
||||||
|
StrToScalars <= STR_TO_SCALARS,
|
||||||
|
StrGetUnsafe <= STR_GET_UNSAFE,
|
||||||
|
StrSubstringUnsafe <= STR_SUBSTRING_UNSAFE,
|
||||||
|
StrReserve <= STR_RESERVE,
|
||||||
|
StrAppendScalar <= STR_APPEND_SCALAR_UNSAFE,
|
||||||
|
StrGetScalarUnsafe <= STR_GET_SCALAR_UNSAFE,
|
||||||
|
StrToNum <= STR_TO_NUM,
|
||||||
|
ListLen <= LIST_LEN,
|
||||||
|
ListWithCapacity <= LIST_WITH_CAPACITY,
|
||||||
|
ListReserve <= LIST_RESERVE,
|
||||||
|
ListIsUnique <= LIST_IS_UNIQUE,
|
||||||
|
ListAppendUnsafe <= LIST_APPEND_UNSAFE,
|
||||||
|
ListPrepend <= LIST_PREPEND,
|
||||||
|
ListGetUnsafe <= LIST_GET_UNSAFE,
|
||||||
|
ListReplaceUnsafe <= LIST_REPLACE_UNSAFE,
|
||||||
|
ListConcat <= LIST_CONCAT,
|
||||||
|
ListSublist <= LIST_SUBLIST_LOWLEVEL,
|
||||||
|
ListDropAt <= LIST_DROP_AT,
|
||||||
|
ListSwap <= LIST_SWAP,
|
||||||
|
DictSize <= DICT_LEN,
|
||||||
|
DictInsert <= DICT_INSERT,
|
||||||
|
DictRemove <= DICT_REMOVE,
|
||||||
|
DictContains <= DICT_CONTAINS,
|
||||||
|
DictGetUnsafe <= DICT_GET_LOWLEVEL,
|
||||||
|
DictKeys <= DICT_KEYS,
|
||||||
|
DictValues <= DICT_VALUES,
|
||||||
|
DictUnion <= DICT_UNION,
|
||||||
|
DictIntersection <= DICT_INTERSECTION,
|
||||||
|
DictDifference <= DICT_DIFFERENCE,
|
||||||
|
SetFromList <= SET_FROM_LIST,
|
||||||
|
SetToDict <= SET_TO_DICT,
|
||||||
|
NumAdd <= NUM_ADD,
|
||||||
|
NumAddWrap <= NUM_ADD_WRAP,
|
||||||
|
NumAddChecked <= NUM_ADD_CHECKED_LOWLEVEL,
|
||||||
|
NumAddSaturated <= NUM_ADD_SATURATED,
|
||||||
|
NumSub <= NUM_SUB,
|
||||||
|
NumSubWrap <= NUM_SUB_WRAP,
|
||||||
|
NumSubChecked <= NUM_SUB_CHECKED_LOWLEVEL,
|
||||||
|
NumSubSaturated <= NUM_SUB_SATURATED,
|
||||||
|
NumMul <= NUM_MUL,
|
||||||
|
NumMulWrap <= NUM_MUL_WRAP,
|
||||||
|
NumMulSaturated <= NUM_MUL_SATURATED,
|
||||||
|
NumMulChecked <= NUM_MUL_CHECKED_LOWLEVEL,
|
||||||
|
NumGt <= NUM_GT,
|
||||||
|
NumGte <= NUM_GTE,
|
||||||
|
NumLt <= NUM_LT,
|
||||||
|
NumLte <= NUM_LTE,
|
||||||
|
NumCompare <= NUM_COMPARE,
|
||||||
|
NumDivCeilUnchecked <= NUM_DIV_CEIL,
|
||||||
|
NumRemUnchecked <= NUM_REM,
|
||||||
|
NumIsMultipleOf <= NUM_IS_MULTIPLE_OF,
|
||||||
|
NumAbs <= NUM_ABS,
|
||||||
|
NumNeg <= NUM_NEG,
|
||||||
|
NumSin <= NUM_SIN,
|
||||||
|
NumCos <= NUM_COS,
|
||||||
|
NumSqrtUnchecked <= NUM_SQRT,
|
||||||
|
NumLogUnchecked <= NUM_LOG,
|
||||||
|
NumRound <= NUM_ROUND,
|
||||||
|
NumToFrac <= NUM_TO_FRAC,
|
||||||
|
NumPow <= NUM_POW,
|
||||||
|
NumCeiling <= NUM_CEILING,
|
||||||
|
NumPowInt <= NUM_POW_INT,
|
||||||
|
NumFloor <= NUM_FLOOR,
|
||||||
|
NumAtan <= NUM_ATAN,
|
||||||
|
NumAcos <= NUM_ACOS,
|
||||||
|
NumAsin <= NUM_ASIN,
|
||||||
|
NumBytesToU16 <= NUM_BYTES_TO_U16_LOWLEVEL,
|
||||||
|
NumBytesToU32 <= NUM_BYTES_TO_U32_LOWLEVEL,
|
||||||
|
NumBitwiseAnd <= NUM_BITWISE_AND,
|
||||||
|
NumBitwiseXor <= NUM_BITWISE_XOR,
|
||||||
|
NumBitwiseOr <= NUM_BITWISE_OR,
|
||||||
|
NumShiftLeftBy <= NUM_SHIFT_LEFT,
|
||||||
|
NumShiftRightBy <= NUM_SHIFT_RIGHT,
|
||||||
|
NumShiftRightZfBy <= NUM_SHIFT_RIGHT_ZERO_FILL,
|
||||||
|
NumToStr <= NUM_TO_STR,
|
||||||
|
Eq <= BOOL_EQ,
|
||||||
|
NotEq <= BOOL_NEQ,
|
||||||
|
And <= BOOL_AND,
|
||||||
|
Or <= BOOL_OR,
|
||||||
|
Not <= BOOL_NOT,
|
||||||
|
Unreachable <= LIST_UNREACHABLE,
|
||||||
|
}
|
||||||
|
|
|
@ -997,6 +997,8 @@ define_builtins! {
|
||||||
28 DEV_TMP3: "#dev_tmp3"
|
28 DEV_TMP3: "#dev_tmp3"
|
||||||
29 DEV_TMP4: "#dev_tmp4"
|
29 DEV_TMP4: "#dev_tmp4"
|
||||||
30 DEV_TMP5: "#dev_tmp5"
|
30 DEV_TMP5: "#dev_tmp5"
|
||||||
|
|
||||||
|
31 ATTR_INVALID: "#attr_invalid"
|
||||||
}
|
}
|
||||||
// Fake module for storing derived function symbols
|
// Fake module for storing derived function symbols
|
||||||
1 DERIVED: "#Derived" => {
|
1 DERIVED: "#Derived" => {
|
||||||
|
@ -1143,6 +1145,11 @@ define_builtins! {
|
||||||
138 NUM_TO_F64_CHECKED: "toF64Checked"
|
138 NUM_TO_F64_CHECKED: "toF64Checked"
|
||||||
139 NUM_MAX_F64: "maxF64"
|
139 NUM_MAX_F64: "maxF64"
|
||||||
140 NUM_MIN_F64: "minF64"
|
140 NUM_MIN_F64: "minF64"
|
||||||
|
141 NUM_ADD_CHECKED_LOWLEVEL: "addCheckedLowlevel"
|
||||||
|
142 NUM_SUB_CHECKED_LOWLEVEL: "subCheckedLowlevel"
|
||||||
|
143 NUM_MUL_CHECKED_LOWLEVEL: "mulCheckedLowlevel"
|
||||||
|
144 NUM_BYTES_TO_U16_LOWLEVEL: "bytesToU16Lowlevel"
|
||||||
|
145 NUM_BYTES_TO_U32_LOWLEVEL: "bytesToU32Lowlevel"
|
||||||
}
|
}
|
||||||
3 BOOL: "Bool" => {
|
3 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" // the Bool.Bool type alias
|
||||||
|
@ -1205,6 +1212,8 @@ define_builtins! {
|
||||||
44 STR_GET_SCALAR_UNSAFE: "getScalarUnsafe"
|
44 STR_GET_SCALAR_UNSAFE: "getScalarUnsafe"
|
||||||
45 STR_WALK_SCALARS: "walkScalars"
|
45 STR_WALK_SCALARS: "walkScalars"
|
||||||
46 STR_WALK_SCALARS_UNTIL: "walkScalarsUntil"
|
46 STR_WALK_SCALARS_UNTIL: "walkScalarsUntil"
|
||||||
|
47 STR_TO_NUM: "strToNum"
|
||||||
|
48 STR_FROM_UTF8_RANGE_LOWLEVEL: "fromUtf8RangeLowlevel"
|
||||||
}
|
}
|
||||||
5 LIST: "List" => {
|
5 LIST: "List" => {
|
||||||
0 LIST_LIST: "List" imported // the List.List type alias
|
0 LIST_LIST: "List" imported // the List.List type alias
|
||||||
|
@ -1274,6 +1283,7 @@ define_builtins! {
|
||||||
64 LIST_UNREACHABLE: "unreachable"
|
64 LIST_UNREACHABLE: "unreachable"
|
||||||
65 LIST_RESERVE: "reserve"
|
65 LIST_RESERVE: "reserve"
|
||||||
66 LIST_APPEND_UNSAFE: "appendUnsafe"
|
66 LIST_APPEND_UNSAFE: "appendUnsafe"
|
||||||
|
67 LIST_SUBLIST_LOWLEVEL: "sublistLowlevel"
|
||||||
}
|
}
|
||||||
6 RESULT: "Result" => {
|
6 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
0 RESULT_RESULT: "Result" // the Result.Result type alias
|
||||||
|
@ -1287,6 +1297,7 @@ define_builtins! {
|
||||||
6 RESULT_AFTER: "after"
|
6 RESULT_AFTER: "after"
|
||||||
7 RESULT_IS_OK: "isOk"
|
7 RESULT_IS_OK: "isOk"
|
||||||
8 RESULT_IS_ERR: "isErr"
|
8 RESULT_IS_ERR: "isErr"
|
||||||
|
9 RESULT_AFTER_ERR: "afterErr"
|
||||||
}
|
}
|
||||||
7 DICT: "Dict" => {
|
7 DICT: "Dict" => {
|
||||||
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
0 DICT_DICT: "Dict" imported // the Dict.Dict type alias
|
||||||
|
@ -1306,6 +1317,8 @@ define_builtins! {
|
||||||
12 DICT_UNION: "union"
|
12 DICT_UNION: "union"
|
||||||
13 DICT_INTERSECTION: "intersection"
|
13 DICT_INTERSECTION: "intersection"
|
||||||
14 DICT_DIFFERENCE: "difference"
|
14 DICT_DIFFERENCE: "difference"
|
||||||
|
|
||||||
|
15 DICT_GET_LOWLEVEL: "getLowlevel"
|
||||||
}
|
}
|
||||||
8 SET: "Set" => {
|
8 SET: "Set" => {
|
||||||
0 SET_SET: "Set" imported // the Set.Set type alias
|
0 SET_SET: "Set" imported // the Set.Set type alias
|
||||||
|
|
|
@ -934,8 +934,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
StrStartsWithScalar => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
StrStartsWithScalar => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant, irrelevant]),
|
||||||
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
|
||||||
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
|
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||||
StrRepeat => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
StrRepeat => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
|
|
|
@ -620,6 +620,7 @@ fn deep_copy_type_vars<'a>(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function,
|
||||||
}) => {
|
}) => {
|
||||||
let new_rec_var = recursion_var.map(|var| descend_var!(var));
|
let new_rec_var = recursion_var.map(|var| descend_var!(var));
|
||||||
for variables_slice_index in solved.variables() {
|
for variables_slice_index in solved.variables() {
|
||||||
|
@ -630,6 +631,7 @@ fn deep_copy_type_vars<'a>(
|
||||||
let Uls(var, _, _) = subs[uls_index];
|
let Uls(var, _, _) = subs[uls_index];
|
||||||
descend_var!(var);
|
descend_var!(var);
|
||||||
}
|
}
|
||||||
|
let new_ambient_function = descend_var!(ambient_function);
|
||||||
|
|
||||||
perform_clone!({
|
perform_clone!({
|
||||||
let new_variable_slices =
|
let new_variable_slices =
|
||||||
|
@ -657,6 +659,7 @@ fn deep_copy_type_vars<'a>(
|
||||||
solved: new_solved,
|
solved: new_solved,
|
||||||
recursion_var: new_rec_var,
|
recursion_var: new_rec_var,
|
||||||
unspecialized: new_unspecialized,
|
unspecialized: new_unspecialized,
|
||||||
|
ambient_function: new_ambient_function,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2317,6 +2317,8 @@ fn from_can_let<'a>(
|
||||||
let (_specialization_mark, (var, specialized_symbol)) =
|
let (_specialization_mark, (var, specialized_symbol)) =
|
||||||
needed_specializations.next().unwrap();
|
needed_specializations.next().unwrap();
|
||||||
|
|
||||||
|
// Make sure rigid variables in the annotation are converted to flex variables.
|
||||||
|
instantiate_rigids(env.subs, def.expr_var);
|
||||||
// Unify the expr_var with the requested specialization once.
|
// Unify the expr_var with the requested specialization once.
|
||||||
let _res = env.unify(var, def.expr_var);
|
let _res = env.unify(var, def.expr_var);
|
||||||
|
|
||||||
|
@ -2333,6 +2335,9 @@ fn from_can_let<'a>(
|
||||||
_n => {
|
_n => {
|
||||||
let mut stmt = rest;
|
let mut stmt = rest;
|
||||||
|
|
||||||
|
// Make sure rigid variables in the annotation are converted to flex variables.
|
||||||
|
instantiate_rigids(env.subs, def.expr_var);
|
||||||
|
|
||||||
// Need to eat the cost and create a specialized version of the body for
|
// Need to eat the cost and create a specialized version of the body for
|
||||||
// each specialization.
|
// each specialization.
|
||||||
for (_specialization_mark, (var, specialized_symbol)) in
|
for (_specialization_mark, (var, specialized_symbol)) in
|
||||||
|
@ -3785,14 +3790,14 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroArgumentTag {
|
ZeroArgumentTag {
|
||||||
variant_var,
|
variant_var: _,
|
||||||
name: tag_name,
|
name: tag_name,
|
||||||
ext_var,
|
ext_var,
|
||||||
closure_name,
|
closure_name,
|
||||||
} => {
|
} => {
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let content = env.subs.get_content_without_compacting(variant_var);
|
let content = env.subs.get_content_without_compacting(variable);
|
||||||
|
|
||||||
if let Content::Structure(FlatType::Func(arg_vars, _, ret_var)) = content {
|
if let Content::Structure(FlatType::Func(arg_vars, _, ret_var)) = content {
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
|
@ -3806,7 +3811,7 @@ pub fn with_hole<'a>(
|
||||||
closure_name,
|
closure_name,
|
||||||
ext_var,
|
ext_var,
|
||||||
procs,
|
procs,
|
||||||
variant_var,
|
variable,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
|
@ -3814,7 +3819,7 @@ pub fn with_hole<'a>(
|
||||||
} else {
|
} else {
|
||||||
convert_tag_union(
|
convert_tag_union(
|
||||||
env,
|
env,
|
||||||
variant_var,
|
variable,
|
||||||
assigned,
|
assigned,
|
||||||
hole,
|
hole,
|
||||||
tag_name,
|
tag_name,
|
||||||
|
@ -5124,6 +5129,7 @@ fn late_resolve_ability_specialization<'a>(
|
||||||
solved,
|
solved,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
recursion_var: _,
|
recursion_var: _,
|
||||||
|
ambient_function: _,
|
||||||
} = env.subs.get_lambda_set(*lambda_set);
|
} = env.subs.get_lambda_set(*lambda_set);
|
||||||
|
|
||||||
debug_assert!(unspecialized.is_empty());
|
debug_assert!(unspecialized.is_empty());
|
||||||
|
@ -6068,7 +6074,7 @@ fn from_can_when<'a>(
|
||||||
let guard_stmt = with_hole(
|
let guard_stmt = with_hole(
|
||||||
env,
|
env,
|
||||||
loc_expr.value,
|
loc_expr.value,
|
||||||
cond_var,
|
Variable::BOOL,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -8952,6 +8958,7 @@ where
|
||||||
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
ToLowLevelCall: Fn(ToLowLevelCallArguments<'a>) -> Call<'a> + Copy,
|
||||||
{
|
{
|
||||||
match lambda_set.runtime_representation() {
|
match lambda_set.runtime_representation() {
|
||||||
|
Layout::VOID => empty_lambda_set_error(),
|
||||||
Layout::Union(union_layout) => {
|
Layout::Union(union_layout) => {
|
||||||
let closure_tag_id_symbol = env.unique_symbol();
|
let closure_tag_id_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
@ -9111,6 +9118,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn empty_lambda_set_error() -> Stmt<'static> {
|
||||||
|
let msg = "a Lambda Set is empty. Most likely there is a type error in your program.";
|
||||||
|
Stmt::RuntimeError(msg)
|
||||||
|
}
|
||||||
|
|
||||||
/// Use the lambda set to figure out how to make a call-by-name
|
/// Use the lambda set to figure out how to make a call-by-name
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn match_on_lambda_set<'a>(
|
fn match_on_lambda_set<'a>(
|
||||||
|
@ -9125,6 +9137,7 @@ fn match_on_lambda_set<'a>(
|
||||||
hole: &'a Stmt<'a>,
|
hole: &'a Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
match lambda_set.runtime_representation() {
|
match lambda_set.runtime_representation() {
|
||||||
|
Layout::VOID => empty_lambda_set_error(),
|
||||||
Layout::Union(union_layout) => {
|
Layout::Union(union_layout) => {
|
||||||
let closure_tag_id_symbol = env.unique_symbol();
|
let closure_tag_id_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
@ -9254,9 +9267,7 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
// there is really nothing we can do here. We generate a runtime error here which allows
|
// there is really nothing we can do here. We generate a runtime error here which allows
|
||||||
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||||
// hitting this one
|
// hitting this one
|
||||||
|
return empty_lambda_set_error();
|
||||||
let msg = "a Lambda Set isempty. Most likely there is a type error in your program.";
|
|
||||||
return Stmt::RuntimeError(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let join_point_id = JoinPointId(env.unique_symbol());
|
let join_point_id = JoinPointId(env.unique_symbol());
|
||||||
|
|
|
@ -1127,11 +1127,13 @@ fn resolve_lambda_set(subs: &Subs, mut var: Variable) -> ResolvedLambdaSet {
|
||||||
solved,
|
solved,
|
||||||
recursion_var: _,
|
recursion_var: _,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
unspecialized.is_empty(),
|
unspecialized.is_empty(),
|
||||||
"unspecialized lambda sets left over during resolution: {:?}",
|
"unspecialized lambda sets left over during resolution: {:?}, {:?}",
|
||||||
roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs),
|
roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(var), subs),
|
||||||
|
subs.uls_of_var
|
||||||
);
|
);
|
||||||
roc_types::pretty_print::push_union(subs, solved, &mut set);
|
roc_types::pretty_print::push_union(subs, solved, &mut set);
|
||||||
return ResolvedLambdaSet::Set(set);
|
return ResolvedLambdaSet::Set(set);
|
||||||
|
@ -1966,6 +1968,7 @@ fn layout_from_lambda_set<'a>(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
} = lset;
|
} = lset;
|
||||||
|
|
||||||
if !unspecialized.is_empty() {
|
if !unspecialized.is_empty() {
|
||||||
|
|
|
@ -277,6 +277,7 @@ impl LambdaSet {
|
||||||
solved,
|
solved,
|
||||||
recursion_var: _,
|
recursion_var: _,
|
||||||
unspecialized: _,
|
unspecialized: _,
|
||||||
|
ambient_function: _,
|
||||||
} = lset;
|
} = lset;
|
||||||
|
|
||||||
// TODO: handle unspecialized
|
// TODO: handle unspecialized
|
||||||
|
@ -700,6 +701,7 @@ impl Layout {
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized: _,
|
unspecialized: _,
|
||||||
|
ambient_function: _,
|
||||||
} = lset;
|
} = lset;
|
||||||
|
|
||||||
// TODO: handle unspecialized lambda set
|
// TODO: handle unspecialized lambda set
|
||||||
|
|
699
crates/compiler/solve/docs/ambient_lambda_set_specialization.md
Normal file
699
crates/compiler/solve/docs/ambient_lambda_set_specialization.md
Normal file
|
@ -0,0 +1,699 @@
|
||||||
|
# Ambient Lambda Set Specialization
|
||||||
|
|
||||||
|
Ayaz Hafiz
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
This document describes how polymorphic lambda sets are specialized and resolved
|
||||||
|
in the compiler's type solver. It's derived from the original document at https://rwx.notion.site/Ambient-Lambda-Set-Specialization-50e0208a39844ad096626f4143a6394e.
|
||||||
|
|
||||||
|
TL;DR: lambda sets are resolved by unifying their ambient arrow types in a “bottom-up” fashion.
|
||||||
|
|
||||||
|
# Background
|
||||||
|
|
||||||
|
In this section I’ll explain how lambda sets and specialization lambda sets work today, mostly from the ground-up. I’ll gloss over a few details and assume an understanding of type unification. The background will leave us with a direct presentation of the current specialization lambda set unification algorithm, and its limitation.
|
||||||
|
|
||||||
|
Lambda sets are a technique Roc uses for static dispatch of closures. For example,
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
id1 = \x -> x
|
||||||
|
id2 = \x -> x
|
||||||
|
f = if True then id1 else id2
|
||||||
|
```
|
||||||
|
|
||||||
|
has the elaboration (solved-type annotations)
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
id1 = \x -> x
|
||||||
|
^^^ id1 : a -[[id1]] -> a
|
||||||
|
id2 = \x -> x
|
||||||
|
^^^ id2 : a -[[id2]] -> a
|
||||||
|
f = if True then id1 else id2
|
||||||
|
^ f : a -[[id1, id2]] -> a
|
||||||
|
```
|
||||||
|
|
||||||
|
The syntax `-[[id1]]->` can be read as “a function that dispatches to `id1`". Then the arrow `-[[id1, id2]]->` then is “a function that dispatches to `id1`, or `id2`". The tag union `[id1, id2]` can contain payloads to represent the captures of `id1` and `id2`; however, the implications of that are out of scope for this discussion, see [Folkert’s great explanation](https://github.com/rtfeldman/roc/pull/2307#discussion_r777042512) for more information. During compile-time, Roc would attach a run-time examinable tag to the value in each branch of the `f` expression body, representing whether to dispatch to `id1` or `id2`. Whenever `f` is dispatched, that tag is examined to determine exactly which function should be dispatched to. This is “**defunctionalization**”.
|
||||||
|
|
||||||
|
In the presence of [abilities](https://docs.google.com/document/d/1kUh53p1Du3fWP_jZp-sdqwb5C9DuS43YJwXHg1NzETY/edit), lambda sets get more complicated. Now, I can write something like
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
Hash has hash : a -> U64 | a has Hash
|
||||||
|
|
||||||
|
zeroHash = \_ -> 0
|
||||||
|
|
||||||
|
f = if True then hash else zeroHash
|
||||||
|
```
|
||||||
|
|
||||||
|
The elaboration of `f` as `f : a -[[hash, zeroHash]]-> U64` is incorrect, in the sense that it is incomplete - `hash` is not an actual definition, and we don’t know exactly what specialization of `hash` to dispatch to until the type variable `a` is resolved. This elaboration does not communicate to the code generator that the value of `hash` is actually polymorphic over `a`.
|
||||||
|
|
||||||
|
To support polymorphic values in lambda sets, we use something we call “**specialization lambda sets**”. In this technique, the lambda under the only arrow in `hash` is parameterized on (1) the type variable the `hash` specialization depends on, and (2) the “region” in the type signature of the specialization that the actual type should be recovered from.
|
||||||
|
|
||||||
|
That was a lot of words, so let me give you an example. To better illustrate how the mechanism works, let’s suppose `Hash` is actually defined as `Hash has hashThunk : a -> ({} -> U64) | a has Hash`. Now let’s consider the following program elaboration:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
Hash has
|
||||||
|
hashThunk : a -> ({} -> U64) | a has Hash
|
||||||
|
# ^^^^^^^^^ a -[[] + a:hashThunk:1]-> ({} -[[] + a:hashThunk:2]-> U64)
|
||||||
|
|
||||||
|
zeroHash = \_ -> \{} -> 0
|
||||||
|
#^^^^^^^ a -[[zeroHash]]-> \{} -[[lam1]]-> U64
|
||||||
|
|
||||||
|
f = if True then hash else zeroHash
|
||||||
|
#^ a -[[zeroHash] + a:hashThunk:1]-> ({} -[[lam1] + a:hashThunk:2]-> U64)
|
||||||
|
```
|
||||||
|
|
||||||
|
The grammar of a lambda set is now
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
lambda_set: [[(concrete_lambda)*] (+ specialization_lambda)*]
|
||||||
|
|
||||||
|
concrete_lambda: lambda_name ( capture_type)*
|
||||||
|
specialization_lambda: <type>:ability_member_name:region
|
||||||
|
region: <number>
|
||||||
|
```
|
||||||
|
|
||||||
|
Since `hashThunk` is a specification for an ability member and not a concrete implementation, it contains only specialization lambdas, in this case parameterized over `a`, which is the type parameter that implementors of `Hash` must specialize. Since `hashThunk` has two function types, we need to distinguish how they should be resolved. For this reason we record a “region” noting where the specialization lambda occurs in an ability member signature. When `a` is resolved to a concrete type `C`, we would resolve `C:hashThunk:2` by looking up the lambda set of `C`'s specialization of `hashThunk`, at region 2.
|
||||||
|
|
||||||
|
`zeroHash` is a concrete implementation, and uses only concrete lambdas, so its two lambda sets are fully resolved with concrete types. I’ve named the anonymous lambda `\{} -> 0` `lam1` for readability.
|
||||||
|
|
||||||
|
At `f`, we unify the function types in both branches. Unification of lambda sets is basically a union of both sides’ concrete lambdas and specialization lambdas (this is not quite true, but that doesn’t matter here). This way, we preserve the fact that how `f` should be dispatched is parameterized over `a`.
|
||||||
|
|
||||||
|
Now, let’s say we apply `f` to a concrete type, like
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
Foo := {}
|
||||||
|
hashThunk = \@Foo {} -> \{} -> 1
|
||||||
|
#^^^^^^^^ Foo -[[Foo#hashThunk]]-> \{} -[[lam2]]-> U64
|
||||||
|
|
||||||
|
f (@Foo {})
|
||||||
|
```
|
||||||
|
|
||||||
|
The unification trace for the call `f (@Foo {})` proceeds as follows. I use `'tN`, where `N` is a number, to represent fresh unbound type variables. Since `f` is a generalized type, `a'` is the fresh type “based on `a`" created for a particular usage of `f`.
|
||||||
|
|
||||||
|
```
|
||||||
|
typeof f
|
||||||
|
~ Foo -'t1-> 't2
|
||||||
|
=>
|
||||||
|
a' -[[zeroHash] + a':hashThunk:1]-> ({} -[[lam1] + a':hashThunk:2]-> U64)
|
||||||
|
~ Foo -'t1-> 't2
|
||||||
|
=>
|
||||||
|
Foo -[[zeroHash] + Foo:hashThunk:1]-> ({} -[[lam1] + Foo:hashThunk:2]-> U64)
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that the specialization lambdas’ type variables point to concrete types, we can resolve the concrete lambdas of `Foo:hashThunk:1` and `Foo:hashThunk:2`. Cool! Let’s do that. We know that
|
||||||
|
|
||||||
|
```
|
||||||
|
hashThunk = \@Foo {} -> \{} -> 1
|
||||||
|
#^^^^^^^^ Foo -[[Foo#hashThunk]]-> \{} -[[lam2]]-> U64
|
||||||
|
```
|
||||||
|
|
||||||
|
So `Foo:hashThunk:1` is `[[Foo#hashThunk]]` and `Foo:hashThunk:2` is `[[lam2]]`. Applying that to the type of `f` we get the trace
|
||||||
|
|
||||||
|
```
|
||||||
|
Foo -[[zeroHash] + Foo:hashThunk:1]-> ({} -[[lam1] + Foo:hashThunk:2]-> U64)
|
||||||
|
<specialization time>
|
||||||
|
Foo:hashThunk:1 -> [[Foo#hashThunk]]
|
||||||
|
Foo:hashThunk:2 -> [[lam2]]
|
||||||
|
=>
|
||||||
|
Foo -[[zeroHash, Foo#hashThunk]]-> ({} -[[lam1, lam2]] -> U64)
|
||||||
|
```
|
||||||
|
|
||||||
|
Great, so now we know our options to dispatch `f` in the call `f (@Foo {})`, and the code-generator will insert tags appropriately for the specialization definition of `f` where `a = Foo` knowing the concrete lambda symbols.
|
||||||
|
|
||||||
|
# The Problem
|
||||||
|
|
||||||
|
This technique for lambda set resolution is all well and good when the specialized lambda sets are monomorphic, that is, they contain only concrete lambdas. So far in our development of the end-to-end compilation model that’s been the case, and when it wasn’t, there’s been enough ambient information to coerce the specializations to be monomorphic.
|
||||||
|
|
||||||
|
Unfortunately we cannot assume that the specializations will be monomorphic in general, and we must now think about how to deal with that. I didn’t think there was any good, efficient solution, but now we have no option other than to come up with something, so this document is a description of my attempt. But before we get there, let’s whet our appetite for what the problem even is. I’ve been waving my hands too long.
|
||||||
|
|
||||||
|
Let’s consider the following program:
|
||||||
|
|
||||||
|
```python
|
||||||
|
F has f : a -> (b -> {}) | a has F, b has G
|
||||||
|
# ^ a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {}) | a has F, b has G
|
||||||
|
|
||||||
|
G has g : b -> {} | b has G
|
||||||
|
# ^ b -[[] + b:g:1]-> {}
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^ Fo -[[Fo#f]]-> (b -[[] + b:g:1]-> {}) | b has G
|
||||||
|
# instantiation with a=Fo of
|
||||||
|
# a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {}) | a has F, b has G
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> {}
|
||||||
|
#^ Go -[[Go#g]]-> {}
|
||||||
|
# instantiation with b=Go of
|
||||||
|
# b -[[] + b:g:1]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Apologies for the complicated types, I know this can be a bit confusing. It helps to look at the specialized types of `f` and `g` relative to the ability member signatures.
|
||||||
|
|
||||||
|
The key thing to notice here is that `Fo#f` must continue to vary over `b | b has G`, since it can only specialize the type parameter `a` (in this case, it specialized it to `Fo`). Its return value is the unspecialized ability member `g`, which has type `b -> {}`, as we wanted. But its lambda set **also** varies over `b`, being `b -[[] + b:g:1]-> {}`.
|
||||||
|
|
||||||
|
Suppose we have the call
|
||||||
|
|
||||||
|
```python
|
||||||
|
(f (@Fo {})) (@Go {})
|
||||||
|
```
|
||||||
|
|
||||||
|
With the present specialization technique, unification proceeds as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
== solve (f (@Fo {})) ==
|
||||||
|
typeof f
|
||||||
|
~ Fo -'t1-> 't2
|
||||||
|
|
||||||
|
a' -[[] + a':f:1]-> (b' -[[] + a':f:2]-> {})
|
||||||
|
~ Fo -'t1-> 't2
|
||||||
|
=> Fo -[[] + Fo:f:1]-> (b' -[[] + Fo:f:2]-> {})
|
||||||
|
<specialization time>
|
||||||
|
Fo:f:1 -> [[Fo#f]]
|
||||||
|
Fo:f:2 -> [[] + b'':g:1] | This is key bit 1!
|
||||||
|
=> Fo -[[Fo#f]]-> (b' -[[] + b'':g:1] -> {})
|
||||||
|
|
||||||
|
== solve (f (@Fo {})) (@Go {}) ==
|
||||||
|
return_typeof f
|
||||||
|
~ Go -'t3-> 't4
|
||||||
|
-
|
||||||
|
b' -[[] + b'':g:1] -> {} | This is key bit 2!
|
||||||
|
~ Go -'t3-> 't4 |
|
||||||
|
=> Go -[[] + b'':g:1] -> {} |
|
||||||
|
-
|
||||||
|
|
||||||
|
== final type of f ==
|
||||||
|
f : Fo -[[Fo#f]]-> (Go -[[] + b'':g:1]-> {})
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's go over what happened. The important pieces are the unification traces I’ve annotated as “key bits”.
|
||||||
|
|
||||||
|
In resolving `Fo:f:2`, we pulled down the let-generalized lambda set `[[] + b:g:2]` at that region in `Fo`, which means we have to generate a fresh type variable for `b` for that particular instantiation of the lambda set. That makes sense, that’s how let-generalization works. So, we get the lambda set `[[] + b'':g:1]` for our particular instance.
|
||||||
|
|
||||||
|
But in key bit 2, we see that we know what we want `b''` to be! We want it to be this `b'`, which gets instantiated to `Go`. But `b'` and `b''` are independent type variables, and so unifying `b' ~ Go` doesn’t solve `b'' = Go`. Instead, `b''` is now totally unbound, and in the end, we get a type for `f` that has an unspecialized lambda set, even though you or I, staring at this program, know exactly what `[[] + b'':g:1]` should really be - `[[Go#g]]`.
|
||||||
|
|
||||||
|
So where did we go wrong? Well, our problem is that we never saw that `b'` and `b''` should really be the same type variable. If only we knew that in this specialization `b'` and `b''` are the same instantiation, we’d be all good.
|
||||||
|
|
||||||
|
# A Solution
|
||||||
|
|
||||||
|
I’ll now explain the best way I’ve thought of for us to solve this problem. If you see a better way, please let me know! I’m not sure I love this solution, but I do like it a lot more than some other naive approaches.
|
||||||
|
|
||||||
|
Okay, so first we’ll enumerate some terminology, and the exact algorithm. Then we’ll illustrate the algorithm with some examples; my hope is this will help explain why it must proceed in the way it does. We’ll see that the algorithm depends on a few key invariants; I’ll discuss them and their consequences along the way. Finally, we’ll discuss a couple details regarding the algorithm not directly related to its operation, but important to recognize. I hope then, you will tell me where I have gone wrong, or where you see a better opportunity to do things.
|
||||||
|
|
||||||
|
## The algorithm
|
||||||
|
|
||||||
|
### Some definitions
|
||||||
|
|
||||||
|
- **The region invariant.** Previously we discussed the “region” of a lambda set in a specialization function definition. The way regions are assigned in the compiler follows a very specific ordering and holds a invariant we’ll call the “region invariant”. First, let’s define a procedure for creating function types and assigning regions:
|
||||||
|
|
||||||
|
```
|
||||||
|
Type = \region ->
|
||||||
|
(Type_atom, region)
|
||||||
|
| Type_function region
|
||||||
|
|
||||||
|
Type_function = \region ->
|
||||||
|
let left_type, new_region = Type (region + 1)
|
||||||
|
let right_type, new_region = Type (new_region)
|
||||||
|
let func_type = left_type -[Lambda region]-> right_type
|
||||||
|
(func_type, new_region)
|
||||||
|
```
|
||||||
|
|
||||||
|
This procedure would create functions that look like the trees(abbreviating `L=Lambda`, `a=atom` below)
|
||||||
|
|
||||||
|
```
|
||||||
|
-[L 1]->
|
||||||
|
a a
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
-[L 1]->
|
||||||
|
-[L 2]-> -[L 3]->
|
||||||
|
a a a a
|
||||||
|
|
||||||
|
===
|
||||||
|
-[L 1]->
|
||||||
|
-[L 2]-> -[L 5]->
|
||||||
|
-[L 3]-> -[L 4]-> -[L 6]-> -[L 7]->
|
||||||
|
a a a a a a a a
|
||||||
|
```
|
||||||
|
|
||||||
|
The invariant is this: for a region `r`, the only functions enclosing `r` have a region number that is less than `r`. Moreover, every region `r' < r`, either the function at `r'` encloses `r`, or is disjoint from `r`.
|
||||||
|
|
||||||
|
- **Ambient functions.** For a given lambda set at region `r`, any function that encloses `r` is called an **ambient function** of `r`. The function directly at region `r` is called the **directly ambient function**.
|
||||||
|
|
||||||
|
For example, the functions identified by `L 4`, `L 2`, and `L 1` in the last example tree above are all ambient functions of the function identified by `L 4`.
|
||||||
|
|
||||||
|
The region invariant means that the only functions that are ambient of a region `r` are those identified by regions `< r`.
|
||||||
|
|
||||||
|
- `uls_of_var`. A look aside table of the unspecialized lambda sets (uls) depending on a variable. For example, in `a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {})`, there would be a mapping of `a => { [[] + a:f:1]; [[] + a:f:2] }`. When `a` gets instantiated with a concrete type, we know that these lambda sets are ready to be resolved.
|
||||||
|
|
||||||
|
### Explicit Description
|
||||||
|
|
||||||
|
The algorithm concerns what happens during the lambda-set-specialization-time. You may want to read it now, but it’s also helpful to first look at the intuition below, then the examples, then revisit the explicit algorithm description.
|
||||||
|
|
||||||
|
Suppose a type variable `a` with `uls_of_var` mapping `uls_a = {l1, ... ln}` has been instantiated to a concrete type `C`. Then,
|
||||||
|
|
||||||
|
1. Let each `l` in `uls_a` be of form `[concrete_lambdas + ... + C:f:r + ...]`. It has to be in this form because of how `uls_of_var` is constructed.
|
||||||
|
1. Note that there may be multiple unspecialized lambdas of form `C:f:r, C:f1:r1, ..., C:fn:rn` in `l`. In this case, let `t1, ... tm` be the other unspecialized lambdas not of form `C:_:_`, that is, none of which are now specialized to the type `C`. Then, deconstruct `l` such that `l' = [concrete_lambdas + t1 + ... + tm + C:f:r` and `l1 = [[] + C:f1:r1], ..., ln = [[] + C:fn:rn]`. Replace `l` with `l', l1, ..., ln` in `uls_a`, flattened.
|
||||||
|
2. Now, each `l` in `uls_a` has a unique unspecialized lambda of form `C:f:r`. Sort `uls_a` primarily by `f` (arbitrary order), and secondarily by `r` in descending order. This sorted list is called `uls_a'`.
|
||||||
|
1. That is, we are sorting `uls_a` so that it is partitioned by ability member name of the unspecialized lambda sets, and each partition is in descending order of region.
|
||||||
|
2. An example of the sort would be `[[] + C:foo:2], [[] + C:bar:3], [[] + C:bar:1]`.
|
||||||
|
3. For each `l` in `uls_a'` with unique unspecialized lambda `C:f:r`:
|
||||||
|
1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`. Remove `C:f:r` from `t_f1`'s lambda set.
|
||||||
|
1. For example, `(b' -[[] + Fo:f:2]-> {})` if `C:f:r=Fo:f:2`. Removing `Fo:f:2`, we get `(b' -[[]]-> {})`.
|
||||||
|
2. Let `t_f2` be the directly ambient function of the specialization lambda set resolved by `C:f:r`.
|
||||||
|
1. For example, `(b -[[] + b:g:1]-> {})` if `C:f:r=Fo:f:2`, running on example from above.
|
||||||
|
3. Unify `t_f1 ~ t_f2`.
|
||||||
|
|
||||||
|
### Intuition
|
||||||
|
|
||||||
|
The intuition is that we walk up the function type being specialized, starting from the leaves. Along the way we pick up bound type variables from both the function type being specialized, and the specialization type. The region invariant makes sure we thread bound variables through an increasingly larger scope.
|
||||||
|
|
||||||
|
## Some Examples
|
||||||
|
|
||||||
|
### The motivating example
|
||||||
|
|
||||||
|
Recall the program from our problem statement
|
||||||
|
|
||||||
|
```python
|
||||||
|
F has f : a -> (b -> {}) | a has F, b has G
|
||||||
|
# ^ a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {}) | a has F, b has G
|
||||||
|
|
||||||
|
G has g : b -> {} | b has G
|
||||||
|
# ^ b -[[] + b:g:1]-> {}
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^ Fo -[[Fo#f]]-> (b -[[] + b:g:1]-> {}) | b has G
|
||||||
|
# instantiation with a=Fo of
|
||||||
|
# a -[[] + a:f:1]-> (b -[[] + a:f:2]-> {}) | a has F, b has G
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> {}
|
||||||
|
#^ Go -[[Go#g]]-> {}
|
||||||
|
# instantiation with b=Go of
|
||||||
|
# b -[[] + b:g:1]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
With our algorithm, the call
|
||||||
|
|
||||||
|
```python
|
||||||
|
(f (@Fo {})) (@Go {})
|
||||||
|
```
|
||||||
|
|
||||||
|
has unification proceed as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
== solve (f (@Fo {})) ==
|
||||||
|
typeof f
|
||||||
|
~ Fo -'t1-> 't2
|
||||||
|
|
||||||
|
a' -[[] + a':f:1]-> (b' -[[] + a':f:2]-> {})
|
||||||
|
~ Fo -'t1-> 't2
|
||||||
|
=> Fo -[[] + Fo:f:1]-> (b' -[[] + Fo:f:2]-> {})
|
||||||
|
<specialization time>
|
||||||
|
step 1:
|
||||||
|
uls_Fo = { [[] + Fo:f:1], [[] + Fo:f:2] }
|
||||||
|
step 2 (sort):
|
||||||
|
uls_Fo' = { [[] + Fo:f:2], [[] + Fo:f:1] }
|
||||||
|
step 3:
|
||||||
|
1. iteration: [[] + Fo:f:2]
|
||||||
|
b' -[[]]-> {} (t_f1 after removing Fo:f:2)
|
||||||
|
~ b'' -[[] + b'':g:1]-> {}
|
||||||
|
= b'' -[[] + b'':g:1]-> {}
|
||||||
|
=> typeof f now Fo -[[] + Fo:f:1]-> (b'' -[[] + b'':g:1]-> {})
|
||||||
|
|
||||||
|
2. iteration: [[] + Fo:f:1]
|
||||||
|
Fo -[[]]-> (b'' -[[] + b'':g:1]-> {}) (t_f1 after removing Fo:f:1)
|
||||||
|
~ Fo -[[Fo#f]]-> (b''' -[[] + b''':g:1]-> {})
|
||||||
|
= Fo -[[Fo#f]]-> (b''' -[[] + b''':g:1]-> {})
|
||||||
|
|
||||||
|
=> typeof f = Fo -[[Fo#f]]-> (b''' -[[] + b''':g:1]-> {})
|
||||||
|
|
||||||
|
== solve (f (@Fo {})) (@Go {}) ==
|
||||||
|
return_typeof f
|
||||||
|
~ Go -'t3-> 't4
|
||||||
|
|
||||||
|
b''' -[[] + b''':g:1]-> {}
|
||||||
|
~ Go -'t3-> 't4
|
||||||
|
=> Go -[[] + Go:g:1] -> {}
|
||||||
|
<specialization time>
|
||||||
|
step 1:
|
||||||
|
uls_Go = { [[] + Go:g:1] }
|
||||||
|
step 2 (sort):
|
||||||
|
uls_Go' = { [[] + Go:g:1] }
|
||||||
|
step 3:
|
||||||
|
1. iteration: [[] + Go:g:1]
|
||||||
|
Go -[[]]-> {} (t_f1 after removing Go:g:1)
|
||||||
|
~ Go -[[Go#g]]-> {}
|
||||||
|
= Go -[[Go#g]]-> {}
|
||||||
|
|
||||||
|
=> typeof f = Fo -[[Fo#f]]-> (Go -[[Go#g]]-> {})
|
||||||
|
|
||||||
|
== final type of f ==
|
||||||
|
f : Fo -[[Fo#f]]-> (Go -[[Go#g]]-> {})
|
||||||
|
```
|
||||||
|
|
||||||
|
There we go. We’ve recovered the specialization type of the second lambda set to `Go#g`, as we wanted.
|
||||||
|
|
||||||
|
### The motivating example, in the presence of let-generalization
|
||||||
|
|
||||||
|
Suppose instead we let-generalized the motivating example, so it was a program like
|
||||||
|
|
||||||
|
```
|
||||||
|
h = f (@Fo {})
|
||||||
|
h (@Go {})
|
||||||
|
```
|
||||||
|
|
||||||
|
`h` still gets resolved correctly in this case. It’s basically the same unification trace as above, except that after we find out that
|
||||||
|
|
||||||
|
```
|
||||||
|
typeof f = Fo -[[Fo#f]]-> (b''' -[[] + b''':g:1]-> {})
|
||||||
|
```
|
||||||
|
|
||||||
|
we see that `h` has type
|
||||||
|
|
||||||
|
```
|
||||||
|
b''' -[[] + b''':g:1]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
We generalize this to
|
||||||
|
|
||||||
|
```
|
||||||
|
h : c -[[] + c:g:1]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, the call `h (@Go {})` has the trace
|
||||||
|
|
||||||
|
```
|
||||||
|
=== solve h (@Go {}) ===
|
||||||
|
typeof h
|
||||||
|
~ Go -'t1-> 't2
|
||||||
|
|
||||||
|
c' -[[] + c':g:1]-> {}
|
||||||
|
~ Go -'t1-> 't2
|
||||||
|
=> Go -[[] + Go:g:1]-> {}
|
||||||
|
<specialization time>
|
||||||
|
step 1:
|
||||||
|
uls_Go = { [[] + Go:g:1] }
|
||||||
|
step 2 (sort):
|
||||||
|
uls_Go' = { [[] + Go:g:1] }
|
||||||
|
step 3:
|
||||||
|
1. iteration: [[] + Go:g:1]
|
||||||
|
Go -[[]]-> {} (t_f1 after removing Go:g:1)
|
||||||
|
~ Go -[[Go#g]]-> {}
|
||||||
|
= Go -[[Go#g]]-> {}
|
||||||
|
=> Go -[[Go#g]]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bindings on the right side of an arrow
|
||||||
|
|
||||||
|
This continues to work if instead of a type variable being bound on the left side of an arrow, it is bound on the right side. Let’s see what that looks like. Consider
|
||||||
|
|
||||||
|
```python
|
||||||
|
F has f : a -> ({} -> b) | a has F, b has G
|
||||||
|
G has g : {} -> b | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^ Fo -[[Fo#f]]-> ({} -[[] + b:g:1]-> b) | b has G
|
||||||
|
# instantiation with a=Fo of
|
||||||
|
# a -[[] + a:f:1]-> ({} -[[] + a:f:2]-> b) | a has F, b has G
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \{} -> @Go {}
|
||||||
|
#^ {} -[[Go#g]]-> Go
|
||||||
|
# instantiation with b=Go of
|
||||||
|
# {} -[[] + b:g:1]-> b
|
||||||
|
```
|
||||||
|
|
||||||
|
This is symmetrical to the first example we ran through. I can include a trace if you all would like, though it could be helpful to go through yourself and see that it would work.
|
||||||
|
|
||||||
|
### Deep specializations and captures
|
||||||
|
|
||||||
|
Alright, bear with me, this is a long and contrived one, but it demonstrates how this works in the presence of polymorphic captures (it’s “nothing special”), and more importantly, why the bottom-up unification is important.
|
||||||
|
|
||||||
|
Here’s the source program:
|
||||||
|
|
||||||
|
```python
|
||||||
|
F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G
|
||||||
|
# ^ a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G
|
||||||
|
G has g : b -> ({} -> {}) | b has G
|
||||||
|
# ^ b -[[] + b:g:1]-> ({} -[[] + b:g:2]-> {}) | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {}, b -> \{} -> g b
|
||||||
|
#^ Fo, b -[[Fo#f]]-> ({} -[[lamF b]]-> ({} -[[] + b:g:2]]-> {})) | b has G
|
||||||
|
# instantiation with a=Fo of
|
||||||
|
# a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {})) | a has F, b has G
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> \{} -> {}
|
||||||
|
#^ {} -[[Go#g]]-> ({} -[[lamG]]-> {})
|
||||||
|
# instantiation with b=Go of
|
||||||
|
# b -[[] + b:g:1]-> ({} -[[] + b:g:2]-> {}) | b has G
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the call we’re going to trace:
|
||||||
|
|
||||||
|
```python
|
||||||
|
(f (@Fo {}) (@Go {})) {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Let’s get to it.
|
||||||
|
|
||||||
|
```
|
||||||
|
=== solve (f (@Fo {}) (@Go {})) ===
|
||||||
|
typeof f
|
||||||
|
~ Fo, Go -'t1-> 't2
|
||||||
|
|
||||||
|
a, b -[[] + a:f:1]-> ({} -[[] + a:f:2]-> ({} -[[] + a:f:3]-> {}))
|
||||||
|
~ Fo, Go -'t1-> 't2
|
||||||
|
=> Fo, Go -[[] + Fo:f:1]-> ({} -[[] + Fo:f:2]-> ({} -[[] + Fo:f:3]-> {}))
|
||||||
|
<specialization time>
|
||||||
|
step 1:
|
||||||
|
uls_Fo = { [[] + Fo:f:1], [[] + Fo:f:2], [[] + Fo:f:3] }
|
||||||
|
step 2:
|
||||||
|
uls_Fo = { [[] + Fo:f:3], [[] + Fo:f:2], [[] + Fo:f:1] } (sorted)
|
||||||
|
step_3:
|
||||||
|
1. iteration: [[] + Fo:f:3]
|
||||||
|
{} -[[]]-> {} (t_f1 after removing Fo:f:3)
|
||||||
|
~ {} -[[] + b':g:2]]-> {}
|
||||||
|
= {} -[[] + b':g:2]-> {}
|
||||||
|
=> Fo, Go -[[] + Fo:f:1]-> ({} -[[] + Fo:f:2]-> ({} -[[] + b':g:2]-> {}))
|
||||||
|
|
||||||
|
2. iteration: [[] + Fo:f:2]
|
||||||
|
{} -[[]]-> ({} -[[] + b':g:2]-> {}) (t_f1 after removing Fo:f:2)
|
||||||
|
~ {} -[[lamF b'']]-> ({} -[[] + b'':g:2]]-> {})
|
||||||
|
= {} -[[lamF b'']]-> ({} -[[] + b'':g:2]]-> {})
|
||||||
|
=> Fo, Go -[[] + Fo:f:1]-> ({} -[[lamF b'']]-> ({} -[[] + b'':g:2]]-> {}))
|
||||||
|
|
||||||
|
3. iteration: [[] + Fo:f:1]
|
||||||
|
Fo, Go -[[]]-> ({} -[[lamF b'']]-> ({} -[[] + b'':g:2]]-> {})) (t_f1 after removing Fo:f:2)
|
||||||
|
~ Fo, b''' -[[Fo#f]]-> ({} -[[lamF b''']]-> ({} -[[] + b''':g:2]]-> {}))
|
||||||
|
= Fo, Go -[[Fo#f]]-> ({} -[[lamF Go]]-> ({} -[[] + Go:g:2]-> {}))
|
||||||
|
<specialization time>
|
||||||
|
step 1:
|
||||||
|
uls_Go = { [[] + Go:g:2] }
|
||||||
|
step 2:
|
||||||
|
uls_Go = { [[] + Go:g:2] } (sorted)
|
||||||
|
step_3:
|
||||||
|
1. iteration: [[] + Go:g:2]
|
||||||
|
{} -[[]]-> {} (t_f1 after removing Go:g:2)
|
||||||
|
~ {} -[[lamG]]-> {}
|
||||||
|
= {} -[[lamG]]-> {}
|
||||||
|
=> Fo, Go -[[Fo#f]]-> ({} -[[lamF Go]]-> ({} -[[lamG]]-> {}))
|
||||||
|
|
||||||
|
== final type of f ==
|
||||||
|
f : Fo, Go -[[Fo#f]]-> ({} -[[lamF Go]]-> ({} -[[lamG]]-> {}))
|
||||||
|
```
|
||||||
|
|
||||||
|
Look at that! Resolved the capture, and all the lambdas.
|
||||||
|
|
||||||
|
Notice that in the first `<specialization time>` trace, had we not sorted the `Fo:f:_` specialization lambdas in descending order of region, we would have resolved `Fo:f:3` last, and not bound the specialized `[[] + b':g:2]` to any `b'` variable. Intuitively, that’s because the variable we need to bind it to occurs in the most ambient function type of all those specialization lambdas: the one at `[[] + Fo:f:1]`
|
||||||
|
|
||||||
|
## An important requirement
|
||||||
|
|
||||||
|
There is one invariant I have left implicit in this construction, that may not hold in general. (Maybe I left others that you noticed that don’t hold - let me know!). That invariant is that any type variable in a signature is bound in either the left or right hand side of an arrow.
|
||||||
|
|
||||||
|
I know what you’re thinking, “of course, how else can you get a type variable?” Well, they have played us for fools. Evil lies in the midst. No sanctity passes unscathed through ad-hoc polymorphism.
|
||||||
|
|
||||||
|
```python
|
||||||
|
Evil has
|
||||||
|
getEvil : {} -> a | a has Evil
|
||||||
|
eatEvil : a -> ({} -> {}) | a has Evil
|
||||||
|
|
||||||
|
f = eatEvil (getEvil {})
|
||||||
|
```
|
||||||
|
|
||||||
|
The type of `f` here is `{} -> [[] + a:eatEvil:2]-> {} | a has Evil`. “Blasphemy!” you cry. Well, you’re totally right, this program is total nonsense. Somehow it’s well-typed, but the code generator can’t just synthesize an `a | a has Evil` out of nowhere.
|
||||||
|
|
||||||
|
Well, okay, the solution is actually pretty simple - make this a type error. It’s actually a more general problem with abilities, for example we can type the following program:
|
||||||
|
|
||||||
|
```python
|
||||||
|
Evil has
|
||||||
|
getEvil : {} -> a | a has Evil
|
||||||
|
eatEvil : a -> {} | a has Evil
|
||||||
|
|
||||||
|
f = eatEvil (getEvil {})
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the type variable `a | a has Evil` isn’t even visible on the surface: `f` has type `f : {}`. But it lies in the middle, snuggly between `getEvil` and `eatEvil` where it can’t be seen.
|
||||||
|
|
||||||
|
In fact, to us, detecting these cases is straightforward - such nonsense programs are identified when they have type variables that don’t escape to either the front or the back of an exposed type. That’s the only way to do monomorphization - otherwise, we could have values that are pathologically polymorphic, which means they are either unused, or this kind of non-codegen-able case.
|
||||||
|
|
||||||
|
How do we make this a type error? A couple options have been considered, but we haven’t settled on anything.
|
||||||
|
|
||||||
|
1. One approach, suggested by Richard, is to sort abilities into strongly-connected components and see if there is any zig-zag chain of member signatures in a SCC where an ability-bound type variable doesn’t escape through the front or back. We can observe two things: (1) such SCCs can only exist within a single module because Roc doesn’t have (source-level) circular dependencies and (2) we only need to examine pairs of functions have at least one type variable only appearing on one side of an arrow. That means the worst case performance of this analysis is quadratic in the number of ability members in a module. The downside of this approach is that it would reject some uses of abilities that can be resolved and code-generated by the compiler.
|
||||||
|
2. Another approach is to check whether generalized variables in a let-bound definition’s body escaped out the front or back of the let-generalized definition’s type (and **not** in a lambda set, for the reasons described above). This admits some programs that would be illegal with the other analysis but can’t be performed until typechecking. As for performance, note that new unbound type variables in a body can only be introduced by using a let-generalized symbol that is polymorphic. Those variables would need to be checked, so the performance of this approach on a per-module basis is linear in the number of let-generalized symbols used in the module (assuming the number of generalized variables returned is a constant factor).
|
||||||
|
|
||||||
|
## A Property that’s lost, and how we can hold on to it
|
||||||
|
|
||||||
|
One question I asked myself was, does this still ensure lambda sets can vary over multiple able type parameters? At first, I believed the answer was yes — however, this may not hold and be sound. For example, consider
|
||||||
|
|
||||||
|
```python
|
||||||
|
J has j : j -> (k -> {}) | j has J, k has K
|
||||||
|
K has k : k -> {} | k has K
|
||||||
|
|
||||||
|
C := {}
|
||||||
|
j = \@C _ -> k
|
||||||
|
|
||||||
|
D := {}
|
||||||
|
j = \@D _ -> k
|
||||||
|
|
||||||
|
E := {}
|
||||||
|
k = \@E _ -> {}
|
||||||
|
|
||||||
|
f = \flag, a, b, c ->
|
||||||
|
it = when flag is
|
||||||
|
A -> j a
|
||||||
|
B -> j b
|
||||||
|
it c
|
||||||
|
```
|
||||||
|
|
||||||
|
The first branch has type (`a` has generalized type `a'`)
|
||||||
|
|
||||||
|
```
|
||||||
|
c'' -[[] + a':j:2]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
The second branch has type (`b` has generalized type `b'`)
|
||||||
|
|
||||||
|
```
|
||||||
|
c''' -[[] + b':j:2]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
So now, how do we unify this? Well, following the construction above, we must unify `a'` and `b'` - but this demands that they are actually the same type variable. Is there another option?
|
||||||
|
|
||||||
|
Well, one idea is that during normal type unification, we simply take the union of unspecialized lambda sets with **disjoint** variables. In the case above, we would get `c' -[[] + a':j:2 + b':j:2]` (supposing `c` has type `c'`). During lambda set compaction, when we unify ambient types, choose one non-concrete type to unify with. Since we’re maintaining the invariant that each generalized type variable appears at least once on one side of an arrow, eventually you will have picked up all type variables in unspecialized lambda sets.
|
||||||
|
|
||||||
|
```
|
||||||
|
=== monomorphize (f A (@C {}) (@D {}) (@E {})) ===
|
||||||
|
(inside f, solving `it`:)
|
||||||
|
|
||||||
|
it ~ E -[[] + C:j:2 + D:j:2]-> {}
|
||||||
|
<specialization time: C>
|
||||||
|
step 1:
|
||||||
|
uls_C = { [[] + C:j:2 + D:j:2] }
|
||||||
|
step 2:
|
||||||
|
uls_C = { [[] + C:j:2 + D:j:2] } (sorted)
|
||||||
|
step_3:
|
||||||
|
1. iteration: [[] + C:j:2 + D:j:2]
|
||||||
|
E -[[] + D:j:2]-> {} (t_f1 after removing C:j:2)
|
||||||
|
~ k' -[[] + k':k:2]-> {}
|
||||||
|
= E -[[] + E:k:2 + D:j:2]-> {} (no non-concrete type to unify with)
|
||||||
|
=> E -[[] + E:k:2 + D:j:2]-> {}
|
||||||
|
<specialization time: D>
|
||||||
|
step 1:
|
||||||
|
uls_D = { [[] + E:k:2 + D:j:2] }
|
||||||
|
step 2:
|
||||||
|
uls_D = { [[] + E:k:2 + D:j:2] } (sorted)
|
||||||
|
step_3:
|
||||||
|
1. iteration: [[] + E:k:2 + D:j:2]
|
||||||
|
E -[[] + E:k:2]-> {} (t_f1 after removing D:j:2)
|
||||||
|
~ k'' -[[] + k'':k:2]-> {}
|
||||||
|
= E -[[] + E:k:2 + E:k:2]-> {} (no non-concrete type to unify with)
|
||||||
|
=> E -[[] + E:k:2 + E:k:2]-> {}
|
||||||
|
<specialization time: E>
|
||||||
|
step 1:
|
||||||
|
uls_E = { [[] + E:k:2], [[] + E:k:2] }
|
||||||
|
step 2:
|
||||||
|
uls_E = { [[] + E:k:2], [[] + E:k:2] } (sorted)
|
||||||
|
step_3:
|
||||||
|
1. iteration: [[] + E:k:2]
|
||||||
|
E -[[]]-> {} (t_f1 after removing E:k:2)
|
||||||
|
~ E -[[lamE]]-> {}
|
||||||
|
= E -[[lamE]]-> {}
|
||||||
|
=> E -[[lamE]]-> {}
|
||||||
|
=> E -[[lamE]]-> {}
|
||||||
|
|
||||||
|
== final type of it ==
|
||||||
|
it : E -[[lamE]]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
The disjointedness is important - we want to unify unspecialized lambdas whose type variables are equivalent. For example,
|
||||||
|
|
||||||
|
```
|
||||||
|
f = \flag, a, c ->
|
||||||
|
it = when flag is
|
||||||
|
A -> j a
|
||||||
|
B -> j a
|
||||||
|
it c
|
||||||
|
```
|
||||||
|
|
||||||
|
Should produce `it` having generalized type
|
||||||
|
|
||||||
|
```
|
||||||
|
c' -[[] + a':j:2]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
and not
|
||||||
|
|
||||||
|
```
|
||||||
|
c' -[[] + a':j:2 + a':j:2]-> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
For now, we will not try to preserve this property, and instead unify all type variables with the same member/region in a lambda set. We can improve the status of this over time.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
Will this work? I think so, but I don’t know. In the sense that, I am sure it will work for some of the problems we are dealing with today, but there may be even more interactions that aren’t clear to us until further down the road.
|
||||||
|
|
||||||
|
Obviously, this is not a rigorous study of this problem. We are making several assumptions, and I have not proved any of the properties I claim. However, the intuition makes sense to me, predicated on the “type variables escape either the front or back of a type” invariant, and this is the only approach that really makes sense to me while only being a little bit complicated. Let me know what you think.
|
||||||
|
|
||||||
|
# Appendix
|
||||||
|
|
||||||
|
## Optimization: only the lowest-region ambient function type is needed
|
||||||
|
|
||||||
|
You may have observed that step 1 and step 2 of the algorithm are somewhat overkill, really, it seems you only need the lowest-number region’s directly ambient function type to unify the specialization with. That’s because by the region invariant, the lowest-region’s ambient function would contain every other region’s ambient function.
|
||||||
|
|
||||||
|
This optimization is correct with a change to the region numbering scheme:
|
||||||
|
|
||||||
|
```python
|
||||||
|
Type = \region ->
|
||||||
|
(Type_atom, region)
|
||||||
|
| Type_function region
|
||||||
|
|
||||||
|
Type_function = \region ->
|
||||||
|
let left_type = Type (region * 2)
|
||||||
|
let right_type = Type (region * 2 + 1)
|
||||||
|
let func_type = left_type -[Lambda region]-> right_type
|
||||||
|
func_type
|
||||||
|
```
|
||||||
|
|
||||||
|
Which produces a tree like
|
||||||
|
|
||||||
|
```
|
||||||
|
-[L 1]->
|
||||||
|
-[L 2]-> -[L 3]->
|
||||||
|
-[L 4]-> -[L 5]-> -[L 6]-> -[L 7]->
|
||||||
|
a a a a a a a a
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, given a set of `uls` sorted in increasing order of region, you can remove all `uls` that have region `r` such that a floored 2-divisor of `r` is another region `r'` of a unspecialized lambda in `uls`. For example, given `[a:f:2, a:f:5, a:f3, a:f:7]`, you only need to keep `[a:f:2, a:f:3]`.
|
||||||
|
|
||||||
|
Then, when running the algorithm, you must remove unspecialized lambdas of form `C:f:_` from **all** nested lambda sets in the directly ambient function, not just in the directly ambient function. This will still be cheaper than unifying deeper lambda sets, but may be an inconvenience.
|
||||||
|
|
||||||
|
## Testing Strategies
|
||||||
|
|
||||||
|
- Quickcheck - the shape of functions we care about is quite clearly defined. Basically just create a bunch of let-bound functions, polymorphic over able variables, use them in an expression that evaluates monomorphically, and check that everything in the monomorphic expression is resolved.
|
|
@ -6,9 +6,10 @@ use roc_can::module::RigidVariables;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
use roc_derive_key::GlobalDerivedSymbols;
|
use roc_derive_key::GlobalDerivedSymbols;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_types::solved_types::Solved;
|
use roc_types::solved_types::Solved;
|
||||||
use roc_types::subs::{ExposedTypesStorageSubs, StorageSubs, Subs, Variable};
|
use roc_types::subs::{Content, ExposedTypesStorageSubs, FlatType, StorageSubs, Subs, Variable};
|
||||||
use roc_types::types::Alias;
|
use roc_types::types::Alias;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -100,10 +101,29 @@ pub fn exposed_types_storage_subs(
|
||||||
for (_, member_specialization) in solved_specializations.iter() {
|
for (_, member_specialization) in solved_specializations.iter() {
|
||||||
for (_, &specialization_lset_var) in member_specialization.specialization_lambda_sets.iter()
|
for (_, &specialization_lset_var) in member_specialization.specialization_lambda_sets.iter()
|
||||||
{
|
{
|
||||||
let new_var = storage_subs
|
let specialization_lset_ambient_function_var = subs
|
||||||
.import_variable_from(subs, specialization_lset_var)
|
.get_lambda_set(specialization_lset_var)
|
||||||
|
.ambient_function;
|
||||||
|
|
||||||
|
// Import the ambient function of this specialization lambda set; that will import the
|
||||||
|
// lambda set as well. The ambient function is needed for the lambda set compaction
|
||||||
|
// algorithm.
|
||||||
|
let imported_lset_ambient_function_var = storage_subs
|
||||||
|
.import_variable_from(subs, specialization_lset_ambient_function_var)
|
||||||
.variable;
|
.variable;
|
||||||
stored_specialization_lambda_set_vars.insert(specialization_lset_var, new_var);
|
|
||||||
|
let imported_lset_var = match storage_subs
|
||||||
|
.as_inner()
|
||||||
|
.get_content_without_compacting(imported_lset_ambient_function_var)
|
||||||
|
{
|
||||||
|
Content::Structure(FlatType::Func(_, lambda_set_var, _)) => *lambda_set_var,
|
||||||
|
content => internal_error!(
|
||||||
|
"ambient lambda set function import is not a function, found: {:?}",
|
||||||
|
roc_types::subs::SubsFmtContent(content, storage_subs.as_inner())
|
||||||
|
),
|
||||||
|
};
|
||||||
|
stored_specialization_lambda_set_vars
|
||||||
|
.insert(specialization_lset_var, imported_lset_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4024,7 +4024,8 @@ mod solve_expr {
|
||||||
{ x, y }
|
{ x, y }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{ x : I64, y ? Bool }* -> { x : I64, y : Bool }",
|
// TODO: when structural types unify with alias, they should take the alias name
|
||||||
|
"{ x : I64, y ? [False, True] }* -> { x : I64, y : Bool }",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6484,6 +6485,7 @@ mod solve_expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO: fix unification of derived types"]
|
||||||
fn encode_record() {
|
fn encode_record() {
|
||||||
infer_queries!(
|
infer_queries!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -6503,6 +6505,7 @@ mod solve_expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO: fix unification of derived types"]
|
||||||
fn encode_record_with_nested_custom_impl() {
|
fn encode_record_with_nested_custom_impl() {
|
||||||
infer_queries!(
|
infer_queries!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -7066,7 +7069,7 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"F U8 Str",
|
"F U8 Str",
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -7083,7 +7086,7 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"F U8 Str -> F U8 Str",
|
"F U8 Str -> F U8 Str",
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -7099,6 +7102,188 @@ mod solve_expr {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"F * {}* -> F * Str",
|
"F * {}* -> F * Str",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_lambda_set_specialization() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
F has f : a -> (b -> {}) | a has F, b has G
|
||||||
|
G has g : b -> {} | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> {}
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
main = (f (@Fo {})) (@Go {})
|
||||||
|
# ^
|
||||||
|
# ^^^^^^^^^^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (b -[[] + b:g(8):1]-> {}) | b has G",
|
||||||
|
"Go#g(11) : Go -[[g(11)]]-> {}",
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (Go -[[g(11)]]-> {})",
|
||||||
|
"f (@Fo {}) : Go -[[g(11)]]-> {}",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_lambda_set_specialization_bound_output() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
F has f : a -> ({} -> b) | a has F, b has G
|
||||||
|
G has g : {} -> b | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \{} -> @Go {}
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
main =
|
||||||
|
foo = 1
|
||||||
|
@Go it = (f (@Fo {})) {}
|
||||||
|
# ^
|
||||||
|
# ^^^^^^^^^^
|
||||||
|
|
||||||
|
{foo, it}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> ({} -[[] + b:g(8):1]-> b) | b has G",
|
||||||
|
"Go#g(11) : {} -[[g(11)]]-> Go",
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> ({} -[[g(11)]]-> Go)",
|
||||||
|
"f (@Fo {}) : {} -[[g(11)]]-> Go",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_lambda_set_specialization_with_let_generalization() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
F has f : a -> (b -> {}) | a has F, b has G
|
||||||
|
G has g : b -> {} | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> {}
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
main =
|
||||||
|
h = f (@Fo {})
|
||||||
|
# ^ ^
|
||||||
|
h (@Go {})
|
||||||
|
# ^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (b -[[] + b:g(8):1]-> {}) | b has G",
|
||||||
|
"Go#g(11) : Go -[[g(11)]]-> {}",
|
||||||
|
// TODO SERIOUS: Let generalization is broken here, and this is NOT correct!!
|
||||||
|
// Two problems:
|
||||||
|
// - 1. `{}` always has its rank adjusted to the toplevel, which forces the rest
|
||||||
|
// of the type to the toplevel, but that is NOT correct here!
|
||||||
|
// - 2. During solving lambda set compaction cannot happen until an entire module
|
||||||
|
// is solved, which forces resolved-but-not-yet-compacted lambdas in
|
||||||
|
// unspecialized lambda sets to pull the rank into a lower, non-generalized
|
||||||
|
// rank. Special-casing for that is a TERRIBLE HACK that interferes very
|
||||||
|
// poorly with (1)
|
||||||
|
//
|
||||||
|
// We are BLOCKED on https://github.com/rtfeldman/roc/issues/3207 to make this work
|
||||||
|
// correctly!
|
||||||
|
// See also https://github.com/rtfeldman/roc/pull/3175, a separate, but similar problem.
|
||||||
|
"h : Go -[[g(11)]]-> {}",
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (Go -[[g(11)]]-> {})",
|
||||||
|
"h : Go -[[g(11)]]-> {}",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_lambda_set_specialization_with_let_generalization_unapplied() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
F has f : a -> (b -> {}) | a has F, b has G
|
||||||
|
G has g : b -> {} | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {} -> g
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> {}
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
main =
|
||||||
|
#^^^^{-1}
|
||||||
|
h = f (@Fo {})
|
||||||
|
# ^ ^
|
||||||
|
h
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (b -[[] + b:g(8):1]-> {}) | b has G",
|
||||||
|
"Go#g(11) : Go -[[g(11)]]-> {}",
|
||||||
|
"main : b -[[] + b:g(8):1]-> {} | b has G",
|
||||||
|
"h : b -[[] + b:g(8):1]-> {} | b has G",
|
||||||
|
"Fo#f(10) : Fo -[[f(10)]]-> (b -[[] + b:g(8):1]-> {}) | b has G",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polymorphic_lambda_set_specialization_with_deep_specialization_and_capture() {
|
||||||
|
infer_queries!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
F has f : a, b -> ({} -> ({} -> {})) | a has F, b has G
|
||||||
|
G has g : b -> ({} -> {}) | b has G
|
||||||
|
|
||||||
|
Fo := {}
|
||||||
|
f = \@Fo {}, b -> \{} -> g b
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
Go := {}
|
||||||
|
g = \@Go {} -> \{} -> {}
|
||||||
|
#^{-1}
|
||||||
|
|
||||||
|
main =
|
||||||
|
(f (@Fo {}) (@Go {})) {}
|
||||||
|
# ^
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
&[
|
||||||
|
"Fo#f(10) : Fo, b -[[f(10)]]-> ({} -[[13(13) b]]-> ({} -[[] + b:g(8):2]-> {})) | b has G",
|
||||||
|
"Go#g(11) : Go -[[g(11)]]-> ({} -[[14(14)]]-> {})",
|
||||||
|
"Fo#f(10) : Fo, Go -[[f(10)]]-> ({} -[[13(13) Go]]-> ({} -[[14(14)]]-> {}))",
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -594,6 +594,7 @@ fn one_field_record() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO #3421 unification of unspecialized variables in lambda sets currently causes this to be derived incorrectly"]
|
||||||
fn two_field_record() {
|
fn two_field_record() {
|
||||||
derive_test(v!({ a: v!(U8), b: v!(STR), }), |golden| {
|
derive_test(v!({ a: v!(U8), b: v!(STR), }), |golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
|
@ -649,6 +650,7 @@ fn tag_one_label_zero_args() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO #3421 unification of unspecialized variables in lambda sets currently causes this to be derived incorrectly"]
|
||||||
fn tag_one_label_two_args() {
|
fn tag_one_label_two_args() {
|
||||||
derive_test(v!([A v!(U8) v!(STR)]), |golden| {
|
derive_test(v!([A v!(U8) v!(STR)]), |golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
|
@ -673,6 +675,7 @@ fn tag_one_label_two_args() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO #3421 unification of unspecialized variables in lambda sets currently causes this to be derived incorrectly"]
|
||||||
fn tag_two_labels() {
|
fn tag_two_labels() {
|
||||||
derive_test(v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
|
derive_test(v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
|
@ -700,6 +703,7 @@ fn tag_two_labels() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "TODO #3421 unification of unspecialized variables in lambda sets currently causes this to be derived incorrectly"]
|
||||||
fn recursive_tag_union() {
|
fn recursive_tag_union() {
|
||||||
derive_test(v!([Nil, Cons v!(U8) v!(*lst) ] as lst), |golden| {
|
derive_test(v!([Nil, Cons v!(U8) v!(*lst) ] as lst), |golden| {
|
||||||
assert_snapshot!(golden, @r###"
|
assert_snapshot!(golden, @r###"
|
||||||
|
|
|
@ -350,3 +350,28 @@ fn encode_use_stdlib() {
|
||||||
RocStr
|
RocStr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn encode_use_stdlib_without_wrapping_custom() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test"
|
||||||
|
imports [Encode.{ toEncoder }, Json]
|
||||||
|
provides [main] to "./platform"
|
||||||
|
|
||||||
|
HelloWorld := {}
|
||||||
|
toEncoder = \@HelloWorld {} -> Encode.string "Hello, World!\n"
|
||||||
|
|
||||||
|
main =
|
||||||
|
result = Str.fromUtf8 (Encode.toBytes (@HelloWorld {}) Json.format)
|
||||||
|
when result is
|
||||||
|
Ok s -> s
|
||||||
|
_ -> "<bad>"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("\"Hello, World!\n\""),
|
||||||
|
RocStr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -268,3 +268,65 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||||
RocResult<i64, bool>
|
RocResult<i64, bool>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn roc_result_after_on_ok() {
|
||||||
|
assert_evals_to!(indoc!(
|
||||||
|
r#"
|
||||||
|
input : Result I64 Str
|
||||||
|
input = Ok 1
|
||||||
|
|
||||||
|
Result.after input \num ->
|
||||||
|
if num < 0 then Err "negative!" else Ok -num
|
||||||
|
"#),
|
||||||
|
RocResult::ok(-1),
|
||||||
|
RocResult<i64, RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn roc_result_after_on_err() {
|
||||||
|
assert_evals_to!(indoc!(
|
||||||
|
r#"
|
||||||
|
input : Result I64 Str
|
||||||
|
input = (Err "already a string")
|
||||||
|
|
||||||
|
Result.after input \num ->
|
||||||
|
if num < 0 then Err "negative!" else Ok -num
|
||||||
|
"#),
|
||||||
|
RocResult::err(RocStr::from("already a string")),
|
||||||
|
RocResult<i64, RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn roc_result_after_err() {
|
||||||
|
assert_evals_to!(indoc!(
|
||||||
|
r#"
|
||||||
|
result : Result Str I64
|
||||||
|
result =
|
||||||
|
Result.afterErr (Ok "already a string") \num ->
|
||||||
|
if num < 0 then Ok "negative!" else Err -num
|
||||||
|
|
||||||
|
result
|
||||||
|
"#),
|
||||||
|
RocResult::ok(RocStr::from("already a string")),
|
||||||
|
RocResult<RocStr, i64>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(indoc!(
|
||||||
|
r#"
|
||||||
|
result : Result Str I64
|
||||||
|
result =
|
||||||
|
Result.afterErr (Err 100) \num ->
|
||||||
|
if num < 0 then Ok "negative!" else Err -num
|
||||||
|
|
||||||
|
result
|
||||||
|
"#),
|
||||||
|
RocResult::err(-100),
|
||||||
|
RocResult<RocStr, i64>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1727,3 +1727,89 @@ fn issue_3261_non_nullable_unwrapped_recursive_union_at_index() {
|
||||||
RocStr
|
RocStr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_toplevel() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
it : Value
|
||||||
|
it = foo
|
||||||
|
|
||||||
|
main =
|
||||||
|
when it is
|
||||||
|
Nil -> 123i64
|
||||||
|
_ -> -1i64
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
123,
|
||||||
|
i64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_polymorphic_expr() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
it : Value
|
||||||
|
it = foo
|
||||||
|
|
||||||
|
when it is
|
||||||
|
Nil -> 123i64
|
||||||
|
_ -> -1i64
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
123,
|
||||||
|
i64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
v1 : Value
|
||||||
|
v1 = foo
|
||||||
|
|
||||||
|
Value2 : [Nil, B U16, Array (List Value)]
|
||||||
|
|
||||||
|
v2 : Value2
|
||||||
|
v2 = foo
|
||||||
|
|
||||||
|
when {v1, v2} is
|
||||||
|
{v1: Nil, v2: Nil} -> 123i64
|
||||||
|
{v1: _, v2: _} -> -1i64
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
123,
|
||||||
|
i64
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -95,8 +95,10 @@ impl<T: FromWasmerMemory + Clone> FromWasmerMemory for RocList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FromWasmerMemory + Wasm32Sized, E: FromWasmerMemory + Wasm32Sized> FromWasmerMemory
|
impl<T, E> FromWasmerMemory for RocResult<T, E>
|
||||||
for RocResult<T, E>
|
where
|
||||||
|
T: FromWasmerMemory + Wasm32Sized,
|
||||||
|
E: FromWasmerMemory + Wasm32Sized,
|
||||||
{
|
{
|
||||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||||
let tag_offset = Ord::max(T::ACTUAL_WIDTH, E::ACTUAL_WIDTH);
|
let tag_offset = Ord::max(T::ACTUAL_WIDTH, E::ACTUAL_WIDTH);
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
let List.283 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
let List.295 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3;
|
||||||
decref #Attr.2;
|
decref #Attr.2;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
let Test.7 : {} = Struct {};
|
let Test.7 : {} = Struct {};
|
||||||
let Test.6 : {} = CallByName Test.8 Test.7;
|
Error a Lambda Set is empty. Most likely there is a type error in your program.
|
||||||
ret Test.6;
|
|
||||||
|
|
||||||
procedure Test.8 (Test.9):
|
|
||||||
Error The `#UserApp.IdentId(8)` function could not be generated, likely due to a type error.
|
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List {} = Array [];
|
let Test.1 : List [] = Array [];
|
||||||
let Test.5 : {} = Struct {};
|
let Test.5 : {} = Struct {};
|
||||||
let Test.4 : List {} = CallByName List.5 Test.1 Test.5;
|
let Test.4 : List {} = CallByName List.5 Test.1 Test.5;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 5030
|
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 5035
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List {} = Array [];
|
let Test.1 : List [] = Array [];
|
||||||
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 4557
|
Error UnresolvedTypeVar crates/compiler/mono/src/ir.rs line 4562
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I128 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.258 : I128 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.6 : I128 = 18446744073709551616i64;
|
let Test.6 : I128 = 18446744073709551616i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U128 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : U128 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : U128 = 170141183460469231731687303715884105728u128;
|
let Test.2 : U128 = 170141183460469231731687303715884105728u128;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : U64 = 9999999999999999999i64;
|
let Test.2 : U64 = 9999999999999999999i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.283 : U64 = lowlevel ListLen #Attr.2;
|
let List.295 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure Test.1 (Test.5):
|
procedure Test.1 (Test.5):
|
||||||
let Test.2 : I64 = 41i64;
|
let Test.2 : I64 = 41i64;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
procedure Dict.1 ():
|
procedure Dict.1 ():
|
||||||
let Dict.16 : Dict [] [] = lowlevel DictEmpty ;
|
let Dict.25 : Dict [] [] = lowlevel DictEmpty ;
|
||||||
ret Dict.16;
|
ret Dict.25;
|
||||||
|
|
||||||
procedure Dict.7 (#Attr.2):
|
procedure Dict.7 (#Attr.2):
|
||||||
let Dict.15 : U64 = lowlevel DictSize #Attr.2;
|
let Dict.24 : U64 = lowlevel DictSize #Attr.2;
|
||||||
dec #Attr.2;
|
dec #Attr.2;
|
||||||
ret Dict.15;
|
ret Dict.24;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : Dict [] [] = CallByName Dict.1;
|
let Test.2 : Dict [] [] = CallByName Dict.1;
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.288 : U64 = CallByName List.6 List.77;
|
let List.300 : U64 = CallByName List.6 List.78;
|
||||||
let List.285 : Int1 = CallByName Num.22 List.78 List.288;
|
let List.297 : Int1 = CallByName Num.22 List.79 List.300;
|
||||||
if List.285 then
|
if List.297 then
|
||||||
let List.287 : {} = CallByName List.60 List.77 List.78;
|
let List.299 : {} = CallByName List.60 List.78 List.79;
|
||||||
let List.286 : [C {}, C {}] = TagId(1) List.287;
|
let List.298 : [C {}, C {}] = TagId(1) List.299;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
else
|
else
|
||||||
let List.284 : {} = Struct {};
|
let List.296 : {} = Struct {};
|
||||||
let List.283 : [C {}, C {}] = TagId(0) List.284;
|
let List.295 : [C {}, C {}] = TagId(0) List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.290 : U64 = lowlevel ListLen #Attr.2;
|
let List.302 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.289 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.301 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.289;
|
ret List.301;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.2 (Test.6):
|
procedure Test.2 (Test.6):
|
||||||
let Test.18 : Str = "bar";
|
let Test.18 : Str = "bar";
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
procedure List.4 (List.88, List.89):
|
procedure List.4 (List.89, List.90):
|
||||||
let List.285 : U64 = 1i64;
|
let List.297 : U64 = 1i64;
|
||||||
let List.284 : List U8 = CallByName List.65 List.88 List.285;
|
let List.296 : List U8 = CallByName List.65 List.89 List.297;
|
||||||
let List.283 : List U8 = CallByName List.66 List.284 List.89;
|
let List.295 : List U8 = CallByName List.66 List.296 List.90;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.65 (#Attr.2, #Attr.3):
|
procedure List.65 (#Attr.2, #Attr.3):
|
||||||
let List.287 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.299 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
ret List.287;
|
ret List.299;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.286 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
let List.298 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
|
|
||||||
procedure Test.20 (Test.22):
|
procedure Test.20 (Test.22):
|
||||||
let Test.34 : {U8} = Struct {Test.22};
|
let Test.34 : {U8} = Struct {Test.22};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
procedure Num.20 (#Attr.2, #Attr.3):
|
procedure Num.20 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Num.21 (#Attr.2, #Attr.3):
|
procedure Num.21 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.15, Test.16):
|
procedure Test.1 (Test.15, Test.16):
|
||||||
joinpoint Test.7 Test.2 Test.3:
|
joinpoint Test.7 Test.2 Test.3:
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.9 : [<rnu><null>, C List *self] = TagId(1) ;
|
||||||
|
let Test.10 : [C List [<rnu><null>, C List *self], C U16, C ] = TagId(2) ;
|
||||||
|
let Test.12 : {[<rnu><null>, C List *self], [C List [<rnu><null>, C List *self], C U16, C ]} = Struct {Test.9, Test.10};
|
||||||
|
ret Test.12;
|
|
@ -0,0 +1,3 @@
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.5 : [<rnu><null>, C List *self] = TagId(1) ;
|
||||||
|
ret Test.5;
|
|
@ -0,0 +1,7 @@
|
||||||
|
procedure Test.4 ():
|
||||||
|
let Test.7 : [<rnu><null>, C List *self] = TagId(1) ;
|
||||||
|
ret Test.7;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.6 : [<rnu><null>, C List *self] = CallByName Test.4;
|
||||||
|
ret Test.6;
|
|
@ -1,10 +1,10 @@
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.283 : U64 = lowlevel ListLen #Attr.2;
|
let List.295 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.190 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.259 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.190;
|
ret Num.259;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List I64 = Array [1i64, 2i64];
|
let Test.1 : List I64 = Array [1i64, 2i64];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : I64 = 1i64;
|
let Test.2 : I64 = 1i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.45 (#Attr.2):
|
procedure Num.45 (#Attr.2):
|
||||||
let Num.188 : I64 = lowlevel NumRound #Attr.2;
|
let Num.257 : I64 = lowlevel NumRound #Attr.2;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : Float64 = 3.6f64;
|
let Test.2 : Float64 = 3.6f64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : I64 = 3i64;
|
let Test.1 : I64 = 3i64;
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
procedure Num.40 (#Attr.2, #Attr.3):
|
procedure Bool.7 (#Attr.2, #Attr.3):
|
||||||
let Num.193 : I64 = 0i64;
|
let Bool.9 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
let Num.190 : Int1 = lowlevel NotEq #Attr.3 Num.193;
|
ret Bool.9;
|
||||||
if Num.190 then
|
|
||||||
let Num.192 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
procedure Num.39 (#Attr.2, #Attr.3):
|
||||||
let Num.191 : [C {}, C I64] = TagId(1) Num.192;
|
let Num.263 : I64 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||||
ret Num.191;
|
ret Num.263;
|
||||||
|
|
||||||
|
procedure Num.40 (Num.229, Num.230):
|
||||||
|
let Num.262 : I64 = 0i64;
|
||||||
|
let Num.259 : Int1 = CallByName Bool.7 Num.230 Num.262;
|
||||||
|
if Num.259 then
|
||||||
|
let Num.261 : {} = Struct {};
|
||||||
|
let Num.260 : [C {}, C I64] = TagId(0) Num.261;
|
||||||
|
ret Num.260;
|
||||||
else
|
else
|
||||||
let Num.189 : {} = Struct {};
|
let Num.258 : I64 = CallByName Num.39 Num.229 Num.230;
|
||||||
let Num.188 : [C {}, C I64] = TagId(0) Num.189;
|
let Num.257 : [C {}, C I64] = TagId(1) Num.258;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.8 : I64 = 1000i64;
|
let Test.8 : I64 = 1000i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 : I64 = 41i64;
|
let Test.10 : I64 = 41i64;
|
||||||
|
|
|
@ -1,55 +1,67 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure Bool.7 (#Attr.2, #Attr.3):
|
||||||
let List.297 : U64 = CallByName List.6 List.77;
|
let Bool.9 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
let List.293 : Int1 = CallByName Num.22 List.78 List.297;
|
ret Bool.9;
|
||||||
if List.293 then
|
|
||||||
let List.295 : I64 = CallByName List.60 List.77 List.78;
|
procedure List.2 (List.78, List.79):
|
||||||
let List.294 : [C {}, C I64] = TagId(1) List.295;
|
let List.309 : U64 = CallByName List.6 List.78;
|
||||||
ret List.294;
|
let List.305 : Int1 = CallByName Num.22 List.79 List.309;
|
||||||
|
if List.305 then
|
||||||
|
let List.307 : I64 = CallByName List.60 List.78 List.79;
|
||||||
|
let List.306 : [C {}, C I64] = TagId(1) List.307;
|
||||||
|
ret List.306;
|
||||||
else
|
else
|
||||||
let List.292 : {} = Struct {};
|
let List.304 : {} = Struct {};
|
||||||
let List.291 : [C {}, C I64] = TagId(0) List.292;
|
let List.303 : [C {}, C I64] = TagId(0) List.304;
|
||||||
ret List.291;
|
ret List.303;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.298 : U64 = lowlevel ListLen #Attr.2;
|
let List.310 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.298;
|
ret List.310;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.296 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.308 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.296;
|
ret List.308;
|
||||||
|
|
||||||
procedure List.9 (List.205):
|
procedure List.9 (List.206):
|
||||||
let List.290 : U64 = 0i64;
|
let List.302 : U64 = 0i64;
|
||||||
let List.283 : [C {}, C I64] = CallByName List.2 List.205 List.290;
|
let List.295 : [C {}, C I64] = CallByName List.2 List.206 List.302;
|
||||||
let List.287 : U8 = 1i64;
|
let List.299 : U8 = 1i64;
|
||||||
let List.288 : U8 = GetTagId List.283;
|
let List.300 : U8 = GetTagId List.295;
|
||||||
let List.289 : Int1 = lowlevel Eq List.287 List.288;
|
let List.301 : Int1 = lowlevel Eq List.299 List.300;
|
||||||
if List.289 then
|
if List.301 then
|
||||||
let List.206 : I64 = UnionAtIndex (Id 1) (Index 0) List.283;
|
let List.207 : I64 = UnionAtIndex (Id 1) (Index 0) List.295;
|
||||||
let List.284 : [C Int1, C I64] = TagId(1) List.206;
|
let List.296 : [C Int1, C I64] = TagId(1) List.207;
|
||||||
ret List.284;
|
ret List.296;
|
||||||
else
|
else
|
||||||
let List.286 : Int1 = true;
|
let List.298 : Int1 = true;
|
||||||
let List.285 : [C Int1, C I64] = TagId(0) List.286;
|
let List.297 : [C Int1, C I64] = TagId(0) List.298;
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Str.27 (#Attr.2):
|
procedure Str.27 (Str.88):
|
||||||
let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
let Str.194 : [C Int1, C I64] = CallByName Str.67 Str.88;
|
||||||
let Str.159 : U8 = StructAtIndex 1 #Attr.3;
|
ret Str.194;
|
||||||
let Str.160 : U8 = 0i64;
|
|
||||||
let Str.156 : Int1 = lowlevel NumGt Str.159 Str.160;
|
procedure Str.47 (#Attr.2):
|
||||||
if Str.156 then
|
let Str.202 : {I64, U8} = lowlevel StrToNum #Attr.2;
|
||||||
let Str.158 : Int1 = false;
|
ret Str.202;
|
||||||
let Str.157 : [C Int1, C I64] = TagId(0) Str.158;
|
|
||||||
ret Str.157;
|
procedure Str.67 (Str.189):
|
||||||
|
let Str.190 : {I64, U8} = CallByName Str.47 Str.189;
|
||||||
|
let Str.200 : U8 = StructAtIndex 1 Str.190;
|
||||||
|
let Str.201 : U8 = 0i64;
|
||||||
|
let Str.197 : Int1 = CallByName Bool.7 Str.200 Str.201;
|
||||||
|
if Str.197 then
|
||||||
|
let Str.199 : I64 = StructAtIndex 0 Str.190;
|
||||||
|
let Str.198 : [C Int1, C I64] = TagId(1) Str.199;
|
||||||
|
ret Str.198;
|
||||||
else
|
else
|
||||||
let Str.155 : I64 = StructAtIndex 0 #Attr.3;
|
let Str.196 : Int1 = false;
|
||||||
let Str.154 : [C Int1, C I64] = TagId(1) Str.155;
|
let Str.195 : [C Int1, C I64] = TagId(0) Str.196;
|
||||||
ret Str.154;
|
ret Str.195;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.4 : Int1 = true;
|
let Test.4 : Int1 = true;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
procedure Num.94 (#Attr.2):
|
procedure Num.94 (#Attr.2):
|
||||||
let Num.188 : Str = lowlevel NumToStr #Attr.2;
|
let Num.257 : Str = lowlevel NumToStr #Attr.2;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Num.94 (#Attr.2):
|
procedure Num.94 (#Attr.2):
|
||||||
let Num.189 : Str = lowlevel NumToStr #Attr.2;
|
let Num.258 : Str = lowlevel NumToStr #Attr.2;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Test.1 (Test.4):
|
procedure Test.1 (Test.4):
|
||||||
let Test.16 : [C U8, C U64] = TagId(1) Test.4;
|
let Test.16 : [C U8, C U64] = TagId(1) Test.4;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
procedure List.4 (List.88, List.89):
|
procedure List.4 (List.89, List.90):
|
||||||
let List.285 : U64 = 1i64;
|
let List.297 : U64 = 1i64;
|
||||||
let List.284 : List I64 = CallByName List.65 List.88 List.285;
|
let List.296 : List I64 = CallByName List.65 List.89 List.297;
|
||||||
let List.283 : List I64 = CallByName List.66 List.284 List.89;
|
let List.295 : List I64 = CallByName List.66 List.296 List.90;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.65 (#Attr.2, #Attr.3):
|
procedure List.65 (#Attr.2, #Attr.3):
|
||||||
let List.287 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.299 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
ret List.287;
|
ret List.299;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.286 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
let List.298 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : List I64 = Array [1i64];
|
let Test.2 : List I64 = Array [1i64];
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
procedure List.4 (List.88, List.89):
|
procedure List.4 (List.89, List.90):
|
||||||
let List.285 : U64 = 1i64;
|
let List.297 : U64 = 1i64;
|
||||||
let List.284 : List I64 = CallByName List.65 List.88 List.285;
|
let List.296 : List I64 = CallByName List.65 List.89 List.297;
|
||||||
let List.283 : List I64 = CallByName List.66 List.284 List.89;
|
let List.295 : List I64 = CallByName List.66 List.296 List.90;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.65 (#Attr.2, #Attr.3):
|
procedure List.65 (#Attr.2, #Attr.3):
|
||||||
let List.287 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
let List.299 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||||
ret List.287;
|
ret List.299;
|
||||||
|
|
||||||
procedure List.66 (#Attr.2, #Attr.3):
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
let List.286 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
let List.298 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.6 : I64 = 42i64;
|
let Test.6 : I64 = 42i64;
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
procedure List.3 (List.85, List.86, List.87):
|
procedure List.3 (List.86, List.87, List.88):
|
||||||
let List.286 : {List I64, I64} = CallByName List.57 List.85 List.86 List.87;
|
let List.298 : {List I64, I64} = CallByName List.57 List.86 List.87 List.88;
|
||||||
let List.285 : List I64 = StructAtIndex 0 List.286;
|
let List.297 : List I64 = StructAtIndex 0 List.298;
|
||||||
inc List.285;
|
inc List.297;
|
||||||
dec List.286;
|
dec List.298;
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure List.57 (List.82, List.83, List.84):
|
procedure List.57 (List.83, List.84, List.85):
|
||||||
let List.291 : U64 = CallByName List.6 List.82;
|
let List.303 : U64 = CallByName List.6 List.83;
|
||||||
let List.288 : Int1 = CallByName Num.22 List.83 List.291;
|
let List.300 : Int1 = CallByName Num.22 List.84 List.303;
|
||||||
if List.288 then
|
if List.300 then
|
||||||
let List.289 : {List I64, I64} = CallByName List.61 List.82 List.83 List.84;
|
let List.301 : {List I64, I64} = CallByName List.61 List.83 List.84 List.85;
|
||||||
ret List.289;
|
ret List.301;
|
||||||
else
|
else
|
||||||
let List.287 : {List I64, I64} = Struct {List.82, List.84};
|
let List.299 : {List I64, I64} = Struct {List.83, List.85};
|
||||||
ret List.287;
|
ret List.299;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.284 : U64 = lowlevel ListLen #Attr.2;
|
let List.296 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.284;
|
ret List.296;
|
||||||
|
|
||||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let List.290 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
let List.302 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.258 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.8 : List I64 = Array [1i64, 2i64, 3i64];
|
let Test.8 : List I64 = Array [1i64, 2i64, 3i64];
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.288 : U64 = CallByName List.6 List.77;
|
let List.300 : U64 = CallByName List.6 List.78;
|
||||||
let List.285 : Int1 = CallByName Num.22 List.78 List.288;
|
let List.297 : Int1 = CallByName Num.22 List.79 List.300;
|
||||||
if List.285 then
|
if List.297 then
|
||||||
let List.287 : I64 = CallByName List.60 List.77 List.78;
|
let List.299 : I64 = CallByName List.60 List.78 List.79;
|
||||||
let List.286 : [C {}, C I64] = TagId(1) List.287;
|
let List.298 : [C {}, C I64] = TagId(1) List.299;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
else
|
else
|
||||||
let List.284 : {} = Struct {};
|
let List.296 : {} = Struct {};
|
||||||
let List.283 : [C {}, C I64] = TagId(0) List.284;
|
let List.295 : [C {}, C I64] = TagId(0) List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.290 : U64 = lowlevel ListLen #Attr.2;
|
let List.302 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.289 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.301 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.289;
|
ret List.301;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.6 : List I64 = Array [1i64, 2i64, 3i64];
|
let Test.6 : List I64 = Array [1i64, 2i64, 3i64];
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.283 : U64 = lowlevel ListLen #Attr.2;
|
let List.295 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.284 : U64 = lowlevel ListLen #Attr.2;
|
let List.296 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.284;
|
ret List.296;
|
||||||
|
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.1 : List I64 = Array [1i64, 2i64, 3i64];
|
let Test.1 : List I64 = Array [1i64, 2i64, 3i64];
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.288 : U64 = CallByName List.6 List.77;
|
let List.300 : U64 = CallByName List.6 List.78;
|
||||||
let List.285 : Int1 = CallByName Num.22 List.78 List.288;
|
let List.297 : Int1 = CallByName Num.22 List.79 List.300;
|
||||||
if List.285 then
|
if List.297 then
|
||||||
let List.287 : Str = CallByName List.60 List.77 List.78;
|
let List.299 : Str = CallByName List.60 List.78 List.79;
|
||||||
let List.286 : [C {}, C Str] = TagId(1) List.287;
|
let List.298 : [C {}, C Str] = TagId(1) List.299;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
else
|
else
|
||||||
let List.284 : {} = Struct {};
|
let List.296 : {} = Struct {};
|
||||||
let List.283 : [C {}, C Str] = TagId(0) List.284;
|
let List.295 : [C {}, C Str] = TagId(0) List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
let List.289 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
let List.301 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||||
ret List.289;
|
ret List.301;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.291 : U64 = lowlevel ListLen #Attr.2;
|
let List.303 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.291;
|
ret List.303;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.290 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.302 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Str.16 (#Attr.2, #Attr.3):
|
procedure Str.16 (#Attr.2, #Attr.3):
|
||||||
let Str.154 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
let Str.194 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
|
||||||
ret Str.154;
|
ret Str.194;
|
||||||
|
|
||||||
procedure Str.3 (#Attr.2, #Attr.3):
|
procedure Str.3 (#Attr.2, #Attr.3):
|
||||||
let Str.155 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
let Str.195 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||||
ret Str.155;
|
ret Str.195;
|
||||||
|
|
||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.288 : U64 = CallByName List.6 List.77;
|
let List.300 : U64 = CallByName List.6 List.78;
|
||||||
let List.285 : Int1 = CallByName Num.22 List.78 List.288;
|
let List.297 : Int1 = CallByName Num.22 List.79 List.300;
|
||||||
if List.285 then
|
if List.297 then
|
||||||
let List.287 : Str = CallByName List.60 List.77 List.78;
|
let List.299 : Str = CallByName List.60 List.78 List.79;
|
||||||
let List.286 : [C {}, C Str] = TagId(1) List.287;
|
let List.298 : [C {}, C Str] = TagId(1) List.299;
|
||||||
ret List.286;
|
ret List.298;
|
||||||
else
|
else
|
||||||
let List.284 : {} = Struct {};
|
let List.296 : {} = Struct {};
|
||||||
let List.283 : [C {}, C Str] = TagId(0) List.284;
|
let List.295 : [C {}, C Str] = TagId(0) List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.5 (#Attr.2, #Attr.3):
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
inc #Attr.2;
|
inc #Attr.2;
|
||||||
let List.289 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
let List.301 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
|
||||||
decref #Attr.2;
|
decref #Attr.2;
|
||||||
ret List.289;
|
ret List.301;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.291 : U64 = lowlevel ListLen #Attr.2;
|
let List.303 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.291;
|
ret List.303;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.290 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.302 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Str.3 (#Attr.2, #Attr.3):
|
procedure Str.3 (#Attr.2, #Attr.3):
|
||||||
let Str.155 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
let Str.195 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||||
ret Str.155;
|
ret Str.195;
|
||||||
|
|
||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
procedure List.3 (List.85, List.86, List.87):
|
procedure List.3 (List.86, List.87, List.88):
|
||||||
let List.284 : {List I64, I64} = CallByName List.57 List.85 List.86 List.87;
|
let List.296 : {List I64, I64} = CallByName List.57 List.86 List.87 List.88;
|
||||||
let List.283 : List I64 = StructAtIndex 0 List.284;
|
let List.295 : List I64 = StructAtIndex 0 List.296;
|
||||||
inc List.283;
|
inc List.295;
|
||||||
dec List.284;
|
dec List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure List.57 (List.82, List.83, List.84):
|
procedure List.57 (List.83, List.84, List.85):
|
||||||
let List.289 : U64 = CallByName List.6 List.82;
|
let List.301 : U64 = CallByName List.6 List.83;
|
||||||
let List.286 : Int1 = CallByName Num.22 List.83 List.289;
|
let List.298 : Int1 = CallByName Num.22 List.84 List.301;
|
||||||
if List.286 then
|
if List.298 then
|
||||||
let List.287 : {List I64, I64} = CallByName List.61 List.82 List.83 List.84;
|
let List.299 : {List I64, I64} = CallByName List.61 List.83 List.84 List.85;
|
||||||
ret List.287;
|
ret List.299;
|
||||||
else
|
else
|
||||||
let List.285 : {List I64, I64} = Struct {List.82, List.84};
|
let List.297 : {List I64, I64} = Struct {List.83, List.85};
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.290 : U64 = lowlevel ListLen #Attr.2;
|
let List.302 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.290;
|
ret List.302;
|
||||||
|
|
||||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let List.288 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
let List.300 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret List.288;
|
ret List.300;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.257 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.2 (Test.3):
|
procedure Test.2 (Test.3):
|
||||||
let Test.6 : U64 = 0i64;
|
let Test.6 : U64 = 0i64;
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
procedure List.28 (#Attr.2, #Attr.3):
|
procedure List.28 (#Attr.2, #Attr.3):
|
||||||
let List.285 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
let List.297 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
|
||||||
let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2;
|
let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2;
|
||||||
if Bool.9 then
|
if Bool.9 then
|
||||||
ret List.285;
|
ret List.297;
|
||||||
else
|
else
|
||||||
decref #Attr.2;
|
decref #Attr.2;
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure List.54 (List.200):
|
procedure List.54 (List.201):
|
||||||
let List.284 : {} = Struct {};
|
let List.296 : {} = Struct {};
|
||||||
let List.283 : List I64 = CallByName List.28 List.200 List.284;
|
let List.295 : List I64 = CallByName List.28 List.201 List.296;
|
||||||
ret List.283;
|
ret List.295;
|
||||||
|
|
||||||
procedure Num.46 (#Attr.2, #Attr.3):
|
procedure Num.46 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;
|
let Num.257 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64];
|
let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.19 : I64 = 41i64;
|
let Test.19 : I64 = 41i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.21 (#Attr.2, #Attr.3):
|
procedure Num.21 (#Attr.2, #Attr.3):
|
||||||
let Num.190 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Num.259 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
ret Num.190;
|
ret Num.259;
|
||||||
|
|
||||||
procedure Test.1 (Test.6):
|
procedure Test.1 (Test.6):
|
||||||
let Test.21 : Int1 = false;
|
let Test.21 : Int1 = false;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Num.20 (#Attr.2, #Attr.3):
|
procedure Num.20 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.190;
|
ret Num.259;
|
||||||
|
|
||||||
procedure Test.1 (Test.24, Test.25, Test.26):
|
procedure Test.1 (Test.24, Test.25, Test.26):
|
||||||
joinpoint Test.12 Test.2 Test.3 Test.4:
|
joinpoint Test.12 Test.2 Test.3 Test.4:
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.298 : U64 = CallByName List.6 List.77;
|
let List.310 : U64 = CallByName List.6 List.78;
|
||||||
let List.295 : Int1 = CallByName Num.22 List.78 List.298;
|
let List.307 : Int1 = CallByName Num.22 List.79 List.310;
|
||||||
if List.295 then
|
if List.307 then
|
||||||
let List.297 : I64 = CallByName List.60 List.77 List.78;
|
let List.309 : I64 = CallByName List.60 List.78 List.79;
|
||||||
let List.296 : [C {}, C I64] = TagId(1) List.297;
|
let List.308 : [C {}, C I64] = TagId(1) List.309;
|
||||||
ret List.296;
|
ret List.308;
|
||||||
else
|
else
|
||||||
let List.294 : {} = Struct {};
|
let List.306 : {} = Struct {};
|
||||||
let List.293 : [C {}, C I64] = TagId(0) List.294;
|
let List.305 : [C {}, C I64] = TagId(0) List.306;
|
||||||
ret List.293;
|
ret List.305;
|
||||||
|
|
||||||
procedure List.3 (List.85, List.86, List.87):
|
procedure List.3 (List.86, List.87, List.88):
|
||||||
let List.286 : {List I64, I64} = CallByName List.57 List.85 List.86 List.87;
|
let List.298 : {List I64, I64} = CallByName List.57 List.86 List.87 List.88;
|
||||||
let List.285 : List I64 = StructAtIndex 0 List.286;
|
let List.297 : List I64 = StructAtIndex 0 List.298;
|
||||||
inc List.285;
|
inc List.297;
|
||||||
dec List.286;
|
dec List.298;
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure List.57 (List.82, List.83, List.84):
|
procedure List.57 (List.83, List.84, List.85):
|
||||||
let List.303 : U64 = CallByName List.6 List.82;
|
let List.315 : U64 = CallByName List.6 List.83;
|
||||||
let List.300 : Int1 = CallByName Num.22 List.83 List.303;
|
let List.312 : Int1 = CallByName Num.22 List.84 List.315;
|
||||||
if List.300 then
|
if List.312 then
|
||||||
let List.301 : {List I64, I64} = CallByName List.61 List.82 List.83 List.84;
|
let List.313 : {List I64, I64} = CallByName List.61 List.83 List.84 List.85;
|
||||||
ret List.301;
|
ret List.313;
|
||||||
else
|
else
|
||||||
let List.299 : {List I64, I64} = Struct {List.82, List.84};
|
let List.311 : {List I64, I64} = Struct {List.83, List.85};
|
||||||
ret List.299;
|
ret List.311;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.304 : U64 = lowlevel ListLen #Attr.2;
|
let List.316 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.304;
|
ret List.316;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.305 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.317 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.305;
|
ret List.317;
|
||||||
|
|
||||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let List.302 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
let List.314 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret List.302;
|
ret List.314;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.190;
|
ret Num.259;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.28 : U64 = 0i64;
|
let Test.28 : U64 = 0i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.4):
|
procedure Test.1 (Test.4):
|
||||||
let Test.2 : I64 = StructAtIndex 0 Test.4;
|
let Test.2 : I64 = StructAtIndex 0 Test.4;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.4):
|
procedure Test.1 (Test.4):
|
||||||
let Test.2 : I64 = 10i64;
|
let Test.2 : I64 = 10i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.3 : I64 = StructAtIndex 0 Test.2;
|
let Test.3 : I64 = StructAtIndex 0 Test.2;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.3 : I64 = 10i64;
|
let Test.3 : I64 = 10i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : U32 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : U32 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.9 : U32 = 0i64;
|
let Test.9 : U32 = 0i64;
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
procedure List.2 (List.77, List.78):
|
procedure List.2 (List.78, List.79):
|
||||||
let List.298 : U64 = CallByName List.6 List.77;
|
let List.310 : U64 = CallByName List.6 List.78;
|
||||||
let List.295 : Int1 = CallByName Num.22 List.78 List.298;
|
let List.307 : Int1 = CallByName Num.22 List.79 List.310;
|
||||||
if List.295 then
|
if List.307 then
|
||||||
let List.297 : I64 = CallByName List.60 List.77 List.78;
|
let List.309 : I64 = CallByName List.60 List.78 List.79;
|
||||||
let List.296 : [C {}, C I64] = TagId(1) List.297;
|
let List.308 : [C {}, C I64] = TagId(1) List.309;
|
||||||
ret List.296;
|
ret List.308;
|
||||||
else
|
else
|
||||||
let List.294 : {} = Struct {};
|
let List.306 : {} = Struct {};
|
||||||
let List.293 : [C {}, C I64] = TagId(0) List.294;
|
let List.305 : [C {}, C I64] = TagId(0) List.306;
|
||||||
ret List.293;
|
ret List.305;
|
||||||
|
|
||||||
procedure List.3 (List.85, List.86, List.87):
|
procedure List.3 (List.86, List.87, List.88):
|
||||||
let List.286 : {List I64, I64} = CallByName List.57 List.85 List.86 List.87;
|
let List.298 : {List I64, I64} = CallByName List.57 List.86 List.87 List.88;
|
||||||
let List.285 : List I64 = StructAtIndex 0 List.286;
|
let List.297 : List I64 = StructAtIndex 0 List.298;
|
||||||
inc List.285;
|
inc List.297;
|
||||||
dec List.286;
|
dec List.298;
|
||||||
ret List.285;
|
ret List.297;
|
||||||
|
|
||||||
procedure List.57 (List.82, List.83, List.84):
|
procedure List.57 (List.83, List.84, List.85):
|
||||||
let List.303 : U64 = CallByName List.6 List.82;
|
let List.315 : U64 = CallByName List.6 List.83;
|
||||||
let List.300 : Int1 = CallByName Num.22 List.83 List.303;
|
let List.312 : Int1 = CallByName Num.22 List.84 List.315;
|
||||||
if List.300 then
|
if List.312 then
|
||||||
let List.301 : {List I64, I64} = CallByName List.61 List.82 List.83 List.84;
|
let List.313 : {List I64, I64} = CallByName List.61 List.83 List.84 List.85;
|
||||||
ret List.301;
|
ret List.313;
|
||||||
else
|
else
|
||||||
let List.299 : {List I64, I64} = Struct {List.82, List.84};
|
let List.311 : {List I64, I64} = Struct {List.83, List.85};
|
||||||
ret List.299;
|
ret List.311;
|
||||||
|
|
||||||
procedure List.6 (#Attr.2):
|
procedure List.6 (#Attr.2):
|
||||||
let List.304 : U64 = lowlevel ListLen #Attr.2;
|
let List.316 : U64 = lowlevel ListLen #Attr.2;
|
||||||
ret List.304;
|
ret List.316;
|
||||||
|
|
||||||
procedure List.60 (#Attr.2, #Attr.3):
|
procedure List.60 (#Attr.2, #Attr.3):
|
||||||
let List.305 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let List.317 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
ret List.305;
|
ret List.317;
|
||||||
|
|
||||||
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
let List.302 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
let List.314 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||||
ret List.302;
|
ret List.314;
|
||||||
|
|
||||||
procedure Num.22 (#Attr.2, #Attr.3):
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
let Num.259 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
ret Num.190;
|
ret Num.259;
|
||||||
|
|
||||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||||
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.258 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Num.21 (#Attr.2, #Attr.3):
|
procedure Num.21 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.1 (Test.2, Test.3):
|
procedure Test.1 (Test.2, Test.3):
|
||||||
let Test.17 : U8 = GetTagId Test.2;
|
let Test.17 : U8 = GetTagId Test.2;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.258 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Num.21 (#Attr.2, #Attr.3):
|
procedure Num.21 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.6 (Test.8, #Attr.12):
|
procedure Test.6 (Test.8, #Attr.12):
|
||||||
let Test.4 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
let Test.4 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Num.20 (#Attr.2, #Attr.3):
|
procedure Num.20 (#Attr.2, #Attr.3):
|
||||||
let Num.189 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
let Num.258 : I64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||||
ret Num.189;
|
ret Num.258;
|
||||||
|
|
||||||
procedure Test.1 (Test.15, Test.16):
|
procedure Test.1 (Test.15, Test.16):
|
||||||
joinpoint Test.7 Test.2 Test.3:
|
joinpoint Test.7 Test.2 Test.3:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.19 : I64 = 41i64;
|
let Test.19 : I64 = 41i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.5 : I64 = 2i64;
|
let Test.5 : I64 = 2i64;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.19 (#Attr.2, #Attr.3):
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
let Num.188 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Num.257 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Num.188;
|
ret Num.257;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.15 : I64 = 3i64;
|
let Test.15 : I64 = 3i64;
|
||||||
|
|
|
@ -1718,3 +1718,65 @@ fn call_function_in_empty_list_unbound() {
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_toplevel() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [it] to "./platform"
|
||||||
|
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
it : Value
|
||||||
|
it = foo
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_polymorphic_expr() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
it : Value
|
||||||
|
it = foo
|
||||||
|
|
||||||
|
it
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn instantiate_annotated_as_recursive_alias_multiple_polymorphic_expr() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
Value : [Nil, Array (List Value)]
|
||||||
|
|
||||||
|
foo : [Nil]*
|
||||||
|
foo = Nil
|
||||||
|
|
||||||
|
v1 : Value
|
||||||
|
v1 = foo
|
||||||
|
|
||||||
|
Value2 : [Nil, B U16, Array (List Value)]
|
||||||
|
|
||||||
|
v2 : Value2
|
||||||
|
v2 = foo
|
||||||
|
|
||||||
|
{v1, v2}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -342,6 +342,7 @@ fn find_names_needed(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
for slice_index in solved.variables() {
|
for slice_index in solved.variables() {
|
||||||
let slice = subs[slice_index];
|
let slice = subs[slice_index];
|
||||||
|
@ -721,6 +722,7 @@ fn write_content<'a>(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
debug_assert!(env.debug.print_lambda_sets);
|
debug_assert!(env.debug.print_lambda_sets);
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ pub fn to_type(
|
||||||
Type::ClosureTag {
|
Type::ClosureTag {
|
||||||
name: *name,
|
name: *name,
|
||||||
captures: new_args,
|
captures: new_args,
|
||||||
|
ambient_function: var_store.fresh(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
FunctionOrTagUnion(tag_name, symbol, ext) => {
|
||||||
|
|
|
@ -15,8 +15,7 @@ use crate::unification_table::{self, UnificationTable};
|
||||||
// if your changes cause this number to go down, great!
|
// if your changes cause this number to go down, great!
|
||||||
// please change it to the lower number.
|
// please change it to the lower number.
|
||||||
// if it went up, maybe check that the change is really required
|
// if it went up, maybe check that the change is really required
|
||||||
roc_error_macros::assert_sizeof_all!(Descriptor, 5 * 8);
|
roc_error_macros::assert_sizeof_all!(Descriptor, 5 * 8 + 4);
|
||||||
roc_error_macros::assert_sizeof_all!(Content, 3 * 8 + 4);
|
|
||||||
roc_error_macros::assert_sizeof_all!(FlatType, 3 * 8);
|
roc_error_macros::assert_sizeof_all!(FlatType, 3 * 8);
|
||||||
roc_error_macros::assert_sizeof_all!(UnionTags, 12);
|
roc_error_macros::assert_sizeof_all!(UnionTags, 12);
|
||||||
roc_error_macros::assert_sizeof_all!(RecordFields, 2 * 8);
|
roc_error_macros::assert_sizeof_all!(RecordFields, 2 * 8);
|
||||||
|
@ -84,7 +83,7 @@ impl SubsHeader {
|
||||||
fn from_subs(subs: &Subs, exposed_vars_by_symbol: usize) -> Self {
|
fn from_subs(subs: &Subs, exposed_vars_by_symbol: usize) -> Self {
|
||||||
// TODO what do we do with problems? they should
|
// TODO what do we do with problems? they should
|
||||||
// be reported and then removed from Subs I think
|
// be reported and then removed from Subs I think
|
||||||
debug_assert!(subs.problems.is_empty());
|
debug_assert!(subs.problems.is_empty(), "{:?}", &subs.problems);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
utable: subs.utable.len() as u64,
|
utable: subs.utable.len() as u64,
|
||||||
|
@ -341,10 +340,10 @@ impl UlsOfVar {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: this does not follow unification links.
|
/// NOTE: this does not follow unification links.
|
||||||
pub fn drain(self) -> impl Iterator<Item = (Variable, impl Iterator<Item = Variable>)> {
|
pub fn drain(self) -> impl Iterator<Item = (Variable, VecSet<Variable>)> {
|
||||||
self.0
|
self.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(v, set): (Variable, VecSet<Variable>)| (v, set.into_iter()))
|
.map(|(v, set): (Variable, VecSet<Variable>)| (v, set))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
|
@ -779,8 +778,20 @@ impl<'a> fmt::Debug for SubsFmtContent<'a> {
|
||||||
|
|
||||||
fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt::Result {
|
fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match this {
|
match this {
|
||||||
Content::FlexVar(name) => write!(f, "Flex({:?})", name),
|
Content::FlexVar(name) => {
|
||||||
Content::FlexAbleVar(name, symbol) => write!(f, "FlexAble({:?}, {:?})", name, symbol),
|
let name = match name {
|
||||||
|
Some(index) => subs[*index].as_str(),
|
||||||
|
None => "_",
|
||||||
|
};
|
||||||
|
write!(f, "Flex({})", name)
|
||||||
|
}
|
||||||
|
Content::FlexAbleVar(name, symbol) => {
|
||||||
|
let name = match name {
|
||||||
|
Some(index) => subs[*index].as_str(),
|
||||||
|
None => "_",
|
||||||
|
};
|
||||||
|
write!(f, "FlexAble({}, {:?})", name, symbol)
|
||||||
|
}
|
||||||
Content::RigidVar(name) => write!(f, "Rigid({:?})", name),
|
Content::RigidVar(name) => write!(f, "Rigid({:?})", name),
|
||||||
Content::RigidAbleVar(name, symbol) => write!(f, "RigidAble({:?}, {:?})", name, symbol),
|
Content::RigidAbleVar(name, symbol) => write!(f, "RigidAble({:?}, {:?})", name, symbol),
|
||||||
Content::RecursionVar {
|
Content::RecursionVar {
|
||||||
|
@ -809,6 +820,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
write!(f, "LambdaSet([")?;
|
write!(f, "LambdaSet([")?;
|
||||||
|
|
||||||
|
@ -839,7 +851,7 @@ fn subs_fmt_content(this: &Content, subs: &Subs, f: &mut fmt::Formatter) -> fmt:
|
||||||
region
|
region
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
write!(f, ")")
|
write!(f, ", ^<{:?}>)", ambient_function_var)
|
||||||
}
|
}
|
||||||
Content::RangedNumber(range) => {
|
Content::RangedNumber(range) => {
|
||||||
write!(f, "RangedNumber( {:?})", range)
|
write!(f, "RangedNumber( {:?})", range)
|
||||||
|
@ -1920,6 +1932,7 @@ impl Subs {
|
||||||
recursive: Variable,
|
recursive: Variable,
|
||||||
solved_lambdas: UnionLambdas,
|
solved_lambdas: UnionLambdas,
|
||||||
unspecialized_lambdas: SubsSlice<Uls>,
|
unspecialized_lambdas: SubsSlice<Uls>,
|
||||||
|
ambient_function_var: Variable,
|
||||||
) {
|
) {
|
||||||
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, solved_lambdas);
|
let (rec_var, new_tags) = self.mark_union_recursive_help(recursive, solved_lambdas);
|
||||||
|
|
||||||
|
@ -1927,6 +1940,7 @@ impl Subs {
|
||||||
solved: new_tags,
|
solved: new_tags,
|
||||||
recursion_var: OptVariable::from(rec_var),
|
recursion_var: OptVariable::from(rec_var),
|
||||||
unspecialized: unspecialized_lambdas,
|
unspecialized: unspecialized_lambdas,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.set_content(recursive, new_lambda_set);
|
self.set_content(recursive, new_lambda_set);
|
||||||
|
@ -2167,10 +2181,11 @@ impl From<Content> for Descriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
roc_error_macros::assert_sizeof_all!(Content, 3 * 8 + 4);
|
roc_error_macros::assert_sizeof_all!(Content, 4 * 8);
|
||||||
roc_error_macros::assert_sizeof_all!((Symbol, AliasVariables, Variable), 2 * 8 + 4);
|
roc_error_macros::assert_sizeof_all!((Symbol, AliasVariables, Variable), 2 * 8 + 4);
|
||||||
roc_error_macros::assert_sizeof_all!(AliasVariables, 8);
|
roc_error_macros::assert_sizeof_all!(AliasVariables, 8);
|
||||||
roc_error_macros::assert_sizeof_all!(FlatType, 3 * 8);
|
roc_error_macros::assert_sizeof_all!(FlatType, 3 * 8);
|
||||||
|
roc_error_macros::assert_sizeof_all!(LambdaSet, 3 * 8 + 4);
|
||||||
|
|
||||||
roc_error_macros::assert_sizeof_aarch64!((Variable, Option<Lowercase>), 4 * 8);
|
roc_error_macros::assert_sizeof_aarch64!((Variable, Option<Lowercase>), 4 * 8);
|
||||||
roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4);
|
roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4);
|
||||||
|
@ -2244,6 +2259,12 @@ pub struct LambdaSet {
|
||||||
pub recursion_var: OptVariable,
|
pub recursion_var: OptVariable,
|
||||||
/// Lambdas we won't know until an ability specialization is resolved.
|
/// Lambdas we won't know until an ability specialization is resolved.
|
||||||
pub unspecialized: SubsSlice<Uls>,
|
pub unspecialized: SubsSlice<Uls>,
|
||||||
|
|
||||||
|
/// Backlink to the function wrapping this lambda set.
|
||||||
|
/// This should never be unified against when unifying a lambda set; that would evidently
|
||||||
|
/// introduce an infinite unification.
|
||||||
|
/// This is used for the ambient lambda set unification algorithm.
|
||||||
|
pub ambient_function: Variable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
@ -3135,6 +3156,7 @@ fn occurs(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized: _,
|
unspecialized: _,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
let mut new_seen = seen.to_owned();
|
let mut new_seen = seen.to_owned();
|
||||||
new_seen.push(root_var);
|
new_seen.push(root_var);
|
||||||
|
@ -3318,6 +3340,7 @@ fn explicit_substitute(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
// NOTE recursion_var is not substituted, verify that this is correct!
|
// NOTE recursion_var is not substituted, verify that this is correct!
|
||||||
let new_solved = explicit_substitute_union(subs, from, to, solved, seen);
|
let new_solved = explicit_substitute_union(subs, from, to, solved, seen);
|
||||||
|
@ -3332,6 +3355,7 @@ fn explicit_substitute(
|
||||||
solved: new_solved,
|
solved: new_solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3439,6 +3463,7 @@ fn get_var_names(
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
let taken_names = get_var_names_union(subs, solved, taken_names);
|
let taken_names = get_var_names_union(subs, solved, taken_names);
|
||||||
let mut taken_names = match recursion_var.into_variable() {
|
let mut taken_names = match recursion_var.into_variable() {
|
||||||
|
@ -3966,6 +3991,7 @@ impl StorageSubs {
|
||||||
pub fn as_inner_mut(&mut self) -> &mut Subs {
|
pub fn as_inner_mut(&mut self) -> &mut Subs {
|
||||||
&mut self.subs
|
&mut self.subs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_inner(&self) -> &Subs {
|
pub fn as_inner(&self) -> &Subs {
|
||||||
&self.subs
|
&self.subs
|
||||||
}
|
}
|
||||||
|
@ -4156,10 +4182,12 @@ impl StorageSubs {
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => LambdaSet(self::LambdaSet {
|
}) => LambdaSet(self::LambdaSet {
|
||||||
solved: Self::offset_lambda_set(offsets, *solved),
|
solved: Self::offset_lambda_set(offsets, *solved),
|
||||||
recursion_var: recursion_var.map(|v| Self::offset_variable(offsets, v)),
|
recursion_var: recursion_var.map(|v| Self::offset_variable(offsets, v)),
|
||||||
unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
|
unspecialized: Self::offset_uls_slice(offsets, *unspecialized),
|
||||||
|
ambient_function: Self::offset_variable(offsets, *ambient_function_var),
|
||||||
}),
|
}),
|
||||||
RangedNumber(range) => RangedNumber(*range),
|
RangedNumber(range) => RangedNumber(*range),
|
||||||
Error => Content::Error,
|
Error => Content::Error,
|
||||||
|
@ -4578,6 +4606,7 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
let new_solved = storage_copy_union(env, solved);
|
let new_solved = storage_copy_union(env, solved);
|
||||||
let new_rec_var = recursion_var.map(|v| storage_copy_var_to_help(env, v));
|
let new_rec_var = recursion_var.map(|v| storage_copy_var_to_help(env, v));
|
||||||
|
@ -4593,10 +4622,13 @@ fn storage_copy_var_to_help(env: &mut StorageCopyVarToEnv<'_>, var: Variable) ->
|
||||||
env.target[target_index] = Uls(new_var, sym, region);
|
env.target[target_index] = Uls(new_var, sym, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let new_ambient_function_var = storage_copy_var_to_help(env, ambient_function_var);
|
||||||
|
|
||||||
let new_content = LambdaSet(self::LambdaSet {
|
let new_content = LambdaSet(self::LambdaSet {
|
||||||
solved: new_solved,
|
solved: new_solved,
|
||||||
recursion_var: new_rec_var,
|
recursion_var: new_rec_var,
|
||||||
unspecialized: new_unspecialized,
|
unspecialized: new_unspecialized,
|
||||||
|
ambient_function: new_ambient_function_var,
|
||||||
});
|
});
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
copy
|
copy
|
||||||
|
@ -5036,6 +5068,7 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
let new_solved = copy_union(env, max_rank, solved);
|
let new_solved = copy_union(env, max_rank, solved);
|
||||||
let new_rec_var =
|
let new_rec_var =
|
||||||
|
@ -5054,10 +5087,13 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let new_ambient_function_var = copy_import_to_help(env, max_rank, ambient_function_var);
|
||||||
|
|
||||||
let new_content = LambdaSet(self::LambdaSet {
|
let new_content = LambdaSet(self::LambdaSet {
|
||||||
solved: new_solved,
|
solved: new_solved,
|
||||||
recursion_var: new_rec_var,
|
recursion_var: new_rec_var,
|
||||||
unspecialized: new_unspecialized,
|
unspecialized: new_unspecialized,
|
||||||
|
ambient_function: new_ambient_function_var,
|
||||||
});
|
});
|
||||||
|
|
||||||
env.target.set(copy, make_descriptor(new_content));
|
env.target.set(copy, make_descriptor(new_content));
|
||||||
|
@ -5214,6 +5250,7 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) {
|
||||||
solved,
|
solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: _,
|
||||||
}) => {
|
}) => {
|
||||||
for slice_index in solved.variables() {
|
for slice_index in solved.variables() {
|
||||||
let slice = subs.variable_slices[slice_index.index as usize];
|
let slice = subs.variable_slices[slice_index.index as usize];
|
||||||
|
|
|
@ -240,8 +240,11 @@ pub enum Type {
|
||||||
ClosureTag {
|
ClosureTag {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
captures: Vec<Type>,
|
captures: Vec<Type>,
|
||||||
|
ambient_function: Variable,
|
||||||
|
},
|
||||||
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls,
|
||||||
},
|
},
|
||||||
UnspecializedLambdaSet(Uls),
|
|
||||||
DelayedAlias(AliasCommon),
|
DelayedAlias(AliasCommon),
|
||||||
Alias {
|
Alias {
|
||||||
symbol: Symbol,
|
symbol: Symbol,
|
||||||
|
@ -312,11 +315,18 @@ impl Clone for Type {
|
||||||
Self::FunctionOrTagUnion(arg0, arg1, arg2) => {
|
Self::FunctionOrTagUnion(arg0, arg1, arg2) => {
|
||||||
Self::FunctionOrTagUnion(arg0.clone(), *arg1, arg2.clone())
|
Self::FunctionOrTagUnion(arg0.clone(), *arg1, arg2.clone())
|
||||||
}
|
}
|
||||||
Self::ClosureTag { name, captures } => Self::ClosureTag {
|
Self::ClosureTag {
|
||||||
|
name,
|
||||||
|
captures,
|
||||||
|
ambient_function,
|
||||||
|
} => Self::ClosureTag {
|
||||||
name: *name,
|
name: *name,
|
||||||
captures: captures.clone(),
|
captures: captures.clone(),
|
||||||
|
ambient_function: *ambient_function,
|
||||||
|
},
|
||||||
|
Self::UnspecializedLambdaSet { unspecialized } => Self::UnspecializedLambdaSet {
|
||||||
|
unspecialized: *unspecialized,
|
||||||
},
|
},
|
||||||
Self::UnspecializedLambdaSet(uls) => Self::UnspecializedLambdaSet(*uls),
|
|
||||||
Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()),
|
Self::DelayedAlias(arg0) => Self::DelayedAlias(arg0.clone()),
|
||||||
Self::Alias {
|
Self::Alias {
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -622,7 +632,11 @@ impl fmt::Debug for Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::ClosureTag { name, captures } => {
|
Type::ClosureTag {
|
||||||
|
name,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => {
|
||||||
write!(f, "ClosureTag(")?;
|
write!(f, "ClosureTag(")?;
|
||||||
|
|
||||||
write!(f, "{:?}, ", name)?;
|
write!(f, "{:?}, ", name)?;
|
||||||
|
@ -655,8 +669,8 @@ impl fmt::Debug for Type {
|
||||||
Type::RangedNumber(range_vars) => {
|
Type::RangedNumber(range_vars) => {
|
||||||
write!(f, "Ranged({:?})", range_vars)
|
write!(f, "Ranged({:?})", range_vars)
|
||||||
}
|
}
|
||||||
Type::UnspecializedLambdaSet(uls) => {
|
Type::UnspecializedLambdaSet { unspecialized } => {
|
||||||
write!(f, "{:?}", uls)
|
write!(f, "{:?}", unspecialized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,7 +727,11 @@ impl Type {
|
||||||
stack.push(closure);
|
stack.push(closure);
|
||||||
stack.push(ret);
|
stack.push(ret);
|
||||||
}
|
}
|
||||||
ClosureTag { name: _, captures } => stack.extend(captures),
|
ClosureTag {
|
||||||
|
name: _,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => stack.extend(captures),
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
for (_, args) in tags {
|
for (_, args) in tags {
|
||||||
stack.extend(args.iter_mut());
|
stack.extend(args.iter_mut());
|
||||||
|
@ -795,7 +813,9 @@ impl Type {
|
||||||
stack.extend(args);
|
stack.extend(args);
|
||||||
}
|
}
|
||||||
RangedNumber(_) => {}
|
RangedNumber(_) => {}
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(v, _, _),
|
||||||
|
} => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
substitutions.get(v).is_none(),
|
substitutions.get(v).is_none(),
|
||||||
"unspecialized lambda sets should never be substituted before solving"
|
"unspecialized lambda sets should never be substituted before solving"
|
||||||
|
@ -824,7 +844,11 @@ impl Type {
|
||||||
stack.push(closure);
|
stack.push(closure);
|
||||||
stack.push(ret);
|
stack.push(ret);
|
||||||
}
|
}
|
||||||
ClosureTag { name: _, captures } => {
|
ClosureTag {
|
||||||
|
name: _,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => {
|
||||||
stack.extend(captures);
|
stack.extend(captures);
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
|
@ -910,7 +934,9 @@ impl Type {
|
||||||
stack.extend(args);
|
stack.extend(args);
|
||||||
}
|
}
|
||||||
RangedNumber(_) => {}
|
RangedNumber(_) => {}
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(v, _, _),
|
||||||
|
} => {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
substitutions.get(v).is_none(),
|
substitutions.get(v).is_none(),
|
||||||
"unspecialized lambda sets should never be substituted before solving"
|
"unspecialized lambda sets should never be substituted before solving"
|
||||||
|
@ -1013,7 +1039,7 @@ impl Type {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
RangedNumber(_) => Ok(()),
|
RangedNumber(_) => Ok(()),
|
||||||
UnspecializedLambdaSet(..) => Ok(()),
|
UnspecializedLambdaSet { .. } => Ok(()),
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,7 +1096,9 @@ impl Type {
|
||||||
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
Apply(symbol, _, _) if *symbol == rep_symbol => true,
|
||||||
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)),
|
||||||
RangedNumber(_) => false,
|
RangedNumber(_) => false,
|
||||||
UnspecializedLambdaSet(Uls(_, sym, _)) => *sym == rep_symbol,
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(_, sym, _),
|
||||||
|
} => *sym == rep_symbol,
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1093,10 +1121,14 @@ impl Type {
|
||||||
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
|| args.iter().any(|arg| arg.contains_variable(rep_variable))
|
||||||
}
|
}
|
||||||
FunctionOrTagUnion(_, _, ext) => Self::contains_variable_ext(ext, rep_variable),
|
FunctionOrTagUnion(_, _, ext) => Self::contains_variable_ext(ext, rep_variable),
|
||||||
ClosureTag { name: _, captures } => {
|
ClosureTag {
|
||||||
captures.iter().any(|t| t.contains_variable(rep_variable))
|
name: _,
|
||||||
}
|
captures,
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => *v == rep_variable,
|
ambient_function: _,
|
||||||
|
} => captures.iter().any(|t| t.contains_variable(rep_variable)),
|
||||||
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(v, _, _),
|
||||||
|
} => *v == rep_variable,
|
||||||
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
RecursiveTagUnion(_, tags, ext) | TagUnion(tags, ext) => {
|
||||||
Self::contains_variable_ext(ext, rep_variable)
|
Self::contains_variable_ext(ext, rep_variable)
|
||||||
|| tags
|
|| tags
|
||||||
|
@ -1384,7 +1416,7 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RangedNumber(_) => {}
|
RangedNumber(_) => {}
|
||||||
UnspecializedLambdaSet(..) => {}
|
UnspecializedLambdaSet { .. } => {}
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1520,7 +1552,9 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
|
||||||
output.push(*alias);
|
output.push(*alias);
|
||||||
}
|
}
|
||||||
RangedNumber(_) => {}
|
RangedNumber(_) => {}
|
||||||
UnspecializedLambdaSet(Uls(_, _sym, _)) => {
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(_, _sym, _),
|
||||||
|
} => {
|
||||||
// ignore the member symbol because unspecialized lambda sets are internal-only
|
// ignore the member symbol because unspecialized lambda sets are internal-only
|
||||||
}
|
}
|
||||||
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
|
||||||
|
@ -1565,12 +1599,18 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
|
||||||
variables_help(ext, accum);
|
variables_help(ext, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClosureTag { name: _, captures } => {
|
ClosureTag {
|
||||||
|
name: _,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => {
|
||||||
for t in captures {
|
for t in captures {
|
||||||
variables_help(t, accum);
|
variables_help(t, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnspecializedLambdaSet(Uls(v, _, _)) => {
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(v, _, _),
|
||||||
|
} => {
|
||||||
accum.insert(*v);
|
accum.insert(*v);
|
||||||
}
|
}
|
||||||
TagUnion(tags, ext) => {
|
TagUnion(tags, ext) => {
|
||||||
|
@ -1700,7 +1740,11 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClosureTag { name: _, captures } => {
|
ClosureTag {
|
||||||
|
name: _,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => {
|
||||||
for t in captures {
|
for t in captures {
|
||||||
variables_help_detailed(t, accum);
|
variables_help_detailed(t, accum);
|
||||||
}
|
}
|
||||||
|
@ -1721,7 +1765,9 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
|
||||||
variables_help_detailed(ext, accum);
|
variables_help_detailed(ext, accum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UnspecializedLambdaSet(Uls(var, _, _)) => {
|
UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(var, _, _),
|
||||||
|
} => {
|
||||||
accum.type_variables.insert(*var);
|
accum.type_variables.insert(*var);
|
||||||
}
|
}
|
||||||
RecursiveTagUnion(rec, tags, ext) => {
|
RecursiveTagUnion(rec, tags, ext) => {
|
||||||
|
@ -2728,7 +2774,9 @@ fn instantiate_lambda_sets_as_unspecialized(
|
||||||
|
|
||||||
let mut new_uls = || {
|
let mut new_uls = || {
|
||||||
region += 1;
|
region += 1;
|
||||||
Type::UnspecializedLambdaSet(Uls(able_var, ability_member, region))
|
Type::UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(able_var, ability_member, region),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
while let Some(typ) = stack.pop() {
|
while let Some(typ) = stack.pop() {
|
||||||
|
@ -2762,10 +2810,14 @@ fn instantiate_lambda_sets_as_unspecialized(
|
||||||
Type::FunctionOrTagUnion(_, _, ext) => {
|
Type::FunctionOrTagUnion(_, _, ext) => {
|
||||||
stack.extend(ext.iter_mut());
|
stack.extend(ext.iter_mut());
|
||||||
}
|
}
|
||||||
Type::ClosureTag { name: _, captures } => {
|
Type::ClosureTag {
|
||||||
|
name: _,
|
||||||
|
captures,
|
||||||
|
ambient_function: _,
|
||||||
|
} => {
|
||||||
stack.extend(captures.iter_mut().rev());
|
stack.extend(captures.iter_mut().rev());
|
||||||
}
|
}
|
||||||
Type::UnspecializedLambdaSet(..) => {
|
Type::UnspecializedLambdaSet { .. } => {
|
||||||
internal_error!("attempting to re-instantiate ULS")
|
internal_error!("attempting to re-instantiate ULS")
|
||||||
}
|
}
|
||||||
Type::DelayedAlias(AliasCommon {
|
Type::DelayedAlias(AliasCommon {
|
||||||
|
@ -2846,7 +2898,9 @@ mod test {
|
||||||
macro_rules! check_uls {
|
macro_rules! check_uls {
|
||||||
($typ:expr, $region:literal) => {{
|
($typ:expr, $region:literal) => {{
|
||||||
match $typ {
|
match $typ {
|
||||||
Type::UnspecializedLambdaSet(Uls(var1, member1, $region)) => {
|
Type::UnspecializedLambdaSet {
|
||||||
|
unspecialized: Uls(var1, member1, $region),
|
||||||
|
} => {
|
||||||
assert!(var1 == able_var && member1 == member)
|
assert!(var1 == able_var && member1 == member)
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
|
|
|
@ -968,6 +968,7 @@ fn unify_lambda_set<M: MetaCollector>(
|
||||||
solved: UnionLabels::default(),
|
solved: UnionLabels::default(),
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: subs.fresh_unnamed_flex_var(),
|
||||||
};
|
};
|
||||||
|
|
||||||
extract_specialization_lambda_set(subs, ctx, lambda_set, zero_lambda_set)
|
extract_specialization_lambda_set(subs, ctx, lambda_set, zero_lambda_set)
|
||||||
|
@ -1013,6 +1014,7 @@ fn extract_specialization_lambda_set<M: MetaCollector>(
|
||||||
solved: member_solved,
|
solved: member_solved,
|
||||||
recursion_var: member_rec_var,
|
recursion_var: member_rec_var,
|
||||||
unspecialized: member_uls_slice,
|
unspecialized: member_uls_slice,
|
||||||
|
ambient_function: _,
|
||||||
} = ability_member_proto_lset;
|
} = ability_member_proto_lset;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -1207,6 +1209,64 @@ fn separate_union_lambdas(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unify_unspecialized_lambdas<M: MetaCollector>(
|
||||||
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
|
uls1: SubsSlice<Uls>,
|
||||||
|
uls2: SubsSlice<Uls>,
|
||||||
|
) -> Result<SubsSlice<Uls>, Outcome<M>> {
|
||||||
|
// For now we merge all variables of unspecialized lambdas in a lambda set that share the same
|
||||||
|
// ability member/region.
|
||||||
|
// See the section "A property that's lost, and how we can hold on to it" of
|
||||||
|
// solve/docs/ambient_lambda_set_specialization.md to see how we can loosen this restriction.
|
||||||
|
|
||||||
|
// Note that we don't need to update the bookkeeping of variable -> lambda set to be resolved,
|
||||||
|
// because if we had v1 -> lset1, and now lset1 ~ lset2, then afterward either lset1 still
|
||||||
|
// resolves to itself or re-points to lset2.
|
||||||
|
// In either case the merged unspecialized lambda sets will be there.
|
||||||
|
match (uls1.is_empty(), uls2.is_empty()) {
|
||||||
|
(true, true) => Ok(SubsSlice::default()),
|
||||||
|
(false, true) => Ok(uls1),
|
||||||
|
(true, false) => Ok(uls2),
|
||||||
|
(false, false) => {
|
||||||
|
let mut all_uls = (subs.get_subs_slice(uls1).iter())
|
||||||
|
.chain(subs.get_subs_slice(uls2))
|
||||||
|
.map(|&Uls(var, sym, region)| {
|
||||||
|
// Take the root key to deduplicate
|
||||||
|
Uls(subs.get_root_key_without_compacting(var), sym, region)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
// Arrange into partitions of (_, member, region).
|
||||||
|
all_uls.sort_by_key(|&Uls(_, sym, region)| (sym, region));
|
||||||
|
|
||||||
|
// Now merge the variables of unspecialized lambdas pointing to the same
|
||||||
|
// member/region.
|
||||||
|
let mut j = 1;
|
||||||
|
while j < all_uls.len() {
|
||||||
|
let i = j - 1;
|
||||||
|
let Uls(var_i, sym_i, region_i) = all_uls[i];
|
||||||
|
let Uls(var_j, sym_j, region_j) = all_uls[j];
|
||||||
|
if sym_i == sym_j && region_i == region_j {
|
||||||
|
let outcome = unify_pool(subs, pool, var_i, var_j, Mode::EQ);
|
||||||
|
if !outcome.mismatches.is_empty() {
|
||||||
|
return Err(outcome);
|
||||||
|
}
|
||||||
|
// Keep the Uls in position `i` and remove the one in position `j`.
|
||||||
|
all_uls.remove(j);
|
||||||
|
} else {
|
||||||
|
// Keep both Uls, look at the next one.
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SubsSlice::extend_new(
|
||||||
|
&mut subs.unspecialized_lambda_sets,
|
||||||
|
all_uls,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn unify_lambda_set_help<M: MetaCollector>(
|
fn unify_lambda_set_help<M: MetaCollector>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
pool: &mut Pool,
|
pool: &mut Pool,
|
||||||
|
@ -1221,13 +1281,20 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
||||||
solved: solved1,
|
solved: solved1,
|
||||||
recursion_var: rec1,
|
recursion_var: rec1,
|
||||||
unspecialized: uls1,
|
unspecialized: uls1,
|
||||||
|
ambient_function: ambient_function_var1,
|
||||||
} = lset1;
|
} = lset1;
|
||||||
let LambdaSet {
|
let LambdaSet {
|
||||||
solved: solved2,
|
solved: solved2,
|
||||||
recursion_var: rec2,
|
recursion_var: rec2,
|
||||||
unspecialized: uls2,
|
unspecialized: uls2,
|
||||||
|
ambient_function: ambient_function_var2,
|
||||||
} = lset2;
|
} = lset2;
|
||||||
|
|
||||||
|
// Assumed precondition: the ambient functions have already been unified, or are in the process
|
||||||
|
// of being unified - otherwise, how could we have reached unification of lambda sets?
|
||||||
|
let _ = ambient_function_var2;
|
||||||
|
let ambient_function_var = ambient_function_var1;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
(rec1.into_variable().into_iter())
|
(rec1.into_variable().into_iter())
|
||||||
.chain(rec2.into_variable().into_iter())
|
.chain(rec2.into_variable().into_iter())
|
||||||
|
@ -1266,26 +1333,11 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
||||||
(None, None) => OptVariable::NONE,
|
(None, None) => OptVariable::NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Combine the unspecialized lambda sets as needed. Note that we don't need to update the
|
let merged_unspecialized = match unify_unspecialized_lambdas(subs, pool, uls1, uls2) {
|
||||||
// bookkeeping of variable -> lambda set to be resolved, because if we had v1 -> lset1, and
|
Ok(merged) => merged,
|
||||||
// now lset1 ~ lset2, then afterward either lset1 still resolves to itself or re-points to
|
Err(outcome) => {
|
||||||
// lset2. In either case the merged unspecialized lambda sets will be there.
|
debug_assert!(!outcome.mismatches.is_empty());
|
||||||
let merged_unspecialized = match (uls1.is_empty(), uls2.is_empty()) {
|
return outcome;
|
||||||
(true, true) => SubsSlice::default(),
|
|
||||||
(false, true) => uls1,
|
|
||||||
(true, false) => uls2,
|
|
||||||
(false, false) => {
|
|
||||||
let mut all_uls = (subs.get_subs_slice(uls1).iter())
|
|
||||||
.chain(subs.get_subs_slice(uls2))
|
|
||||||
.map(|&Uls(var, sym, region)| {
|
|
||||||
// Take the root key to deduplicate
|
|
||||||
Uls(subs.get_root_key_without_compacting(var), sym, region)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
all_uls.sort();
|
|
||||||
all_uls.dedup();
|
|
||||||
|
|
||||||
SubsSlice::extend_new(&mut subs.unspecialized_lambda_sets, all_uls)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1294,6 +1346,7 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
||||||
solved: new_solved,
|
solved: new_solved,
|
||||||
recursion_var,
|
recursion_var,
|
||||||
unspecialized: merged_unspecialized,
|
unspecialized: merged_unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
});
|
});
|
||||||
|
|
||||||
merge(subs, ctx, new_lambda_set)
|
merge(subs, ctx, new_lambda_set)
|
||||||
|
@ -1927,8 +1980,9 @@ fn maybe_mark_union_recursive(subs: &mut Subs, union_var: Variable) {
|
||||||
solved,
|
solved,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized,
|
unspecialized,
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
}) => {
|
}) => {
|
||||||
subs.mark_lambda_set_recursive(v, solved, unspecialized);
|
subs.mark_lambda_set_recursive(v, solved, unspecialized, ambient_function_var);
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
_ => { /* fall through */ }
|
_ => { /* fall through */ }
|
||||||
|
@ -2681,10 +2735,12 @@ fn unify_function_or_tag_union_and_func<M: MetaCollector>(
|
||||||
|
|
||||||
{
|
{
|
||||||
let union_tags = UnionLambdas::tag_without_arguments(subs, tag_symbol);
|
let union_tags = UnionLambdas::tag_without_arguments(subs, tag_symbol);
|
||||||
|
let ambient_function_var = if left { ctx.first } else { ctx.second };
|
||||||
let lambda_set_content = LambdaSet(self::LambdaSet {
|
let lambda_set_content = LambdaSet(self::LambdaSet {
|
||||||
solved: union_tags,
|
solved: union_tags,
|
||||||
recursion_var: OptVariable::NONE,
|
recursion_var: OptVariable::NONE,
|
||||||
unspecialized: SubsSlice::default(),
|
unspecialized: SubsSlice::default(),
|
||||||
|
ambient_function: ambient_function_var,
|
||||||
});
|
});
|
||||||
|
|
||||||
let tag_lambda_set = register(
|
let tag_lambda_set = register(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue