Merge remote-tracking branch 'origin/trunk' into list-unreachable

This commit is contained in:
Folkert 2022-07-06 01:38:50 +02:00
commit 3a30e77726
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
57 changed files with 975 additions and 520 deletions

View file

@ -758,34 +758,6 @@ fn call_spec(
add_loop(builder, block, state_type, init_state, loop_body)
}
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
ListMapWithIndex { xs } => {
let list = env.symbols[xs];
let loop_body = |builder: &mut FuncDefBuilder, block, state| {
let input_bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?;
let element = builder.add_bag_get(block, input_bag)?;
let index = builder.add_make_tuple(block, &[])?;
// before, Nat -> after
let new_element = call_function!(builder, block, [element, index]);
list_append(builder, block, update_mode_var, state, new_element)
};
let output_element_type =
layout_spec(builder, return_layout, &WhenRecursive::Unreachable)?;
let state_layout = Layout::Builtin(Builtin::List(return_layout));
let state_type =
layout_spec(builder, &state_layout, &WhenRecursive::Unreachable)?;
let init_state = new_list(builder, block, output_element_type)?;
add_loop(builder, block, state_type, init_state, loop_body)
}
ListMap { xs } => {
let list = env.symbols[xs];

View file

@ -65,6 +65,16 @@ pub fn link(
}
fn find_zig_str_path() -> PathBuf {
// First try using the lib path relative to the executable location.
let exe_relative_str_path = std::env::current_exe()
.ok()
.and_then(|path| Some(path.parent()?.join("lib").join("str.zig")));
if let Some(exe_relative_str_path) = exe_relative_str_path {
if std::path::Path::exists(&exe_relative_str_path) {
return exe_relative_str_path;
}
}
let zig_str_path = PathBuf::from("crates/compiler/builtins/bitcode/src/str.zig");
if std::path::Path::exists(&zig_str_path) {

View file

@ -16,6 +16,7 @@ lazy_static = "1.4.0"
[build-dependencies]
# dunce can be removed once ziglang/zig#5109 is fixed
dunce = "1.0.2"
fs_extra = "1.2.0"
[target.'cfg(target_os = "macos")'.build-dependencies]
tempfile = "3.2.0"

View file

@ -227,38 +227,6 @@ pub fn listMap(
}
}
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
pub fn listMapWithIndex(
list: RocList,
caller: Caller2,
data: Opaque,
inc_n_data: IncN,
data_is_owned: bool,
alignment: u32,
old_element_width: usize,
new_element_width: usize,
) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
var i: usize = 0;
const output = RocList.allocate(alignment, size, new_element_width);
const target_ptr = output.bytes orelse unreachable;
if (data_is_owned) {
inc_n_data(data, size);
}
while (i < size) : (i += 1) {
// before, Nat -> after
caller(data, source_ptr + (i * old_element_width), @ptrCast(?[*]u8, &i), target_ptr + (i * new_element_width));
}
return output;
} else {
return RocList.empty();
}
}
fn decrementTail(list: RocList, start_index: usize, element_width: usize, dec: Dec) void {
if (list.bytes) |source| {
var i = start_index;

View file

@ -40,7 +40,6 @@ comptime {
exportListFn(list.listMap2, "map2");
exportListFn(list.listMap3, "map3");
exportListFn(list.listMap4, "map4");
exportListFn(list.listMapWithIndex, "map_with_index");
exportListFn(list.listAppend, "append");
exportListFn(list.listPrepend, "prepend");
exportListFn(list.listWithCapacity, "with_capacity");
@ -147,6 +146,7 @@ comptime {
exportStrFn(str.strSplitInPlaceC, "str_split_in_place");
exportStrFn(str.countSegments, "count_segments");
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
exportStrFn(str.countUtf8Bytes, "count_utf8_bytes");
exportStrFn(str.startsWith, "starts_with");
exportStrFn(str.startsWithScalar, "starts_with_scalar");
exportStrFn(str.endsWith, "ends_with");
@ -155,6 +155,11 @@ comptime {
exportStrFn(str.strNumberOfBytes, "number_of_bytes");
exportStrFn(str.strFromFloatC, "from_float");
exportStrFn(str.strEqual, "equal");
exportStrFn(str.substringUnsafe, "substring_unsafe");
exportStrFn(str.getUnsafe, "get_unsafe");
exportStrFn(str.reserve, "reserve");
exportStrFn(str.getScalarUnsafe, "get_scalar_unsafe");
exportStrFn(str.appendScalar, "append_scalar");
exportStrFn(str.strToUtf8C, "to_utf8");
exportStrFn(str.fromUtf8C, "from_utf8");
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");

View file

@ -112,7 +112,7 @@ pub const RocStr = extern struct {
pub fn eq(self: RocStr, other: RocStr) bool {
// If they are byte-for-byte equal, they're definitely equal!
if (self.str_bytes == other.str_bytes and self.str_len == other.str_len) {
if (self.str_bytes == other.str_bytes and self.str_len == other.str_len and self.str_capacity == other.str_capacity) {
return true;
}
@ -173,10 +173,10 @@ pub const RocStr = extern struct {
const element_width = 1;
if (self.str_bytes) |source_ptr| {
if (self.isUnique()) {
if (self.isUnique() and !self.isSmallStr()) {
const new_source = utils.unsafeReallocate(source_ptr, RocStr.alignment, self.len(), new_length, element_width);
return RocStr{ .str_bytes = new_source, .str_len = new_length };
return RocStr{ .str_bytes = new_source, .str_len = new_length, .str_capacity = new_length };
}
}
@ -228,7 +228,11 @@ pub const RocStr = extern struct {
}
pub fn capacity(self: RocStr) usize {
return self.str_capacity ^ MASK;
if (self.isSmallStr()) {
return SMALL_STR_MAX_LENGTH;
} else {
return self.str_capacity;
}
}
// This does a small string check, but no bounds checking whatsoever!
@ -311,6 +315,10 @@ pub const RocStr = extern struct {
return self.asU8ptr()[0..self.len()];
}
pub fn asSliceWithCapacity(self: RocStr) []u8 {
return self.asU8ptr()[0..self.capacity()];
}
pub fn asU8ptr(self: RocStr) [*]u8 {
// Since this conditional would be prone to branch misprediction,
@ -1199,6 +1207,56 @@ test "countGraphemeClusters: emojis, ut8, and ascii characters" {
try expectEqual(count, 10);
}
pub fn countUtf8Bytes(string: RocStr) callconv(.C) usize {
return string.len();
}
pub fn substringUnsafe(string: RocStr, start: usize, length: usize) callconv(.C) RocStr {
const slice = string.asSlice()[start .. start + length];
return RocStr.fromSlice(slice);
}
pub fn getUnsafe(string: RocStr, index: usize) callconv(.C) u8 {
return string.getUnchecked(index);
}
test "substringUnsafe: start" {
const str = RocStr.fromSlice("abcdef");
defer str.deinit();
const expected = RocStr.fromSlice("abc");
defer expected.deinit();
const actual = substringUnsafe(str, 0, 3);
try expect(RocStr.eq(actual, expected));
}
test "substringUnsafe: middle" {
const str = RocStr.fromSlice("abcdef");
defer str.deinit();
const expected = RocStr.fromSlice("bcd");
defer expected.deinit();
const actual = substringUnsafe(str, 1, 3);
try expect(RocStr.eq(actual, expected));
}
test "substringUnsafe: end" {
const str = RocStr.fromSlice("a string so long it is heap-allocated");
defer str.deinit();
const expected = RocStr.fromSlice("heap-allocated");
defer expected.deinit();
const actual = substringUnsafe(str, 23, 37 - 23);
try expect(RocStr.eq(actual, expected));
}
// Str.startsWith
pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
const bytes_len = string.len();
@ -2315,3 +2373,119 @@ test "ReverseUtf8View: empty" {
try expect(false);
}
}
test "capacity: small string" {
const data_bytes = "foobar";
var data = RocStr.init(data_bytes, data_bytes.len);
defer data.deinit();
try expectEqual(data.capacity(), SMALL_STR_MAX_LENGTH);
}
test "capacity: big string" {
const data_bytes = "a string so large that it must be heap-allocated";
var data = RocStr.init(data_bytes, data_bytes.len);
defer data.deinit();
try expectEqual(data.capacity(), data_bytes.len);
}
pub fn appendScalar(string: RocStr, scalar_u32: u32) callconv(.C) RocStr {
const scalar = @intCast(u21, scalar_u32);
const width = std.unicode.utf8CodepointSequenceLength(scalar) catch unreachable;
var output = string.reallocate(string.len() + width);
var slice = output.asSliceWithCapacity();
_ = std.unicode.utf8Encode(scalar, slice[string.len() .. string.len() + width]) catch unreachable;
return output;
}
test "appendScalar: small A" {
const A: []const u8 = "A";
const data_bytes = "hello";
var data = RocStr.init(data_bytes, data_bytes.len);
const actual = appendScalar(data, A[0]);
defer actual.deinit();
const expected_bytes = "helloA";
const expected = RocStr.init(expected_bytes, expected_bytes.len);
defer expected.deinit();
try expect(actual.eq(expected));
}
test "appendScalar: small 😀" {
const data_bytes = "hello";
var data = RocStr.init(data_bytes, data_bytes.len);
const actual = appendScalar(data, 0x1F600);
defer actual.deinit();
const expected_bytes = "hello😀";
const expected = RocStr.init(expected_bytes, expected_bytes.len);
defer expected.deinit();
try expect(actual.eq(expected));
}
test "appendScalar: big A" {
const A: []const u8 = "A";
const data_bytes = "a string so large that it must be heap-allocated";
var data = RocStr.init(data_bytes, data_bytes.len);
const actual = appendScalar(data, A[0]);
defer actual.deinit();
const expected_bytes = "a string so large that it must be heap-allocatedA";
const expected = RocStr.init(expected_bytes, expected_bytes.len);
defer expected.deinit();
try expect(actual.eq(expected));
}
test "appendScalar: big 😀" {
const data_bytes = "a string so large that it must be heap-allocated";
var data = RocStr.init(data_bytes, data_bytes.len);
const actual = appendScalar(data, 0x1F600);
defer actual.deinit();
const expected_bytes = "a string so large that it must be heap-allocated😀";
const expected = RocStr.init(expected_bytes, expected_bytes.len);
defer expected.deinit();
try expect(actual.eq(expected));
}
pub fn reserve(string: RocStr, capacity: usize) callconv(.C) RocStr {
if (capacity > string.capacity()) {
return string.reallocate(capacity);
} else {
return string;
}
}
pub fn getScalarUnsafe(string: RocStr, index: usize) callconv(.C) extern struct { bytesParsed: usize, scalar: u32 } {
const slice = string.asSlice();
const bytesParsed = @intCast(usize, std.unicode.utf8ByteSequenceLength(slice[index]) catch unreachable);
const scalar = std.unicode.utf8Decode(slice[index .. index + bytesParsed]) catch unreachable;
return .{ .bytesParsed = bytesParsed, .scalar = @intCast(u32, scalar) };
}
test "getScalarUnsafe" {
const data_bytes = "A";
var data = RocStr.init(data_bytes, data_bytes.len);
const result = getScalarUnsafe(data, 0);
const expected = try std.unicode.utf8Decode("A");
try expectEqual(result.scalar, @intCast(u32, expected));
try expectEqual(result.bytesParsed, 1);
}

View file

@ -256,6 +256,10 @@ pub fn unsafeReallocate(
const old_width = align_width + old_length * element_width;
const new_width = align_width + new_length * element_width;
if (old_width == new_width) {
return source_ptr;
}
// TODO handle out of memory
// NOTE realloc will dealloc the original allocation
const old_allocation = source_ptr - align_width;

View file

@ -67,6 +67,8 @@ fn main() {
"builtins-wasm32.o",
);
copy_zig_builtins_to_target_dir(&bitcode_path);
get_zig_files(bitcode_path.as_path(), &|path| {
let path: &Path = path;
println!(
@ -144,6 +146,39 @@ fn generate_bc_file(bitcode_path: &Path, zig_object: &str, file_name: &str) {
);
}
fn copy_zig_builtins_to_target_dir(bitcode_path: &Path) {
// To enable roc to find the zig biultins, we want them to be moved to a folder next to the roc executable.
// So if <roc_folder>/roc is the executable. The zig files will be in <roc_folder>/lib/*.zig
// Currently we have the OUT_DIR variable which points to `/target/debug/build/roc_builtins-*/out/`.
// So we just need to shed a 3 of the outer layers to get `/target/debug/` and then add `lib`.
let out_dir = env::var_os("OUT_DIR").unwrap();
let target_profile_dir = Path::new(&out_dir)
.parent()
.and_then(|path| path.parent())
.and_then(|path| path.parent())
.unwrap()
.join("lib");
let zig_src_dir = bitcode_path.join("src");
std::fs::create_dir_all(&target_profile_dir).unwrap_or_else(|err| {
panic!(
"Failed to create output library directory for zig bitcode {:?}: {:?}",
target_profile_dir, err
);
});
let mut options = fs_extra::dir::CopyOptions::new();
options.content_only = true;
options.overwrite = true;
fs_extra::dir::copy(&zig_src_dir, &target_profile_dir, &options).unwrap_or_else(|err| {
panic!(
"Failed to copy zig bitcode files {:?} to {:?}: {:?}",
zig_src_dir, target_profile_dir, err
);
});
}
fn run_command<S, I: Copy, P: AsRef<Path> + Copy>(path: P, command_str: &str, args: I)
where
I: IntoIterator<Item = S>,

View file

@ -564,6 +564,23 @@ map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
## This works like [List.map], except it also passes the index
## of the element to the conversion function.
mapWithIndex : List a, (a, Nat -> b) -> List b
mapWithIndex = \src, func ->
length = len src
dest = withCapacity length
mapWithIndexHelp src dest func 0 length
# Internal helper
mapWithIndexHelp : List a, List b, (a, Nat -> b), Nat, Nat -> List b
mapWithIndexHelp = \src, dest, func, index, length ->
if index < length then
elem = getUnsafe src index
mappedElem = func elem index
newDest = append dest mappedElem
mapWithIndexHelp src newDest func (index + 1) length
else
dest
## Returns a list of all the integers between one and another,
## including both of the given numbers.

View file

@ -9,6 +9,7 @@ interface Str
split,
repeat,
countGraphemes,
countUtf8Bytes,
startsWithScalar,
toUtf8,
fromUtf8,
@ -33,6 +34,13 @@ interface Str
toU8,
toI8,
toScalars,
splitFirst,
splitLast,
walkUtf8WithIndex,
reserve,
appendScalar,
walkScalars,
walkScalarsUntil,
]
imports [Bool.{ Bool }, Result.{ Result }]
@ -221,3 +229,169 @@ toU16 : Str -> Result U16 [InvalidNumStr]*
toI16 : Str -> Result I16 [InvalidNumStr]*
toU8 : Str -> Result U8 [InvalidNumStr]*
toI8 : Str -> Result I8 [InvalidNumStr]*
## Gets the byte at the given index, without performing a bounds check
getUnsafe : Str, Nat -> U8
## gives the number of string bytes
countUtf8Bytes : Str -> Nat
## string slice that does not do bounds checking or utf-8 verification
substringUnsafe : Str, Nat, Nat -> Str
## Returns the string before the first occurrence of a delimiter, as well as the
## rest of the string after that occurrence. If the delimiter is not found, returns `Err`.
##
## Str.splitFirst "foo/bar/baz" "/" == Ok { before: "foo", after: "bar/baz" }
splitFirst : Str, Str -> Result { before : Str, after : Str } [NotFound]*
splitFirst = \haystack, needle ->
when firstMatch haystack needle is
Some index ->
remaining = Str.countUtf8Bytes haystack - Str.countUtf8Bytes needle - index
before = Str.substringUnsafe haystack 0 index
after = Str.substringUnsafe haystack (index + Str.countUtf8Bytes needle) remaining
Ok { before, after }
None ->
Err NotFound
firstMatch : Str, Str -> [Some Nat, None]
firstMatch = \haystack, needle ->
haystackLength = Str.countUtf8Bytes haystack
needleLength = Str.countUtf8Bytes needle
lastPossible = Num.subSaturated haystackLength needleLength
firstMatchHelp haystack needle 0 lastPossible
firstMatchHelp : Str, Str, Nat, Nat -> [Some Nat, None]
firstMatchHelp = \haystack, needle, index, lastPossible ->
if index < lastPossible then
if matchesAt haystack index needle then
Some index
else
firstMatchHelp haystack needle (index + 1) lastPossible
else
None
## Returns the string before the last occurrence of a delimiter, as well as the
## rest of the string after that occurrence. If the delimiter is not found, returns `Err`.
##
## Str.splitLast "foo/bar/baz" "/" == Ok { before: "foo/bar", after: "baz" }
splitLast : Str, Str -> Result { before : Str, after : Str } [NotFound]*
splitLast = \haystack, needle ->
when lastMatch haystack needle is
Some index ->
remaining = Str.countUtf8Bytes haystack - Str.countUtf8Bytes needle - index
before = Str.substringUnsafe haystack 0 index
after = Str.substringUnsafe haystack (index + Str.countUtf8Bytes needle) remaining
Ok { before, after }
None ->
Err NotFound
lastMatch : Str, Str -> [Some Nat, None]
lastMatch = \haystack, needle ->
haystackLength = Str.countUtf8Bytes haystack
needleLength = Str.countUtf8Bytes needle
lastPossibleIndex = Num.subSaturated haystackLength (needleLength + 1)
lastMatchHelp haystack needle lastPossibleIndex
lastMatchHelp : Str, Str, Nat -> [Some Nat, None]
lastMatchHelp = \haystack, needle, index ->
if matchesAt haystack index needle then
Some index
else
when Num.subChecked index 1 is
Ok nextIndex ->
lastMatchHelp haystack needle nextIndex
Err _ ->
None
min = \x, y -> if x < y then x else y
matchesAt : Str, Nat, Str -> Bool
matchesAt = \haystack, haystackIndex, needle ->
haystackLength = Str.countUtf8Bytes haystack
needleLength = Str.countUtf8Bytes needle
endIndex = min (haystackIndex + needleLength) haystackLength
matchesAtHelp haystack haystackIndex needle 0 endIndex
matchesAtHelp : Str, Nat, Str, Nat, Nat -> Bool
matchesAtHelp = \haystack, haystackIndex, needle, needleIndex, endIndex ->
if haystackIndex < endIndex then
if Str.getUnsafe haystack haystackIndex == Str.getUnsafe needle needleIndex then
matchesAtHelp haystack (haystackIndex + 1) needle (needleIndex + 1) endIndex
else
False
else
True
## Walks over the string's UTF-8 bytes, calling a function which updates a state using each
## UTF-8 `U8` byte as well as the index of that byte within the string.
walkUtf8WithIndex : Str, state, (state, U8, Nat -> state) -> state
walkUtf8WithIndex = \string, state, step ->
walkUtf8WithIndexHelp string state step 0 (Str.countUtf8Bytes string)
walkUtf8WithIndexHelp : Str, state, (state, U8, Nat -> state), Nat, Nat -> state
walkUtf8WithIndexHelp = \string, state, step, index, length ->
if index < length then
byte = Str.getUnsafe string index
newState = step state byte index
walkUtf8WithIndexHelp string newState step (index + 1) length
else
state
## Make sure at least some number of bytes fit in this string without reallocating
reserve : Str, Nat -> Str
## is UB when the scalar is invalid
appendScalarUnsafe : Str, U32 -> Str
appendScalar : Str, U32 -> Result Str [InvalidScalar]*
appendScalar = \string, scalar ->
if isValidScalar scalar then
Ok (appendScalarUnsafe string scalar)
else
Err InvalidScalar
isValidScalar : U32 -> Bool
isValidScalar = \scalar ->
scalar <= 0xD7FF || (scalar >= 0xE000 && scalar <= 0x10FFFF)
getScalarUnsafe : Str, Nat -> { scalar : U32, bytesParsed : Nat }
walkScalars : Str, state, (state, U32 -> state) -> state
walkScalars = \string, init, step ->
walkScalarsHelp string init step 0 (Str.countUtf8Bytes string)
walkScalarsHelp : Str, state, (state, U32 -> state), Nat, Nat -> state
walkScalarsHelp = \string, state, step, index, length ->
if index < length then
{ scalar, bytesParsed } = getScalarUnsafe string index
newState = step state scalar
walkScalarsHelp string newState step (index + bytesParsed) length
else
state
walkScalarsUntil : Str, state, (state, U32 -> [Break state, Continue state]) -> state
walkScalarsUntil = \string, init, step ->
walkScalarsUntilHelp string init step 0 (Str.countUtf8Bytes string)
walkScalarsUntilHelp : Str, state, (state, U32 -> [Break state, Continue state]), Nat, Nat -> state
walkScalarsUntilHelp = \string, state, step, index, length ->
if index < length then
{ scalar, bytesParsed } = getScalarUnsafe string index
when step state scalar is
Continue newState ->
walkScalarsUntilHelp string newState step (index + bytesParsed) length
Break newState ->
newState
else
state

View file

@ -314,6 +314,7 @@ pub const STR_STR_SPLIT: &str = "roc_builtins.str.str_split";
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
pub const STR_TO_SCALARS: &str = "roc_builtins.str.to_scalars";
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
pub const STR_COUNT_UTF8_BYTES: &str = "roc_builtins.str.count_utf8_bytes";
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
pub const STR_STARTS_WITH_SCALAR: &str = "roc_builtins.str.starts_with_scalar";
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
@ -324,6 +325,7 @@ pub const STR_TO_INT: IntrinsicName = int_intrinsic!("roc_builtins.str.to_int");
pub const STR_TO_FLOAT: IntrinsicName = float_intrinsic!("roc_builtins.str.to_float");
pub const STR_TO_DECIMAL: &str = "roc_builtins.str.to_decimal";
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
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_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
@ -331,6 +333,10 @@ pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
pub const STR_TRIM: &str = "roc_builtins.str.trim";
pub const STR_TRIM_LEFT: &str = "roc_builtins.str.trim_left";
pub const STR_TRIM_RIGHT: &str = "roc_builtins.str.trim_right";
pub const STR_GET_UNSAFE: &str = "roc_builtins.str.get_unsafe";
pub const STR_RESERVE: &str = "roc_builtins.str.reserve";
pub const STR_APPEND_SCALAR: &str = "roc_builtins.str.append_scalar";
pub const STR_GET_SCALAR_UNSAFE: &str = "roc_builtins.str.get_scalar_unsafe";
pub const DICT_HASH: &str = "roc_builtins.dict.hash";
pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str";
@ -354,7 +360,6 @@ pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
pub const LIST_MAP3: &str = "roc_builtins.list.map3";
pub const LIST_MAP4: &str = "roc_builtins.list.map4";
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
pub const LIST_APPEND: &str = "roc_builtins.list.append";
pub const LIST_PREPEND: &str = "roc_builtins.list.prepend";
pub const LIST_SUBLIST: &str = "roc_builtins.list.sublist";

View file

@ -74,12 +74,18 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
STR_CONCAT => str_concat,
STR_JOIN_WITH => str_join_with,
STR_TO_SCALARS => str_to_scalars,
STR_GET_UNSAFE => str_get_unsafe,
STR_SPLIT => str_split,
STR_IS_EMPTY => str_is_empty,
STR_STARTS_WITH => str_starts_with,
STR_STARTS_WITH_SCALAR => str_starts_with_scalar,
STR_ENDS_WITH => str_ends_with,
STR_COUNT_GRAPHEMES => str_count_graphemes,
STR_COUNT_UTF8_BYTES => str_count_bytes,
STR_SUBSTRING_UNSAFE => str_substring_unsafe,
STR_RESERVE => str_reserve,
STR_APPEND_SCALAR_UNSAFE => str_append_scalar_unsafe,
STR_GET_SCALAR_UNSAFE => str_get_scalar_unsafe,
STR_FROM_UTF8 => str_from_utf8,
STR_FROM_UTF8_RANGE => str_from_utf8_range,
STR_TO_UTF8 => str_to_utf8,
@ -119,7 +125,6 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_DROP => list_drop,
LIST_DROP_AT => list_drop_at,
LIST_SWAP => list_swap,
LIST_MAP_WITH_INDEX => list_map_with_index,
LIST_SORT_WITH => list_sort_with,
LIST_IS_UNIQUE => list_is_unique,
DICT_LEN => dict_len,
@ -1670,67 +1675,24 @@ fn str_concat(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// List.getUnsafe : Str, Nat -> U8
fn str_get_unsafe(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::StrGetUnsafe, var_store)
}
/// Str.toScalars : Str -> List U32
fn str_to_scalars(symbol: Symbol, var_store: &mut VarStore) -> Def {
let str_var = var_store.fresh();
let list_u32_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::StrToScalars,
args: vec![(str_var, Var(Symbol::ARG_1))],
ret_var: list_u32_var,
};
defn(
symbol,
vec![(str_var, Symbol::ARG_1)],
var_store,
body,
list_u32_var,
)
lowlevel_1(symbol, LowLevel::StrToScalars, var_store)
}
/// Str.joinWith : List Str, Str -> Str
fn str_join_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_str_var = var_store.fresh();
let str_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::StrJoinWith,
args: vec![
(list_str_var, Var(Symbol::ARG_1)),
(str_var, Var(Symbol::ARG_2)),
],
ret_var: str_var,
};
defn(
symbol,
vec![(list_str_var, Symbol::ARG_1), (str_var, Symbol::ARG_2)],
var_store,
body,
str_var,
)
lowlevel_2(symbol, LowLevel::StrJoinWith, var_store)
}
/// Str.isEmpty : Str -> Bool
fn str_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
let str_var = var_store.fresh();
let bool_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::StrIsEmpty,
args: vec![(str_var, Var(Symbol::ARG_1))],
ret_var: bool_var,
};
defn(
symbol,
vec![(str_var, Symbol::ARG_1)],
var_store,
body,
bool_var,
)
lowlevel_1(symbol, LowLevel::StrIsEmpty, var_store)
}
/// Str.startsWith : Str, Str -> Bool
@ -1765,22 +1727,32 @@ fn str_ends_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
/// Str.countGraphemes : Str -> Int
fn str_count_graphemes(symbol: Symbol, var_store: &mut VarStore) -> Def {
let str_var = var_store.fresh();
let int_var = var_store.fresh();
lowlevel_1(symbol, LowLevel::StrCountGraphemes, var_store)
}
let body = RunLowLevel {
op: LowLevel::StrCountGraphemes,
args: vec![(str_var, Var(Symbol::ARG_1))],
ret_var: int_var,
};
/// Str.countUtf8Bytes : Str -> Nat
fn str_count_bytes(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::StrCountUtf8Bytes, var_store)
}
defn(
symbol,
vec![(str_var, Symbol::ARG_1)],
var_store,
body,
int_var,
)
/// Str.substringUnsafe : Str, Nat, Nat -> Nat
fn str_substring_unsafe(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_3(symbol, LowLevel::StrSubstringUnsafe, var_store)
}
/// Str.reserve : Str, Nat -> Str
fn str_reserve(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::StrReserve, var_store)
}
/// Str.appendScalarUnsafe : Str, U32 -> Str
fn str_append_scalar_unsafe(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::StrAppendScalar, var_store)
}
/// Str.getScalarUnsafe : Str, Nat -> { scalar : U32, bytesParsed : Nat }
fn str_get_scalar_unsafe(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::StrGetScalarUnsafe, var_store)
}
/// Str.fromUtf8 : List U8 -> Result Str [BadUtf8 { byteIndex : Nat, problem : Utf8Problem } }]*
@ -2398,11 +2370,6 @@ fn list_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListMap, var_store)
}
/// List.mapWithIndex : List before, (before, Nat -> after) -> List after
fn list_map_with_index(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListMapWithIndex, var_store)
}
/// List.map2 : List a, List b, (a, b -> c) -> List c
fn list_map2(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_3(symbol, LowLevel::ListMap2, var_store)

View file

@ -29,7 +29,7 @@ impl Length {
} else if self.0 > 0 {
Kind::Interned(self.0 as usize)
} else {
Kind::Generated(self.0.abs() as usize)
Kind::Generated(self.0.unsigned_abs() as usize)
}
}

View file

@ -9,9 +9,9 @@ use crate::llvm::build_dict::{
use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
self, allocate_list, empty_polymorphic_list, list_append, list_concat, list_drop_at,
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_map_with_index,
list_prepend, list_replace_unsafe, list_sort_with, list_sublist, list_swap,
list_symbol_to_c_abi, list_to_c_abi, list_with_capacity,
list_get_unsafe, list_len, list_map, list_map2, list_map3, list_map4, list_prepend,
list_replace_unsafe, list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi,
list_to_c_abi, list_with_capacity,
};
use crate::llvm::build_str::{
str_from_float, str_from_int, str_from_utf8, str_from_utf8_range, str_split,
@ -5083,35 +5083,6 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
_ => unreachable!("invalid list layout"),
}
}
ListMapWithIndex { xs } => {
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, xs);
let (function, closure, closure_layout) = function_details!();
match (list_layout, return_layout) {
(
Layout::Builtin(Builtin::List(element_layout)),
Layout::Builtin(Builtin::List(result_layout)),
) => {
let argument_layouts = &[**element_layout, Layout::usize(env.target_info)];
let roc_function_call = roc_function_call(
env,
layout_ids,
function,
closure,
closure_layout,
function_owns_closure_data,
argument_layouts,
**result_layout,
);
list_map_with_index(env, roc_function_call, list, element_layout, result_layout)
}
_ => unreachable!("invalid list layout"),
}
}
ListSortWith { xs } => {
// List.sortWith : List a, (a, a -> Ordering) -> List a
let (list, list_layout) = load_symbol_and_layout(scope, xs);
@ -5365,12 +5336,52 @@ fn run_low_level<'a, 'ctx, 'env>(
BasicValueEnum::IntValue(is_zero)
}
StrCountGraphemes => {
// Str.countGraphemes : Str -> Int
// Str.countGraphemes : Str -> Nat
debug_assert_eq!(args.len(), 1);
let string = load_symbol(scope, &args[0]);
call_bitcode_fn(env, &[string], bitcode::STR_COUNT_GRAPEHEME_CLUSTERS)
}
StrGetScalarUnsafe => {
// Str.getScalarUnsafe : Str, Nat -> { bytesParsed : Nat, scalar : U32 }
debug_assert_eq!(args.len(), 2);
let string = load_symbol(scope, &args[0]);
let index = load_symbol(scope, &args[1]);
call_bitcode_fn(env, &[string, index], bitcode::STR_GET_SCALAR_UNSAFE)
}
StrCountUtf8Bytes => {
// Str.countGraphemes : Str -> Nat
debug_assert_eq!(args.len(), 1);
let string = load_symbol(scope, &args[0]);
call_bitcode_fn(env, &[string], bitcode::STR_COUNT_UTF8_BYTES)
}
StrSubstringUnsafe => {
// Str.substringUnsafe : Str, Nat, Nat -> Str
debug_assert_eq!(args.len(), 3);
let string = load_symbol(scope, &args[0]);
let start = load_symbol(scope, &args[1]);
let length = load_symbol(scope, &args[2]);
call_str_bitcode_fn(env, &[string, start, length], bitcode::STR_SUBSTRING_UNSAFE)
}
StrReserve => {
// Str.reserve : Str, Nat -> Str
debug_assert_eq!(args.len(), 2);
let string = load_symbol(scope, &args[0]);
let capacity = load_symbol(scope, &args[1]);
call_str_bitcode_fn(env, &[string, capacity], bitcode::STR_RESERVE)
}
StrAppendScalar => {
// Str.appendScalar : Str, U32 -> Str
debug_assert_eq!(args.len(), 2);
let string = load_symbol(scope, &args[0]);
let capacity = load_symbol(scope, &args[1]);
call_str_bitcode_fn(env, &[string, capacity], bitcode::STR_APPEND_SCALAR)
}
StrTrim => {
// Str.trim : Str -> Str
debug_assert_eq!(args.len(), 1);
@ -5499,8 +5510,17 @@ fn run_low_level<'a, 'ctx, 'env>(
list_prepend(env, original_wrapper, elem, elem_layout)
}
StrGetUnsafe => {
// List.getUnsafe : List elem, Nat -> elem
debug_assert_eq!(args.len(), 2);
let wrapper_struct = load_symbol(scope, &args[0]);
let elem_index = load_symbol(scope, &args[1]);
call_bitcode_fn(env, &[wrapper_struct, elem_index], bitcode::STR_GET_UNSAFE)
}
ListGetUnsafe => {
// List.get : List elem, Nat -> [Ok elem, OutOfBounds]*
// List.getUnsafe : List elem, Nat -> elem
debug_assert_eq!(args.len(), 2);
let (wrapper_struct, list_layout) = load_symbol_and_layout(scope, &args[0]);
@ -5976,7 +5996,7 @@ fn run_low_level<'a, 'ctx, 'env>(
set
}
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListSortWith | DictWalk => {
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith | DictWalk => {
unreachable!("these are higher order, and are handled elsewhere")
}

View file

@ -367,30 +367,6 @@ pub fn list_sort_with<'a, 'ctx, 'env>(
)
}
/// List.mapWithIndex : List before, (before, Nat -> after) -> List after
pub fn list_map_with_index<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
roc_function_call: RocFunctionCall<'ctx>,
list: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
return_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
call_list_bitcode_fn(
env,
&[
list_to_c_abi(env, list).into(),
roc_function_call.caller.into(),
pass_as_opaque(env, roc_function_call.data),
roc_function_call.inc_n_data.into(),
roc_function_call.data_is_owned.into(),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
layout_width(env, return_layout),
],
bitcode::LIST_MAP_WITH_INDEX,
)
}
/// List.map : List before, (before -> after) -> List after
pub fn list_map<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -218,6 +218,7 @@ impl<'a> LowLevelCall<'a> {
// Str
StrConcat => self.load_args_and_call_zig(backend, bitcode::STR_CONCAT),
StrToScalars => self.load_args_and_call_zig(backend, bitcode::STR_TO_SCALARS),
StrGetUnsafe => self.load_args_and_call_zig(backend, bitcode::STR_GET_UNSAFE),
StrJoinWith => self.load_args_and_call_zig(backend, bitcode::STR_JOIN_WITH),
StrIsEmpty => match backend.storage.get(&self.arguments[0]) {
StoredValue::StackMemory { location, .. } => {
@ -239,6 +240,9 @@ impl<'a> LowLevelCall<'a> {
StrCountGraphemes => {
self.load_args_and_call_zig(backend, bitcode::STR_COUNT_GRAPEHEME_CLUSTERS)
}
StrCountUtf8Bytes => {
self.load_args_and_call_zig(backend, bitcode::STR_COUNT_UTF8_BYTES)
}
StrToNum => {
let number_layout = match self.ret_layout {
Layout::Struct { field_layouts, .. } => field_layouts[0],
@ -285,8 +289,16 @@ impl<'a> LowLevelCall<'a> {
StrTrimLeft => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_LEFT),
StrTrimRight => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_RIGHT),
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
StrReserve => self.load_args_and_call_zig(backend, bitcode::STR_RESERVE),
StrRepeat => self.load_args_and_call_zig(backend, bitcode::STR_REPEAT),
StrAppendScalar => self.load_args_and_call_zig(backend, bitcode::STR_APPEND_SCALAR),
StrTrim => self.load_args_and_call_zig(backend, bitcode::STR_TRIM),
StrGetScalarUnsafe => {
self.load_args_and_call_zig(backend, bitcode::STR_GET_SCALAR_UNSAFE)
}
StrSubstringUnsafe => {
self.load_args_and_call_zig(backend, bitcode::STR_SUBSTRING_UNSAFE)
}
// List
ListLen => match backend.storage.get(&self.arguments[0]) {
@ -301,8 +313,7 @@ impl<'a> LowLevelCall<'a> {
ListIsUnique => self.load_args_and_call_zig(backend, bitcode::LIST_IS_UNIQUE),
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListSortWith
| DictWalk => {
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith | DictWalk => {
internal_error!("HigherOrder lowlevels should not be handled here")
}
@ -2083,7 +2094,7 @@ pub fn call_higher_order_lowlevel<'a>(
*owns_captured_environment,
),
ListMapWithIndex { .. } | ListSortWith { .. } | DictWalk { .. } => todo!("{:?}", op),
ListSortWith { .. } | DictWalk { .. } => todo!("{:?}", op),
}
}

View file

@ -176,6 +176,7 @@ wasm_result_primitive!(u16, i32_store16, Align::Bytes2);
wasm_result_primitive!(i16, i32_store16, Align::Bytes2);
wasm_result_primitive!(u32, i32_store, Align::Bytes4);
wasm_result_primitive!(i32, i32_store, Align::Bytes4);
wasm_result_primitive!(char, i32_store, Align::Bytes4);
wasm_result_primitive!(u64, i64_store, Align::Bytes8);
wasm_result_primitive!(i64, i64_store, Align::Bytes8);
wasm_result_primitive!(usize, i32_store, Align::Bytes4);

View file

@ -23,7 +23,7 @@ macro_rules! wasm32_sized_primitive {
}
wasm32_sized_primitive!(
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
u8, i8, u16, i16, u32, i32, char, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
);
impl Wasm32Sized for () {

View file

@ -106,7 +106,7 @@ impl<'a> From<&'a str> for ModuleName {
}
}
impl<'a> From<IdentStr> for ModuleName {
impl From<IdentStr> for ModuleName {
fn from(string: IdentStr) -> Self {
Self(string.as_str().into())
}
@ -152,7 +152,7 @@ impl<'a> From<&'a str> for ForeignSymbol {
}
}
impl<'a> From<String> for ForeignSymbol {
impl From<String> for ForeignSymbol {
fn from(string: String) -> Self {
Self(string.into())
}
@ -174,7 +174,7 @@ impl<'a> From<&'a str> for Uppercase {
}
}
impl<'a> From<String> for Uppercase {
impl From<String> for Uppercase {
fn from(string: String) -> Self {
Self(string.into())
}
@ -198,7 +198,7 @@ impl<'a> From<&'a Lowercase> for &'a str {
}
}
impl<'a> From<String> for Lowercase {
impl From<String> for Lowercase {
fn from(string: String) -> Self {
Self(string.into())
}

View file

@ -13,6 +13,7 @@ pub enum LowLevel {
StrEndsWith,
StrSplit,
StrCountGraphemes,
StrCountUtf8Bytes,
StrFromInt,
StrFromUtf8,
StrFromUtf8Range,
@ -24,6 +25,11 @@ pub enum LowLevel {
StrTrimRight,
StrToNum,
StrToScalars,
StrGetUnsafe,
StrSubstringUnsafe,
StrReserve,
StrAppendScalar,
StrGetScalarUnsafe,
ListLen,
ListWithCapacity,
ListGetUnsafe,
@ -35,7 +41,6 @@ pub enum LowLevel {
ListMap2,
ListMap3,
ListMap4,
ListMapWithIndex,
ListSortWith,
ListSublist,
ListDropAt,
@ -121,7 +126,7 @@ pub enum LowLevel {
macro_rules! higher_order {
() => {
ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListSortWith | DictWalk
ListMap | ListMap2 | ListMap3 | ListMap4 | ListSortWith | DictWalk
};
}
@ -142,7 +147,6 @@ impl LowLevel {
ListMap2 => 2,
ListMap3 => 3,
ListMap4 => 4,
ListMapWithIndex => 1,
ListSortWith => 1,
DictWalk => 2,
_ => unreachable!(),
@ -168,6 +172,7 @@ impl 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),
@ -176,10 +181,13 @@ impl LowLevelWrapperType {
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),
@ -207,7 +215,6 @@ impl LowLevelWrapperType {
Symbol::LIST_MAP2 => WrapperIsRequired,
Symbol::LIST_MAP3 => WrapperIsRequired,
Symbol::LIST_MAP4 => WrapperIsRequired,
Symbol::LIST_MAP_WITH_INDEX => WrapperIsRequired,
Symbol::LIST_SORT_WITH => WrapperIsRequired,
Symbol::LIST_SUBLIST => WrapperIsRequired,
Symbol::LIST_DROP_AT => CanBeReplacedBy(ListDropAt),

View file

@ -181,7 +181,10 @@ impl fmt::Debug for Symbol {
// might be used in a panic error message, and if we panick
// while we're already panicking it'll kill the process
// without printing any of the errors!
println!("DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err);
use std::io::Write;
let mut stderr = std::io::stderr();
writeln!(stderr, "DEBUG INFO: Failed to acquire lock for Debug reading from DEBUG_IDENT_IDS_BY_MODULE_ID, presumably because a thread panicked: {:?}", err).unwrap();
fallback_debug_fmt(*self, f)
}
@ -1190,6 +1193,18 @@ define_builtins! {
32 STR_TO_U8: "toU8"
33 STR_TO_I8: "toI8"
34 STR_TO_SCALARS: "toScalars"
35 STR_GET_UNSAFE: "getUnsafe"
36 STR_COUNT_UTF8_BYTES: "countUtf8Bytes"
37 STR_SUBSTRING_UNSAFE: "substringUnsafe"
38 STR_SPLIT_FIRST: "splitFirst"
39 STR_SPLIT_LAST: "splitLast"
40 STR_WALK_UTF8_WITH_INDEX: "walkUtf8WithIndex"
41 STR_RESERVE: "reserve"
42 STR_APPEND_SCALAR_UNSAFE: "appendScalarUnsafe"
43 STR_APPEND_SCALAR: "appendScalar"
44 STR_GET_SCALAR_UNSAFE: "getScalarUnsafe"
45 STR_WALK_SCALARS: "walkScalars"
46 STR_WALK_SCALARS_UNTIL: "walkScalarsUntil"
}
5 LIST: "List" => {
0 LIST_LIST: "List" imported // the List.List type alias

View file

@ -561,13 +561,6 @@ impl<'a> BorrowInfState<'a> {
self.own_var(*xs);
}
}
ListMapWithIndex { xs } => {
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
// own the list if the function wants to own the element (before, index 0)
if !function_ps[0].borrow {
self.own_var(*xs);
}
}
ListMap2 { xs, ys } => {
// own the lists if the function wants to own the element
if !function_ps[0].borrow {
@ -892,14 +885,18 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
// - other refcounted arguments are Borrowed
match op {
Unreachable => arena.alloc_slice_copy(&[irrelevant]),
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes => {
ListLen | StrIsEmpty | StrToScalars | StrCountGraphemes | StrCountUtf8Bytes => {
arena.alloc_slice_copy(&[borrowed])
}
ListWithCapacity => arena.alloc_slice_copy(&[irrelevant]),
ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrGetUnsafe | ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListConcat => arena.alloc_slice_copy(&[owned, owned]),
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
StrSubstringUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
StrReserve => arena.alloc_slice_copy(&[owned, irrelevant]),
StrAppendScalar => arena.alloc_slice_copy(&[owned, irrelevant]),
StrGetScalarUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrTrim => arena.alloc_slice_copy(&[owned]),
StrTrimLeft => arena.alloc_slice_copy(&[owned]),
StrTrimRight => arena.alloc_slice_copy(&[owned]),
@ -907,7 +904,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
StrToNum => arena.alloc_slice_copy(&[borrowed]),
ListPrepend => arena.alloc_slice_copy(&[owned, owned]),
StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, function, closure_data]),
ListMap => arena.alloc_slice_copy(&[owned, function, closure_data]),
ListMap2 => arena.alloc_slice_copy(&[owned, owned, function, closure_data]),
ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, function, closure_data]),
ListMap4 => arena.alloc_slice_copy(&[owned, owned, owned, owned, function, closure_data]),

View file

@ -748,17 +748,6 @@ impl<'a> Context<'a> {
handle_ownerships_pre!(Stmt::Let(z, v, l, b), ownerships)
}
ListMapWithIndex { xs } => {
let ownerships = [(xs, function_ps[0])];
let b = self.add_dec_after_lowlevel(after_arguments, &borrows, b, b_live_vars);
let b = handle_ownerships_post!(b, ownerships);
let v = create_call!(function_ps.get(2));
handle_ownerships_pre!(Stmt::Let(z, v, l, b), ownerships)
}
ListSortWith { xs } => {
// NOTE: we may apply the function to the same argument multiple times.
// for that to be valid, the function must borrow its argument. This is not

View file

@ -5108,12 +5108,6 @@ pub fn with_hole<'a>(
let xs = arg_symbols[0];
match_on_closure_argument!(ListMap, [xs])
}
ListMapWithIndex => {
debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0];
match_on_closure_argument!(ListMapWithIndex, [xs])
}
ListSortWith => {
debug_assert_eq!(arg_symbols.len(), 2);
let xs = arg_symbols[0];

View file

@ -20,9 +20,6 @@ pub enum HigherOrder {
zs: Symbol,
ws: Symbol,
},
ListMapWithIndex {
xs: Symbol,
},
ListSortWith {
xs: Symbol,
},
@ -39,7 +36,6 @@ impl HigherOrder {
HigherOrder::ListMap2 { .. } => 2,
HigherOrder::ListMap3 { .. } => 3,
HigherOrder::ListMap4 { .. } => 4,
HigherOrder::ListMapWithIndex { .. } => 2,
HigherOrder::ListSortWith { .. } => 2,
HigherOrder::DictWalk { .. } => 2,
}
@ -51,7 +47,7 @@ impl HigherOrder {
use HigherOrder::*;
match self {
ListMap { .. } | ListMapWithIndex { .. } | ListSortWith { .. } => 2,
ListMap { .. } | ListSortWith { .. } => 2,
ListMap2 { .. } => 3,
ListMap3 { .. } => 4,
ListMap4 { .. } => 5,

View file

@ -1451,7 +1451,7 @@ macro_rules! word1_check_indent {
($word:expr, $word_problem:expr, $min_indent:expr, $indent_problem:expr) => {
and!(
word1($word, $word_problem),
crate::parser::check_indent($min_indent, $indent_problem)
$crate::parser::check_indent($min_indent, $indent_problem)
)
};
}

View file

@ -2567,7 +2567,7 @@ fn list_keep_errs() {
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn list_map_with_index() {
assert_evals_to!(
"List.mapWithIndex [0,0,0] (\\x, index -> Num.intCast index + x)",

View file

@ -1700,3 +1700,75 @@ fn to_scalar_4_byte() {
RocList<u32>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_split_first() {
assert_evals_to!(
indoc!(
r#"
Str.splitFirst "foo/bar/baz" "/"
"#
),
// the result is a { before, after } record, and because of
// alphabetic ordering the fields here are flipped
RocResult::ok((RocStr::from("bar/baz"), RocStr::from("foo"))),
RocResult<(RocStr, RocStr), ()>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_split_last() {
assert_evals_to!(
indoc!(
r#"
Str.splitLast"foo/bar/baz" "/"
"#
),
RocResult::ok((RocStr::from("baz"), RocStr::from("foo/bar"))),
RocResult<(RocStr, RocStr), ()>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_walk_utf8_with_index() {
assert_evals_to!(
indoc!(
r#"
Str.walkUtf8WithIndex "abcd" [] (\list, byte, index -> List.append list (Pair index byte))
"#
),
RocList::from_slice(&[(0, b'a'), (1, b'b'), (2, b'c'), (3, b'd')]),
RocList<(u64, u8)>
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_append_scalar() {
assert_evals_to!(
indoc!(
r#"
Str.appendScalar "abcd" 'A'
"#
),
RocStr::from("abcdA"),
RocStr
);
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn str_walk_scalars() {
assert_evals_to!(
indoc!(
r#"
Str.walkScalars "abcd" [] List.append
"#
),
RocList::from_slice(&['a', 'b', 'c', 'd']),
RocList<char>
);
}

View file

@ -40,7 +40,7 @@ macro_rules! from_wasm_memory_primitive {
}
from_wasm_memory_primitive!(
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
u8, i8, u16, i16, u32, i32, char, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
);
impl FromWasmerMemory for () {

View file

@ -1314,3 +1314,16 @@ fn str_to_dec() {
RocDec
);
}
#[test]
fn str_walk_scalars() {
assert_evals_to!(
indoc!(
r#"
Str.walkScalars "abcd" [] List.append
"#
),
RocList::from_slice(&['a', 'b', 'c', 'd']),
RocList<char>
);
}

View file

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

View file

@ -1,22 +1,22 @@
procedure List.2 (List.74, List.75):
let List.272 : U64 = CallByName List.6 List.74;
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
if List.268 then
let List.270 : {} = CallByName List.60 List.74 List.75;
let List.269 : [C {}, C {}] = TagId(1) List.270;
ret List.269;
procedure List.2 (List.75, List.76):
let List.285 : U64 = CallByName List.6 List.75;
let List.281 : Int1 = CallByName Num.22 List.76 List.285;
if List.281 then
let List.283 : {} = CallByName List.60 List.75 List.76;
let List.282 : [C {}, C {}] = TagId(1) List.283;
ret List.282;
else
let List.267 : {} = Struct {};
let List.266 : [C {}, C {}] = TagId(0) List.267;
ret List.266;
let List.280 : {} = Struct {};
let List.279 : [C {}, C {}] = TagId(0) List.280;
ret List.279;
procedure List.6 (#Attr.2):
let List.275 : U64 = lowlevel ListLen #Attr.2;
ret List.275;
let List.288 : U64 = lowlevel ListLen #Attr.2;
ret List.288;
procedure List.60 (#Attr.2, #Attr.3):
let List.274 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.274;
let List.287 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.287;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.266 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.266;
let List.279 : List U8 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.279;
procedure Test.20 (Test.22):
let Test.34 : {U8} = Struct {Test.22};

View file

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

View file

@ -1,37 +1,37 @@
procedure List.2 (List.74, List.75):
let List.281 : U64 = CallByName List.6 List.74;
let List.277 : Int1 = CallByName Num.22 List.75 List.281;
if List.277 then
let List.279 : I64 = CallByName List.60 List.74 List.75;
let List.278 : [C {}, C I64] = TagId(1) List.279;
ret List.278;
procedure List.2 (List.75, List.76):
let List.294 : U64 = CallByName List.6 List.75;
let List.290 : Int1 = CallByName Num.22 List.76 List.294;
if List.290 then
let List.292 : I64 = CallByName List.60 List.75 List.76;
let List.291 : [C {}, C I64] = TagId(1) List.292;
ret List.291;
else
let List.276 : {} = Struct {};
let List.275 : [C {}, C I64] = TagId(0) List.276;
ret List.275;
let List.289 : {} = Struct {};
let List.288 : [C {}, C I64] = TagId(0) List.289;
ret List.288;
procedure List.6 (#Attr.2):
let List.282 : U64 = lowlevel ListLen #Attr.2;
ret List.282;
let List.295 : U64 = lowlevel ListLen #Attr.2;
ret List.295;
procedure List.60 (#Attr.2, #Attr.3):
let List.280 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.280;
let List.293 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.293;
procedure List.9 (List.188):
let List.273 : U64 = 0i64;
let List.266 : [C {}, C I64] = CallByName List.2 List.188 List.273;
let List.270 : U8 = 1i64;
let List.271 : U8 = GetTagId List.266;
let List.272 : Int1 = lowlevel Eq List.270 List.271;
if List.272 then
let List.189 : I64 = UnionAtIndex (Id 1) (Index 0) List.266;
let List.267 : [C Int1, C I64] = TagId(1) List.189;
ret List.267;
procedure List.9 (List.201):
let List.286 : U64 = 0i64;
let List.279 : [C {}, C I64] = CallByName List.2 List.201 List.286;
let List.283 : U8 = 1i64;
let List.284 : U8 = GetTagId List.279;
let List.285 : Int1 = lowlevel Eq List.283 List.284;
if List.285 then
let List.202 : I64 = UnionAtIndex (Id 1) (Index 0) List.279;
let List.280 : [C Int1, C I64] = TagId(1) List.202;
ret List.280;
else
let List.269 : Int1 = true;
let List.268 : [C Int1, C I64] = TagId(0) List.269;
ret List.268;
let List.282 : Int1 = true;
let List.281 : [C Int1, C I64] = TagId(0) List.282;
ret List.281;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
@ -39,17 +39,17 @@ procedure Num.22 (#Attr.2, #Attr.3):
procedure Str.27 (#Attr.2):
let #Attr.3 : {I64, U8} = lowlevel StrToNum #Attr.2;
let Str.42 : U8 = StructAtIndex 1 #Attr.3;
let Str.43 : U8 = 0i64;
let Str.39 : Int1 = lowlevel NumGt Str.42 Str.43;
if Str.39 then
let Str.41 : Int1 = false;
let Str.40 : [C Int1, C I64] = TagId(0) Str.41;
ret Str.40;
let Str.159 : U8 = StructAtIndex 1 #Attr.3;
let Str.160 : U8 = 0i64;
let Str.156 : Int1 = lowlevel NumGt Str.159 Str.160;
if Str.156 then
let Str.158 : Int1 = false;
let Str.157 : [C Int1, C I64] = TagId(0) Str.158;
ret Str.157;
else
let Str.38 : I64 = StructAtIndex 0 #Attr.3;
let Str.37 : [C Int1, C I64] = TagId(1) Str.38;
ret Str.37;
let Str.155 : I64 = StructAtIndex 0 #Attr.3;
let Str.154 : [C Int1, C I64] = TagId(1) Str.155;
ret Str.154;
procedure Test.0 ():
let Test.4 : Int1 = true;

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.266;
let List.279 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.279;
procedure Test.0 ():
let Test.2 : List I64 = Array [1i64];

View file

@ -1,6 +1,6 @@
procedure List.4 (#Attr.2, #Attr.3):
let List.266 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.266;
let List.279 : List I64 = lowlevel ListAppend #Attr.2 #Attr.3;
ret List.279;
procedure Test.1 (Test.2):
let Test.6 : I64 = 42i64;

View file

@ -1,27 +1,27 @@
procedure List.3 (List.82, List.83, List.84):
let List.269 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
let List.268 : List I64 = StructAtIndex 0 List.269;
inc List.268;
dec List.269;
ret List.268;
procedure List.3 (List.83, List.84, List.85):
let List.282 : {List I64, I64} = CallByName List.57 List.83 List.84 List.85;
let List.281 : List I64 = StructAtIndex 0 List.282;
inc List.281;
dec List.282;
ret List.281;
procedure List.57 (List.79, List.80, List.81):
let List.275 : U64 = CallByName List.6 List.79;
let List.272 : Int1 = CallByName Num.22 List.80 List.275;
if List.272 then
let List.273 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
ret List.273;
procedure List.57 (List.80, List.81, List.82):
let List.288 : U64 = CallByName List.6 List.80;
let List.285 : Int1 = CallByName Num.22 List.81 List.288;
if List.285 then
let List.286 : {List I64, I64} = CallByName List.61 List.80 List.81 List.82;
ret List.286;
else
let List.271 : {List I64, I64} = Struct {List.79, List.81};
ret List.271;
let List.284 : {List I64, I64} = Struct {List.80, List.82};
ret List.284;
procedure List.6 (#Attr.2):
let List.267 : U64 = lowlevel ListLen #Attr.2;
ret List.267;
let List.280 : U64 = lowlevel ListLen #Attr.2;
ret List.280;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.274 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.274;
let List.287 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.287;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,22 +1,22 @@
procedure List.2 (List.74, List.75):
let List.272 : U64 = CallByName List.6 List.74;
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
if List.268 then
let List.270 : I64 = CallByName List.60 List.74 List.75;
let List.269 : [C {}, C I64] = TagId(1) List.270;
ret List.269;
procedure List.2 (List.75, List.76):
let List.285 : U64 = CallByName List.6 List.75;
let List.281 : Int1 = CallByName Num.22 List.76 List.285;
if List.281 then
let List.283 : I64 = CallByName List.60 List.75 List.76;
let List.282 : [C {}, C I64] = TagId(1) List.283;
ret List.282;
else
let List.267 : {} = Struct {};
let List.266 : [C {}, C I64] = TagId(0) List.267;
ret List.266;
let List.280 : {} = Struct {};
let List.279 : [C {}, C I64] = TagId(0) List.280;
ret List.279;
procedure List.6 (#Attr.2):
let List.275 : U64 = lowlevel ListLen #Attr.2;
ret List.275;
let List.288 : U64 = lowlevel ListLen #Attr.2;
ret List.288;
procedure List.60 (#Attr.2, #Attr.3):
let List.274 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.274;
let List.287 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.287;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,10 +1,10 @@
procedure List.6 (#Attr.2):
let List.266 : U64 = lowlevel ListLen #Attr.2;
ret List.266;
let List.279 : U64 = lowlevel ListLen #Attr.2;
ret List.279;
procedure List.6 (#Attr.2):
let List.267 : U64 = lowlevel ListLen #Attr.2;
ret List.267;
let List.280 : U64 = lowlevel ListLen #Attr.2;
ret List.280;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.188 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;

View file

@ -1,38 +1,38 @@
procedure List.2 (List.74, List.75):
let List.272 : U64 = CallByName List.6 List.74;
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
if List.268 then
let List.270 : Str = CallByName List.60 List.74 List.75;
let List.269 : [C {}, C Str] = TagId(1) List.270;
ret List.269;
procedure List.2 (List.75, List.76):
let List.285 : U64 = CallByName List.6 List.75;
let List.281 : Int1 = CallByName Num.22 List.76 List.285;
if List.281 then
let List.283 : Str = CallByName List.60 List.75 List.76;
let List.282 : [C {}, C Str] = TagId(1) List.283;
ret List.282;
else
let List.267 : {} = Struct {};
let List.266 : [C {}, C Str] = TagId(0) List.267;
ret List.266;
let List.280 : {} = Struct {};
let List.279 : [C {}, C Str] = TagId(0) List.280;
ret List.279;
procedure List.5 (#Attr.2, #Attr.3):
let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.274;
let List.287 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
ret List.287;
procedure List.6 (#Attr.2):
let List.276 : U64 = lowlevel ListLen #Attr.2;
ret List.276;
let List.289 : U64 = lowlevel ListLen #Attr.2;
ret List.289;
procedure List.60 (#Attr.2, #Attr.3):
let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.275;
let List.288 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.288;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.188;
procedure Str.16 (#Attr.2, #Attr.3):
let Str.37 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
ret Str.37;
let Str.154 : Str = lowlevel StrRepeat #Attr.2 #Attr.3;
ret Str.154;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.38 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.38;
let Str.155 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.155;
procedure Test.1 ():
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";

View file

@ -1,36 +1,36 @@
procedure List.2 (List.74, List.75):
let List.272 : U64 = CallByName List.6 List.74;
let List.268 : Int1 = CallByName Num.22 List.75 List.272;
if List.268 then
let List.270 : Str = CallByName List.60 List.74 List.75;
let List.269 : [C {}, C Str] = TagId(1) List.270;
ret List.269;
procedure List.2 (List.75, List.76):
let List.285 : U64 = CallByName List.6 List.75;
let List.281 : Int1 = CallByName Num.22 List.76 List.285;
if List.281 then
let List.283 : Str = CallByName List.60 List.75 List.76;
let List.282 : [C {}, C Str] = TagId(1) List.283;
ret List.282;
else
let List.267 : {} = Struct {};
let List.266 : [C {}, C Str] = TagId(0) List.267;
ret List.266;
let List.280 : {} = Struct {};
let List.279 : [C {}, C Str] = TagId(0) List.280;
ret List.279;
procedure List.5 (#Attr.2, #Attr.3):
inc #Attr.2;
let List.274 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
let List.287 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3;
decref #Attr.2;
ret List.274;
ret List.287;
procedure List.6 (#Attr.2):
let List.276 : U64 = lowlevel ListLen #Attr.2;
ret List.276;
let List.289 : U64 = lowlevel ListLen #Attr.2;
ret List.289;
procedure List.60 (#Attr.2, #Attr.3):
let List.275 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.275;
let List.288 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.288;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
ret Num.188;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.38 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.38;
let Str.155 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.155;
procedure Test.1 ():
let Test.21 : Str = "lllllllllllllllllllllooooooooooong";

View file

@ -1,27 +1,27 @@
procedure List.3 (List.82, List.83, List.84):
let List.267 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
let List.266 : List I64 = StructAtIndex 0 List.267;
inc List.266;
dec List.267;
ret List.266;
procedure List.3 (List.83, List.84, List.85):
let List.280 : {List I64, I64} = CallByName List.57 List.83 List.84 List.85;
let List.279 : List I64 = StructAtIndex 0 List.280;
inc List.279;
dec List.280;
ret List.279;
procedure List.57 (List.79, List.80, List.81):
let List.273 : U64 = CallByName List.6 List.79;
let List.270 : Int1 = CallByName Num.22 List.80 List.273;
if List.270 then
let List.271 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
ret List.271;
procedure List.57 (List.80, List.81, List.82):
let List.286 : U64 = CallByName List.6 List.80;
let List.283 : Int1 = CallByName Num.22 List.81 List.286;
if List.283 then
let List.284 : {List I64, I64} = CallByName List.61 List.80 List.81 List.82;
ret List.284;
else
let List.269 : {List I64, I64} = Struct {List.79, List.81};
ret List.269;
let List.282 : {List I64, I64} = Struct {List.80, List.82};
ret List.282;
procedure List.6 (#Attr.2):
let List.274 : U64 = lowlevel ListLen #Attr.2;
ret List.274;
let List.287 : U64 = lowlevel ListLen #Attr.2;
ret List.287;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.272 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.272;
let List.285 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.285;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.188 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,16 +1,16 @@
procedure List.28 (#Attr.2, #Attr.3):
let List.269 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let List.282 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3;
let Bool.9 : Int1 = lowlevel ListIsUnique #Attr.2;
if Bool.9 then
ret List.269;
ret List.282;
else
decref #Attr.2;
ret List.269;
ret List.282;
procedure List.54 (List.183):
let List.267 : {} = Struct {};
let List.266 : List I64 = CallByName List.28 List.183 List.267;
ret List.266;
procedure List.54 (List.196):
let List.280 : {} = Struct {};
let List.279 : List I64 = CallByName List.28 List.196 List.280;
ret List.279;
procedure Num.46 (#Attr.2, #Attr.3):
let Num.188 : U8 = lowlevel NumCompare #Attr.2 #Attr.3;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.74, List.75):
let List.286 : U64 = CallByName List.6 List.74;
let List.282 : Int1 = CallByName Num.22 List.75 List.286;
if List.282 then
let List.284 : I64 = CallByName List.60 List.74 List.75;
let List.283 : [C {}, C I64] = TagId(1) List.284;
ret List.283;
procedure List.2 (List.75, List.76):
let List.299 : U64 = CallByName List.6 List.75;
let List.295 : Int1 = CallByName Num.22 List.76 List.299;
if List.295 then
let List.297 : I64 = CallByName List.60 List.75 List.76;
let List.296 : [C {}, C I64] = TagId(1) List.297;
ret List.296;
else
let List.281 : {} = Struct {};
let List.280 : [C {}, C I64] = TagId(0) List.281;
ret List.280;
let List.294 : {} = Struct {};
let List.293 : [C {}, C I64] = TagId(0) List.294;
ret List.293;
procedure List.3 (List.82, List.83, List.84):
let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
let List.269 : List I64 = StructAtIndex 0 List.270;
inc List.269;
dec List.270;
ret List.269;
procedure List.3 (List.83, List.84, List.85):
let List.283 : {List I64, I64} = CallByName List.57 List.83 List.84 List.85;
let List.282 : List I64 = StructAtIndex 0 List.283;
inc List.282;
dec List.283;
ret List.282;
procedure List.57 (List.79, List.80, List.81):
let List.292 : U64 = CallByName List.6 List.79;
let List.289 : Int1 = CallByName Num.22 List.80 List.292;
if List.289 then
let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
ret List.290;
procedure List.57 (List.80, List.81, List.82):
let List.305 : U64 = CallByName List.6 List.80;
let List.302 : Int1 = CallByName Num.22 List.81 List.305;
if List.302 then
let List.303 : {List I64, I64} = CallByName List.61 List.80 List.81 List.82;
ret List.303;
else
let List.288 : {List I64, I64} = Struct {List.79, List.81};
ret List.288;
let List.301 : {List I64, I64} = Struct {List.80, List.82};
ret List.301;
procedure List.6 (#Attr.2):
let List.293 : U64 = lowlevel ListLen #Attr.2;
ret List.293;
let List.306 : U64 = lowlevel ListLen #Attr.2;
ret List.306;
procedure List.60 (#Attr.2, #Attr.3):
let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.294;
let List.307 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.307;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.291;
let List.304 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.304;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -1,43 +1,43 @@
procedure List.2 (List.74, List.75):
let List.286 : U64 = CallByName List.6 List.74;
let List.282 : Int1 = CallByName Num.22 List.75 List.286;
if List.282 then
let List.284 : I64 = CallByName List.60 List.74 List.75;
let List.283 : [C {}, C I64] = TagId(1) List.284;
ret List.283;
procedure List.2 (List.75, List.76):
let List.299 : U64 = CallByName List.6 List.75;
let List.295 : Int1 = CallByName Num.22 List.76 List.299;
if List.295 then
let List.297 : I64 = CallByName List.60 List.75 List.76;
let List.296 : [C {}, C I64] = TagId(1) List.297;
ret List.296;
else
let List.281 : {} = Struct {};
let List.280 : [C {}, C I64] = TagId(0) List.281;
ret List.280;
let List.294 : {} = Struct {};
let List.293 : [C {}, C I64] = TagId(0) List.294;
ret List.293;
procedure List.3 (List.82, List.83, List.84):
let List.270 : {List I64, I64} = CallByName List.57 List.82 List.83 List.84;
let List.269 : List I64 = StructAtIndex 0 List.270;
inc List.269;
dec List.270;
ret List.269;
procedure List.3 (List.83, List.84, List.85):
let List.283 : {List I64, I64} = CallByName List.57 List.83 List.84 List.85;
let List.282 : List I64 = StructAtIndex 0 List.283;
inc List.282;
dec List.283;
ret List.282;
procedure List.57 (List.79, List.80, List.81):
let List.292 : U64 = CallByName List.6 List.79;
let List.289 : Int1 = CallByName Num.22 List.80 List.292;
if List.289 then
let List.290 : {List I64, I64} = CallByName List.61 List.79 List.80 List.81;
ret List.290;
procedure List.57 (List.80, List.81, List.82):
let List.305 : U64 = CallByName List.6 List.80;
let List.302 : Int1 = CallByName Num.22 List.81 List.305;
if List.302 then
let List.303 : {List I64, I64} = CallByName List.61 List.80 List.81 List.82;
ret List.303;
else
let List.288 : {List I64, I64} = Struct {List.79, List.81};
ret List.288;
let List.301 : {List I64, I64} = Struct {List.80, List.82};
ret List.301;
procedure List.6 (#Attr.2):
let List.293 : U64 = lowlevel ListLen #Attr.2;
ret List.293;
let List.306 : U64 = lowlevel ListLen #Attr.2;
ret List.306;
procedure List.60 (#Attr.2, #Attr.3):
let List.294 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.294;
let List.307 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
ret List.307;
procedure List.61 (#Attr.2, #Attr.3, #Attr.4):
let List.291 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.291;
let List.304 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
ret List.304;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.190 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;

View file

@ -6,6 +6,7 @@ use crate::types::{name_type_var, RecordField, Uls};
use roc_collections::all::MutMap;
use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, ModuleId, Symbol};
use std::fmt::Write;
pub static WILDCARD: &str = "*";
static EMPTY_RECORD: &str = "{}";
@ -1145,9 +1146,7 @@ fn write_flat_type<'a>(
)
})
}
Erroneous(problem) => {
buf.push_str(&format!("<Type Mismatch: {:?}>", problem));
}
Erroneous(problem) => write!(buf, "<Type Mismatch: {:?}>", problem).unwrap(),
}
}

View file

@ -11,6 +11,7 @@ use roc_module::low_level::LowLevel;
use roc_module::symbol::{Interns, ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use std::fmt;
use std::fmt::Write;
pub const TYPE_NUM: &str = "Num";
pub const TYPE_INTEGER: &str = "Integer";
@ -2299,7 +2300,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
buf.push('(');
}
buf.push_str(name.as_str());
buf.push_str(&format!(" has {:?}", symbol));
write!(buf, "has {:?}", symbol).unwrap();
if write_parens {
buf.push(')');
}
@ -2310,7 +2311,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
if write_parens {
buf.push('(');
}
buf.push_str(&format!("{:?}", symbol));
write!(buf, "{:?}", symbol).unwrap();
for arg in arguments {
buf.push(' ');
@ -2355,7 +2356,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
if write_parens {
buf.push('(');
}
buf.push_str(&format!("{:?}", symbol));
write!(buf, "{:?}", symbol).unwrap();
for arg in arguments {
buf.push(' ');
@ -2434,7 +2435,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
let mut it = tags.into_iter().peekable();
while let Some((tag, args)) = it.next() {
buf.push_str(&format!("{:?}", tag));
write!(buf, "{:?}", tag).unwrap();
for arg in args {
buf.push(' ');
write_debug_error_type_help(arg, buf, Parens::InTypeParam);
@ -2453,7 +2454,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
let mut it = tags.into_iter().peekable();
while let Some((tag, args)) = it.next() {
buf.push_str(&format!("{:?}", tag));
write!(buf, "{:?}", tag).unwrap();
for arg in args {
buf.push(' ');
write_debug_error_type_help(arg, buf, Parens::Unnecessary);