refactor: List is slice-backed { items: []T; cap } — directly iterable
items is now a []T slice whose .len IS the live element count (cap = allocated
capacity), so a List iterates directly: `for xs.items (e) { ... }`. A
`len :: (self) -> i64 #get => items.len` accessor keeps `xs.len` reads working;
`.len` WRITES become `.items.len`. List stays 24 bytes (`[]T`=16 + cap=8).
- list.sx: append/ensure_capacity/deinit rewritten for the slice backing. deinit
guards the free on `cap > 0` (true ownership) and resets via explicit
ptr=null/len=0 (a `.{}` slice assignment yields a garbage len; `.[]` is the
empty-slice literal but can't be assigned to a generic []T — both worked around).
- Compiler coupling updated: comptime_vm makeStringList/readStringList write/read
items as a {ptr,len} fat pointer at field 0 + cap at field 1; control_flow
listView views an `items: []T` slice (keeps the legacy {[*]T,len} shape too).
- Migrated List `.len` writes to `.items.len` in sched.sx + ui/{render,pipeline,
glyph_cache} + platform/{sdl3,android,uikit}.
- Snapshots: List's type-table layout changed → ~40 .ir + memory/0800 (items now
prints as a slice) regenerated; diagnostics/1183 retargeted to a genuine
many-pointer (xs.items is a slice now). Example memory/0840 locks for-each.
This commit is contained in:
@@ -2516,18 +2516,22 @@ pub const Vm = struct {
|
||||
for (items, 0..) |s, i| {
|
||||
try self.writeField(table, backing + i * str_size, .string, try self.makeStringValue(table, s));
|
||||
}
|
||||
// The List struct: field 0 = items ([*]string), 1 = len (i64), 2 = cap (i64).
|
||||
// The List struct: field 0 = items (`[]string` fat pointer {ptr, len}),
|
||||
// field 1 = cap (i64). `items.ptr` = backing, `items.len` = cap = n.
|
||||
const addr = self.machine.allocBytes(table.typeSizeBytes(list_ty), 8);
|
||||
const items_fty = table.memberType(list_ty, 0) orelse
|
||||
return self.failMsg("comptime List builder: result type has no items field");
|
||||
const len_fty = table.memberType(list_ty, 1) orelse
|
||||
return self.failMsg("comptime List builder: result type has no len field");
|
||||
const cap_fty = table.memberType(list_ty, 2) orelse
|
||||
const cap_fty = table.memberType(list_ty, 1) orelse
|
||||
return self.failMsg("comptime List builder: result type has no cap field");
|
||||
const n: Reg = @bitCast(@as(i64, @intCast(items.len)));
|
||||
try self.writeField(table, addr + fieldOffset(table, list_ty, 0), items_fty, backing);
|
||||
try self.writeField(table, addr + fieldOffset(table, list_ty, 1), len_fty, n);
|
||||
try self.writeField(table, addr + fieldOffset(table, list_ty, 2), cap_fty, n);
|
||||
// Write the `items` slice as a {ptr, len} fat pointer at field 0.
|
||||
if (items_fty.isBuiltin() or table.get(items_fty) != .slice)
|
||||
return self.failMsg("comptime List builder: items field is not a slice");
|
||||
const items_off = addr + fieldOffset(table, list_ty, 0);
|
||||
try self.machine.writeWord(items_off, table.pointer_size, backing);
|
||||
try self.machine.writeWord(items_off + table.pointer_size, 8, n);
|
||||
// cap = live count (the backing is exactly `n` elements).
|
||||
try self.writeField(table, addr + fieldOffset(table, list_ty, 1), cap_fty, n);
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -2547,8 +2551,11 @@ pub const Vm = struct {
|
||||
fn readStringList(self: *Vm, table: *const types.TypeTable, list_ty: TypeId, addr: Addr) Error![]const []const u8 {
|
||||
if (list_ty.isBuiltin() or table.get(list_ty) != .@"struct")
|
||||
return self.failMsg("comptime List reader: arg type is not a List struct");
|
||||
const items_ptr = try self.machine.readWord(addr + fieldOffset(table, list_ty, 0), table.pointer_size);
|
||||
const len: usize = @intCast(try self.machine.readWord(addr + fieldOffset(table, list_ty, 1), 8));
|
||||
// `items` is a `[]string` fat pointer at field 0: ptr at offset 0, len
|
||||
// at offset `pointer_size` (there is no separate `len` field now).
|
||||
const items_off = addr + fieldOffset(table, list_ty, 0);
|
||||
const items_ptr = try self.machine.readWord(items_off, table.pointer_size);
|
||||
const len: usize = @intCast(try self.machine.readWord(items_off + table.pointer_size, 8));
|
||||
const str_size = table.typeSizeBytes(.string);
|
||||
const out = self.gpa.alloc([]const u8, len) catch return self.failMsg("comptime List reader: out of memory");
|
||||
var i: usize = 0;
|
||||
|
||||
@@ -249,14 +249,34 @@ pub fn lowerWhile(self: *Lowering, we: *const ast.WhileExpr) Ref {
|
||||
return self.builder.constInt(0, .void);
|
||||
}
|
||||
|
||||
/// View a `List(T)`-like struct (`{ items: [*]T, len, … }`) as its backing
|
||||
/// `items` pointer + element type + `len`, so `for list: (x)` iterates the
|
||||
/// elements. Null for anything that isn't such a struct.
|
||||
/// View a `List(T)`-like struct as its backing `items` pointer + element type
|
||||
/// + live length, so `for list (x)` iterates the elements. Two shapes:
|
||||
/// - CURRENT `List`: `{ items: []T, cap }` — `items` is a `[]T` slice whose
|
||||
/// own `.ptr`/`.len` ARE the backing pointer and live count.
|
||||
/// - LEGACY: `{ items: [*]T, len, … }` — a many-pointer `items` paired with a
|
||||
/// sibling `len` field (kept so a user struct of that shape still iterates).
|
||||
/// Null for anything that isn't such a struct.
|
||||
pub fn listView(self: *Lowering, value: Ref, ty: TypeId) ?struct { data: Ref, data_ty: TypeId, len: Ref } {
|
||||
if (ty.isBuiltin()) return null;
|
||||
const info = self.module.types.get(ty);
|
||||
if (info != .@"struct") return null;
|
||||
const items_id = self.module.types.internString("items");
|
||||
|
||||
// Current shape: an `items: []T` slice — view via its `.ptr`/`.len`.
|
||||
for (info.@"struct".fields, 0..) |f, i| {
|
||||
if (f.name == items_id and !f.ty.isBuiltin() and self.module.types.get(f.ty) == .slice) {
|
||||
const slice_val = self.builder.emit(.{ .struct_get = .{ .base = value, .field_index = @intCast(i) } }, f.ty);
|
||||
const elem = self.module.types.get(f.ty).slice.element;
|
||||
const mp_ty = self.module.types.manyPtrTo(elem);
|
||||
return .{
|
||||
.data = self.builder.emit(.{ .data_ptr = .{ .operand = slice_val } }, mp_ty),
|
||||
.data_ty = mp_ty,
|
||||
.len = self.builder.emit(.{ .length = .{ .operand = slice_val } }, .i64),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy shape: `items: [*]T` + a sibling `len` field.
|
||||
const len_id = self.module.types.internString("len");
|
||||
var items_idx: ?u32 = null;
|
||||
var items_ty: TypeId = .unresolved;
|
||||
|
||||
Reference in New Issue
Block a user