lang: opt-in UFCS — ufcs-marked fns + alias dot-dispatch, generic binding via receiver; one binding builder for plan-side generic returns

This commit is contained in:
agra
2026-06-11 17:04:51 +03:00
parent 84e0fb0752
commit a47ea1416e
27 changed files with 316 additions and 137 deletions

View File

@@ -4,16 +4,16 @@
//
// The user-facing allocation surface over the Allocator protocol's
// bytes-level primitives (`alloc_bytes` / `dealloc_bytes`). Free
// functions — call directly or fluently via the pipe operator:
// functions declared `ufcs` — dot-call, pipe, or call directly:
//
// s := context.allocator |> create(Session);
// s := context.allocator.create(Session);
// s.* = Session.{}; // no zero-init (Zig-aligned)
// defer context.allocator |> destroy(s);
// defer context.allocator.destroy(s);
//
// moves := context.allocator |> alloc(Move, 64);
// defer context.allocator |> free(moves);
// moves := context.allocator.alloc(Move, 64);
// defer context.allocator.free(moves);
//
// copied := bytes |> clone(context.allocator);
// copied := bytes.clone(context.allocator);
//
// Bodies are complete for the 2-method protocol era: `mem_realloc` is
// alloc+copy+dealloc (the only shape without resize/remap primitives),
@@ -21,17 +21,17 @@
// allocation lands with the protocol expansion.
// Allocate one T. Contents are UNINITIALISED — assign before reading.
create :: (a: Allocator, $T: Type) -> *T {
create :: ufcs (a: Allocator, $T: Type) -> *T {
xx a.alloc_bytes(size_of(T))
}
// Free a *T obtained from `create`.
destroy :: (a: Allocator, ptr: *$T) {
destroy :: ufcs (a: Allocator, ptr: *$T) {
a.dealloc_bytes(xx ptr);
}
// Allocate a []T of `count` elements. Contents are UNINITIALISED.
alloc :: (a: Allocator, $T: Type, count: s64) -> []T {
alloc :: ufcs (a: Allocator, $T: Type, count: s64) -> []T {
raw := a.alloc_bytes(count * size_of(T));
s : []T = ---;
s.ptr = xx raw;
@@ -40,12 +40,12 @@ alloc :: (a: Allocator, $T: Type, count: s64) -> []T {
}
// Free a []T obtained from `alloc` / `clone` / `resize`.
free :: (a: Allocator, slice: []$T) {
free :: ufcs (a: Allocator, slice: []$T) {
a.dealloc_bytes(xx slice.ptr);
}
// Copy a slice into fresh storage owned by `a`.
clone :: (src: []$T, a: Allocator) -> []T {
clone :: ufcs (src: []$T, a: Allocator) -> []T {
raw := a.alloc_bytes(src.len * size_of(T));
memcpy(raw, xx src.ptr, src.len * size_of(T));
s : []T = ---;
@@ -57,7 +57,7 @@ clone :: (src: []$T, a: Allocator) -> []T {
// Reallocate a slice to `new_count` elements: fresh storage, contents
// copied up to min(len, new_count), old backing freed. The returned
// slice replaces the operand — the old slice is dangling after this.
resize :: (slice: []$T, a: Allocator, new_count: s64) -> []T {
resize :: ufcs (slice: []$T, a: Allocator, new_count: s64) -> []T {
raw := a.alloc_bytes(new_count * size_of(T));
n := if slice.len < new_count then slice.len else new_count;
memcpy(raw, xx slice.ptr, n * size_of(T));
@@ -72,7 +72,7 @@ resize :: (slice: []$T, a: Allocator, new_count: s64) -> []T {
// dealloc — there is no in-place grow primitive to try yet, and
// `align` beyond the heap's natural 8 is not honored until the
// protocol carries alignment.
mem_realloc :: (a: Allocator, ptr: *void, old: s64, new: s64, align: s64) -> *void {
mem_realloc :: ufcs (a: Allocator, ptr: *void, old: s64, new: s64, align: s64) -> *void {
raw := a.alloc_bytes(new);
n := if old < new then old else new;
memcpy(raw, ptr, n);