mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 03:42:17 +00:00
Merge remote-tracking branch 'origin/main' into save-construction-children
This commit is contained in:
commit
6e6e1ce833
46 changed files with 890 additions and 214 deletions
|
@ -209,6 +209,10 @@ pub const RocDec = extern struct {
|
|||
return RocStr.init(&str_bytes, position);
|
||||
}
|
||||
|
||||
pub fn toI128(self: RocDec) i128 {
|
||||
return self.num;
|
||||
}
|
||||
|
||||
pub fn eq(self: RocDec, other: RocDec) bool {
|
||||
return self.num == other.num;
|
||||
}
|
||||
|
@ -1108,6 +1112,10 @@ pub fn fromF64C(arg: f64) callconv(.C) i128 {
|
|||
return if (@call(.{ .modifier = always_inline }, RocDec.fromF64, .{arg})) |dec| dec.num else @panic("TODO runtime exception failing convert f64 to RocDec");
|
||||
}
|
||||
|
||||
pub fn toI128(arg: RocDec) callconv(.C) i128 {
|
||||
return @call(.{ .modifier = always_inline }, RocDec.toI128, .{arg});
|
||||
}
|
||||
|
||||
pub fn eqC(arg1: RocDec, arg2: RocDec) callconv(.C) bool {
|
||||
return @call(.{ .modifier = always_inline }, RocDec.eq, .{ arg1, arg2 });
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ comptime {
|
|||
exportDecFn(dec.fromStr, "from_str");
|
||||
exportDecFn(dec.toStr, "to_str");
|
||||
exportDecFn(dec.fromF64C, "from_f64");
|
||||
exportDecFn(dec.toI128, "to_i128");
|
||||
exportDecFn(dec.eqC, "eq");
|
||||
exportDecFn(dec.neqC, "neq");
|
||||
exportDecFn(dec.negateC, "negate");
|
||||
|
@ -188,6 +189,7 @@ comptime {
|
|||
exportUtilsFn(utils.isUnique, "is_unique");
|
||||
exportUtilsFn(utils.decrefCheckNullC, "decref_check_null");
|
||||
exportUtilsFn(utils.allocateWithRefcountC, "allocate_with_refcount");
|
||||
exportUtilsFn(utils.dictPseudoSeed, "dict_pseudo_seed");
|
||||
|
||||
@export(panic_utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||
|
||||
|
|
|
@ -436,3 +436,15 @@ test "increfC, static data" {
|
|||
increfRcPtrC(ptr_to_refcount, 2);
|
||||
try std.testing.expectEqual(mock_rc, REFCOUNT_MAX_ISIZE);
|
||||
}
|
||||
|
||||
// This returns a compilation dependent pseudo random seed for dictionaries.
|
||||
// The seed is the address of this function.
|
||||
// This avoids all roc Dicts using a known seed and being trivial to DOS.
|
||||
// Still not as secure as true random, but a lot better.
|
||||
// This value must not change between calls unless Dict is changed to store the seed on creation.
|
||||
// Note: On esstentially all OSes, this will be affected by ASLR and different each run.
|
||||
// In wasm, the value will be constant to the build as a whole.
|
||||
// Either way, it can not be know by an attacker unless they get access to the executable.
|
||||
pub fn dictPseudoSeed() callconv(.C) u64 {
|
||||
return @intCast(u64, @ptrToInt(dictPseudoSeed));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ interface Box
|
|||
exposes [box, unbox]
|
||||
imports []
|
||||
|
||||
## Allocate a value on the heap. Boxing is an expensive process as it copies
|
||||
## Allocates a value on the heap. Boxing is an expensive process as it copies
|
||||
## the value from the stack to the heap. This may provide a performance
|
||||
## optimization for advanced use cases with large values. A platform may require
|
||||
## that some values are boxed.
|
||||
|
|
|
@ -80,7 +80,7 @@ interface Dict
|
|||
## vacated spot.
|
||||
##
|
||||
## This move is done as a performance optimization, and it lets [remove] have
|
||||
## [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time). ##
|
||||
## [constant time complexity](https://en.wikipedia.org/wiki/Time_complexity#Constant_time).
|
||||
##
|
||||
## Dict is inspired by [IndexMap](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html).
|
||||
## The internal implementation of a dictionary is similar to [absl::flat_hash_map](https://abseil.io/docs/cpp/guides/container).
|
||||
|
@ -98,6 +98,30 @@ Dict k v := {
|
|||
data : List (T k v),
|
||||
size : Nat,
|
||||
} | k has Hash & Eq
|
||||
has [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashDict,
|
||||
},
|
||||
]
|
||||
|
||||
isEq : Dict k v, Dict k v -> Bool | k has Hash & Eq, v has Eq
|
||||
isEq = \xs, ys ->
|
||||
if len xs != len ys then
|
||||
Bool.false
|
||||
else
|
||||
walkUntil xs Bool.true \_, k, xVal ->
|
||||
when get ys k is
|
||||
Ok yVal if yVal == xVal ->
|
||||
Continue Bool.true
|
||||
|
||||
_ ->
|
||||
Break Bool.false
|
||||
|
||||
hashDict : hasher, Dict k v -> hasher | k has Hash & Eq, v has Hash, hasher has Hasher
|
||||
hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
|
||||
|
||||
## Return an empty dictionary.
|
||||
## ```
|
||||
|
@ -127,7 +151,7 @@ capacity = \@Dict { dataIndices } ->
|
|||
cap - Num.shiftRightZfBy cap 3
|
||||
|
||||
## Return a dictionary with space allocated for a number of entries. This
|
||||
## may provide a performance optimisation if you know how many entries will be
|
||||
## may provide a performance optimization if you know how many entries will be
|
||||
## inserted.
|
||||
withCapacity : Nat -> Dict k v | k has Hash & Eq
|
||||
withCapacity = \_ ->
|
||||
|
@ -262,7 +286,7 @@ walkUntil = \@Dict { data }, initialState, transform ->
|
|||
get : Dict k v, k -> Result v [KeyNotFound] | k has Hash & Eq
|
||||
get = \@Dict { metadata, dataIndices, data }, key ->
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -290,7 +314,7 @@ get = \@Dict { metadata, dataIndices, data }, key ->
|
|||
contains : Dict k v, k -> Bool | k has Hash & Eq
|
||||
contains = \@Dict { metadata, dataIndices, data }, key ->
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -315,7 +339,7 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
|
|||
insert : Dict k v, k, v -> Dict k v | k has Hash & Eq
|
||||
insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -362,7 +386,7 @@ remove : Dict k v, k -> Dict k v | k has Hash & Eq
|
|||
remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
||||
# TODO: change this from swap remove to tombstone and test is performance is still good.
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -388,7 +412,7 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
|
|||
@Dict { metadata, dataIndices, data, size }
|
||||
|
||||
## Insert or remove a value for a specified key. This function enables a
|
||||
## performance optimisation for the use case of providing a default when a value
|
||||
## performance optimization for the use case of providing a default when a value
|
||||
## is missing. This is more efficient than doing both a `Dict.get` and then a
|
||||
## `Dict.insert` call, and supports being piped.
|
||||
## ```
|
||||
|
@ -545,7 +569,7 @@ swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k has Hash & Eq
|
|||
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
|
||||
(T key _) = listGetUnsafe data lastIndex
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -687,7 +711,7 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
|||
insertForRehash : Dict k v, k, Nat -> Dict k v | k has Hash & Eq
|
||||
insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex ->
|
||||
hashKey =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher PseudoRandSeed
|
||||
|> Hash.hash key
|
||||
|> complete
|
||||
h1Key = h1 hashKey
|
||||
|
@ -747,6 +771,71 @@ expect
|
|||
|
||||
val == Ok "bar"
|
||||
|
||||
expect
|
||||
dict1 =
|
||||
empty {}
|
||||
|> insert 1 "bar"
|
||||
|> insert 2 "baz"
|
||||
|
||||
dict2 =
|
||||
empty {}
|
||||
|> insert 2 "baz"
|
||||
|> insert 1 "bar"
|
||||
|
||||
dict1 == dict2
|
||||
|
||||
expect
|
||||
dict1 =
|
||||
empty {}
|
||||
|> insert 1 "bar"
|
||||
|> insert 2 "baz"
|
||||
|
||||
dict2 =
|
||||
empty {}
|
||||
|> insert 1 "bar"
|
||||
|> insert 2 "baz!"
|
||||
|
||||
dict1 != dict2
|
||||
|
||||
expect
|
||||
inner1 =
|
||||
empty {}
|
||||
|> insert 1 "bar"
|
||||
|> insert 2 "baz"
|
||||
|
||||
inner2 =
|
||||
empty {}
|
||||
|> insert 2 "baz"
|
||||
|> insert 1 "bar"
|
||||
|
||||
outer =
|
||||
empty {}
|
||||
|> insert inner1 "wrong"
|
||||
|> insert inner2 "right"
|
||||
|
||||
get outer inner1 == Ok "right"
|
||||
|
||||
expect
|
||||
inner1 =
|
||||
empty {}
|
||||
|> insert 1 "bar"
|
||||
|> insert 2 "baz"
|
||||
|
||||
inner2 =
|
||||
empty {}
|
||||
|> insert 2 "baz"
|
||||
|> insert 1 "bar"
|
||||
|
||||
outer1 =
|
||||
empty {}
|
||||
|> insert inner1 "val"
|
||||
|
||||
outer2 =
|
||||
empty {}
|
||||
|> insert inner2 "val"
|
||||
|
||||
outer1 == outer2
|
||||
|
||||
expect
|
||||
val =
|
||||
empty {}
|
||||
|
@ -904,8 +993,16 @@ LowLevelHasher := { originalSeed : U64, state : U64 } has [
|
|||
# TODO hide behind an InternalList.roc module
|
||||
listGetUnsafe : List a, Nat -> a
|
||||
|
||||
createLowLevelHasher : { seed ? U64 } -> LowLevelHasher
|
||||
createLowLevelHasher = \{ seed ? 0x526F_6352_616E_643F } ->
|
||||
# Returns a application specific pseudo random seed for Dict.
|
||||
# This avoids trivial DOS attacks.
|
||||
pseudoSeed : {} -> U64
|
||||
|
||||
createLowLevelHasher : [PseudoRandSeed, WithSeed U64] -> LowLevelHasher
|
||||
createLowLevelHasher = \seedOpt ->
|
||||
seed =
|
||||
when seedOpt is
|
||||
PseudoRandSeed -> pseudoSeed {}
|
||||
WithSeed s -> s
|
||||
@LowLevelHasher { originalSeed: seed, state: seed }
|
||||
|
||||
combineState : LowLevelHasher, { a : U64, b : U64, seed : U64, length : U64 } -> LowLevelHasher
|
||||
|
@ -1099,12 +1196,14 @@ wyr3 = \list, index, k ->
|
|||
|
||||
Num.bitwiseOr a p3
|
||||
|
||||
testSeed = WithSeed 0x526F_6352_616E_643F
|
||||
|
||||
# TODO: would be great to have table driven expects for this.
|
||||
# Would also be great to have some sort of property based hasher
|
||||
# where we can compare `addU*` functions to the `addBytes` function.
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes []
|
||||
|> complete
|
||||
|
||||
|
@ -1112,7 +1211,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0x42]
|
||||
|> complete
|
||||
|
||||
|
@ -1120,7 +1219,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU8 0x42
|
||||
|> complete
|
||||
|
||||
|
@ -1128,7 +1227,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0xFF, 0xFF]
|
||||
|> complete
|
||||
|
||||
|
@ -1136,7 +1235,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU16 0xFFFF
|
||||
|> complete
|
||||
|
||||
|
@ -1144,7 +1243,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0x36, 0xA7]
|
||||
|> complete
|
||||
|
||||
|
@ -1152,7 +1251,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU16 0xA736
|
||||
|> complete
|
||||
|
||||
|
@ -1160,7 +1259,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0x00, 0x00, 0x00, 0x00]
|
||||
|> complete
|
||||
|
||||
|
@ -1168,7 +1267,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU32 0x0000_0000
|
||||
|> complete
|
||||
|
||||
|
@ -1176,7 +1275,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0xA9, 0x2F, 0xEE, 0x21]
|
||||
|> complete
|
||||
|
||||
|
@ -1184,7 +1283,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU32 0x21EE_2FA9
|
||||
|> complete
|
||||
|
||||
|
@ -1192,7 +1291,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes [0x5D, 0x66, 0xB1, 0x8F, 0x68, 0x44, 0xC7, 0x03, 0xE1, 0xDD, 0x23, 0x34, 0xBB, 0x9A, 0x42, 0xA7]
|
||||
|> complete
|
||||
|
||||
|
@ -1200,7 +1299,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addU128 0xA742_9ABB_3423_DDE1_03C7_4468_8FB1_665D
|
||||
|> complete
|
||||
|
||||
|
@ -1208,7 +1307,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashStrBytes "abcdefghijklmnopqrstuvwxyz"
|
||||
|> complete
|
||||
|
||||
|
@ -1216,7 +1315,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashStrBytes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|> complete
|
||||
|
||||
|
@ -1224,7 +1323,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashStrBytes "1234567890123456789012345678901234567890123456789012345678901234567890"
|
||||
|> complete
|
||||
|
||||
|
@ -1232,7 +1331,7 @@ expect
|
|||
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> addBytes (List.repeat 0x77 100)
|
||||
|> complete
|
||||
|
||||
|
@ -1242,7 +1341,7 @@ expect
|
|||
# Apparently it won't pick the default integer.
|
||||
expect
|
||||
hash =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashUnordered [8u8, 82u8, 3u8, 8u8, 24u8] List.walk
|
||||
|> complete
|
||||
|
||||
|
@ -1250,12 +1349,12 @@ expect
|
|||
|
||||
expect
|
||||
hash1 =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashUnordered ([0u8, 1u8, 2u8, 3u8, 4u8]) List.walk
|
||||
|> complete
|
||||
|
||||
hash2 =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashUnordered [4u8, 3u8, 2u8, 1u8, 0u8] List.walk
|
||||
|> complete
|
||||
|
||||
|
@ -1263,12 +1362,12 @@ expect
|
|||
|
||||
expect
|
||||
hash1 =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashUnordered [0u8, 1u8, 2u8, 3u8, 4u8] List.walk
|
||||
|> complete
|
||||
|
||||
hash2 =
|
||||
createLowLevelHasher {}
|
||||
createLowLevelHasher testSeed
|
||||
|> Hash.hashUnordered [4u8, 3u8, 2u8, 1u8, 0u8, 0u8] List.walk
|
||||
|> complete
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ interface Hash
|
|||
hashI64,
|
||||
hashI128,
|
||||
hashNat,
|
||||
hashDec,
|
||||
complete,
|
||||
hashStrBytes,
|
||||
hashList,
|
||||
|
@ -24,7 +25,7 @@ interface Hash
|
|||
Bool.{ Bool, isEq },
|
||||
List,
|
||||
Str,
|
||||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, Nat },
|
||||
Num.{ U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, Nat, Dec },
|
||||
]
|
||||
|
||||
## A value that can hashed.
|
||||
|
@ -112,6 +113,13 @@ hashNat = \hasher, n ->
|
|||
else
|
||||
addU64 hasher (Num.toU64 n)
|
||||
|
||||
## LOWLEVEL get the i128 representation of a Dec.
|
||||
i128OfDec : Dec -> I128
|
||||
|
||||
## Adds a single [Dec] to a hasher.
|
||||
hashDec : a, Dec -> a | a has Hasher
|
||||
hashDec = \hasher, n -> hashI128 hasher (i128OfDec n)
|
||||
|
||||
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element.
|
||||
## The container is iterated using the walk method passed in.
|
||||
## The order of the elements does not affect the final hash.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
## JSON is a data format that is easy for humans to read and write. It is
|
||||
## commonly used to exhange data between two systems such as a server and a
|
||||
## commonly used to exchange data between two systems such as a server and a
|
||||
## client (e.g. web browser).
|
||||
##
|
||||
## This module implements functionality to serialise and de-serialise Roc types
|
||||
## This module implements functionality to serialize and de-serialize Roc types
|
||||
## to and from JSON data. Using the `Encode` and `Decode` builtins this process
|
||||
## can be achieved without the need to write custom encoder and decoder functions
|
||||
## to parse UTF-8 strings.
|
||||
|
|
|
@ -72,7 +72,8 @@ interface List
|
|||
Num.{ Nat, Num, Int },
|
||||
]
|
||||
|
||||
## Types
|
||||
## ## Types
|
||||
##
|
||||
## A sequential list of values.
|
||||
## ```
|
||||
## [1, 2, 3] # a list of numbers
|
||||
|
@ -512,7 +513,7 @@ all = \list, predicate ->
|
|||
## length that's too low, it would have to re-allocate.
|
||||
##
|
||||
## (If you want to do an operation like this which reduces the memory footprint
|
||||
## of the resulting list, you can do two passes over the lis with [List.walk] - one
|
||||
## of the resulting list, you can do two passes over the list with [List.walk] - one
|
||||
## to calculate the precise new size, and another to populate the new list.)
|
||||
##
|
||||
## If given a unique list, [List.keepIf] will mutate it in place to assemble the appropriate list.
|
||||
|
|
|
@ -233,7 +233,7 @@ Num range := range
|
|||
##
|
||||
## [I8] is a signed integer that takes up 8 bits. The `I` is for Integer, since
|
||||
## integers in mathematics are signed by default. Because it has 8 bits just
|
||||
## like [U8], it can store 256 numbers (still 2^16), but because it is signed,
|
||||
## like [U8], it can store 256 numbers (still 2^8), but because it is signed,
|
||||
## the range is different. Its 256 numbers range from -128 to 127.
|
||||
##
|
||||
## Here are some other examples:
|
||||
|
@ -283,7 +283,7 @@ Num range := range
|
|||
## general trade-offs are:
|
||||
##
|
||||
## * Larger integer sizes can represent a wider range of numbers. If you absolutely need to represent numbers in a certain range, make sure to pick an integer size that can hold them!
|
||||
## * Smaller integer sizes take up less memory. This savings rarely matters in variables and function arguments, but the sizes of integers that you use in data structures can add up. This can also affect whether those data structures fit in [cache lines](https://en.wikipedia.org/wiki/CPU_cache#Cache_performance), which can be a performance bottleneck.
|
||||
## * Smaller integer sizes take up less memory. These savings rarely matter in variables and function arguments, but the sizes of integers that you use in data structures can add up. This can also affect whether those data structures fit in [cache lines](https://en.wikipedia.org/wiki/CPU_cache#Cache_performance), which can be a performance bottleneck.
|
||||
## * Certain CPUs work faster on some numeric sizes than others. If the CPU is taking too long to run numeric calculations, you may find a performance improvement by experimenting with numeric sizes that are larger than otherwise necessary. However, in practice, doing this typically degrades overall performance, so be careful to measure properly!
|
||||
##
|
||||
## Here are the different fixed size integer types:
|
||||
|
@ -327,7 +327,7 @@ Num range := range
|
|||
##
|
||||
## A common use for [Nat] is to store the length ("len" for short) of a
|
||||
## collection like a [List]. 64-bit systems can represent longer
|
||||
## lists in memory than 32-bit systems can, which is why the length of a list
|
||||
## lists in memory than 32-bit systems, which is why the length of a list
|
||||
## is represented as a [Nat] in Roc.
|
||||
##
|
||||
## If any operation would result in an [Int] that is either too big
|
||||
|
@ -442,7 +442,7 @@ U8 : Num (Integer Unsigned8)
|
|||
## on 32-bit systems, and so on.
|
||||
##
|
||||
## This system-specific size makes it useful for certain data structure
|
||||
## functions like [List.len], because the number of elements many data strucures
|
||||
## functions like [List.len], because the number of elements many data structures
|
||||
## can hold is also system-specific. For example, the maximum number of elements
|
||||
## a [List] can hold on a 64-bit system fits in a 64-bit unsigned integer, and
|
||||
## on a 32-bit system it fits in 32-bit unsigned integer. This makes [Nat] a
|
||||
|
@ -473,7 +473,7 @@ F32 : Num (FloatingPoint Binary32)
|
|||
##
|
||||
## This means a [Dec] can represent whole numbers up to slightly over 170
|
||||
## quintillion, along with 18 decimal places. (To be precise, it can store
|
||||
## numbers betwween `-170_141_183_460_469_231_731.687303715884105728`
|
||||
## numbers between `-170_141_183_460_469_231_731.687303715884105728`
|
||||
## and `170_141_183_460_469_231_731.687303715884105727`.) Why 18
|
||||
## decimal places? It's the highest number of decimal places where you can still
|
||||
## convert any [U64] to a [Dec] without losing information.
|
||||
|
@ -647,7 +647,7 @@ isInfinite : Frac * -> Bool
|
|||
## ```
|
||||
isFinite : Frac * -> Bool
|
||||
|
||||
## Return the absolute value of the number.
|
||||
## Returns the absolute value of the number.
|
||||
##
|
||||
## * For a positive number, returns the same number.
|
||||
## * For a negative number, returns the same number except positive.
|
||||
|
@ -670,7 +670,7 @@ isFinite : Frac * -> Bool
|
|||
## Calling this on an unsigned integer (like [U32] or [U64]) never does anything.
|
||||
abs : Num a -> Num a
|
||||
|
||||
## Return the absolute difference between two numbers.
|
||||
## Returns the absolute difference between two numbers.
|
||||
##
|
||||
## ```
|
||||
## Num.absDiff 5 3
|
||||
|
@ -691,7 +691,7 @@ absDiff = \a, b ->
|
|||
else
|
||||
b - a
|
||||
|
||||
## Return a negative number when given a positive one, and vice versa.
|
||||
## Returns a negative number when given a positive one, and vice versa.
|
||||
## ```
|
||||
## Num.neg 5
|
||||
##
|
||||
|
@ -712,7 +712,7 @@ absDiff = \a, b ->
|
|||
## (It will never crash when given a [Frac], however, because of how floating point numbers represent positive and negative numbers.)
|
||||
neg : Num a -> Num a
|
||||
|
||||
## Add two numbers of the same type.
|
||||
## Adds two numbers of the same type.
|
||||
##
|
||||
## (To add an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
|
||||
##
|
||||
|
@ -733,7 +733,7 @@ neg : Num a -> Num a
|
|||
## ∞ or -∞. For all other number types, overflow results in a panic.
|
||||
add : Num a, Num a -> Num a
|
||||
|
||||
## Subtract two numbers of the same type.
|
||||
## Subtracts two numbers of the same type.
|
||||
##
|
||||
## (To subtract an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
|
||||
##
|
||||
|
@ -754,7 +754,7 @@ add : Num a, Num a -> Num a
|
|||
## ∞ or -∞. For all other number types, overflow results in a panic.
|
||||
sub : Num a, Num a -> Num a
|
||||
|
||||
## Multiply two numbers of the same type.
|
||||
## Multiplies two numbers of the same type.
|
||||
##
|
||||
## (To multiply an [Int] and a [Frac], first convert one so that they both have the same type. There are functions in this module that can convert both [Int] to [Frac] and the other way around.)
|
||||
##
|
||||
|
@ -833,12 +833,12 @@ logChecked = \x ->
|
|||
else
|
||||
Ok (Num.log x)
|
||||
|
||||
## Divide one [Frac] by another.
|
||||
## Divides one [Frac] by another.
|
||||
##
|
||||
## `a / b` is shorthand for `Num.div a b`.
|
||||
##
|
||||
## [Division by zero is undefined in mathematics](https://en.wikipedia.org/wiki/Division_by_zero).
|
||||
## As such, you should make sure never to pass zero as the denomaintor to this function!
|
||||
## As such, you should make sure never to pass zero as the denominator to this function!
|
||||
## Calling [div] on a [Dec] denominator of zero will cause a panic.
|
||||
##
|
||||
## Calling [div] on [F32] and [F64] values follows these rules:
|
||||
|
@ -882,12 +882,12 @@ divCeilChecked = \a, b ->
|
|||
else
|
||||
Ok (Num.divCeil a b)
|
||||
|
||||
## Divide two integers, truncating the result towards zero.
|
||||
## Divides two integers, truncating the result towards zero.
|
||||
##
|
||||
## `a // b` is shorthand for `Num.divTrunc a b`.
|
||||
##
|
||||
## Division by zero is undefined in mathematics. As such, you should make
|
||||
## sure never to pass zero as the denomaintor to this function! If you do,
|
||||
## sure never to pass zero as the denominator to this function! If you do,
|
||||
## it will crash.
|
||||
## ```
|
||||
## 5 // 7
|
||||
|
@ -907,7 +907,7 @@ divTruncChecked = \a, b ->
|
|||
else
|
||||
Ok (Num.divTrunc a b)
|
||||
|
||||
## Obtain the remainder (truncating modulo) from the division of two integers.
|
||||
## Obtains the remainder (truncating modulo) from the division of two integers.
|
||||
##
|
||||
## `a % b` is shorthand for `Num.rem a b`.
|
||||
## ```
|
||||
|
@ -1046,7 +1046,7 @@ countOneBits : Int a -> Nat
|
|||
|
||||
addWrap : Int range, Int range -> Int range
|
||||
|
||||
## Add two numbers, clamping on the maximum representable number rather than
|
||||
## Adds two numbers, clamping on the maximum representable number rather than
|
||||
## overflowing.
|
||||
##
|
||||
## This is the same as [Num.add] except for the saturating behavior if the
|
||||
|
@ -1055,7 +1055,7 @@ addWrap : Int range, Int range -> Int range
|
|||
## yield 255, the maximum value of a `U8`.
|
||||
addSaturated : Num a, Num a -> Num a
|
||||
|
||||
## Add two numbers and check for overflow.
|
||||
## Adds two numbers and checks for overflow.
|
||||
##
|
||||
## This is the same as [Num.add] except if the operation overflows, instead of
|
||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||
|
@ -1072,7 +1072,7 @@ addCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
|||
|
||||
subWrap : Int range, Int range -> Int range
|
||||
|
||||
## Subtract two numbers, clamping on the minimum representable number rather
|
||||
## Subtracts two numbers, clamping on the minimum representable number rather
|
||||
## than overflowing.
|
||||
##
|
||||
## This is the same as [Num.sub] except for the saturating behavior if the
|
||||
|
@ -1081,7 +1081,7 @@ subWrap : Int range, Int range -> Int range
|
|||
## yield 0, the minimum value of a `U8`.
|
||||
subSaturated : Num a, Num a -> Num a
|
||||
|
||||
## Subtract two numbers and check for overflow.
|
||||
## Subtracts two numbers and checks for overflow.
|
||||
##
|
||||
## This is the same as [Num.sub] except if the operation overflows, instead of
|
||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||
|
@ -1098,14 +1098,14 @@ subCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
|||
|
||||
mulWrap : Int range, Int range -> Int range
|
||||
|
||||
## Multiply two numbers, clamping on the maximum representable number rather than
|
||||
## Multiplies two numbers, clamping on the maximum representable number rather than
|
||||
## overflowing.
|
||||
##
|
||||
## This is the same as [Num.mul] except for the saturating behavior if the
|
||||
## addition is to overflow.
|
||||
mulSaturated : Num a, Num a -> Num a
|
||||
|
||||
## Multiply two numbers and check for overflow.
|
||||
## Multiplies two numbers and checks for overflow.
|
||||
##
|
||||
## This is the same as [Num.mul] except if the operation overflows, instead of
|
||||
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
|
||||
|
@ -1120,8 +1120,8 @@ mulChecked = \a, b ->
|
|||
|
||||
mulCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
||||
|
||||
## The lowest number that can be stored in an [I8] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in an [I8] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `-128`.
|
||||
##
|
||||
|
@ -1130,8 +1130,8 @@ mulCheckedLowlevel : Num a, Num a -> { b : Bool, a : Num a }
|
|||
minI8 : I8
|
||||
minI8 = -128i8
|
||||
|
||||
## The highest number that can be stored in an [I8] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in an [I8] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `127`.
|
||||
##
|
||||
|
@ -1140,8 +1140,8 @@ minI8 = -128i8
|
|||
maxI8 : I8
|
||||
maxI8 = 127i8
|
||||
|
||||
## The lowest number that can be stored in a [U8] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in a [U8] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is zero, because [U8] is
|
||||
## [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations),
|
||||
|
@ -1150,15 +1150,15 @@ maxI8 = 127i8
|
|||
minU8 : U8
|
||||
minU8 = 0u8
|
||||
|
||||
## The highest number that can be stored in a [U8] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in a [U8] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `255`.
|
||||
maxU8 : U8
|
||||
maxU8 = 255u8
|
||||
|
||||
## The lowest number that can be stored in an [I16] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in an [I16] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `-32_768`.
|
||||
##
|
||||
|
@ -1167,8 +1167,8 @@ maxU8 = 255u8
|
|||
minI16 : I16
|
||||
minI16 = -32768i16
|
||||
|
||||
## The highest number that can be stored in an [I16] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in an [I16] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `32_767`.
|
||||
##
|
||||
|
@ -1177,8 +1177,8 @@ minI16 = -32768i16
|
|||
maxI16 : I16
|
||||
maxI16 = 32767i16
|
||||
|
||||
## The lowest number that can be stored in a [U16] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in a [U16] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is zero, because [U16] is
|
||||
## [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations),
|
||||
|
@ -1187,15 +1187,15 @@ maxI16 = 32767i16
|
|||
minU16 : U16
|
||||
minU16 = 0u16
|
||||
|
||||
## The highest number that can be stored in a [U16] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in a [U16] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `65_535`.
|
||||
maxU16 : U16
|
||||
maxU16 = 65535u16
|
||||
|
||||
## The lowest number that can be stored in an [I32] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in an [I32] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `-2_147_483_648`.
|
||||
##
|
||||
|
@ -1204,8 +1204,8 @@ maxU16 = 65535u16
|
|||
minI32 : I32
|
||||
minI32 = -2147483648
|
||||
|
||||
## The highest number that can be stored in an [I32] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in an [I32] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `2_147_483_647`,
|
||||
## which is over 2 million.
|
||||
|
@ -1215,8 +1215,8 @@ minI32 = -2147483648
|
|||
maxI32 : I32
|
||||
maxI32 = 2147483647
|
||||
|
||||
## The lowest number that can be stored in a [U32] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in a [U32] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is zero, because [U32] is
|
||||
## [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations),
|
||||
|
@ -1225,15 +1225,15 @@ maxI32 = 2147483647
|
|||
minU32 : U32
|
||||
minU32 = 0
|
||||
|
||||
## The highest number that can be stored in a [U32] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in a [U32] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `4_294_967_295`.
|
||||
maxU32 : U32
|
||||
maxU32 = 4294967295
|
||||
|
||||
## The lowest number that can be stored in an [I64] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in an [I64] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `-9_223_372_036_854_775_808`,
|
||||
## which is under 9 quintillion.
|
||||
|
@ -1243,8 +1243,8 @@ maxU32 = 4294967295
|
|||
minI64 : I64
|
||||
minI64 = -9223372036854775808
|
||||
|
||||
## The highest number that can be stored in an [I64] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in an [I64] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `9_223_372_036_854_775_807`,
|
||||
## which is over 9 quintillion.
|
||||
|
@ -1254,8 +1254,8 @@ minI64 = -9223372036854775808
|
|||
maxI64 : I64
|
||||
maxI64 = 9223372036854775807
|
||||
|
||||
## The lowest number that can be stored in a [U64] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in a [U64] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is zero, because [U64] is
|
||||
## [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations),
|
||||
|
@ -1264,16 +1264,16 @@ maxI64 = 9223372036854775807
|
|||
minU64 : U64
|
||||
minU64 = 0
|
||||
|
||||
## The highest number that can be stored in a [U64] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in a [U64] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `18_446_744_073_709_551_615`,
|
||||
## which is over 18 quintillion.
|
||||
maxU64 : U64
|
||||
maxU64 = 18446744073709551615
|
||||
|
||||
## The lowest number that can be stored in an [I128] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in an [I128] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `-170_141_183_460_469_231_731_687_303_715_884_105_728`.
|
||||
## which is under 170 undecillion.
|
||||
|
@ -1283,8 +1283,8 @@ maxU64 = 18446744073709551615
|
|||
minI128 : I128
|
||||
minI128 = -170141183460469231731687303715884105728
|
||||
|
||||
## The highest number that can be stored in an [I128] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in an [I128] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `170_141_183_460_469_231_731_687_303_715_884_105_727`,
|
||||
## which is over 170 undecillion.
|
||||
|
@ -1294,8 +1294,8 @@ minI128 = -170141183460469231731687303715884105728
|
|||
maxI128 : I128
|
||||
maxI128 = 170141183460469231731687303715884105727
|
||||
|
||||
## The lowest number that can be stored in a [U128] without underflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the lowest number that can be stored in a [U128] without underflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is zero, because [U128] is
|
||||
## [unsigned](https://en.wikipedia.org/wiki/Signed_number_representations),
|
||||
|
@ -1304,8 +1304,8 @@ maxI128 = 170141183460469231731687303715884105727
|
|||
minU128 : U128
|
||||
minU128 = 0
|
||||
|
||||
## The highest number that can be stored in a [U128] without overflowing its
|
||||
## available memory and crashing.
|
||||
## Returns the highest number that can be stored in a [U128] without overflowing
|
||||
## its available memory and crashing.
|
||||
##
|
||||
## For reference, this number is `340_282_366_920_938_463_463_374_607_431_768_211_455`,
|
||||
## which is over 340 undecillion.
|
||||
|
@ -1337,7 +1337,7 @@ toU32 : Int * -> U32
|
|||
toU64 : Int * -> U64
|
||||
toU128 : Int * -> U128
|
||||
|
||||
## Convert an [Int] to a [Nat]. If the given number doesn't fit in [Nat], it will be truncated.
|
||||
## Converts an [Int] to a [Nat]. If the given number doesn't fit in [Nat], it will be truncated.
|
||||
## Since [Nat] has a different maximum number depending on the system you're building
|
||||
## for, this may give a different answer on different systems.
|
||||
##
|
||||
|
|
|
@ -6,7 +6,7 @@ interface Result
|
|||
## okay, or else there was an error of some sort.
|
||||
Result ok err : [Ok ok, Err err]
|
||||
|
||||
## Return `Bool.true` if the result indicates a success, else return `Bool.false`
|
||||
## Returns `Bool.true` if the result indicates a success, else returns `Bool.false`
|
||||
## ```
|
||||
## Result.isOk (Ok 5)
|
||||
## ```
|
||||
|
@ -16,7 +16,7 @@ isOk = \result ->
|
|||
Ok _ -> Bool.true
|
||||
Err _ -> Bool.false
|
||||
|
||||
## Return `Bool.true` if the result indicates a failure, else return `Bool.false`
|
||||
## Returns `Bool.true` if the result indicates a failure, else returns `Bool.false`
|
||||
## ```
|
||||
## Result.isErr (Err "uh oh")
|
||||
## ```
|
||||
|
@ -26,7 +26,7 @@ isErr = \result ->
|
|||
Ok _ -> Bool.false
|
||||
Err _ -> Bool.true
|
||||
|
||||
## If the result is `Ok`, return the value it holds. Otherwise, return
|
||||
## If the result is `Ok`, returns the value it holds. Otherwise, returns
|
||||
## the given default value.
|
||||
## ```
|
||||
## Result.withDefault (Ok 7) 42
|
||||
|
@ -38,8 +38,8 @@ withDefault = \result, default ->
|
|||
Ok value -> value
|
||||
Err _ -> default
|
||||
|
||||
## If the result is `Ok`, transform the value it holds by running a conversion
|
||||
## function on it. Then return a new `Ok` holding the transformed value. If the
|
||||
## If the result is `Ok`, transforms the value it holds by running a conversion
|
||||
## function on it. Then returns a new `Ok` holding the transformed value. If the
|
||||
## result is `Err`, this has no effect. Use [mapErr] to transform an `Err`.
|
||||
## ```
|
||||
## Result.map (Ok 12) Num.negate
|
||||
|
@ -54,8 +54,8 @@ map = \result, transform ->
|
|||
Ok v -> Ok (transform v)
|
||||
Err e -> Err e
|
||||
|
||||
## If the result is `Err`, transform the value it holds by running a conversion
|
||||
## function on it. Then return a new `Err` holding the transformed value. If
|
||||
## If the result is `Err`, transforms the value it holds by running a conversion
|
||||
## function on it. Then returns a new `Err` holding the transformed value. If
|
||||
## the result is `Ok`, this has no effect. Use [map] to transform an `Ok`.
|
||||
## ```
|
||||
## Result.mapErr (Err "yipes!") Str.isEmpty
|
||||
|
@ -67,8 +67,8 @@ mapErr = \result, transform ->
|
|||
Ok v -> Ok v
|
||||
Err e -> Err (transform e)
|
||||
|
||||
## If the result is `Ok`, transform the entire result by running a conversion
|
||||
## function on the value the `Ok` holds. Then return that new result. If the
|
||||
## If the result is `Ok`, transforms the entire result by running a conversion
|
||||
## function on the value the `Ok` holds. Then returns that new result. If the
|
||||
## result is `Err`, this has no effect. Use `onErr` to transform an `Err`.
|
||||
## ```
|
||||
## Result.try (Ok -1) \num -> if num < 0 then Err "negative!" else Ok -num
|
||||
|
@ -80,8 +80,8 @@ try = \result, transform ->
|
|||
Ok v -> transform v
|
||||
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
|
||||
## If the result is `Err`, transforms the entire result by running a conversion
|
||||
## function on the value the `Err` holds. Then returns that new result. If the
|
||||
## result is `Ok`, this has no effect. Use `try` to transform an `Ok`.
|
||||
## ```
|
||||
## Result.onErr (Ok 10) \errorNum -> Str.toNat errorNum
|
||||
|
|
|
@ -20,19 +20,19 @@ interface Set
|
|||
Bool.{ Bool, Eq },
|
||||
Dict.{ Dict },
|
||||
Num.{ Nat },
|
||||
Hash.{ Hash },
|
||||
Hash.{ Hash, Hasher },
|
||||
]
|
||||
|
||||
# We should have this line above the next has.
|
||||
# It causes the formatter to fail currently.
|
||||
# | k has Hash & Eq
|
||||
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
|
||||
## type which stores a collection of unique values, without any ordering
|
||||
Set k := Dict.Dict k {}
|
||||
Set k := Dict.Dict k {} | k has Hash & Eq
|
||||
has [
|
||||
Eq {
|
||||
isEq,
|
||||
},
|
||||
Hash {
|
||||
hash: hashSet,
|
||||
},
|
||||
]
|
||||
|
||||
isEq : Set k, Set k -> Bool | k has Hash & Eq
|
||||
|
@ -46,6 +46,9 @@ isEq = \xs, ys ->
|
|||
else
|
||||
Break Bool.false
|
||||
|
||||
hashSet : hasher, Set k -> hasher | k has Hash & Eq, hasher has Hasher
|
||||
hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
||||
|
||||
## Creates a new empty `Set`.
|
||||
## ```
|
||||
## emptySet = Set.empty {}
|
||||
|
@ -353,3 +356,26 @@ expect
|
|||
|> insert 9
|
||||
|
||||
x == fromList (toList x)
|
||||
|
||||
expect
|
||||
orderOne : Set Nat
|
||||
orderOne =
|
||||
single 1
|
||||
|> insert 2
|
||||
|
||||
orderTwo : Set Nat
|
||||
orderTwo =
|
||||
single 2
|
||||
|> insert 1
|
||||
|
||||
wrapperOne : Set (Set Nat)
|
||||
wrapperOne =
|
||||
single orderOne
|
||||
|> insert orderTwo
|
||||
|
||||
wrapperTwo : Set (Set Nat)
|
||||
wrapperTwo =
|
||||
single orderTwo
|
||||
|> insert orderOne
|
||||
|
||||
wrapperOne == wrapperTwo
|
||||
|
|
|
@ -216,7 +216,7 @@ withCapacity : Nat -> Str
|
|||
## In this example:
|
||||
## 1. We start with `greeting`, which has both a length and capacity of 24 (bytes).
|
||||
## 2. `|> Str.concat ", "` will see that there isn't enough capacity to add 2 more bytes for the `", "`, so it will create a new heap allocation with enough bytes to hold both. (This probably will be more than 7 bytes, because when [Str] functions reallocate, they apply a multiplier to the exact capacity required. This makes it less likely that future realloctions will be needed. The multiplier amount is not specified, because it may change in future releases of Roc, but it will likely be around 1.5 to 2 times the exact capacity required.) Then it will copy the current bytes (`"Hello"`) into the new allocation, and finally concatenate the `", "` into the new allocation. The old allocation will then be deallocated because it's no longer referenced anywhere in the program.
|
||||
## 3. `|> Str.concat subject` will again check if there is enough capacity in the string. If it doesn't find enough capacity once again, it will make a third allocation, copy the existing bytes (`"Hello, "`) into that third allocation, and then deallocate the second allocation because it's already no longer being referenced anywhere else in the program. (It may find enough capacity in this prticular case, because the previous [Str.concat] allocated something like 1.5 to 2 times the necessary capacity in order to anticipate future concatenations like this...but if something longer than `"World"` were being concatenated here, it might still require further reallocation and copying.)
|
||||
## 3. `|> Str.concat subject` will again check if there is enough capacity in the string. If it doesn't find enough capacity once again, it will make a third allocation, copy the existing bytes (`"Hello, "`) into that third allocation, and then deallocate the second allocation because it's already no longer being referenced anywhere else in the program. (It may find enough capacity in this particular case, because the previous [Str.concat] allocated something like 1.5 to 2 times the necessary capacity in order to anticipate future concatenations like this...but if something longer than `"World"` were being concatenated here, it might still require further reallocation and copying.)
|
||||
## 4. `|> Str.concat "!\n"` will repeat this process once more.
|
||||
##
|
||||
## This process can have significant performance costs due to multiple reallocation of new strings, copying between old strings and new strings, and deallocation of immediately obsolete strings.
|
||||
|
@ -704,7 +704,7 @@ expect Str.replaceLast "abXdeXghi" "X" "_" == Ok "abXde_ghi"
|
|||
|
||||
## Returns the given [Str] before the first occurrence of a [delimiter](https://www.computerhope.com/jargon/d/delimite.htm), as well
|
||||
## as the rest of the string after that occurrence.
|
||||
## Returns [ Err NotFound] if the delimiter is not found.
|
||||
## Returns [Err NotFound] if the delimiter is not found.
|
||||
## ```
|
||||
## expect Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" }
|
||||
## expect Str.splitFirst "no slashes here" "/" == Err NotFound
|
||||
|
@ -894,7 +894,7 @@ walkUtf8Help = \str, state, step, index, length ->
|
|||
expect (walkUtf8 "ABC" [] List.append) == [65, 66, 67]
|
||||
expect (walkUtf8 "鹏" [] List.append) == [233, 185, 143]
|
||||
|
||||
## Shrink the memory footprint of a str such that it's capacity and length are equal.
|
||||
## Shrink the memory footprint of a str such that its capacity and length are equal.
|
||||
## Note: This will also convert seamless slices to regular lists.
|
||||
releaseExcessCapacity : Str -> Str
|
||||
|
||||
|
|
|
@ -368,6 +368,7 @@ pub const LIST_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.list.release_excess
|
|||
pub const DEC_FROM_STR: &str = "roc_builtins.dec.from_str";
|
||||
pub const DEC_TO_STR: &str = "roc_builtins.dec.to_str";
|
||||
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||
pub const DEC_TO_I128: &str = "roc_builtins.dec.to_i128";
|
||||
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
||||
pub const DEC_NEQ: &str = "roc_builtins.dec.neq";
|
||||
pub const DEC_NEGATE: &str = "roc_builtins.dec.negate";
|
||||
|
@ -390,6 +391,7 @@ pub const UTILS_INCREF_DATA_PTR: &str = "roc_builtins.utils.incref_data_ptr";
|
|||
pub const UTILS_DECREF_DATA_PTR: &str = "roc_builtins.utils.decref_data_ptr";
|
||||
pub const UTILS_IS_UNIQUE: &str = "roc_builtins.utils.is_unique";
|
||||
pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null";
|
||||
pub const UTILS_DICT_PSEUDO_SEED: &str = "roc_builtins.utils.dict_pseudo_seed";
|
||||
|
||||
pub const UTILS_EXPECT_FAILED_START_SHARED_BUFFER: &str =
|
||||
"roc_builtins.utils.expect_failed_start_shared_buffer";
|
||||
|
|
|
@ -448,8 +448,9 @@ pub fn find_type_def_symbols(
|
|||
As(actual, _, _) => {
|
||||
stack.push(&actual.value);
|
||||
}
|
||||
Tuple { elems: _, ext: _ } => {
|
||||
todo!("find_type_def_symbols: Tuple");
|
||||
Tuple { elems, ext } => {
|
||||
stack.extend(elems.iter().map(|t| &t.value));
|
||||
stack.extend(ext.iter().map(|t| &t.value));
|
||||
}
|
||||
Record { fields, ext } => {
|
||||
let mut inner_stack = Vec::with_capacity(fields.items.len());
|
||||
|
|
|
@ -208,6 +208,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
NumCountLeadingZeroBits; NUM_COUNT_LEADING_ZERO_BITS; 1,
|
||||
NumCountTrailingZeroBits; NUM_COUNT_TRAILING_ZERO_BITS; 1,
|
||||
NumCountOneBits; NUM_COUNT_ONE_BITS; 1,
|
||||
I128OfDec; I128_OF_DEC; 1,
|
||||
|
||||
Eq; BOOL_STRUCTURAL_EQ; 2,
|
||||
NotEq; BOOL_STRUCTURAL_NOT_EQ; 2,
|
||||
|
@ -217,6 +218,7 @@ map_symbol_to_lowlevel_and_arity! {
|
|||
BoxExpr; BOX_BOX_FUNCTION; 1,
|
||||
UnboxExpr; BOX_UNBOX; 1,
|
||||
Unreachable; LIST_UNREACHABLE; 1,
|
||||
DictPseudoSeed; DICT_PSEUDO_SEED; 1,
|
||||
}
|
||||
|
||||
/// Some builtins cannot be constructed in code gen alone, and need to be defined
|
||||
|
|
|
@ -512,13 +512,13 @@ pub fn constrain_pattern(
|
|||
let expected =
|
||||
constraints.push_pat_expected_type(PExpected::NoExpectation(pat_type_index));
|
||||
|
||||
let (guard_var, loc_guard) = typ;
|
||||
let (guard_var, loc_pattern) = typ;
|
||||
let elem_type = {
|
||||
let guard_type = constraints.push_variable(*guard_var);
|
||||
let expected_pat = constraints.push_pat_expected_type(PExpected::ForReason(
|
||||
PReason::PatternGuard,
|
||||
pat_type_index,
|
||||
loc_guard.region,
|
||||
loc_pattern.region,
|
||||
));
|
||||
|
||||
state.constraints.push(constraints.pattern_presence(
|
||||
|
@ -533,8 +533,8 @@ pub fn constrain_pattern(
|
|||
types,
|
||||
constraints,
|
||||
env,
|
||||
&loc_guard.value,
|
||||
loc_guard.region,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
expected,
|
||||
state,
|
||||
);
|
||||
|
@ -676,6 +676,17 @@ pub fn constrain_pattern(
|
|||
RecordField::Optional(pat_type)
|
||||
}
|
||||
DestructType::Required => {
|
||||
// Named destructures like
|
||||
// {foo} -> ...
|
||||
// are equivalent to wildcards on the type of `foo`, so if `foo` is a tag
|
||||
// union, we must add a constraint to ensure that this destructure opens it
|
||||
// up.
|
||||
if could_be_a_tag_union(types, pat_type_index) {
|
||||
state
|
||||
.delayed_is_open_constraints
|
||||
.push(constraints.is_open_type(pat_type_index));
|
||||
}
|
||||
|
||||
// No extra constraints necessary.
|
||||
RecordField::Demanded(pat_type)
|
||||
}
|
||||
|
|
|
@ -194,6 +194,9 @@ const fn builtin_symbol_to_hash_lambda(symbol: Symbol) -> Option<FlatHash> {
|
|||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_NAT))
|
||||
}
|
||||
Symbol::NUM_DEC | Symbol::NUM_DECIMAL => {
|
||||
Some(SingleLambdaSetImmediate(Symbol::HASH_HASH_DEC))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1548,6 +1548,13 @@ trait Backend<'a> {
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::DictPseudoSeed => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::UTILS_DICT_PSEUDO_SEED.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::NumToStr => {
|
||||
let arg_layout = arg_layouts[0];
|
||||
let intrinsic = match self.interner().get(arg_layout).repr {
|
||||
|
|
|
@ -1123,7 +1123,10 @@ fn build_tag_eq_help<'a, 'ctx>(
|
|||
|
||||
env.builder.build_return(Some(&answer));
|
||||
}
|
||||
NullableWrapped { other_tags, .. } => {
|
||||
NullableWrapped {
|
||||
other_tags,
|
||||
nullable_id,
|
||||
} => {
|
||||
let ptr_equal = env.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
env.builder
|
||||
|
@ -1204,7 +1207,9 @@ fn build_tag_eq_help<'a, 'ctx>(
|
|||
let tags = other_tags;
|
||||
let mut cases = Vec::with_capacity_in(tags.len(), env.arena);
|
||||
|
||||
for (tag_id, field_layouts) in tags.iter().enumerate() {
|
||||
for (i, field_layouts) in tags.iter().enumerate() {
|
||||
let tag_id = if i >= (*nullable_id as _) { i + 1 } else { i };
|
||||
|
||||
let block = env.context.append_basic_block(parent, "tag_id_modify");
|
||||
env.builder.position_at_end(block);
|
||||
|
||||
|
|
|
@ -1187,6 +1187,10 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
// which could be useful to look at when implementing this.
|
||||
todo!("implement checked float conversion");
|
||||
}
|
||||
I128OfDec => {
|
||||
arguments!(dec);
|
||||
dec_to_i128(env, dec)
|
||||
}
|
||||
Eq => {
|
||||
arguments_with_layouts!((lhs_arg, lhs_layout), (rhs_arg, rhs_layout));
|
||||
|
||||
|
@ -1310,6 +1314,11 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
ptr.into()
|
||||
}
|
||||
},
|
||||
DictPseudoSeed => {
|
||||
// Dict.pseudoSeed : {} -> u64
|
||||
|
||||
call_bitcode_fn(env, &[], bitcode::UTILS_DICT_PSEUDO_SEED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1802,6 +1811,25 @@ fn dec_to_str<'ctx>(env: &Env<'_, 'ctx, '_>, dec: BasicValueEnum<'ctx>) -> Basic
|
|||
}
|
||||
}
|
||||
|
||||
fn dec_to_i128<'ctx>(env: &Env<'_, 'ctx, '_>, dec: BasicValueEnum<'ctx>) -> BasicValueEnum<'ctx> {
|
||||
use roc_target::OperatingSystem::*;
|
||||
|
||||
let dec = dec.into_int_value();
|
||||
|
||||
match env.target_info.operating_system {
|
||||
Windows => {
|
||||
//
|
||||
call_bitcode_fn(env, &[dec_alloca(env, dec).into()], bitcode::DEC_TO_I128)
|
||||
}
|
||||
Unix => {
|
||||
let (low, high) = dec_split_into_words(env, dec);
|
||||
|
||||
call_bitcode_fn(env, &[low.into(), high.into()], bitcode::DEC_TO_I128)
|
||||
}
|
||||
Wasi => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn dec_binop_with_overflow<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
fn_name: &str,
|
||||
|
|
|
@ -1935,6 +1935,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
NumToFloatChecked => {
|
||||
todo!("implement toF32Checked and toF64Checked");
|
||||
}
|
||||
I128OfDec => self.load_args_and_call_zig(backend, bitcode::DEC_TO_I128),
|
||||
And => {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.i32_and();
|
||||
|
@ -1982,6 +1983,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
},
|
||||
StoredValue::StackMemory { .. } => { /* do nothing */ }
|
||||
},
|
||||
DictPseudoSeed => self.load_args_and_call_zig(backend, bitcode::UTILS_DICT_PSEUDO_SEED),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,7 @@ pub enum LowLevel {
|
|||
NumCountLeadingZeroBits,
|
||||
NumCountTrailingZeroBits,
|
||||
NumCountOneBits,
|
||||
I128OfDec,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
@ -126,6 +127,7 @@ pub enum LowLevel {
|
|||
BoxExpr,
|
||||
UnboxExpr,
|
||||
Unreachable,
|
||||
DictPseudoSeed,
|
||||
}
|
||||
|
||||
macro_rules! higher_order {
|
||||
|
@ -339,10 +341,12 @@ map_symbol_to_lowlevel! {
|
|||
NumCountLeadingZeroBits <= NUM_COUNT_LEADING_ZERO_BITS,
|
||||
NumCountTrailingZeroBits <= NUM_COUNT_TRAILING_ZERO_BITS,
|
||||
NumCountOneBits <= NUM_COUNT_ONE_BITS,
|
||||
I128OfDec <= I128_OF_DEC,
|
||||
Eq <= BOOL_STRUCTURAL_EQ,
|
||||
NotEq <= BOOL_STRUCTURAL_NOT_EQ,
|
||||
And <= BOOL_AND,
|
||||
Or <= BOOL_OR,
|
||||
Not <= BOOL_NOT,
|
||||
Unreachable <= LIST_UNREACHABLE,
|
||||
DictPseudoSeed <= DICT_PSEUDO_SEED,
|
||||
}
|
||||
|
|
|
@ -1463,6 +1463,7 @@ define_builtins! {
|
|||
21 DICT_UPDATE: "update"
|
||||
|
||||
22 DICT_LIST_GET_UNSAFE: "listGetUnsafe"
|
||||
23 DICT_PSEUDO_SEED: "pseudoSeed"
|
||||
}
|
||||
9 SET: "Set" => {
|
||||
0 SET_SET: "Set" exposed_type=true // the Set.Set type alias
|
||||
|
@ -1565,10 +1566,12 @@ define_builtins! {
|
|||
13 HASH_HASH_I64: "hashI64"
|
||||
14 HASH_HASH_I128: "hashI128"
|
||||
15 HASH_HASH_NAT: "hashNat"
|
||||
16 HASH_COMPLETE: "complete"
|
||||
17 HASH_HASH_STR_BYTES: "hashStrBytes"
|
||||
18 HASH_HASH_LIST: "hashList"
|
||||
19 HASH_HASH_UNORDERED: "hashUnordered"
|
||||
16 I128_OF_DEC: "i128OfDec"
|
||||
17 HASH_HASH_DEC: "hashDec"
|
||||
18 HASH_COMPLETE: "complete"
|
||||
19 HASH_HASH_STR_BYTES: "hashStrBytes"
|
||||
20 HASH_HASH_LIST: "hashList"
|
||||
21 HASH_HASH_UNORDERED: "hashUnordered"
|
||||
}
|
||||
14 JSON: "Json" => {
|
||||
0 JSON_JSON: "Json"
|
||||
|
|
|
@ -947,6 +947,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
|
|||
// - other refcounted arguments are Borrowed
|
||||
match op {
|
||||
Unreachable => arena.alloc_slice_copy(&[irrelevant]),
|
||||
DictPseudoSeed => arena.alloc_slice_copy(&[irrelevant]),
|
||||
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrGraphemes
|
||||
| StrCountUtf8Bytes | StrGetCapacity | ListGetCapacity => {
|
||||
arena.alloc_slice_copy(&[borrowed])
|
||||
|
@ -1014,7 +1015,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[Ownership] {
|
|||
| NumToFloatChecked
|
||||
| NumCountLeadingZeroBits
|
||||
| NumCountTrailingZeroBits
|
||||
| NumCountOneBits => arena.alloc_slice_copy(&[irrelevant]),
|
||||
| NumCountOneBits
|
||||
| I128OfDec => arena.alloc_slice_copy(&[irrelevant]),
|
||||
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
NumBytesToU64 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
|
|
|
@ -108,9 +108,18 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
|
||||
alloc_let_with_continuation!(environment)
|
||||
}
|
||||
_ => {
|
||||
// TODO perhaps allow for some e.g. lowlevel functions to be called if they cannot modify the RC of the symbol.
|
||||
// Check whether the increments can be passed to the continuation.
|
||||
CallType::LowLevel { op, .. } => match low_level_no_rc(&op) {
|
||||
// It should be safe to pass the increments to the continuation.
|
||||
RC::NoRc => alloc_let_with_continuation!(environment),
|
||||
// We probably should not pass the increments to the continuation.
|
||||
RC::Rc | RC::Uknown => {
|
||||
let mut new_environment = environment.clone_without_incremented();
|
||||
|
||||
alloc_let_with_continuation!(&mut new_environment)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Calls can modify the RC of the symbol.
|
||||
// If we move a increment of children after the function,
|
||||
// the function might deallocate the child before we can use it after the function.
|
||||
|
@ -256,7 +265,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
let new_branches = branches
|
||||
.iter()
|
||||
.map(|(label, info, branch)| {
|
||||
let mut branch_env = environment.clone_without_incremented();
|
||||
let mut branch_env = environment.clone();
|
||||
|
||||
insert_branch_info!(branch_env, info);
|
||||
|
||||
|
@ -268,7 +277,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
branch,
|
||||
);
|
||||
|
||||
(*label, info.clone(), new_branch.clone())
|
||||
(*label, info.clone(), new_branch.clone(), branch_env)
|
||||
})
|
||||
.collect_in::<Vec<_>>(arena)
|
||||
.into_bump_slice();
|
||||
|
@ -276,7 +285,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
let new_default_branch = {
|
||||
let (info, branch) = default_branch;
|
||||
|
||||
let mut branch_env = environment.clone_without_incremented();
|
||||
let mut branch_env = environment.clone();
|
||||
|
||||
insert_branch_info!(branch_env, info);
|
||||
|
||||
|
@ -288,14 +297,91 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
branch,
|
||||
);
|
||||
|
||||
(info.clone(), new_branch, branch_env)
|
||||
};
|
||||
|
||||
// Find consumed increments in each branch and make sure they are consumed in all branches.
|
||||
// By incrementing them in each branch where they were not consumed.
|
||||
{
|
||||
let branch_envs = {
|
||||
let mut branch_environments =
|
||||
Vec::with_capacity_in(new_branches.len() + 1, arena);
|
||||
|
||||
for (_, _, _, branch_env) in new_branches.iter() {
|
||||
branch_environments.push(branch_env);
|
||||
}
|
||||
|
||||
branch_environments.push(&new_default_branch.2);
|
||||
|
||||
branch_environments
|
||||
};
|
||||
|
||||
// Find the lowest symbol count for each symbol in each branch, and update the environment to match.
|
||||
for (symbol, count) in environment.incremented_symbols.iter_mut() {
|
||||
let consumed = branch_envs
|
||||
.iter()
|
||||
.map(|branch_env| branch_env.incremented_symbols.get(symbol).unwrap_or(&0))
|
||||
.min()
|
||||
.unwrap();
|
||||
|
||||
// Update the existing env to match the lowest count.
|
||||
*count = *consumed;
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! insert_incs {
|
||||
($branch_env:expr, $branch:expr ) => {{
|
||||
let symbol_differences =
|
||||
environment
|
||||
.incremented_symbols
|
||||
.iter()
|
||||
.filter_map(|(symbol, count)| {
|
||||
let branch_count =
|
||||
$branch_env.incremented_symbols.get(symbol).unwrap_or(&0);
|
||||
|
||||
match branch_count - count {
|
||||
0 => None,
|
||||
difference => Some((symbol, difference)),
|
||||
}
|
||||
});
|
||||
|
||||
symbol_differences.fold($branch, |new_branch, (symbol, difference)| {
|
||||
arena.alloc(Stmt::Refcounting(
|
||||
ModifyRc::Inc(*symbol, difference),
|
||||
new_branch,
|
||||
))
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
let newer_branches = new_branches
|
||||
.iter()
|
||||
.map(|(label, info, branch, branch_env)| {
|
||||
let new_branch = insert_incs!(branch_env, branch);
|
||||
|
||||
(*label, info.clone(), new_branch.clone())
|
||||
})
|
||||
.collect_in::<Vec<_>>(arena)
|
||||
.into_bump_slice();
|
||||
|
||||
let newer_default_branch = {
|
||||
let (info, branch, branch_env) = new_default_branch;
|
||||
|
||||
let new_branch = insert_incs!(branch_env, branch);
|
||||
|
||||
(info.clone(), new_branch)
|
||||
};
|
||||
|
||||
// Remove all 0 counts as cleanup.
|
||||
environment
|
||||
.incremented_symbols
|
||||
.retain(|_, count| *count > 0);
|
||||
|
||||
arena.alloc(Stmt::Switch {
|
||||
cond_symbol: *cond_symbol,
|
||||
cond_layout: *cond_layout,
|
||||
branches: new_branches,
|
||||
default_branch: new_default_branch,
|
||||
branches: newer_branches,
|
||||
default_branch: newer_default_branch,
|
||||
ret_layout: *ret_layout,
|
||||
})
|
||||
}
|
||||
|
@ -351,24 +437,32 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
continuation,
|
||||
)
|
||||
} else {
|
||||
// Collect all children that were incremented and make sure that one increment remains in the environment afterwards.
|
||||
// Collect all children (recursively) that were incremented and make sure that one increment remains in the environment afterwards.
|
||||
// To prevent
|
||||
// let a = index b; inc a; dec b; ...; dec a
|
||||
// from being translated to
|
||||
// let a = index b; dec b
|
||||
// As a might get dropped as a result of the decrement of b.
|
||||
let mut incremented_children = environment
|
||||
.get_children(symbol)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|child| environment.pop_incremented(&child).then_some(child))
|
||||
.collect::<MutSet<_>>();
|
||||
let mut incremented_children = {
|
||||
let mut todo_children = bumpalo::vec![in arena; *symbol];
|
||||
let mut incremented_children = MutSet::default();
|
||||
|
||||
while let Some(child) = todo_children.pop() {
|
||||
if environment.pop_incremented(&child) {
|
||||
incremented_children.insert(child);
|
||||
} else {
|
||||
todo_children.extend(environment.get_children(&child));
|
||||
}
|
||||
}
|
||||
|
||||
incremented_children
|
||||
};
|
||||
|
||||
// This decremented symbol was not incremented before, perhaps the children were.
|
||||
let in_layout = environment.get_symbol_layout(symbol);
|
||||
let runtime_layout = layout_interner.runtime_representation(*in_layout);
|
||||
|
||||
let new_dec = match runtime_layout.repr {
|
||||
let updated_stmt = match runtime_layout.repr {
|
||||
// Layout has children, try to inline them.
|
||||
LayoutRepr::Struct(field_layouts) => specialize_struct(
|
||||
arena,
|
||||
|
@ -429,7 +523,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
environment.add_incremented(*child_symbol, 1)
|
||||
}
|
||||
|
||||
new_dec
|
||||
updated_stmt
|
||||
}
|
||||
}
|
||||
ModifyRc::DecRef(_) => {
|
||||
|
@ -1315,10 +1409,7 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||
|
||||
fn pop_incremented(&mut self, symbol: &Symbol) -> bool {
|
||||
match self.incremented_symbols.get_mut(symbol) {
|
||||
Some(1) => {
|
||||
self.incremented_symbols.remove(symbol);
|
||||
true
|
||||
}
|
||||
Some(0) => false,
|
||||
Some(c) => {
|
||||
*c -= 1;
|
||||
true
|
||||
|
@ -1326,6 +1417,112 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO assert that a parent is only inlined once / assert max single dec per parent.
|
||||
}
|
||||
|
||||
/**
|
||||
Reference count information
|
||||
*/
|
||||
enum RC {
|
||||
// Rc is important, moving an increment to after this function might break the program.
|
||||
// E.g. if the function checks for uniqueness and behaves differently based on that.
|
||||
Rc,
|
||||
// Rc is not important, moving an increment to after this function should have no effect.
|
||||
NoRc,
|
||||
// Rc effect is unknown.
|
||||
Uknown,
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the reference count of arguments to this function is relevant to the program.
|
||||
*/
|
||||
fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
|
||||
use LowLevel::*;
|
||||
|
||||
match lowlevel {
|
||||
Unreachable => RC::Uknown,
|
||||
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrGraphemes
|
||||
| StrCountUtf8Bytes | StrGetCapacity | ListGetCapacity => RC::NoRc,
|
||||
ListWithCapacity | StrWithCapacity => RC::NoRc,
|
||||
ListReplaceUnsafe => RC::Rc,
|
||||
StrGetUnsafe | ListGetUnsafe => RC::NoRc,
|
||||
ListConcat => RC::Rc,
|
||||
StrConcat => RC::Rc,
|
||||
StrSubstringUnsafe => RC::NoRc,
|
||||
StrReserve => RC::Rc,
|
||||
StrAppendScalar => RC::Rc,
|
||||
StrGetScalarUnsafe => RC::NoRc,
|
||||
StrTrim => RC::Rc,
|
||||
StrTrimLeft => RC::Rc,
|
||||
StrTrimRight => RC::Rc,
|
||||
StrSplit => RC::NoRc,
|
||||
StrToNum => RC::NoRc,
|
||||
ListPrepend => RC::Rc,
|
||||
StrJoinWith => RC::NoRc,
|
||||
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith => RC::Rc,
|
||||
|
||||
ListAppendUnsafe
|
||||
| ListReserve
|
||||
| ListSublist
|
||||
| ListDropAt
|
||||
| ListSwap
|
||||
| ListReleaseExcessCapacity
|
||||
| StrReleaseExcessCapacity => RC::Rc,
|
||||
|
||||
Eq | NotEq => RC::NoRc,
|
||||
|
||||
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
|
||||
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
|
||||
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
|
||||
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf
|
||||
| NumPow | NumPowInt | NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy
|
||||
| NumShiftRightBy | NumShiftRightZfBy => RC::NoRc,
|
||||
|
||||
NumToStr
|
||||
| NumAbs
|
||||
| NumNeg
|
||||
| NumSin
|
||||
| NumCos
|
||||
| NumSqrtUnchecked
|
||||
| NumLogUnchecked
|
||||
| NumRound
|
||||
| NumCeiling
|
||||
| NumFloor
|
||||
| NumToFrac
|
||||
| Not
|
||||
| NumIsNan
|
||||
| NumIsInfinite
|
||||
| NumIsFinite
|
||||
| NumAtan
|
||||
| NumAcos
|
||||
| NumAsin
|
||||
| NumIntCast
|
||||
| NumToIntChecked
|
||||
| NumToFloatCast
|
||||
| NumToFloatChecked
|
||||
| NumCountLeadingZeroBits
|
||||
| NumCountTrailingZeroBits
|
||||
| NumCountOneBits => RC::NoRc,
|
||||
NumBytesToU16 => RC::NoRc,
|
||||
NumBytesToU32 => RC::NoRc,
|
||||
NumBytesToU64 => RC::NoRc,
|
||||
NumBytesToU128 => RC::NoRc,
|
||||
StrStartsWith | StrEndsWith => RC::NoRc,
|
||||
StrStartsWithScalar => RC::NoRc,
|
||||
StrFromUtf8Range => RC::Rc,
|
||||
StrToUtf8 => RC::Rc,
|
||||
StrRepeat => RC::NoRc,
|
||||
StrFromInt | StrFromFloat => RC::NoRc,
|
||||
Hash => RC::NoRc,
|
||||
|
||||
ListIsUnique => RC::Rc,
|
||||
|
||||
BoxExpr | UnboxExpr => {
|
||||
unreachable!("These lowlevel operations are turned into mono Expr's")
|
||||
}
|
||||
|
||||
PtrCast | PtrWrite | RefCountIncRcPtr | RefCountDecRcPtr | RefCountIncDataPtr
|
||||
| RefCountDecDataPtr | RefCountIsUnique => {
|
||||
unreachable!("Only inserted *after* borrow checking: {:?}", lowlevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1609,6 +1609,16 @@ mod hash {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "gen-wasm"))] // shr not implemented for U128
|
||||
fn dec() {
|
||||
assert_evals_to!(
|
||||
&build_test("1.1dec"),
|
||||
RocList::from_slice(&[0, 0, 238, 4, 44, 252, 67, 15, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
RocList<u8>
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string() {
|
||||
assert_evals_to!(
|
||||
|
|
|
@ -2189,3 +2189,34 @@ fn issue_5162_recast_nested_nullable_unwrapped_layout() {
|
|||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm"))]
|
||||
fn nullable_wrapped_eq_issue_5434() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r###"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Value : [
|
||||
A,
|
||||
B I64,
|
||||
C,
|
||||
D (List [T Str Value]),
|
||||
]
|
||||
|
||||
main =
|
||||
x : Value
|
||||
x = B 32
|
||||
y : Value
|
||||
y = B 0
|
||||
if x == y then
|
||||
"OK"
|
||||
else
|
||||
"FAIL"
|
||||
"###
|
||||
),
|
||||
RocStr::from("FAIL"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
|
|
@ -298,6 +298,10 @@ fn create_llvm_module<'a>(
|
|||
);
|
||||
}
|
||||
|
||||
if let Ok(path) = std::env::var("ROC_DEBUG_LLVM") {
|
||||
env.module.print_to_file(path).unwrap();
|
||||
}
|
||||
|
||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
procedure Dict.1 (Dict.515):
|
||||
let Dict.518 : List {[], []} = Array [];
|
||||
let Dict.525 : U64 = 0i64;
|
||||
let Dict.526 : U64 = 8i64;
|
||||
let Dict.519 : List U64 = CallByName List.11 Dict.525 Dict.526;
|
||||
let Dict.522 : I8 = CallByName Dict.34;
|
||||
let Dict.523 : U64 = 8i64;
|
||||
let Dict.520 : List I8 = CallByName List.11 Dict.522 Dict.523;
|
||||
let Dict.521 : U64 = 0i64;
|
||||
let Dict.517 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.518, Dict.519, Dict.520, Dict.521};
|
||||
ret Dict.517;
|
||||
procedure Dict.1 (Dict.547):
|
||||
let Dict.556 : List {[], []} = Array [];
|
||||
let Dict.563 : U64 = 0i64;
|
||||
let Dict.564 : U64 = 8i64;
|
||||
let Dict.557 : List U64 = CallByName List.11 Dict.563 Dict.564;
|
||||
let Dict.560 : I8 = CallByName Dict.37;
|
||||
let Dict.561 : U64 = 8i64;
|
||||
let Dict.558 : List I8 = CallByName List.11 Dict.560 Dict.561;
|
||||
let Dict.559 : U64 = 0i64;
|
||||
let Dict.555 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.556, Dict.557, Dict.558, Dict.559};
|
||||
ret Dict.555;
|
||||
|
||||
procedure Dict.34 ():
|
||||
let Dict.524 : I8 = -128i64;
|
||||
ret Dict.524;
|
||||
procedure Dict.37 ():
|
||||
let Dict.562 : I8 = -128i64;
|
||||
ret Dict.562;
|
||||
|
||||
procedure Dict.4 (Dict.504):
|
||||
let Dict.85 : U64 = StructAtIndex 3 Dict.504;
|
||||
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.504;
|
||||
procedure Dict.4 (Dict.553):
|
||||
let Dict.99 : U64 = StructAtIndex 3 Dict.553;
|
||||
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.553;
|
||||
dec #Derived_gen.2;
|
||||
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.504;
|
||||
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.553;
|
||||
dec #Derived_gen.1;
|
||||
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.504;
|
||||
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.553;
|
||||
dec #Derived_gen.0;
|
||||
ret Dict.85;
|
||||
ret Dict.99;
|
||||
|
||||
procedure List.11 (List.115, List.116):
|
||||
let List.495 : List I8 = CallByName List.68 List.116;
|
||||
|
|
|
@ -41,8 +41,8 @@ procedure Decode.26 (Decode.105, Decode.106):
|
|||
procedure Decode.27 (Decode.107, Decode.108):
|
||||
let Decode.122 : {List U8, [C {}, C Str]} = CallByName Decode.26 Decode.107 Decode.108;
|
||||
let Decode.110 : List U8 = StructAtIndex 0 Decode.122;
|
||||
let Decode.109 : [C {}, C Str] = StructAtIndex 1 Decode.122;
|
||||
inc Decode.110;
|
||||
let Decode.109 : [C {}, C Str] = StructAtIndex 1 Decode.122;
|
||||
let Decode.125 : Int1 = CallByName List.1 Decode.110;
|
||||
if Decode.125 then
|
||||
dec Decode.110;
|
||||
|
@ -532,9 +532,9 @@ procedure Json.68 ():
|
|||
procedure Json.69 (Json.1467):
|
||||
joinpoint Json.1197 Json.1165:
|
||||
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
||||
inc Json.599;
|
||||
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
||||
let Json.1315 : U64 = 0i64;
|
||||
inc Json.599;
|
||||
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
||||
let Json.1314 : U64 = 1i64;
|
||||
inc Json.599;
|
||||
|
|
|
@ -140,9 +140,9 @@ procedure Test.1 (Test.77):
|
|||
let Test.51 : [<r>C I64, C List *self] = StructAtIndex 1 Test.6;
|
||||
dec Test.52;
|
||||
let Test.14 : List [<r>C I64, C List *self] = UnionAtIndex (Id 1) (Index 0) Test.51;
|
||||
inc Test.14;
|
||||
joinpoint #Derived_gen.2:
|
||||
let Test.35 : {} = Struct {};
|
||||
inc Test.14;
|
||||
let Test.33 : List {[<r>C I64, C List *self], [<r>C I64, C List *self]} = CallByName List.23 Test.12 Test.14 Test.35;
|
||||
let Test.34 : {} = Struct {};
|
||||
let Test.29 : Int1 = CallByName List.56 Test.33 Test.34;
|
||||
|
|
|
@ -506,9 +506,9 @@ procedure Json.68 ():
|
|||
procedure Json.69 (Json.1467):
|
||||
joinpoint Json.1197 Json.1165:
|
||||
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
||||
inc Json.599;
|
||||
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
||||
let Json.1315 : U64 = 0i64;
|
||||
inc Json.599;
|
||||
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
||||
let Json.1314 : U64 = 1i64;
|
||||
inc Json.599;
|
||||
|
|
|
@ -203,8 +203,8 @@ procedure Test.3 (Test.9, Test.10, Test.11):
|
|||
let Test.173 : Int1 = true;
|
||||
let Test.177 : Int1 = lowlevel Eq Test.173 Test.172;
|
||||
if Test.177 then
|
||||
let #Derived_gen.272 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.16, id: UpdateModeId { id: 242 } };
|
||||
inc Test.19;
|
||||
let #Derived_gen.272 : [<rnu>C *self I64 *self I32 Int1, <null>] = Reset { symbol: Test.16, id: UpdateModeId { id: 242 } };
|
||||
let Test.118 : [<rnu>C *self I64 *self I32 Int1, <null>] = CallByName Test.3 Test.19 Test.10 Test.11;
|
||||
joinpoint Test.137 #Derived_gen.317 #Derived_gen.318:
|
||||
let Test.136 : [<rnu>C *self I64 *self I32 Int1, <null>] = UnionAtIndex (Id 1) (Index 0) Test.118;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.282 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.282;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Num.283 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||
ret Num.283;
|
||||
|
||||
procedure Test.2 (Test.9, Test.10):
|
||||
let Test.38 : U8 = 1i64;
|
||||
let Test.39 : U8 = GetTagId Test.9;
|
||||
let Test.40 : Int1 = lowlevel Eq Test.38 Test.39;
|
||||
if Test.40 then
|
||||
let Test.20 : U64 = CallByName Test.3 Test.10;
|
||||
ret Test.20;
|
||||
else
|
||||
let Test.11 : Str = UnionAtIndex (Id 0) (Index 0) Test.9;
|
||||
let Test.12 : [<rnu><null>, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.9;
|
||||
let Test.35 : U8 = 1i64;
|
||||
let Test.36 : U8 = GetTagId Test.10;
|
||||
let Test.37 : Int1 = lowlevel Eq Test.35 Test.36;
|
||||
if Test.37 then
|
||||
let Test.29 : U64 = CallByName Test.3 Test.9;
|
||||
ret Test.29;
|
||||
else
|
||||
joinpoint #Derived_gen.3:
|
||||
let Test.13 : Str = UnionAtIndex (Id 0) (Index 0) Test.10;
|
||||
let Test.14 : [<rnu><null>, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.10;
|
||||
let Test.33 : U64 = CallByName Test.3 Test.12;
|
||||
let Test.34 : U64 = 1i64;
|
||||
let Test.15 : U64 = CallByName Num.19 Test.33 Test.34;
|
||||
let Test.16 : U64 = CallByName Test.3 Test.10;
|
||||
let Test.31 : Int1 = CallByName Num.24 Test.15 Test.16;
|
||||
if Test.31 then
|
||||
ret Test.15;
|
||||
else
|
||||
ret Test.16;
|
||||
in
|
||||
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.9;
|
||||
if #Derived_gen.4 then
|
||||
dec Test.11;
|
||||
decref Test.9;
|
||||
jump #Derived_gen.3;
|
||||
else
|
||||
inc Test.12;
|
||||
decref Test.9;
|
||||
jump #Derived_gen.3;
|
||||
|
||||
procedure Test.3 (Test.17):
|
||||
let Test.26 : U8 = 1i64;
|
||||
let Test.27 : U8 = GetTagId Test.17;
|
||||
let Test.28 : Int1 = lowlevel Eq Test.26 Test.27;
|
||||
if Test.28 then
|
||||
let Test.22 : U64 = 0i64;
|
||||
ret Test.22;
|
||||
else
|
||||
let Test.18 : [<rnu><null>, C Str *self] = UnionAtIndex (Id 0) (Index 1) Test.17;
|
||||
joinpoint #Derived_gen.0:
|
||||
let Test.24 : U64 = 1i64;
|
||||
let Test.25 : U64 = CallByName Test.3 Test.18;
|
||||
let Test.23 : U64 = CallByName Num.19 Test.24 Test.25;
|
||||
ret Test.23;
|
||||
in
|
||||
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.17;
|
||||
if #Derived_gen.2 then
|
||||
let #Derived_gen.1 : Str = UnionAtIndex (Id 0) (Index 0) Test.17;
|
||||
dec #Derived_gen.1;
|
||||
decref Test.17;
|
||||
jump #Derived_gen.0;
|
||||
else
|
||||
inc Test.18;
|
||||
decref Test.17;
|
||||
jump #Derived_gen.0;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.5 : [<rnu><null>, C Str *self] = TagId(1) ;
|
||||
let Test.6 : [<rnu><null>, C Str *self] = TagId(1) ;
|
||||
let Test.19 : U64 = CallByName Test.2 Test.5 Test.6;
|
||||
ret Test.19;
|
|
@ -3014,6 +3014,39 @@ fn rb_tree_fbip() {
|
|||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn specialize_after_match() {
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
main =
|
||||
listA : LinkedList Str
|
||||
listA = Nil
|
||||
|
||||
listB : LinkedList Str
|
||||
listB = Nil
|
||||
longestLinkedList listA listB
|
||||
LinkedList a : [Cons a (LinkedList a), Nil]
|
||||
longestLinkedList : LinkedList a, LinkedList a -> Nat
|
||||
longestLinkedList = \listA, listB -> when listA is
|
||||
Nil -> linkedListLength listB
|
||||
Cons a aa -> when listB is
|
||||
Nil -> linkedListLength listA
|
||||
Cons b bb ->
|
||||
lengthA = (linkedListLength aa) + 1
|
||||
lengthB = linkedListLength listB
|
||||
if lengthA > lengthB
|
||||
then lengthA
|
||||
else lengthB
|
||||
|
||||
linkedListLength : LinkedList a -> Nat
|
||||
linkedListLength = \list -> when list is
|
||||
Nil -> 0
|
||||
Cons _ rest -> 1 + linkedListLength rest
|
||||
"#
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test]
|
||||
fn drop_specialize_after_struct() {
|
||||
indoc!(
|
||||
|
|
|
@ -5300,7 +5300,6 @@ pub struct CopiedImport {
|
|||
pub rigid: Vec<Variable>,
|
||||
pub flex_able: Vec<Variable>,
|
||||
pub rigid_able: Vec<Variable>,
|
||||
pub translations: Vec<(Variable, Variable)>,
|
||||
pub registered: Vec<Variable>,
|
||||
}
|
||||
|
||||
|
@ -5320,7 +5319,6 @@ struct CopyImportEnv<'a> {
|
|||
rigid: Vec<Variable>,
|
||||
flex_able: Vec<Variable>,
|
||||
rigid_able: Vec<Variable>,
|
||||
translations: Vec<(Variable, Variable)>,
|
||||
registered: Vec<Variable>,
|
||||
}
|
||||
|
||||
|
@ -5348,7 +5346,6 @@ pub fn copy_import_to(
|
|||
rigid: Vec::new(),
|
||||
flex_able: Vec::new(),
|
||||
rigid_able: Vec::new(),
|
||||
translations: Vec::new(),
|
||||
registered: Vec::new(),
|
||||
};
|
||||
|
||||
|
@ -5362,7 +5359,6 @@ pub fn copy_import_to(
|
|||
rigid,
|
||||
flex_able,
|
||||
rigid_able,
|
||||
translations,
|
||||
registered,
|
||||
target: _,
|
||||
bookkeep_unspecialized_lambda_sets: _,
|
||||
|
@ -5374,7 +5370,6 @@ pub fn copy_import_to(
|
|||
rigid,
|
||||
flex_able,
|
||||
rigid_able,
|
||||
translations,
|
||||
registered,
|
||||
}
|
||||
};
|
||||
|
@ -5668,13 +5663,21 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
|||
let name = env.source.field_names[name_index.index as usize].clone();
|
||||
let new_name_index = SubsIndex::push_new(&mut env.target.field_names, name);
|
||||
|
||||
env.target
|
||||
.set(copy, make_descriptor(RigidVar(new_name_index)));
|
||||
// If we are copying the import as generalized, we can keep it as rigid.
|
||||
// Otherwise we must make it flex, as this is copying to a non-generalized site.
|
||||
//
|
||||
// The rigid distinction is never necessary for imports, since their types have already
|
||||
// been checked completely.
|
||||
let content = if max_rank.is_generalized() {
|
||||
RigidVar(new_name_index)
|
||||
} else {
|
||||
FlexVar(Some(new_name_index))
|
||||
};
|
||||
|
||||
env.target.set(copy, make_descriptor(content));
|
||||
|
||||
env.rigid.push(copy);
|
||||
|
||||
env.translations.push((var, copy));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
|
@ -5687,15 +5690,21 @@ fn copy_import_to_help(env: &mut CopyImportEnv<'_>, max_rank: Rank, var: Variabl
|
|||
env.source.get_subs_slice(abilities).iter().copied(),
|
||||
);
|
||||
|
||||
env.target.set(
|
||||
copy,
|
||||
make_descriptor(RigidAbleVar(new_name_index, new_abilities)),
|
||||
);
|
||||
// If we are copying the import as generalized, we can keep it as rigid.
|
||||
// Otherwise we must make it flex, as this is copying to a non-generalized site.
|
||||
//
|
||||
// The rigid distinction is never necessary for imports, since their types have already
|
||||
// been checked completely.
|
||||
let content = if max_rank.is_generalized() {
|
||||
RigidAbleVar(new_name_index, new_abilities)
|
||||
} else {
|
||||
FlexAbleVar(Some(new_name_index), new_abilities)
|
||||
};
|
||||
|
||||
env.target.set(copy, make_descriptor(content));
|
||||
|
||||
env.rigid_able.push(copy);
|
||||
|
||||
env.translations.push((var, copy));
|
||||
|
||||
copy
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
main =
|
||||
\h -> Hash.hash h 1.1dec
|
||||
# ^^^^^^^^^ Hash#Hash.hash(1): a, Dec -[[Hash.hashDec(17)]]-> a | a has Hasher
|
|
@ -0,0 +1,16 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
|
||||
app "test" provides [limitedKind] to "./platform"
|
||||
|
||||
Kind : [ A, B, C ]
|
||||
Data : {kind: Kind}
|
||||
|
||||
limitedKind : Data -> Str
|
||||
limitedKind = \data ->
|
||||
when data is
|
||||
# ^^^^ { kind : [A, B, C] }
|
||||
{kind: A} -> "A is special"
|
||||
{kind} -> when kind is
|
||||
B -> "B"
|
||||
C -> "C"
|
||||
_ -> ""
|
|
@ -0,0 +1,16 @@
|
|||
# +opt infer:print_only_under_alias
|
||||
|
||||
app "test" provides [limitedKind] to "./platform"
|
||||
|
||||
Kind : [ A, B, C ]
|
||||
Data : ({}, Kind)
|
||||
|
||||
limitedKind : Data -> Str
|
||||
limitedKind = \data ->
|
||||
when data is
|
||||
# ^^^^ ( {}, [A, B, C] )*
|
||||
({}, A) -> "A is special"
|
||||
({}, kind) -> when kind is
|
||||
B -> "B"
|
||||
C -> "C"
|
||||
_ -> ""
|
|
@ -3,22 +3,22 @@
|
|||
app "test" provides [main] to "./platform"
|
||||
|
||||
f = \{} ->
|
||||
#^{-1} <1527><116>{} -<119>[[f(1)]]-> <115>[Ok <1535>{}]<79>*
|
||||
#^{-1} <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
|
||||
when g {} is
|
||||
# ^ <1517><1535>{} -<1525>[[g(2)]]-> <71>[Ok <1535>{}]<101>*
|
||||
# ^ <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
|
||||
_ -> Ok {}
|
||||
|
||||
g = \{} ->
|
||||
#^{-1} <1517><1535>{} -<1525>[[g(2)]]-> <71>[Ok <1535>{}]<101>*
|
||||
#^{-1} <1589><1607>{} -<1597>[[g(2)]]-> <71>[Ok <1607>{}]<101>*
|
||||
when h {} is
|
||||
# ^ <1522><1535>{} -<1530>[[h(3)]]-> <93>[Ok <1535>{}]<123>*
|
||||
# ^ <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
|
||||
_ -> Ok {}
|
||||
|
||||
h = \{} ->
|
||||
#^{-1} <1522><1535>{} -<1530>[[h(3)]]-> <93>[Ok <1535>{}]<123>*
|
||||
#^{-1} <1594><1607>{} -<1602>[[h(3)]]-> <93>[Ok <1607>{}]<123>*
|
||||
when f {} is
|
||||
# ^ <1527><116>{} -<119>[[f(1)]]-> <115>[Ok <1535>{}]<79>*
|
||||
# ^ <1599><116>{} -<119>[[f(1)]]-> <115>[Ok <1607>{}]<79>*
|
||||
_ -> Ok {}
|
||||
|
||||
main = f {}
|
||||
# ^ <1537><132>{} -<135>[[f(1)]]-> <137>[Ok <1535>{}]<1536>w_a
|
||||
# ^ <1609><132>{} -<135>[[f(1)]]-> <137>[Ok <1607>{}]<1608>w_a
|
||||
|
|
5
crates/compiler/uitest/tests/solve/tuple_annotation.txt
Normal file
5
crates/compiler/uitest/tests/solve/tuple_annotation.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
interface Test exposes [x] imports []
|
||||
|
||||
x : (I64, Str)
|
||||
x = (1, "")
|
||||
#^{-1} ( I64, Str )*
|
Loading…
Add table
Add a link
Reference in a new issue