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:
agra
2026-06-22 11:55:19 +03:00
parent 9d3a019670
commit 5cc45a2b38
58 changed files with 59733 additions and 59479 deletions

View File

@@ -400,7 +400,7 @@ impl Platform for AndroidPlatform {
}
poll_events :: (self: *AndroidPlatform) -> []Event {
self.events.len = 0;
self.events.items.len = 0;
sx_android_drain_touches(self, @self.events);
result : []Event = ---;
result.ptr = self.events.items;

View File

@@ -144,7 +144,7 @@ impl Platform for SdlPlatform {
}
poll_events :: (self: *SdlPlatform) -> []Event {
self.events.len = 0;
self.events.items.len = 0;
sdl_event : SDL_Event = .none;
while SDL_PollEvent(@sdl_event) {
if sdl_event == {

View File

@@ -379,7 +379,7 @@ impl Platform for UIKitPlatform {
result : []Event = ---;
result.ptr = self.events.items;
result.len = self.events.len;
self.events.len = 0;
self.events.items.len = 0;
result
}

View File

@@ -2,45 +2,58 @@
// directly — std.sx re-exports `List`.
#import "modules/std/core.sx";
// `items` is a `[]T` slice whose `.len` IS the live element count, so a `List`
// is directly iterable: `for xs.items (e) { ... }`. `cap` is the allocated
// capacity (`>= items.len`); the buffer spans `cap` elements, the slice spans
// the live `items.len`. The `len` `#get` accessor exposes the live count as
// `xs.len` (read), delegating to `items.len`; writes use `xs.items.len`.
List :: struct ($T: Type) {
items: [*]T = null;
len: i64 = 0;
items: []T = .[]; // empty slice ({ptr, len=0}); `.[]` is the empty-slice
// literal — `.{}` would init the slice's underlying
// {ptr,len} struct (and currently yields a garbage len).
cap: i64 = 0;
// No-paren read accessor: `xs.len` → the live element count.
len :: (self: *List(T)) -> i64 #get => self.items.len;
append :: (list: *List(T), item: T, alloc: Allocator = context.allocator) {
if list.len >= list.cap {
if list.items.len >= list.cap {
new_cap := if list.cap == 0 then 4 else list.cap * 2;
new_items : [*]T = xx alloc.alloc_bytes(new_cap * size_of(T));
if list.len > 0 {
memcpy(new_items, list.items, list.len * size_of(T));
alloc.dealloc_bytes(list.items);
new_ptr : [*]T = xx alloc.alloc_bytes(new_cap * size_of(T));
if list.items.len > 0 {
memcpy(new_ptr, list.items.ptr, list.items.len * size_of(T));
alloc.dealloc_bytes(list.items.ptr);
}
list.items = new_items;
list.items.ptr = new_ptr; // keep the live len; only the buffer moves
list.cap = new_cap;
}
list.items[list.len] = item;
list.len += 1;
list.items.ptr[list.items.len] = item; // write at the live index (within cap)
list.items.len += 1;
}
ensure_capacity :: (list: *List(T), n: i64, alloc: Allocator = context.allocator) {
if list.cap >= n { return; }
new_cap := if list.cap == 0 then 4 else list.cap;
while new_cap < n { new_cap = new_cap * 2; }
new_items : [*]T = xx alloc.alloc_bytes(new_cap * size_of(T));
if list.len > 0 {
memcpy(new_items, list.items, list.len * size_of(T));
alloc.dealloc_bytes(list.items);
new_ptr : [*]T = xx alloc.alloc_bytes(new_cap * size_of(T));
if list.items.len > 0 {
memcpy(new_ptr, list.items.ptr, list.items.len * size_of(T));
alloc.dealloc_bytes(list.items.ptr);
}
list.items = new_items;
list.items.ptr = new_ptr;
list.cap = new_cap;
}
deinit :: (list: *List(T), alloc: Allocator = context.allocator) {
if list.items != null {
alloc.dealloc_bytes(list.items);
// `cap > 0` is the ownership signal: a List holds an allocated buffer
// ONLY after a growth set `cap`. Guarding on `cap` (not `items.ptr`)
// makes deinit idempotent and safe on a never-grown / borrowed-items
// list — and `.[]` leaves a len-0 slice whose ptr need not be null.
if list.cap > 0 {
alloc.dealloc_bytes(list.items.ptr);
}
list.items = null;
list.len = 0;
list.items.ptr = null;
list.items.len = 0;
list.cap = 0;
}
}

View File

@@ -647,7 +647,7 @@ remove_timer :: (self: *Scheduler, idx: i64) {
self.timers.items[i] = self.timers.items[i + 1];
i = i + 1;
}
self.timers.len = self.timers.len - 1;
self.timers.items.len = self.timers.items.len - 1;
}
// Remove a pending sleep timer referencing fiber `f`, if any. A fiber has at
@@ -676,7 +676,7 @@ remove_io_waiter :: (self: *Scheduler, idx: i64) {
self.io_waiters.items[i] = self.io_waiters.items[i + 1];
i = i + 1;
}
self.io_waiters.len = self.io_waiters.len - 1;
self.io_waiters.items.len = self.io_waiters.items.len - 1;
}
// Remove a pending fd-waiter referencing fiber `f`, if any. A fiber has at most

View File

@@ -575,7 +575,7 @@ GlyphCache :: struct {
return; // shaped_buf already has the result
}
self.shaped_buf.len = 0;
self.shaped_buf.items.len = 0;
if text.len == 0 { return; }
if is_ascii(text) {

View File

@@ -141,7 +141,7 @@ UIPipeline :: struct {
// Reset render_tree nodes (backing is stale after arena reset)
self.render_tree.nodes.items = null;
self.render_tree.nodes.len = 0;
self.render_tree.nodes.items.len = 0;
self.render_tree.nodes.cap = 0;
push Context.{ allocator = xx build_arena, data = context.data } {

View File

@@ -47,7 +47,7 @@ RenderTree :: struct {
}
clear :: (self: *RenderTree) {
self.nodes.len = 0;
self.nodes.items.len = 0;
self.generation += 1;
}