mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'trunk' into ci_ssl_fix
This commit is contained in:
commit
c1ee743dc9
28 changed files with 648 additions and 325 deletions
|
@ -86,6 +86,7 @@ test-rust:
|
||||||
FROM +build-rust
|
FROM +build-rust
|
||||||
ARG RUSTC_WRAPPER=/usr/local/cargo/bin/sccache
|
ARG RUSTC_WRAPPER=/usr/local/cargo/bin/sccache
|
||||||
ARG SCCACHE_DIR=/earthbuild/sccache_dir
|
ARG SCCACHE_DIR=/earthbuild/sccache_dir
|
||||||
|
ARG RUST_BACKTRACE=1
|
||||||
RUN cargo test --release
|
RUN cargo test --release
|
||||||
|
|
||||||
test-all:
|
test-all:
|
||||||
|
|
|
@ -228,7 +228,6 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
#[serial(astar)]
|
#[serial(astar)]
|
||||||
fn run_astar_optimized_1() {
|
fn run_astar_optimized_1() {
|
||||||
check_output_with_stdin(
|
check_output_with_stdin(
|
||||||
|
@ -279,11 +278,11 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial(closure4)]
|
#[serial(closure)]
|
||||||
fn closure4() {
|
fn closure() {
|
||||||
check_output(
|
check_output(
|
||||||
&example_file("benchmarks", "Closure4.roc"),
|
&example_file("benchmarks", "Closure.roc"),
|
||||||
"closure4",
|
"closure",
|
||||||
&[],
|
&[],
|
||||||
"",
|
"",
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -296,6 +296,10 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
fn getKey(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
||||||
|
if (key_width == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const offset = blk: {
|
const offset = blk: {
|
||||||
if (alignment.keyFirst()) {
|
if (alignment.keyFirst()) {
|
||||||
break :blk (index * key_width);
|
break :blk (index * key_width);
|
||||||
|
@ -329,6 +333,10 @@ pub const RocDict = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
fn getValue(self: *const RocDict, index: usize, alignment: Alignment, key_width: usize, value_width: usize) Opaque {
|
||||||
|
if (value_width == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const offset = blk: {
|
const offset = blk: {
|
||||||
if (alignment.keyFirst()) {
|
if (alignment.keyFirst()) {
|
||||||
break :blk (self.capacity() * key_width) + (index * value_width);
|
break :blk (self.capacity() * key_width) + (index * value_width);
|
||||||
|
@ -492,6 +500,8 @@ pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width:
|
||||||
if (dict.dict_entries_len == 0) {
|
if (dict.dict_entries_len == 0) {
|
||||||
const data_bytes = dict.capacity() * slotSize(key_width, value_width);
|
const data_bytes = dict.capacity() * slotSize(key_width, value_width);
|
||||||
decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes);
|
decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes);
|
||||||
|
output.* = RocDict.empty();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.* = dict;
|
output.* = dict;
|
||||||
|
@ -660,7 +670,9 @@ pub fn dictUnion(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width
|
||||||
|
|
||||||
// we need an extra RC token for the key
|
// we need an extra RC token for the key
|
||||||
inc_key(key);
|
inc_key(key);
|
||||||
|
inc_value(value);
|
||||||
|
|
||||||
|
// we know the newly added key is not a duplicate, so the `dec`s are unreachable
|
||||||
const dec_key = doNothing;
|
const dec_key = doNothing;
|
||||||
const dec_value = doNothing;
|
const dec_value = doNothing;
|
||||||
|
|
||||||
|
@ -702,7 +714,7 @@ pub fn dictIntersection(dict1: RocDict, dict2: RocDict, alignment: Alignment, ke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Inc, dec_value: Inc, output: *RocDict) callconv(.C) void {
|
pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void {
|
||||||
output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width);
|
output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -748,8 +760,14 @@ pub fn setFromList(list: RocList, alignment: Alignment, key_width: usize, value_
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, output: Opaque) callconv(.C) void {
|
pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, inc_key: Inc, inc_value: Inc, output: Opaque) callconv(.C) void {
|
||||||
@memcpy(output orelse unreachable, accum orelse unreachable, accum_width);
|
// allocate space to write the result of the stepper into
|
||||||
|
// experimentally aliasing the accum and output pointers is not a good idea
|
||||||
|
const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable);
|
||||||
|
var b1 = output orelse unreachable;
|
||||||
|
var b2 = alloc;
|
||||||
|
|
||||||
|
@memcpy(b2, accum orelse unreachable, accum_width);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
const size = dict.capacity();
|
const size = dict.capacity();
|
||||||
|
@ -759,12 +777,19 @@ pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, a
|
||||||
const key = dict.getKey(i, alignment, key_width, value_width);
|
const key = dict.getKey(i, alignment, key_width, value_width);
|
||||||
const value = dict.getValue(i, alignment, key_width, value_width);
|
const value = dict.getValue(i, alignment, key_width, value_width);
|
||||||
|
|
||||||
stepper_caller(stepper, key, value, output, output);
|
stepper_caller(stepper, key, value, b2, b1);
|
||||||
|
|
||||||
|
const temp = b1;
|
||||||
|
b2 = b1;
|
||||||
|
b1 = temp;
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@memcpy(output orelse unreachable, b2, accum_width);
|
||||||
|
std.heap.c_allocator.free(alloc[0..accum_width]);
|
||||||
|
|
||||||
const data_bytes = dict.capacity() * slotSize(key_width, value_width);
|
const data_bytes = dict.capacity() * slotSize(key_width, value_width);
|
||||||
decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes);
|
decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ const Allocator = mem.Allocator;
|
||||||
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
||||||
const Opaque = ?[*]u8;
|
const Opaque = ?[*]u8;
|
||||||
|
|
||||||
|
const Inc = fn (?[*]u8) callconv(.C) void;
|
||||||
|
const Dec = fn (?[*]u8) callconv(.C) void;
|
||||||
|
|
||||||
pub const RocList = extern struct {
|
pub const RocList = extern struct {
|
||||||
bytes: ?[*]u8,
|
bytes: ?[*]u8,
|
||||||
length: usize,
|
length: usize,
|
||||||
|
@ -58,7 +61,7 @@ pub const RocList = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// unfortunately, we have to clone
|
// unfortunately, we have to clone
|
||||||
var new_list = RocList.allocate(allocator, self.length, alignment, element_width);
|
var new_list = RocList.allocate(allocator, alignment, self.length, element_width);
|
||||||
|
|
||||||
var old_bytes: [*]u8 = @ptrCast([*]u8, self.bytes);
|
var old_bytes: [*]u8 = @ptrCast([*]u8, self.bytes);
|
||||||
var new_bytes: [*]u8 = @ptrCast([*]u8, new_list.bytes);
|
var new_bytes: [*]u8 = @ptrCast([*]u8, new_list.bytes);
|
||||||
|
@ -149,7 +152,7 @@ pub fn listMapWithIndex(list: RocList, transform: Opaque, caller: Caller2, align
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize) callconv(.C) RocList {
|
pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList {
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -160,6 +163,7 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment:
|
||||||
while (i < size) : (i += 1) {
|
while (i < size) : (i += 1) {
|
||||||
var keep = false;
|
var keep = false;
|
||||||
const element = source_ptr + (i * element_width);
|
const element = source_ptr + (i * element_width);
|
||||||
|
inc(element);
|
||||||
caller(transform, element, @ptrCast(?[*]u8, &keep));
|
caller(transform, element, @ptrCast(?[*]u8, &keep));
|
||||||
|
|
||||||
if (keep) {
|
if (keep) {
|
||||||
|
@ -167,29 +171,36 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment:
|
||||||
|
|
||||||
kept += 1;
|
kept += 1;
|
||||||
} else {
|
} else {
|
||||||
// TODO decrement the value?
|
dec(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output.length = kept;
|
// consume the input list
|
||||||
|
|
||||||
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width);
|
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width);
|
||||||
|
|
||||||
return output;
|
if (kept == 0) {
|
||||||
|
// if the output is empty, deallocate the space we made for the result
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, output.bytes, size * element_width);
|
||||||
|
return RocList.empty();
|
||||||
|
} else {
|
||||||
|
output.length = kept;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return RocList.empty();
|
return RocList.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) callconv(.C) RocList {
|
pub fn listKeepOks(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList {
|
||||||
return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width);
|
return listKeepResult(list, RocResult.isOk, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) callconv(.C) RocList {
|
pub fn listKeepErrs(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) callconv(.C) RocList {
|
||||||
return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width);
|
return listKeepResult(list, RocResult.isErr, transform, caller, alignment, before_width, result_width, after_width, inc_closure, dec_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize) RocList {
|
pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, transform: Opaque, caller: Caller1, alignment: usize, before_width: usize, result_width: usize, after_width: usize, inc_closure: Inc, dec_result: Dec) RocList {
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -200,23 +211,31 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t
|
||||||
|
|
||||||
var kept: usize = 0;
|
var kept: usize = 0;
|
||||||
while (i < size) : (i += 1) {
|
while (i < size) : (i += 1) {
|
||||||
const element = source_ptr + (i * before_width);
|
const before_element = source_ptr + (i * before_width);
|
||||||
caller(transform, element, temporary);
|
inc_closure(transform);
|
||||||
|
caller(transform, before_element, temporary);
|
||||||
|
|
||||||
const result = utils.RocResult{ .bytes = temporary };
|
const result = utils.RocResult{ .bytes = temporary };
|
||||||
|
|
||||||
|
const after_element = temporary + @sizeOf(i64);
|
||||||
if (is_good_constructor(result)) {
|
if (is_good_constructor(result)) {
|
||||||
@memcpy(target_ptr + (kept * after_width), temporary + @sizeOf(i64), after_width);
|
@memcpy(target_ptr + (kept * after_width), after_element, after_width);
|
||||||
|
|
||||||
kept += 1;
|
kept += 1;
|
||||||
|
} else {
|
||||||
|
dec_result(temporary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output.length = kept;
|
|
||||||
|
|
||||||
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * before_width);
|
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * before_width);
|
||||||
|
std.heap.c_allocator.free(temporary[0..result_width]);
|
||||||
|
|
||||||
return output;
|
if (kept == 0) {
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, output.bytes, size * after_width);
|
||||||
|
return RocList.empty();
|
||||||
|
} else {
|
||||||
|
output.length = kept;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return RocList.empty();
|
return RocList.empty();
|
||||||
}
|
}
|
||||||
|
@ -278,3 +297,30 @@ pub fn listContains(list: RocList, key: Opaque, key_width: usize, is_eq: EqFn) c
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listRepeat(count: usize, alignment: usize, element: Opaque, element_width: usize, inc_n_element: Inc) callconv(.C) RocList {
|
||||||
|
if (count == 0) {
|
||||||
|
return RocList.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const allocator = std.heap.c_allocator;
|
||||||
|
var output = RocList.allocate(allocator, alignment, count, element_width);
|
||||||
|
|
||||||
|
if (output.bytes) |target_ptr| {
|
||||||
|
var i: usize = 0;
|
||||||
|
const source = element orelse unreachable;
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
@memcpy(target_ptr + i * element_width, source, element_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO do all increments at once!
|
||||||
|
i = 0;
|
||||||
|
while (i < count) : (i += 1) {
|
||||||
|
inc_n_element(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
} else {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ comptime {
|
||||||
exportListFn(list.listKeepOks, "keep_oks");
|
exportListFn(list.listKeepOks, "keep_oks");
|
||||||
exportListFn(list.listKeepErrs, "keep_errs");
|
exportListFn(list.listKeepErrs, "keep_errs");
|
||||||
exportListFn(list.listContains, "contains");
|
exportListFn(list.listContains, "contains");
|
||||||
|
exportListFn(list.listRepeat, "repeat");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dict Module
|
// Dict Module
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const utils = @import("utils.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
|
||||||
|
@ -47,18 +48,7 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initBig(allocator: *Allocator, in_place: InPlace, number_of_chars: u64) RocStr {
|
pub fn initBig(allocator: *Allocator, in_place: InPlace, number_of_chars: u64) RocStr {
|
||||||
const length = @sizeOf(usize) + number_of_chars;
|
const first_element = utils.allocateWithRefcount(allocator, @sizeOf(usize), number_of_chars);
|
||||||
var new_bytes: []usize = allocator.alloc(usize, length) catch unreachable;
|
|
||||||
|
|
||||||
if (in_place == InPlace.InPlace) {
|
|
||||||
new_bytes[0] = @intCast(usize, number_of_chars);
|
|
||||||
} else {
|
|
||||||
const v: isize = std.math.minInt(isize);
|
|
||||||
new_bytes[0] = @bitCast(usize, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
var first_element = @ptrCast([*]align(@alignOf(usize)) u8, new_bytes);
|
|
||||||
first_element += @sizeOf(usize);
|
|
||||||
|
|
||||||
return RocStr{
|
return RocStr{
|
||||||
.str_bytes = first_element,
|
.str_bytes = first_element,
|
||||||
|
@ -833,8 +823,10 @@ pub fn strConcatC(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv
|
||||||
|
|
||||||
fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr {
|
||||||
if (arg1.isEmpty()) {
|
if (arg1.isEmpty()) {
|
||||||
|
// the second argument is borrowed, so we must increment its refcount before returning
|
||||||
return RocStr.clone(allocator, result_in_place, arg2);
|
return RocStr.clone(allocator, result_in_place, arg2);
|
||||||
} else if (arg2.isEmpty()) {
|
} else if (arg2.isEmpty()) {
|
||||||
|
// the first argument is owned, so we can return it without cloning
|
||||||
return RocStr.clone(allocator, result_in_place, arg1);
|
return RocStr.clone(allocator, result_in_place, arg1);
|
||||||
} else {
|
} else {
|
||||||
const combined_length = arg1.len() + arg2.len();
|
const combined_length = arg1.len() + arg2.len();
|
||||||
|
|
|
@ -16,25 +16,25 @@ pub fn decref(
|
||||||
|
|
||||||
var bytes = bytes_or_null orelse return;
|
var bytes = bytes_or_null orelse return;
|
||||||
|
|
||||||
const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
|
const isizes: [*]isize = @ptrCast([*]isize, @alignCast(8, bytes));
|
||||||
|
|
||||||
const refcount = (usizes - 1)[0];
|
const refcount = (isizes - 1)[0];
|
||||||
const refcount_isize = @bitCast(isize, refcount);
|
const refcount_isize = @bitCast(isize, refcount);
|
||||||
|
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
16 => {
|
16 => {
|
||||||
if (refcount == REFCOUNT_ONE) {
|
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||||
allocator.free((bytes - 16)[0 .. 16 + data_bytes]);
|
allocator.free((bytes - 16)[0 .. 16 + data_bytes]);
|
||||||
} else if (refcount_isize < 0) {
|
} else if (refcount_isize < 0) {
|
||||||
(usizes - 1)[0] = refcount + 1;
|
(isizes - 1)[0] = refcount - 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
// NOTE enums can currently have an alignment of < 8
|
// NOTE enums can currently have an alignment of < 8
|
||||||
if (refcount == REFCOUNT_ONE) {
|
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||||
allocator.free((bytes - 8)[0 .. 8 + data_bytes]);
|
allocator.free((bytes - 8)[0 .. 8 + data_bytes]);
|
||||||
} else if (refcount_isize < 0) {
|
} else if (refcount_isize < 0) {
|
||||||
(usizes - 1)[0] = refcount + 1;
|
(isizes - 1)[0] = refcount - 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,11 @@ pub fn allocateWithRefcount(
|
||||||
|
|
||||||
var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable;
|
var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable;
|
||||||
|
|
||||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
var as_usize_array = @ptrCast([*]isize, new_bytes);
|
||||||
if (result_in_place) {
|
if (result_in_place) {
|
||||||
as_usize_array[0] = @intCast(usize, number_of_slots);
|
as_usize_array[0] = @intCast(isize, number_of_slots);
|
||||||
} else {
|
} else {
|
||||||
as_usize_array[0] = REFCOUNT_ONE;
|
as_usize_array[0] = REFCOUNT_ONE_ISIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||||
|
|
|
@ -67,3 +67,4 @@ pub const LIST_KEEP_ERRS: &str = "roc_builtins.list.keep_errs";
|
||||||
pub const LIST_WALK: &str = "roc_builtins.list.walk";
|
pub const LIST_WALK: &str = "roc_builtins.list.walk";
|
||||||
pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
|
pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
|
||||||
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
||||||
|
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
|
||||||
|
|
|
@ -2042,7 +2042,7 @@ fn dict_get(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let arg_dict = Symbol::ARG_1;
|
let arg_dict = Symbol::ARG_1;
|
||||||
let arg_key = Symbol::ARG_2;
|
let arg_key = Symbol::ARG_2;
|
||||||
|
|
||||||
let temp_record = Symbol::ARG_3;
|
let temp_record = Symbol::DICT_GET_RESULT;
|
||||||
|
|
||||||
let bool_var = var_store.fresh();
|
let bool_var = var_store.fresh();
|
||||||
let flag_var = var_store.fresh();
|
let flag_var = var_store.fresh();
|
||||||
|
|
|
@ -207,6 +207,15 @@ fn build_transform_caller_help<'a, 'ctx, 'env>(
|
||||||
function_value
|
function_value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_inc_n_wrapper<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
|
n: u64,
|
||||||
|
) -> FunctionValue<'ctx> {
|
||||||
|
build_rc_wrapper(env, layout_ids, layout, Mode::Inc(n))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_inc_wrapper<'a, 'ctx, 'env>(
|
pub fn build_inc_wrapper<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
|
|
@ -3595,9 +3595,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
let list_len = load_symbol(scope, &args[0]).into_int_value();
|
let list_len = load_symbol(scope, &args[0]).into_int_value();
|
||||||
let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]);
|
let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]);
|
||||||
|
|
||||||
let inplace = get_inplace_from_layout(layout);
|
list_repeat(env, layout_ids, list_len, elem, elem_layout)
|
||||||
|
|
||||||
list_repeat(env, inplace, parent, list_len, elem, elem_layout)
|
|
||||||
}
|
}
|
||||||
ListReverse => {
|
ListReverse => {
|
||||||
// List.reverse : List elem -> List elem
|
// List.reverse : List elem -> List elem
|
||||||
|
|
|
@ -747,6 +747,9 @@ pub fn dict_walk<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
|
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
|
||||||
|
|
||||||
|
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
||||||
|
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
||||||
|
|
||||||
call_void_bitcode_fn(
|
call_void_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -758,6 +761,8 @@ pub fn dict_walk<'a, 'ctx, 'env>(
|
||||||
key_width.into(),
|
key_width.into(),
|
||||||
value_width.into(),
|
value_width.into(),
|
||||||
accum_width.into(),
|
accum_width.into(),
|
||||||
|
inc_key_fn.as_global_value().as_pointer_value().into(),
|
||||||
|
inc_value_fn.as_global_value().as_pointer_value().into(),
|
||||||
env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"),
|
env.builder.build_bitcast(output_ptr, u8_ptr, "to_opaque"),
|
||||||
],
|
],
|
||||||
&bitcode::DICT_WALK,
|
&bitcode::DICT_WALK,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
use crate::llvm::bitcode::{
|
use crate::llvm::bitcode::{
|
||||||
build_eq_wrapper, build_transform_caller, call_bitcode_fn, call_void_bitcode_fn,
|
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, build_transform_caller,
|
||||||
|
call_bitcode_fn, call_void_bitcode_fn,
|
||||||
};
|
};
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
allocate_with_refcount_help, build_num_binop, cast_basic_basic, complex_bitcast, Env, InPlace,
|
allocate_with_refcount_help, build_num_binop, cast_basic_basic, complex_bitcast, Env, InPlace,
|
||||||
|
@ -53,90 +54,43 @@ pub fn list_single<'a, 'ctx, 'env>(
|
||||||
/// List.repeat : Int, elem -> List elem
|
/// List.repeat : Int, elem -> List elem
|
||||||
pub fn list_repeat<'a, 'ctx, 'env>(
|
pub fn list_repeat<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
inplace: InPlace,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
parent: FunctionValue<'ctx>,
|
|
||||||
list_len: IntValue<'ctx>,
|
list_len: IntValue<'ctx>,
|
||||||
elem: BasicValueEnum<'ctx>,
|
element: BasicValueEnum<'ctx>,
|
||||||
elem_layout: &Layout<'a>,
|
element_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
|
||||||
|
|
||||||
// list_len > 0
|
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||||
// We have to do a loop below, continuously adding the `elem`
|
|
||||||
// to the output list `List elem` until we have reached the
|
let element_ptr = builder.build_alloca(element.get_type(), "element_ptr");
|
||||||
// number of repeats. This `comparison` is used to check
|
env.builder.build_store(element_ptr, element);
|
||||||
// if we need to do any looping; because if we dont, then we
|
|
||||||
// dont need to allocate memory for the index or the check
|
let element_width = env
|
||||||
// if index != 0
|
.ptr_int()
|
||||||
let comparison = builder.build_int_compare(
|
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
IntPredicate::UGT,
|
|
||||||
list_len,
|
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||||
ctx.i64_type().const_int(0, false),
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
"atleastzero",
|
let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout);
|
||||||
|
|
||||||
|
let output = call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
list_len.into(),
|
||||||
|
alignment_iv.into(),
|
||||||
|
env.builder.build_bitcast(element_ptr, u8_ptr, "to_u8_ptr"),
|
||||||
|
element_width.into(),
|
||||||
|
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||||
|
],
|
||||||
|
bitcode::LIST_REPEAT,
|
||||||
);
|
);
|
||||||
|
|
||||||
let build_then = || {
|
complex_bitcast(
|
||||||
// Allocate space for the new array that we'll copy into.
|
env.builder,
|
||||||
let list_ptr = allocate_list(env, inplace, elem_layout, list_len);
|
output,
|
||||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
collection(env.context, env.ptr_bytes).into(),
|
||||||
|
"from_i128",
|
||||||
let index_name = "#index";
|
|
||||||
let start_alloca = builder.build_alloca(ctx.i64_type(), index_name);
|
|
||||||
|
|
||||||
// Start at the last element in the list.
|
|
||||||
let last_elem_index = builder.build_int_sub(
|
|
||||||
list_len,
|
|
||||||
ctx.i64_type().const_int(1, false),
|
|
||||||
"lastelemindex",
|
|
||||||
);
|
|
||||||
builder.build_store(start_alloca, last_elem_index);
|
|
||||||
|
|
||||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
|
||||||
builder.build_unconditional_branch(loop_bb);
|
|
||||||
builder.position_at_end(loop_bb);
|
|
||||||
|
|
||||||
// #index = #index - 1
|
|
||||||
let curr_index = builder
|
|
||||||
.build_load(start_alloca, index_name)
|
|
||||||
.into_int_value();
|
|
||||||
let next_index =
|
|
||||||
builder.build_int_sub(curr_index, ctx.i64_type().const_int(1, false), "nextindex");
|
|
||||||
|
|
||||||
builder.build_store(start_alloca, next_index);
|
|
||||||
let elem_ptr =
|
|
||||||
unsafe { builder.build_in_bounds_gep(list_ptr, &[curr_index], "load_index") };
|
|
||||||
|
|
||||||
// Mutate the new array in-place to change the element.
|
|
||||||
builder.build_store(elem_ptr, elem);
|
|
||||||
|
|
||||||
// #index != 0
|
|
||||||
let end_cond = builder.build_int_compare(
|
|
||||||
IntPredicate::NE,
|
|
||||||
ctx.i64_type().const_int(0, false),
|
|
||||||
curr_index,
|
|
||||||
"loopcond",
|
|
||||||
);
|
|
||||||
|
|
||||||
let after_bb = ctx.append_basic_block(parent, "afterloop");
|
|
||||||
|
|
||||||
builder.build_conditional_branch(end_cond, loop_bb, after_bb);
|
|
||||||
builder.position_at_end(after_bb);
|
|
||||||
|
|
||||||
store_list(env, list_ptr, list_len)
|
|
||||||
};
|
|
||||||
|
|
||||||
let build_else = || empty_polymorphic_list(env);
|
|
||||||
|
|
||||||
let struct_type = collection(ctx, env.ptr_bytes);
|
|
||||||
|
|
||||||
build_basic_phi2(
|
|
||||||
env,
|
|
||||||
parent,
|
|
||||||
comparison,
|
|
||||||
build_then,
|
|
||||||
build_else,
|
|
||||||
BasicTypeEnum::StructType(struct_type),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1028,6 +982,9 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||||
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||||
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
|
|
||||||
|
let inc_element_fn = build_inc_wrapper(env, layout_ids, element_layout);
|
||||||
|
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||||
|
|
||||||
let output = call_bitcode_fn(
|
let output = call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -1037,6 +994,8 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||||
stepper_caller.into(),
|
stepper_caller.into(),
|
||||||
alignment_iv.into(),
|
alignment_iv.into(),
|
||||||
element_width.into(),
|
element_width.into(),
|
||||||
|
inc_element_fn.as_global_value().as_pointer_value().into(),
|
||||||
|
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||||
],
|
],
|
||||||
&bitcode::LIST_KEEP_IF,
|
&bitcode::LIST_KEEP_IF,
|
||||||
);
|
);
|
||||||
|
@ -1138,6 +1097,9 @@ pub fn list_keep_result<'a, 'ctx, 'env>(
|
||||||
let alignment = before_layout.alignment_bytes(env.ptr_bytes);
|
let alignment = before_layout.alignment_bytes(env.ptr_bytes);
|
||||||
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
|
|
||||||
|
let inc_closure = build_inc_wrapper(env, layout_ids, transform_layout);
|
||||||
|
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||||
|
|
||||||
let output = call_bitcode_fn(
|
let output = call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -1149,6 +1111,8 @@ pub fn list_keep_result<'a, 'ctx, 'env>(
|
||||||
before_width.into(),
|
before_width.into(),
|
||||||
result_width.into(),
|
result_width.into(),
|
||||||
after_width.into(),
|
after_width.into(),
|
||||||
|
inc_closure.as_global_value().as_pointer_value().into(),
|
||||||
|
dec_result_fn.as_global_value().as_pointer_value().into(),
|
||||||
],
|
],
|
||||||
op,
|
op,
|
||||||
);
|
);
|
||||||
|
@ -1532,7 +1496,7 @@ where
|
||||||
"bounds_check",
|
"bounds_check",
|
||||||
);
|
);
|
||||||
|
|
||||||
let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop");
|
let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop_1");
|
||||||
|
|
||||||
builder.build_conditional_branch(condition, loop_bb, after_loop_bb);
|
builder.build_conditional_branch(condition, loop_bb, after_loop_bb);
|
||||||
builder.position_at_end(after_loop_bb);
|
builder.position_at_end(after_loop_bb);
|
||||||
|
@ -1599,7 +1563,7 @@ where
|
||||||
// #index < end
|
// #index < end
|
||||||
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
let loop_end_cond = bounds_check_comparison(builder, next_index, end);
|
||||||
|
|
||||||
let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop");
|
let after_loop_bb = ctx.append_basic_block(parent, "after_outer_loop_2");
|
||||||
|
|
||||||
builder.build_conditional_branch(loop_end_cond, loop_bb, after_loop_bb);
|
builder.build_conditional_branch(loop_end_cond, loop_bb, after_loop_bb);
|
||||||
builder.position_at_end(after_loop_bb);
|
builder.position_at_end(after_loop_bb);
|
||||||
|
|
|
@ -285,6 +285,7 @@ fn modify_refcount_struct<'a, 'ctx, 'env>(
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
layouts: &[Layout<'a>],
|
layouts: &[Layout<'a>],
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
) {
|
) {
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
|
|
||||||
|
@ -295,7 +296,15 @@ fn modify_refcount_struct<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
.build_extract_value(wrapper_struct, i as u32, "decrement_struct_field")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
modify_refcount_layout(env, parent, layout_ids, mode, field_ptr, field_layout);
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
field_ptr,
|
||||||
|
field_layout,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,9 +339,9 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
fn modify_refcount_builtin<'a, 'ctx, 'env>(
|
fn modify_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
builtin: &Builtin<'a>,
|
builtin: &Builtin<'a>,
|
||||||
|
@ -342,30 +351,17 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
match builtin {
|
match builtin {
|
||||||
List(memory_mode, element_layout) => {
|
List(memory_mode, element_layout) => {
|
||||||
let wrapper_struct = value.into_struct_value();
|
let wrapper_struct = value.into_struct_value();
|
||||||
if element_layout.contains_refcounted() {
|
|
||||||
let ptr_type =
|
|
||||||
basic_type_from_layout(env.arena, env.context, element_layout, env.ptr_bytes)
|
|
||||||
.ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let (len, ptr) = load_list(env.builder, wrapper_struct, ptr_type);
|
|
||||||
|
|
||||||
let loop_fn = |_index, element| {
|
|
||||||
modify_refcount_layout(env, parent, layout_ids, mode, element, element_layout);
|
|
||||||
};
|
|
||||||
|
|
||||||
incrementing_elem_loop(
|
|
||||||
env.builder,
|
|
||||||
env.context,
|
|
||||||
parent,
|
|
||||||
ptr,
|
|
||||||
len,
|
|
||||||
"modify_rc_index",
|
|
||||||
loop_fn,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let MemoryMode::Refcounted = memory_mode {
|
if let MemoryMode::Refcounted = memory_mode {
|
||||||
modify_refcount_list(env, layout_ids, mode, layout, wrapper_struct);
|
modify_refcount_list(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
layout,
|
||||||
|
element_layout,
|
||||||
|
wrapper_struct,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set(element_layout) => {
|
Set(element_layout) => {
|
||||||
|
@ -380,6 +376,7 @@ fn modify_refcount_builtin<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
when_recursive,
|
||||||
layout,
|
layout,
|
||||||
key_layout,
|
key_layout,
|
||||||
value_layout,
|
value_layout,
|
||||||
|
@ -404,13 +401,45 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
&WhenRecursive::Unreachable,
|
||||||
|
value,
|
||||||
|
layout,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum WhenRecursive<'a> {
|
||||||
|
Unreachable,
|
||||||
|
Loop(UnionLayout<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify_refcount_layout_help<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
|
value: BasicValueEnum<'ctx>,
|
||||||
|
layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
use Layout::*;
|
use Layout::*;
|
||||||
|
|
||||||
match layout {
|
match layout {
|
||||||
Builtin(builtin) => {
|
Builtin(builtin) => modify_refcount_builtin(
|
||||||
modify_refcount_builtin(env, parent, layout_ids, mode, value, layout, builtin)
|
env,
|
||||||
}
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
value,
|
||||||
|
layout,
|
||||||
|
builtin,
|
||||||
|
),
|
||||||
|
|
||||||
Union(variant) => {
|
Union(variant) => {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
@ -425,6 +454,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
&WhenRecursive::Loop(variant.clone()),
|
||||||
tags,
|
tags,
|
||||||
value.into_pointer_value(),
|
value.into_pointer_value(),
|
||||||
true,
|
true,
|
||||||
|
@ -440,6 +470,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
&WhenRecursive::Loop(variant.clone()),
|
||||||
&*env.arena.alloc([other_fields]),
|
&*env.arena.alloc([other_fields]),
|
||||||
value.into_pointer_value(),
|
value.into_pointer_value(),
|
||||||
true,
|
true,
|
||||||
|
@ -453,6 +484,7 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
&WhenRecursive::Loop(variant.clone()),
|
||||||
&*env.arena.alloc([*fields]),
|
&*env.arena.alloc([*fields]),
|
||||||
value.into_pointer_value(),
|
value.into_pointer_value(),
|
||||||
true,
|
true,
|
||||||
|
@ -465,13 +497,16 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
&WhenRecursive::Loop(variant.clone()),
|
||||||
tags,
|
tags,
|
||||||
value.into_pointer_value(),
|
value.into_pointer_value(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
NonRecursive(tags) => modify_refcount_union(env, layout_ids, mode, tags, value),
|
NonRecursive(tags) => {
|
||||||
|
modify_refcount_union(env, layout_ids, mode, when_recursive, tags, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Closure(_, closure_layout, _) => {
|
Closure(_, closure_layout, _) => {
|
||||||
|
@ -483,11 +518,12 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(wrapper_struct, 1, "modify_rc_closure_data")
|
.build_extract_value(wrapper_struct, 1, "modify_rc_closure_data")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
modify_refcount_layout(
|
modify_refcount_layout_help(
|
||||||
env,
|
env,
|
||||||
parent,
|
parent,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
when_recursive,
|
||||||
field_ptr,
|
field_ptr,
|
||||||
&closure_layout.as_block_of_memory_layout(),
|
&closure_layout.as_block_of_memory_layout(),
|
||||||
)
|
)
|
||||||
|
@ -495,12 +531,45 @@ fn modify_refcount_layout<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Struct(layouts) => {
|
Struct(layouts) => {
|
||||||
modify_refcount_struct(env, parent, layout_ids, value, layouts, mode);
|
modify_refcount_struct(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
value,
|
||||||
|
layouts,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PhantomEmptyStruct => {}
|
PhantomEmptyStruct => {}
|
||||||
|
|
||||||
RecursivePointer => todo!("TODO implement decrement layout of recursive tag union"),
|
Layout::RecursivePointer => match when_recursive {
|
||||||
|
WhenRecursive::Unreachable => {
|
||||||
|
unreachable!("recursion pointers should never be hashed directly")
|
||||||
|
}
|
||||||
|
WhenRecursive::Loop(union_layout) => {
|
||||||
|
let layout = Layout::Union(union_layout.clone());
|
||||||
|
|
||||||
|
let bt = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
|
||||||
|
|
||||||
|
// cast the i64 pointer to a pointer to block of memory
|
||||||
|
let field_cast = env
|
||||||
|
.builder
|
||||||
|
.build_bitcast(value, bt, "i64_to_opaque")
|
||||||
|
.into_pointer_value();
|
||||||
|
|
||||||
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
field_cast.into(),
|
||||||
|
&layout,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
FunctionPointer(_, _) | Pointer(_) => {}
|
FunctionPointer(_, _) | Pointer(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -510,7 +579,9 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
element_layout: &Layout<'a>,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||||
|
@ -531,7 +602,15 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
|
||||||
let basic_type = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
|
let basic_type = basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
|
||||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||||
|
|
||||||
modify_refcount_list_help(env, mode, layout, function_value);
|
modify_refcount_list_help(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
layout,
|
||||||
|
element_layout,
|
||||||
|
function_value,
|
||||||
|
);
|
||||||
|
|
||||||
function_value
|
function_value
|
||||||
}
|
}
|
||||||
|
@ -553,8 +632,11 @@ fn mode_to_call_mode(function: FunctionValue<'_>, mode: Mode) -> CallMode<'_> {
|
||||||
|
|
||||||
fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
|
element_layout: &Layout<'a>,
|
||||||
fn_val: FunctionValue<'ctx>,
|
fn_val: FunctionValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
@ -593,6 +675,36 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(modification_block);
|
builder.position_at_end(modification_block);
|
||||||
|
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
|
let ptr_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, element_layout, env.ptr_bytes)
|
||||||
|
.ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
|
let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type);
|
||||||
|
|
||||||
|
let loop_fn = |_index, element| {
|
||||||
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
element,
|
||||||
|
element_layout,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
incrementing_elem_loop(
|
||||||
|
env.builder,
|
||||||
|
env.context,
|
||||||
|
parent,
|
||||||
|
ptr,
|
||||||
|
len,
|
||||||
|
"modify_rc_index",
|
||||||
|
loop_fn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
|
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
|
||||||
let call_mode = mode_to_call_mode(fn_val, mode);
|
let call_mode = mode_to_call_mode(fn_val, mode);
|
||||||
refcount_ptr.modify(call_mode, layout, env);
|
refcount_ptr.modify(call_mode, layout, env);
|
||||||
|
@ -701,10 +813,12 @@ fn modify_refcount_str_help<'a, 'ctx, 'env>(
|
||||||
builder.build_return(None);
|
builder.build_return(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn modify_refcount_dict<'a, 'ctx, 'env>(
|
fn modify_refcount_dict<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
key_layout: &Layout<'a>,
|
key_layout: &Layout<'a>,
|
||||||
value_layout: &Layout<'a>,
|
value_layout: &Layout<'a>,
|
||||||
|
@ -732,6 +846,7 @@ fn modify_refcount_dict<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
mode,
|
mode,
|
||||||
|
when_recursive,
|
||||||
layout,
|
layout,
|
||||||
key_layout,
|
key_layout,
|
||||||
value_layout,
|
value_layout,
|
||||||
|
@ -749,15 +864,23 @@ fn modify_refcount_dict<'a, 'ctx, 'env>(
|
||||||
call_help(env, function, mode, original_wrapper.into(), call_name);
|
call_help(env, function, mode, original_wrapper.into(), call_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn modify_refcount_dict_help<'a, 'ctx, 'env>(
|
fn modify_refcount_dict_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
key_layout: &Layout<'a>,
|
key_layout: &Layout<'a>,
|
||||||
value_layout: &Layout<'a>,
|
value_layout: &Layout<'a>,
|
||||||
fn_val: FunctionValue<'ctx>,
|
fn_val: FunctionValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
when_recursive,
|
||||||
|
&WhenRecursive::Unreachable,
|
||||||
|
"TODO pipe when_recursive through the dict key/value inc/dec"
|
||||||
|
);
|
||||||
|
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
|
@ -784,7 +907,7 @@ fn modify_refcount_dict_help<'a, 'ctx, 'env>(
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
// the block we'll always jump to when we're done
|
// the block we'll always jump to when we're done
|
||||||
let cont_block = ctx.append_basic_block(parent, "modify_rc_str_cont");
|
let cont_block = ctx.append_basic_block(parent, "modify_rc_dict_cont");
|
||||||
let modification_block = ctx.append_basic_block(parent, "modify_rc");
|
let modification_block = ctx.append_basic_block(parent, "modify_rc");
|
||||||
|
|
||||||
let is_non_empty = builder.build_int_compare(
|
let is_non_empty = builder.build_int_compare(
|
||||||
|
@ -894,6 +1017,7 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
fields: &'a [&'a [Layout<'a>]],
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
value: PointerValue<'ctx>,
|
value: PointerValue<'ctx>,
|
||||||
is_nullable: bool,
|
is_nullable: bool,
|
||||||
|
@ -920,7 +1044,15 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
||||||
.into();
|
.into();
|
||||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||||
|
|
||||||
build_rec_union_help(env, layout_ids, mode, fields, function_value, is_nullable);
|
build_rec_union_help(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
fields,
|
||||||
|
function_value,
|
||||||
|
is_nullable,
|
||||||
|
);
|
||||||
|
|
||||||
env.builder.position_at_end(block);
|
env.builder.position_at_end(block);
|
||||||
env.builder
|
env.builder
|
||||||
|
@ -937,6 +1069,7 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
tags: &[&[Layout<'a>]],
|
tags: &[&[Layout<'a>]],
|
||||||
fn_val: FunctionValue<'ctx>,
|
fn_val: FunctionValue<'ctx>,
|
||||||
is_nullable: bool,
|
is_nullable: bool,
|
||||||
|
@ -1093,7 +1226,15 @@ fn build_rec_union_help<'a, 'ctx, 'env>(
|
||||||
refcount_ptr.modify(call_mode, &layout, env);
|
refcount_ptr.modify(call_mode, &layout, env);
|
||||||
|
|
||||||
for (field, field_layout) in deferred_nonrec {
|
for (field, field_layout) in deferred_nonrec {
|
||||||
modify_refcount_layout(env, parent, layout_ids, mode, field, field_layout);
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
field,
|
||||||
|
field_layout,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let call_name = pick("recursive_tag_increment", "recursive_tag_decrement");
|
let call_name = pick("recursive_tag_increment", "recursive_tag_decrement");
|
||||||
|
@ -1208,6 +1349,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
fields: &'a [&'a [Layout<'a>]],
|
fields: &'a [&'a [Layout<'a>]],
|
||||||
value: BasicValueEnum<'ctx>,
|
value: BasicValueEnum<'ctx>,
|
||||||
) {
|
) {
|
||||||
|
@ -1231,7 +1373,14 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
||||||
let basic_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
let basic_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||||
|
|
||||||
modify_refcount_union_help(env, layout_ids, mode, fields, function_value);
|
modify_refcount_union_help(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
fields,
|
||||||
|
function_value,
|
||||||
|
);
|
||||||
|
|
||||||
function_value
|
function_value
|
||||||
}
|
}
|
||||||
|
@ -1248,6 +1397,7 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
when_recursive: &WhenRecursive<'a>,
|
||||||
tags: &[&[Layout<'a>]],
|
tags: &[&[Layout<'a>]],
|
||||||
fn_val: FunctionValue<'ctx>,
|
fn_val: FunctionValue<'ctx>,
|
||||||
) {
|
) {
|
||||||
|
@ -1332,7 +1482,15 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||||
.build_extract_value(wrapper_struct, i as u32, "modify_tag_field")
|
.build_extract_value(wrapper_struct, i as u32, "modify_tag_field")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
modify_refcount_layout(env, parent, layout_ids, mode, field_ptr, field_layout);
|
modify_refcount_layout_help(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
layout_ids,
|
||||||
|
mode,
|
||||||
|
when_recursive,
|
||||||
|
field_ptr,
|
||||||
|
field_layout,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -912,23 +912,24 @@ define_builtins! {
|
||||||
2 DICT_EMPTY: "empty"
|
2 DICT_EMPTY: "empty"
|
||||||
3 DICT_SINGLETON: "singleton"
|
3 DICT_SINGLETON: "singleton"
|
||||||
4 DICT_GET: "get"
|
4 DICT_GET: "get"
|
||||||
5 DICT_INSERT: "insert"
|
5 DICT_GET_RESULT: "#get_result" // symbol used in the definition of Dict.get
|
||||||
6 DICT_LEN: "len"
|
6 DICT_WALK: "walk"
|
||||||
|
7 DICT_INSERT: "insert"
|
||||||
|
8 DICT_LEN: "len"
|
||||||
|
|
||||||
// This should not be exposed to users, its for testing the
|
// This should not be exposed to users, its for testing the
|
||||||
// hash function ONLY
|
// hash function ONLY
|
||||||
7 DICT_TEST_HASH: "hashTestOnly"
|
9 DICT_TEST_HASH: "hashTestOnly"
|
||||||
|
|
||||||
8 DICT_REMOVE: "remove"
|
10 DICT_REMOVE: "remove"
|
||||||
9 DICT_CONTAINS: "contains"
|
11 DICT_CONTAINS: "contains"
|
||||||
10 DICT_KEYS: "keys"
|
12 DICT_KEYS: "keys"
|
||||||
11 DICT_VALUES: "values"
|
13 DICT_VALUES: "values"
|
||||||
|
|
||||||
12 DICT_UNION: "union"
|
14 DICT_UNION: "union"
|
||||||
13 DICT_INTERSECTION: "intersection"
|
15 DICT_INTERSECTION: "intersection"
|
||||||
14 DICT_DIFFERENCE: "difference"
|
16 DICT_DIFFERENCE: "difference"
|
||||||
|
|
||||||
15 DICT_WALK: "walk"
|
|
||||||
|
|
||||||
}
|
}
|
||||||
7 SET: "Set" => {
|
7 SET: "Set" => {
|
||||||
|
|
|
@ -610,7 +610,9 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[bool] {
|
pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[bool] {
|
||||||
let all = bumpalo::vec![in arena; false; arity];
|
// NOTE this means that Roc is responsible for cleaning up resources;
|
||||||
|
// the host cannot (currently) take ownership
|
||||||
|
let all = bumpalo::vec![in arena; true; arity];
|
||||||
all.into_bump_slice()
|
all.into_bump_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,16 +634,16 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListConcat | StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
ListConcat | StrConcat => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
ListSingle => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
ListRepeat => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]),
|
||||||
ListReverse => arena.alloc_slice_copy(&[owned]),
|
ListReverse => arena.alloc_slice_copy(&[owned]),
|
||||||
ListPrepend => arena.alloc_slice_copy(&[owned, owned]),
|
ListPrepend => arena.alloc_slice_copy(&[owned, owned]),
|
||||||
StrJoinWith => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
||||||
ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
||||||
|
@ -651,9 +653,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
// List.append should own its first argument
|
// List.append should own its first argument
|
||||||
ListAppend => arena.alloc_slice_copy(&[borrowed, owned]),
|
ListAppend => arena.alloc_slice_copy(&[borrowed, owned]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
|
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte
|
|
||||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
|
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap | NumSubChecked
|
||||||
|
| NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare
|
||||||
|
| NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd
|
||||||
| NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
| NumBitwiseXor => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||||
|
|
||||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||||
|
|
|
@ -729,8 +729,12 @@ impl<'a> Context<'a> {
|
||||||
layout,
|
layout,
|
||||||
} => {
|
} => {
|
||||||
// TODO this combines parts of Let and Switch. Did this happen correctly?
|
// TODO this combines parts of Let and Switch. Did this happen correctly?
|
||||||
let mut case_live_vars = collect_stmt(stmt, &self.jp_live_vars, MutSet::default());
|
let mut case_live_vars = collect_stmt(pass, &self.jp_live_vars, MutSet::default());
|
||||||
|
case_live_vars.extend(collect_stmt(fail, &self.jp_live_vars, MutSet::default()));
|
||||||
|
|
||||||
|
// the result of an invoke should not be touched in the fail branch
|
||||||
|
// but it should be present in the pass branch (otherwise it would be dead)
|
||||||
|
debug_assert!(case_live_vars.contains(symbol));
|
||||||
case_live_vars.remove(symbol);
|
case_live_vars.remove(symbol);
|
||||||
|
|
||||||
let fail = {
|
let fail = {
|
||||||
|
@ -758,9 +762,50 @@ impl<'a> Context<'a> {
|
||||||
layout: layout.clone(),
|
layout: layout.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stmt = self.arena.alloc(invoke);
|
let cont = self.arena.alloc(invoke);
|
||||||
|
|
||||||
(stmt, case_live_vars)
|
use crate::ir::CallType;
|
||||||
|
let stmt = match &call.call_type {
|
||||||
|
CallType::LowLevel { op } => {
|
||||||
|
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||||
|
self.add_dec_after_lowlevel(call.arguments, ps, cont, &case_live_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
CallType::Foreign { .. } => {
|
||||||
|
let ps = crate::borrow::foreign_borrow_signature(
|
||||||
|
self.arena,
|
||||||
|
call.arguments.len(),
|
||||||
|
);
|
||||||
|
self.add_dec_after_lowlevel(call.arguments, ps, cont, &case_live_vars)
|
||||||
|
}
|
||||||
|
|
||||||
|
CallType::ByName {
|
||||||
|
name, full_layout, ..
|
||||||
|
} => {
|
||||||
|
// get the borrow signature
|
||||||
|
match self.param_map.get_symbol(*name, full_layout.clone()) {
|
||||||
|
Some(ps) => self.add_dec_after_application(
|
||||||
|
call.arguments,
|
||||||
|
ps,
|
||||||
|
cont,
|
||||||
|
&case_live_vars,
|
||||||
|
),
|
||||||
|
None => self.add_inc_before_consume_all(
|
||||||
|
call.arguments,
|
||||||
|
cont,
|
||||||
|
&case_live_vars,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CallType::ByPointer { .. } => {
|
||||||
|
self.add_inc_before_consume_all(call.arguments, cont, &case_live_vars)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut invoke_live_vars = case_live_vars;
|
||||||
|
occuring_variables_call(call, &mut invoke_live_vars);
|
||||||
|
|
||||||
|
(stmt, invoke_live_vars)
|
||||||
}
|
}
|
||||||
Join {
|
Join {
|
||||||
id: j,
|
id: j,
|
||||||
|
|
|
@ -5183,10 +5183,7 @@ fn store_pattern_help<'a>(
|
||||||
return StorePattern::NotProductive(stmt);
|
return StorePattern::NotProductive(stmt);
|
||||||
}
|
}
|
||||||
AppliedTag {
|
AppliedTag {
|
||||||
arguments,
|
arguments, layout, ..
|
||||||
layout,
|
|
||||||
tag_name,
|
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
let wrapped = Wrapped::from_layout(layout);
|
let wrapped = Wrapped::from_layout(layout);
|
||||||
let write_tag = wrapped == Wrapped::MultiTagUnion;
|
let write_tag = wrapped == Wrapped::MultiTagUnion;
|
||||||
|
@ -5241,12 +5238,6 @@ fn store_pattern_help<'a>(
|
||||||
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
||||||
StorePattern::Productive(new) => {
|
StorePattern::Productive(new) => {
|
||||||
is_productive = true;
|
is_productive = true;
|
||||||
println!(
|
|
||||||
"Access {:?}.{:?} {:?}",
|
|
||||||
tag_name.clone(),
|
|
||||||
outer_symbol,
|
|
||||||
index
|
|
||||||
);
|
|
||||||
stmt = new;
|
stmt = new;
|
||||||
// only if we bind one of its (sub)fields to a used name should we
|
// only if we bind one of its (sub)fields to a used name should we
|
||||||
// extract the field
|
// extract the field
|
||||||
|
@ -5948,7 +5939,8 @@ fn call_by_name<'a>(
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
arg_layouts.len(),
|
arg_layouts.len(),
|
||||||
field_symbols.len(),
|
field_symbols.len(),
|
||||||
"see call_by_name for background (scroll down a bit)"
|
"see call_by_name for background (scroll down a bit), function is {:?}",
|
||||||
|
proc_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
let call = self::Call {
|
let call = self::Call {
|
||||||
|
@ -5999,7 +5991,8 @@ fn call_by_name<'a>(
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
arg_layouts.len(),
|
arg_layouts.len(),
|
||||||
field_symbols.len(),
|
field_symbols.len(),
|
||||||
"see call_by_name for background (scroll down a bit)"
|
"see call_by_name for background (scroll down a bit), function is {:?}",
|
||||||
|
proc_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
let call = self::Call {
|
let call = self::Call {
|
||||||
|
@ -6521,8 +6514,10 @@ fn from_can_pattern_help<'a>(
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
argument_layouts[1..].len(),
|
argument_layouts[1..].len(),
|
||||||
"{:?}",
|
"The {:?} tag got {} arguments, but its layout expects {}!",
|
||||||
tag_name
|
tag_name,
|
||||||
|
arguments.len(),
|
||||||
|
argument_layouts[1..].len(),
|
||||||
);
|
);
|
||||||
let it = argument_layouts[1..].iter();
|
let it = argument_layouts[1..].iter();
|
||||||
|
|
||||||
|
|
|
@ -880,7 +880,7 @@ impl<'a> Builtin<'a> {
|
||||||
|
|
||||||
/// Number of machine words in an empty one of these
|
/// Number of machine words in an empty one of these
|
||||||
pub const STR_WORDS: u32 = 2;
|
pub const STR_WORDS: u32 = 2;
|
||||||
pub const DICT_WORDS: u32 = 6;
|
pub const DICT_WORDS: u32 = 3;
|
||||||
pub const SET_WORDS: u32 = Builtin::DICT_WORDS; // Set is an alias for Dict with {} for value
|
pub const SET_WORDS: u32 = Builtin::DICT_WORDS; // Set is an alias for Dict with {} for value
|
||||||
pub const LIST_WORDS: u32 = 2;
|
pub const LIST_WORDS: u32 = 2;
|
||||||
|
|
||||||
|
|
|
@ -646,13 +646,13 @@ mod test_mono {
|
||||||
let Test.4 = lowlevel DictEmpty ;
|
let Test.4 = lowlevel DictEmpty ;
|
||||||
ret Test.4;
|
ret Test.4;
|
||||||
|
|
||||||
procedure Dict.6 (#Attr.2):
|
procedure Dict.8 (#Attr.2):
|
||||||
let Test.3 = lowlevel DictSize #Attr.2;
|
let Test.3 = lowlevel DictSize #Attr.2;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 = FunctionPointer Dict.2;
|
let Test.2 = FunctionPointer Dict.2;
|
||||||
let Test.1 = CallByName Dict.6 Test.2;
|
let Test.1 = CallByName Dict.8 Test.2;
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
interface AStar exposes [ findPath, Model, initialModel ] imports [Quicksort]
|
interface AStar exposes [ findPath, Model, initialModel, cheapestOpen, takeStep, reconstructPath ] imports [Quicksort]
|
||||||
|
|
||||||
findPath = \costFn, moveFn, start, end ->
|
findPath = \costFn, moveFn, start, end ->
|
||||||
astar costFn moveFn end (initialModel start)
|
astar costFn moveFn end (initialModel start)
|
||||||
|
|
||||||
Model position :
|
Model position :
|
||||||
|
@ -14,9 +14,9 @@ Model position :
|
||||||
initialModel : position -> Model position
|
initialModel : position -> Model position
|
||||||
initialModel = \start ->
|
initialModel = \start ->
|
||||||
{
|
{
|
||||||
evaluated : Set.empty,
|
evaluated : Set.empty,
|
||||||
openSet : Set.singleton start,
|
openSet : Set.singleton start,
|
||||||
costs : Dict.singleton start 0,
|
costs : Dict.singleton start 0,
|
||||||
cameFrom : Dict.empty
|
cameFrom : Dict.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,44 +50,28 @@ reconstructPath = \cameFrom, goal ->
|
||||||
|
|
||||||
updateCost : position, position, Model position -> Model position
|
updateCost : position, position, Model position -> Model position
|
||||||
updateCost = \current, neighbor, model ->
|
updateCost = \current, neighbor, model ->
|
||||||
|
newCameFrom =
|
||||||
|
Dict.insert model.cameFrom neighbor current
|
||||||
|
|
||||||
|
newCosts =
|
||||||
|
Dict.insert model.costs neighbor distanceTo
|
||||||
|
|
||||||
|
distanceTo =
|
||||||
|
reconstructPath newCameFrom neighbor
|
||||||
|
|> List.len
|
||||||
|
|> Num.toFloat
|
||||||
|
|
||||||
|
newModel =
|
||||||
|
{ model &
|
||||||
|
costs: newCosts,
|
||||||
|
cameFrom: newCameFrom
|
||||||
|
}
|
||||||
|
|
||||||
when Dict.get model.costs neighbor is
|
when Dict.get model.costs neighbor is
|
||||||
Err _ ->
|
Err _ ->
|
||||||
newCameFrom =
|
newModel
|
||||||
Dict.insert model.cameFrom neighbor current
|
|
||||||
|
|
||||||
newCosts =
|
|
||||||
Dict.insert model.costs neighbor distanceTo
|
|
||||||
|
|
||||||
distanceTo =
|
|
||||||
reconstructPath newCameFrom neighbor
|
|
||||||
|> List.len
|
|
||||||
|> Num.toFloat
|
|
||||||
|
|
||||||
{ model &
|
|
||||||
costs: newCosts,
|
|
||||||
cameFrom: newCameFrom
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok previousDistance ->
|
Ok previousDistance ->
|
||||||
|
|
||||||
newCameFrom =
|
|
||||||
Dict.insert model.cameFrom neighbor current
|
|
||||||
|
|
||||||
newCosts =
|
|
||||||
Dict.insert model.costs neighbor distanceTo
|
|
||||||
|
|
||||||
distanceTo =
|
|
||||||
reconstructPath newCameFrom neighbor
|
|
||||||
|> List.len
|
|
||||||
|> Num.toFloat
|
|
||||||
|
|
||||||
newModel =
|
|
||||||
{ model &
|
|
||||||
costs: newCosts,
|
|
||||||
cameFrom: newCameFrom
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if distanceTo < previousDistance then
|
if distanceTo < previousDistance then
|
||||||
newModel
|
newModel
|
||||||
|
|
||||||
|
@ -126,3 +110,27 @@ astar = \costFn, moveFn, goal, model ->
|
||||||
Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors
|
Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors
|
||||||
|
|
||||||
astar costFn moveFn goal modelWithCosts
|
astar costFn moveFn goal modelWithCosts
|
||||||
|
|
||||||
|
takeStep = \moveFn, _goal, model, current ->
|
||||||
|
modelPopped =
|
||||||
|
{ model &
|
||||||
|
openSet: Set.remove model.openSet current,
|
||||||
|
evaluated: Set.insert model.evaluated current,
|
||||||
|
}
|
||||||
|
|
||||||
|
neighbors = moveFn current
|
||||||
|
|
||||||
|
newNeighbors = Set.difference neighbors modelPopped.evaluated
|
||||||
|
|
||||||
|
modelWithNeighbors = { modelPopped & openSet: Set.union modelPopped.openSet newNeighbors }
|
||||||
|
|
||||||
|
# a lot goes wrong here
|
||||||
|
modelWithCosts =
|
||||||
|
Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors
|
||||||
|
|
||||||
|
modelWithCosts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,20 +3,18 @@ app "astar-tests"
|
||||||
imports [base.Task, AStar]
|
imports [base.Task, AStar]
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
fromList : List a -> Set a
|
|
||||||
fromList = \list -> List.walk list (\x, a -> Set.insert a x) Set.empty
|
|
||||||
|
|
||||||
|
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main =
|
main =
|
||||||
Task.after Task.getInt \n ->
|
Task.putLine (showBool test1)
|
||||||
when n is
|
|
||||||
1 ->
|
|
||||||
Task.putLine (showBool test1)
|
|
||||||
|
|
||||||
_ ->
|
# Task.after Task.getInt \n ->
|
||||||
ns = Str.fromInt n
|
# when n is
|
||||||
Task.putLine "No test \(ns)"
|
# 1 ->
|
||||||
|
# Task.putLine (showBool test1)
|
||||||
|
#
|
||||||
|
# _ ->
|
||||||
|
# ns = Str.fromInt n
|
||||||
|
# Task.putLine "No test \(ns)"
|
||||||
|
|
||||||
showBool : Bool -> Str
|
showBool : Bool -> Str
|
||||||
showBool = \b ->
|
showBool = \b ->
|
||||||
|
@ -26,17 +24,17 @@ showBool = \b ->
|
||||||
|
|
||||||
test1 : Bool
|
test1 : Bool
|
||||||
test1 =
|
test1 =
|
||||||
example1 == [3, 4]
|
example1 == [2, 4]
|
||||||
|
|
||||||
example1 : List I64
|
example1 : List I64
|
||||||
example1 =
|
example1 =
|
||||||
step : I64 -> Set I64
|
step : I64 -> Set I64
|
||||||
step = \n ->
|
step = \n ->
|
||||||
when n is
|
when n is
|
||||||
1 -> fromList [ 2,3 ]
|
1 -> Set.fromList [ 2,3 ]
|
||||||
2 -> fromList [4]
|
2 -> Set.fromList [4]
|
||||||
3 -> fromList [4]
|
3 -> Set.fromList [4]
|
||||||
_ -> fromList []
|
_ -> Set.fromList []
|
||||||
|
|
||||||
cost : I64, I64 -> F64
|
cost : I64, I64 -> F64
|
||||||
cost = \_, _ -> 1
|
cost = \_, _ -> 1
|
||||||
|
|
58
examples/benchmarks/Closure.roc
Normal file
58
examples/benchmarks/Closure.roc
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
app "closure"
|
||||||
|
packages { base: "platform" }
|
||||||
|
imports [base.Task]
|
||||||
|
provides [ main ] to base
|
||||||
|
|
||||||
|
# see https://github.com/rtfeldman/roc/issues/985
|
||||||
|
|
||||||
|
main : Task.Task {} []
|
||||||
|
main = closure1 {}
|
||||||
|
|> Task.after (\_ -> closure2 {})
|
||||||
|
|> Task.after (\_ -> closure2 {})
|
||||||
|
|> Task.after (\_ -> closure2 {})
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
closure1 : {} -> Task.Task {} []
|
||||||
|
closure1 = \_ ->
|
||||||
|
Task.succeed (foo toUnitBorrowed "a long string such that it's malloced")
|
||||||
|
|> Task.map (\_ -> {})
|
||||||
|
|
||||||
|
toUnitBorrowed = \x -> Str.countGraphemes x
|
||||||
|
|
||||||
|
foo = \f, x -> f x
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
closure2 : {} -> Task.Task {} []
|
||||||
|
closure2 = \_ ->
|
||||||
|
x : Str
|
||||||
|
x = "a long string such that it's malloced"
|
||||||
|
|
||||||
|
Task.succeed {}
|
||||||
|
|> Task.map (\_ -> x)
|
||||||
|
|> Task.map toUnit
|
||||||
|
|
||||||
|
toUnit = (\_ -> {})
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
closure3 : {} -> Task.Task {} []
|
||||||
|
closure3 = \_ ->
|
||||||
|
x : Str
|
||||||
|
x = "a long string such that it's malloced"
|
||||||
|
|
||||||
|
Task.succeed {}
|
||||||
|
|> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {}))
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
closure4 : {} -> Task.Task {} []
|
||||||
|
closure4 = \_ ->
|
||||||
|
x : Str
|
||||||
|
x = "a long string such that it's malloced"
|
||||||
|
|
||||||
|
Task.succeed {}
|
||||||
|
|> Task.after (\_ -> Task.succeed x)
|
||||||
|
|> Task.map (\_ -> {})
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
app "closure1"
|
|
||||||
packages { base: "platform" }
|
|
||||||
imports [base.Task]
|
|
||||||
provides [ main ] to base
|
|
||||||
|
|
||||||
# see https://github.com/rtfeldman/roc/issues/985
|
|
||||||
|
|
||||||
main : Task.Task {} []
|
|
||||||
main =
|
|
||||||
Task.succeed (foo toUnitBorrowed "a long string such that it's malloced")
|
|
||||||
|> Task.map (\_ -> {})
|
|
||||||
|
|
||||||
toUnitBorrowed = \x -> Str.countGraphemes x
|
|
||||||
|
|
||||||
foo = \f, x -> f x
|
|
|
@ -1,17 +0,0 @@
|
||||||
app "closure2"
|
|
||||||
packages { base: "platform" }
|
|
||||||
imports [base.Task]
|
|
||||||
provides [ main ] to base
|
|
||||||
|
|
||||||
# see https://github.com/rtfeldman/roc/issues/985
|
|
||||||
|
|
||||||
main : Task.Task {} []
|
|
||||||
main =
|
|
||||||
x : Str
|
|
||||||
x = "a long string such that it's malloced"
|
|
||||||
|
|
||||||
Task.succeed {}
|
|
||||||
|> Task.map (\_ -> x)
|
|
||||||
|> Task.map toUnit
|
|
||||||
|
|
||||||
toUnit = (\_ -> {})
|
|
|
@ -1,14 +0,0 @@
|
||||||
app "closure3"
|
|
||||||
packages { base: "platform" }
|
|
||||||
imports [base.Task]
|
|
||||||
provides [ main ] to base
|
|
||||||
|
|
||||||
# see https://github.com/rtfeldman/roc/issues/985
|
|
||||||
|
|
||||||
main : Task.Task {} []
|
|
||||||
main =
|
|
||||||
x : Str
|
|
||||||
x = "a long string such that it's malloced"
|
|
||||||
|
|
||||||
Task.succeed {}
|
|
||||||
|> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {}))
|
|
|
@ -1,16 +0,0 @@
|
||||||
app "closure4"
|
|
||||||
packages { base: "platform" }
|
|
||||||
imports [base.Task]
|
|
||||||
provides [ main ] to base
|
|
||||||
|
|
||||||
# see https://github.com/rtfeldman/roc/issues/985
|
|
||||||
|
|
||||||
main : Task.Task {} []
|
|
||||||
main =
|
|
||||||
x : Str
|
|
||||||
x = "a long string such that it's malloced"
|
|
||||||
|
|
||||||
Task.succeed {}
|
|
||||||
|> Task.after (\_ -> Task.succeed x)
|
|
||||||
|> Task.map (\_ -> {})
|
|
||||||
|
|
77
examples/benchmarks/Quicksort.roc
Normal file
77
examples/benchmarks/Quicksort.roc
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
interface Quicksort exposes [ sortBy, show ] imports []
|
||||||
|
|
||||||
|
show : List I64 -> Str
|
||||||
|
show = \list ->
|
||||||
|
if List.isEmpty list then
|
||||||
|
"[]"
|
||||||
|
else
|
||||||
|
content =
|
||||||
|
list
|
||||||
|
|> List.map Str.fromInt
|
||||||
|
|> Str.joinWith ", "
|
||||||
|
|
||||||
|
"[ \(content) ]"
|
||||||
|
|
||||||
|
sortBy : List a, (a -> Num *) -> List a
|
||||||
|
sortBy = \list, toComparable ->
|
||||||
|
sortWith list (\x, y -> Num.compare (toComparable x) (toComparable y))
|
||||||
|
|
||||||
|
Order a : a, a -> [ LT, GT, EQ ]
|
||||||
|
|
||||||
|
sortWith : List a, (a, a -> [ LT, GT, EQ ]) -> List a
|
||||||
|
sortWith = \list, order ->
|
||||||
|
n = List.len list
|
||||||
|
quicksortHelp list order 0 (n - 1)
|
||||||
|
|
||||||
|
quicksortHelp : List a, Order a, Nat, Nat -> List a
|
||||||
|
quicksortHelp = \list, order, low, high ->
|
||||||
|
if low < high then
|
||||||
|
when partition low high list order is
|
||||||
|
Pair partitionIndex partitioned ->
|
||||||
|
partitioned
|
||||||
|
|> quicksortHelp order low (partitionIndex - 1)
|
||||||
|
|> quicksortHelp order (partitionIndex + 1) high
|
||||||
|
else
|
||||||
|
list
|
||||||
|
|
||||||
|
|
||||||
|
partition : Nat, Nat, List a, Order a -> [ Pair Nat (List a) ]
|
||||||
|
partition = \low, high, initialList, order ->
|
||||||
|
when List.get initialList high is
|
||||||
|
Ok pivot ->
|
||||||
|
when partitionHelp (low - 1) low initialList order high pivot is
|
||||||
|
Pair newI newList ->
|
||||||
|
Pair (newI + 1) (swap (newI + 1) high newList)
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
Pair (low - 1) initialList
|
||||||
|
|
||||||
|
partitionHelp : Nat, Nat, List c, Order c, Nat, c -> [ Pair Nat (List c) ]
|
||||||
|
partitionHelp = \i, j, list, order, high, pivot ->
|
||||||
|
if j < high then
|
||||||
|
when List.get list j is
|
||||||
|
Ok value ->
|
||||||
|
when order value pivot is
|
||||||
|
LT | EQ ->
|
||||||
|
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) order high pivot
|
||||||
|
|
||||||
|
GT ->
|
||||||
|
partitionHelp i (j + 1) list order high pivot
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
Pair i list
|
||||||
|
else
|
||||||
|
Pair i list
|
||||||
|
|
||||||
|
|
||||||
|
swap : Nat, Nat, List a -> List a
|
||||||
|
swap = \i, j, list ->
|
||||||
|
when Pair (List.get list i) (List.get list j) is
|
||||||
|
Pair (Ok atI) (Ok atJ) ->
|
||||||
|
list
|
||||||
|
|> List.set i atJ
|
||||||
|
|> List.set j atI
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue