mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Merge pull request #8474 from avillega/list-with-capacity-builtin
Adds List.with_capacity builtin
This commit is contained in:
commit
cdcd9dd05e
6 changed files with 188 additions and 29 deletions
|
|
@ -154,6 +154,9 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
|
|||
if (env.common.findIdent("Builtin.List.concat")) |list_concat_ident| {
|
||||
try low_level_map.put(list_concat_ident, .list_concat);
|
||||
}
|
||||
if (env.common.findIdent("Builtin.List.with_capacity")) |list_with_capacity_ident| {
|
||||
try low_level_map.put(list_with_capacity_ident, .list_with_capacity);
|
||||
}
|
||||
if (env.common.findIdent("list_get_unsafe")) |list_get_unsafe_ident| {
|
||||
try low_level_map.put(list_get_unsafe_ident, .list_get_unsafe);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ Builtin :: [].{
|
|||
len : List(_item) -> U64
|
||||
is_empty : List(_item) -> Bool
|
||||
concat : List(item), List(item) -> List(item)
|
||||
with_capacity: U64 -> List(item)
|
||||
|
||||
is_eq : List(item), List(item) -> Bool
|
||||
where [item.is_eq : item, item -> Bool]
|
||||
|
|
|
|||
|
|
@ -472,6 +472,7 @@ pub const Expr = union(enum) {
|
|||
list_is_empty,
|
||||
list_get_unsafe,
|
||||
list_concat,
|
||||
list_with_capacity,
|
||||
|
||||
// Set operations
|
||||
set_is_empty,
|
||||
|
|
|
|||
|
|
@ -3571,39 +3571,47 @@ pub const Interpreter = struct {
|
|||
const list_a: *const builtins.list.RocList = @ptrCast(@alignCast(list_a_arg.ptr.?));
|
||||
const list_b: *const builtins.list.RocList = @ptrCast(@alignCast(list_b_arg.ptr.?));
|
||||
|
||||
// Get element layout
|
||||
const elem_layout_idx = list_a_arg.layout.data.list;
|
||||
const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx);
|
||||
const elem_size = self.runtime_layout_store.layoutSize(elem_layout);
|
||||
const elem_alignment = elem_layout.alignment(self.runtime_layout_store.targetUsize()).toByteUnits();
|
||||
const elem_alignment_u32: u32 = @intCast(elem_alignment);
|
||||
// Call listConcat, handling zero-sized types specially
|
||||
const result_list = if (list_a_arg.layout.tag == .list_of_zst)
|
||||
builtins.list.listConcat(
|
||||
list_a.*,
|
||||
list_b.*,
|
||||
1,
|
||||
0,
|
||||
false,
|
||||
null,
|
||||
&builtins.list.rcNone,
|
||||
null,
|
||||
&builtins.list.rcNone,
|
||||
roc_ops,
|
||||
)
|
||||
else blk: {
|
||||
const elem_layout_idx = list_a_arg.layout.data.list;
|
||||
const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx);
|
||||
const elem_size = self.runtime_layout_store.layoutSize(elem_layout);
|
||||
const elem_alignment: u32 = @intCast(elem_layout.alignment(self.runtime_layout_store.targetUsize()).toByteUnits());
|
||||
const elements_refcounted = elem_layout.isRefcounted();
|
||||
|
||||
// Determine if elements are refcounted
|
||||
const elements_refcounted = elem_layout.isRefcounted();
|
||||
var refcount_context = RefcountContext{
|
||||
.layout_store = &self.runtime_layout_store,
|
||||
.elem_layout = elem_layout,
|
||||
.roc_ops = roc_ops,
|
||||
};
|
||||
|
||||
// Set up context for refcount callbacks
|
||||
var refcount_context = RefcountContext{
|
||||
.layout_store = &self.runtime_layout_store,
|
||||
.elem_layout = elem_layout,
|
||||
.roc_ops = roc_ops,
|
||||
break :blk builtins.list.listConcat(
|
||||
list_a.*,
|
||||
list_b.*,
|
||||
elem_alignment,
|
||||
elem_size,
|
||||
elements_refcounted,
|
||||
if (elements_refcounted) @ptrCast(&refcount_context) else null,
|
||||
if (elements_refcounted) &listElementInc else &builtins.list.rcNone,
|
||||
if (elements_refcounted) @ptrCast(&refcount_context) else null,
|
||||
if (elements_refcounted) &listElementDec else &builtins.list.rcNone,
|
||||
roc_ops,
|
||||
);
|
||||
};
|
||||
|
||||
// Call listConcat with proper inc/dec callbacks.
|
||||
// If elements are refcounted, pass callbacks that will inc/dec each element.
|
||||
// Otherwise, pass no-op callbacks.
|
||||
const result_list = builtins.list.listConcat(
|
||||
list_a.*,
|
||||
list_b.*,
|
||||
elem_alignment_u32,
|
||||
elem_size,
|
||||
elements_refcounted,
|
||||
if (elements_refcounted) @ptrCast(&refcount_context) else null,
|
||||
if (elements_refcounted) &listElementInc else &builtins.list.rcNone,
|
||||
if (elements_refcounted) @ptrCast(&refcount_context) else null,
|
||||
if (elements_refcounted) &listElementDec else &builtins.list.rcNone,
|
||||
roc_ops,
|
||||
);
|
||||
|
||||
// Allocate space for the result list
|
||||
const result_layout = list_a_arg.layout; // Same layout as input
|
||||
var out = try self.pushRaw(result_layout, 0);
|
||||
|
|
@ -3616,6 +3624,64 @@ pub const Interpreter = struct {
|
|||
out.is_initialized = true;
|
||||
return out;
|
||||
},
|
||||
.list_with_capacity => {
|
||||
// List.with_capacity : U64 -> List(a)
|
||||
std.debug.assert(args.len == 1);
|
||||
const capacity_arg = args[0];
|
||||
const capacity_value = try self.extractNumericValue(capacity_arg);
|
||||
const capacity: u64 = @intCast(capacity_value.int);
|
||||
|
||||
std.debug.assert(return_rt_var != null);
|
||||
|
||||
const list_layout = try self.getRuntimeLayout(return_rt_var.?);
|
||||
std.debug.assert(list_layout.tag == .list or list_layout.tag == .list_of_zst);
|
||||
|
||||
// Call listWithCapacity, handling zero-sized types specially
|
||||
const result_list = if (list_layout.tag == .list_of_zst)
|
||||
builtins.list.listWithCapacity(
|
||||
@intCast(capacity),
|
||||
1,
|
||||
0,
|
||||
false,
|
||||
null,
|
||||
&builtins.list.rcNone,
|
||||
roc_ops,
|
||||
)
|
||||
else blk: {
|
||||
const elem_layout_idx = list_layout.data.list;
|
||||
const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx);
|
||||
const elem_width = self.runtime_layout_store.layoutSize(elem_layout);
|
||||
const elem_alignment = elem_layout.alignment(self.runtime_layout_store.targetUsize()).toByteUnits();
|
||||
const elements_refcounted = elem_layout.isRefcounted();
|
||||
|
||||
var refcount_context = RefcountContext{
|
||||
.layout_store = &self.runtime_layout_store,
|
||||
.elem_layout = elem_layout,
|
||||
.roc_ops = roc_ops,
|
||||
};
|
||||
|
||||
break :blk builtins.list.listWithCapacity(
|
||||
@intCast(capacity),
|
||||
@intCast(elem_alignment),
|
||||
elem_width,
|
||||
elements_refcounted,
|
||||
if (elements_refcounted) @ptrCast(&refcount_context) else null,
|
||||
if (elements_refcounted) &listElementInc else &builtins.list.rcNone,
|
||||
roc_ops,
|
||||
);
|
||||
};
|
||||
|
||||
// Allocate space for the result list
|
||||
var out = try self.pushRaw(list_layout, 0);
|
||||
out.is_initialized = false;
|
||||
|
||||
// Copy the result list structure to the output
|
||||
const result_ptr: *builtins.list.RocList = @ptrCast(@alignCast(out.ptr.?));
|
||||
result_ptr.* = result_list;
|
||||
|
||||
out.is_initialized = true;
|
||||
return out;
|
||||
},
|
||||
.set_is_empty => {
|
||||
// TODO: implement Set.is_empty
|
||||
self.triggerCrash("Set.is_empty not yet implemented", false, roc_ops);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ test "list refcount builtins - phase 12 limitation documented" {
|
|||
// - "e_low_level_lambda - List.concat with strings (refcounted elements)"
|
||||
// - "e_low_level_lambda - List.concat with nested lists (refcounted elements)"
|
||||
// - "e_low_level_lambda - List.concat with empty string list"
|
||||
// - "e_low_level_lambda - List.concat with zero-sized type"
|
||||
//
|
||||
// - "e_low_level_lambda - List.with_capacity of non refcounted elements creates empty list"
|
||||
// - "e_low_level_lambda - List.with_capacity of str (refcounted elements) creates empty list"
|
||||
// - "e_low_level_lambda - List.with_capacity of non refcounted elements can concat"
|
||||
// - "e_low_level_lambda - List.with_capacity of str (refcounted elements) can concat"
|
||||
// - "e_low_level_lambda - List.with_capacity without capacity, of str (refcounted elements) can concat"
|
||||
// - "e_low_level_lambda - List.with_capacity of zero-sized type creates empty list"
|
||||
//
|
||||
// interpreter_style_test.zig:
|
||||
// - "interpreter: match list pattern destructures"
|
||||
|
|
|
|||
|
|
@ -665,6 +665,86 @@ test "e_low_level_lambda - List.concat with empty string list" {
|
|||
try testing.expectEqual(@as(i128, 3), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.concat with zero-sized type" {
|
||||
const src =
|
||||
\\x : List({})
|
||||
\\x = List.concat([{}, {}], [{}, {}, {}])
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 1);
|
||||
try testing.expectEqual(@as(i128, 5), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity of non refcounted elements creates empty list" {
|
||||
const src =
|
||||
\\x : List(U64)
|
||||
\\x = List.with_capacity(10)
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 1);
|
||||
try testing.expectEqual(@as(i128, 0), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity of str (refcounted elements) creates empty list" {
|
||||
const src =
|
||||
\\x : List(Str)
|
||||
\\x = List.with_capacity(10)
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 1);
|
||||
try testing.expectEqual(@as(i128, 0), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity of non refcounted elements can concat" {
|
||||
const src =
|
||||
\\y : List(U64)
|
||||
\\y = List.with_capacity(10)
|
||||
\\x = List.concat(y, [1])
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 2);
|
||||
try testing.expectEqual(@as(i128, 1), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity of str (refcounted elements) can concat" {
|
||||
const src =
|
||||
\\y : List(Str)
|
||||
\\y = List.with_capacity(10)
|
||||
\\x = List.concat(y, ["hello", "world"])
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 2);
|
||||
try testing.expectEqual(@as(i128, 2), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity without capacity, of str (refcounted elements) can concat" {
|
||||
const src =
|
||||
\\y : List(Str)
|
||||
\\y = List.with_capacity(0)
|
||||
\\x = List.concat(y, ["hello", "world"])
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 2);
|
||||
try testing.expectEqual(@as(i128, 2), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - List.with_capacity of zero-sized type creates empty list" {
|
||||
const src =
|
||||
\\x : List({})
|
||||
\\x = List.with_capacity(10)
|
||||
\\len = List.len(x)
|
||||
;
|
||||
|
||||
const len_value = try evalModuleAndGetInt(src, 1);
|
||||
try testing.expectEqual(@as(i128, 0), len_value);
|
||||
}
|
||||
|
||||
test "e_low_level_lambda - Dec.to_str returns string representation of decimal" {
|
||||
const src =
|
||||
\\a : Dec
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue