Building on the Option 3 lvalue-borrow rule, the long-lived allocators
in `library/modules/allocators.sx` (GPA, Arena, TrackingAllocator) now
return their state by value instead of via a heap-allocated `*T`. The
caller binds the result to a local; the local IS the allocator state.
`xx local` borrows that storage under Option 3, so the `Allocator`
protocol value's `ctx` points at the local — no heap allocation for
the state struct, no `free` of the state needed.
```sx
gpa := GPA.init(); // GPA (value)
arena := Arena.init(xx gpa, 4096); // Arena (value)
tracker := TrackingAllocator.init(xx gpa); // TrackingAllocator (value)
push Context.{ allocator = xx tracker, data = null } { ... }
```
Why by-value:
- One fewer `libc_malloc` per allocator instance.
- No state-struct leak. The local is reclaimed at scope exit; `deinit`
only handles downstream resources (chunks, etc.) — not its own struct.
- Owning structs can embed allocators as value fields directly.
Callsite changes:
- `library/modules/ui/pipeline.sx`: `arena_a: Arena;` / `arena_b:
Arena;` (was `*Arena;`). The `build_arena: *Arena` local takes
`@self.arena_a` / `@self.arena_b`.
- `examples/126-xx-recover-then-dispatch.sx`: `recovered == @gpa`
instead of `recovered == gpa` (gpa is a value now).
- `examples/135-xx-lvalue-borrows.sx`: drop the `tracker_ptr.*`
deref — `init` already returns the value.
- `examples/50-smoke.sx`: Arena alloc counts dropped by 1 (no
state-struct allocation). Comments + snapshot updated.
`Arena.deinit` drops the trailing `parent.dealloc(xx a)` — the
caller's local owns the storage.
FFI IR snapshots regenerated to reflect the new signatures:
`@GPA.init` returns `i64` (was `ptr`); `@Arena.init` and
`@TrackingAllocator.init` use sret returns (was `ptr`).
CLAUDE.md "Allocator construction" rule rewritten around the
by-value convention. The forbidden caller-provides-storage and
redundant-pointer-rename patterns are still forbidden but for the
right reasons now (verbose, fragile) rather than as a workaround
for the old `init() -> *T` shape.
157/157 example tests pass; chess clean on macOS, iOS sim, and
Android via `tools/verify-step.sh`.
621 lines
9.7 KiB
Plaintext
621 lines
9.7 KiB
Plaintext
=== 1. Literals ===
|
|
decimal: 42
|
|
hex: 255
|
|
binary: 10
|
|
float: 3.140000
|
|
f64: 2.718281
|
|
true: true
|
|
false: false
|
|
escapes: hello world
|
|
multiline: line1
|
|
line2
|
|
heredoc: raw heredoc
|
|
|
|
undef-then-set: 77
|
|
enum-lit: .green
|
|
null-ptr: null
|
|
string-len: 5
|
|
empty-string: 0
|
|
=== 2. Operators ===
|
|
add: 7
|
|
sub: 7
|
|
mul: 42
|
|
div: 5
|
|
mod: 2
|
|
neg: -5
|
|
eq: true
|
|
neq: true
|
|
lt: true
|
|
gt: true
|
|
le: true
|
|
ge: true
|
|
chain: true
|
|
chain-gt: true
|
|
chain-mixed: true
|
|
eq-chain: true
|
|
eq-chain-f: false
|
|
band: 15
|
|
bor: 7
|
|
bxor: 240
|
|
bxor2: 5
|
|
bnot: -1
|
|
bnot2: -2
|
|
shl: 16
|
|
shr: 16
|
|
shl2: 24
|
|
shr2: 127
|
|
band-var: 15
|
|
bor-var: 7
|
|
bxor-var: 240
|
|
shl-var: 16
|
|
shr-var: 15
|
|
bnot-var: -16
|
|
and-assign: 15
|
|
or-assign: 255
|
|
xor-assign: 240
|
|
shl-assign: 256
|
|
shr-assign: 16
|
|
mod-var: 2
|
|
and: true
|
|
and-false: false
|
|
or: true
|
|
or-false: false
|
|
short-and: false
|
|
short-or: true
|
|
ca+=: 15
|
|
ca-=: 12
|
|
ca*=: 24
|
|
ca/=: 4
|
|
prec1: 14
|
|
prec2: 20
|
|
xx-cast: 200
|
|
widen-u8-s64: 200
|
|
widen-s32-f64: 42.000000
|
|
widen-f32-f64: 1.500000
|
|
widen-u8-s16: 100
|
|
xx-s64-s32: 12345
|
|
xx-f64-f32: 1.500000
|
|
xx-f64-s32: 7
|
|
=== 3. Types ===
|
|
s8: 127
|
|
s16: 32000
|
|
s32: 100000
|
|
u8: 255
|
|
u16: 65000
|
|
u32: 4000000
|
|
alias: 1.500000
|
|
struct-pos: Point{x: 1, y: 2}
|
|
struct-prefix: Point{x: 3, y: 4}
|
|
struct-named: Point{x: 20, y: 10}
|
|
struct-shorthand: Point{x: 5, y: 6}
|
|
defaults: a=0 b=99
|
|
field-assign: Point{x: 42, y: 99}
|
|
enum: .red
|
|
enum-eq: true
|
|
enum-neq: true
|
|
backing: .err
|
|
tagged: .circle(3.140000)
|
|
payload: 3.140000
|
|
void-variant: .none
|
|
reassign: .circle(1.000000)
|
|
reassign2: .rect(Shape.rect{w: 5.000000, h: 3.000000})
|
|
enum-prefix: .circle(2.500000)
|
|
match: rect
|
|
match-expr: 10
|
|
match-expr-else: 99
|
|
capture: 9.500000
|
|
capture-arrow: 7.500000
|
|
else-match: other
|
|
int-match: two
|
|
int-match-else: unknown
|
|
bool-match-t: yes
|
|
bool-match-f: no
|
|
bool: true
|
|
union-f: 3.140000
|
|
union-i: 1078523331
|
|
promoted-x: 1.000000
|
|
promoted-data0: 1.000000
|
|
arr[2]: 30
|
|
arr.len: 5
|
|
arr-assign: [1, 99, 3]
|
|
sl[0]: 1
|
|
sl.len: 5
|
|
sl-assign: [10, 55, 30]
|
|
sub: [20, 30, 40]
|
|
head: [10, 20, 30]
|
|
tail: [30, 40, 50]
|
|
slice-of-slice: [20, 30]
|
|
strsub: world
|
|
str-prefix: hello
|
|
str-suffix: world
|
|
global-addr-of: 99
|
|
deref: Point{x: 10, y: 20}
|
|
auto-deref: 10
|
|
mp[0]: 10
|
|
mp[3]: 40
|
|
mp-write: 99
|
|
ptr==null: true
|
|
ptr!=null: false
|
|
ptr2==null: false
|
|
ptr2!=null: true
|
|
ptr-nested-field: 1.000000 2.000000 3.000000
|
|
mp-store-sentinel: 42
|
|
vec-construct: [1.000000, 3.000000, 2.000000]
|
|
vec-add: [5.000000, 7.000000, 9.000000]
|
|
vec-sub: [4.000000, 3.000000, 2.000000]
|
|
vec-mul: [2.000000, 6.000000, 12.000000]
|
|
vec-div: [5.000000, 3.000000, 2.000000]
|
|
vec-scalar: [2.000000, 6.000000, 4.000000]
|
|
vec-neg: [-1.000000, -3.000000, -2.000000]
|
|
vec-x: 10.000000
|
|
vec-y: 20.000000
|
|
vec-z: 30.000000
|
|
vec-idx: 20.000000
|
|
=== 4. Control Flow ===
|
|
ite: 1
|
|
ite-both: 10 20
|
|
if-block: yes
|
|
if-no-else: after
|
|
nested-if: deep
|
|
if-else-if: second
|
|
if-block-expr: 15
|
|
while: 5
|
|
while-false: skipped
|
|
while-break: 7
|
|
while-continue: 25
|
|
while-sum: 55
|
|
nested-while: 9
|
|
nested-break: 2 2
|
|
for: 10 20 30 40
|
|
for-print: 10 20 30 40
|
|
for-idx: 0 1 2 3
|
|
for-2arg: 10@0 20@1 30@2 40@3
|
|
for-break: 10 20
|
|
for-continue: 10 30 40
|
|
for-slice: 10 20 30
|
|
for-slice-idx: 0:10 1:20 2:30
|
|
for-nested: (0,0) (0,1) (1,0) (1,1)
|
|
for-break-idx: 2
|
|
multi: 1 2 3
|
|
=== 5. Functions ===
|
|
const: 42
|
|
typed-const: 3.140000
|
|
default-init: 0
|
|
implicit-ret: 42
|
|
early-ret: 5
|
|
early-ret2: 99
|
|
void-return: ok
|
|
generic-s32: 42
|
|
generic-f32: 1.500000
|
|
generic-bool: true
|
|
generic-multi: 30
|
|
lambda: 14
|
|
lambda-ret: 5.000000
|
|
local-fn: 7
|
|
fn-nested: 26
|
|
varargs: 15
|
|
spread: 60
|
|
fp: 7
|
|
fp-reassign: 12
|
|
fp-apply: 30
|
|
=== 6. Scoping ===
|
|
inner: 200
|
|
outer: 100
|
|
shadow-type: 42
|
|
shadow-type: 3.140000
|
|
nest3: 3
|
|
nest2: 2
|
|
nest1: 1
|
|
scope-isolate: 100
|
|
scope-reuse: 1
|
|
scope-reuse: 2
|
|
scope-reuse: 1
|
|
defer-a
|
|
defer-b
|
|
defer-c
|
|
d4
|
|
d3
|
|
d2
|
|
d1
|
|
inner-defer
|
|
outer-defer
|
|
defer-in-if: body
|
|
defer-in-if: deferred
|
|
=== 7. Builtins ===
|
|
out-ok
|
|
sqrt: 3.000000
|
|
sqrt-f64: 4.000000
|
|
sizeof-s32: 4
|
|
sizeof-f64: 8
|
|
sizeof-struct: 8
|
|
alignof-u8: 1
|
|
alignof-s32: 4
|
|
alignof-s64: 8
|
|
alignof-struct: 4
|
|
typeof: int
|
|
typeof-float: float
|
|
typeof-string: string
|
|
typeof-bool: bool
|
|
typeof-struct: struct
|
|
typeof-enum: enum
|
|
typename: Point
|
|
fieldcount: 2
|
|
fieldcount-enum: 3
|
|
fieldname0: x
|
|
fieldname1: y
|
|
fieldname-enum0: red
|
|
fieldname-enum2: blue
|
|
fieldval0: 11
|
|
fieldval1: 22
|
|
fieldidx: 1
|
|
fieldidx-tagged: 0
|
|
fieldidx-tagged2: 2
|
|
cast: 3
|
|
cast-int-f64: 42.000000
|
|
=== 8. Comptime ===
|
|
run-const: 25
|
|
run-expr: 42
|
|
run-chain: 30
|
|
ct-opt-coalesce: 141
|
|
ct-opt-unwrap: 77
|
|
ct-opt-guard: 10
|
|
insert-ok
|
|
insert-gen: 42
|
|
=== 9. Flags ===
|
|
flags: .read | .write
|
|
has-read: yes
|
|
flags-neg: no-read
|
|
flags-single: .execute
|
|
flags-all: .read | .write | .execute
|
|
flags-raw: 3
|
|
flags-explicit: .vsync | .resizable
|
|
flags-explicit-raw: 68
|
|
--- swap ---
|
|
var swap: 20 10
|
|
arr swap: 3 1
|
|
3-way: 3 1 2
|
|
--- destructure ---
|
|
basic: 10 20
|
|
fn: 2 1
|
|
discard: 200
|
|
triple: 1 2 3
|
|
=== 15. Foreign ===
|
|
foreign-rename: 42
|
|
=== 16. Compound Assign ===
|
|
f64+=f32: 13.000000
|
|
s64-=s32: 93
|
|
=== 17. Slice Ptr ===
|
|
sl-ptr[0]: 20
|
|
sl-ptr[1]: 30
|
|
=== 18. Array of Structs ===
|
|
arr-struct-x: 3
|
|
for-struct: Point{x: 1, y: 2}
|
|
for-struct: Point{x: 3, y: 4}
|
|
=== 19. Local Fn Return ===
|
|
local-struct: 42 99
|
|
local-enum: .circle(2.500000)
|
|
=== 20. UFCS Return Type ===
|
|
direct: 7
|
|
ufcs: 7
|
|
=== 21. Type-Named Vars ===
|
|
s2: 42
|
|
s2+1: 43
|
|
=== 22. If-Struct ===
|
|
if-struct: 10 20
|
|
else-struct: 30 40
|
|
=== 23. Nested Arrays ===
|
|
m[0][0]: 1
|
|
m[0][2]: 3
|
|
m[1][0]: 4
|
|
m[1][2]: 6
|
|
=== 24. String Comparison ===
|
|
str-eq: true
|
|
str-neq: true
|
|
str-diff: false
|
|
empty-eq: true
|
|
=== 25. Array Loop Mutation ===
|
|
loop-fill: 1 2 3 4
|
|
compound: 13
|
|
=== 26. #using ===
|
|
using-x: 1
|
|
using-y: 2
|
|
using-z: 3
|
|
pkt-id: 10
|
|
pkt-ver: 42
|
|
pkt-pay: 99
|
|
sprite-px: 10
|
|
sprite-r: 255
|
|
sprite-scale: 1
|
|
say: hello (len=5)
|
|
n=42
|
|
=== Tuples ===
|
|
40
|
|
2
|
|
10
|
|
10
|
|
42
|
|
0
|
|
0
|
|
=== UFCS Aliases ===
|
|
42
|
|
42
|
|
42
|
|
42
|
|
42
|
|
3
|
|
3
|
|
3
|
|
2
|
|
1
|
|
99
|
|
=== Tuple Operators ===
|
|
true
|
|
false
|
|
true
|
|
false
|
|
1
|
|
2
|
|
3
|
|
4
|
|
1
|
|
2
|
|
1
|
|
2
|
|
1
|
|
2
|
|
true
|
|
false
|
|
false
|
|
true
|
|
true
|
|
true
|
|
true
|
|
false
|
|
--- directory imports ---
|
|
7
|
|
30
|
|
hello from testpkg
|
|
cwd-import-ok
|
|
--- pipe operator ---
|
|
7
|
|
30
|
|
14
|
|
hello world
|
|
piped ok!
|
|
alloc len: 5
|
|
alloc[0]: 10
|
|
alloc[4]: 50
|
|
bytes len: 3
|
|
--- allocators ---
|
|
gpa allocs: 2
|
|
gpa final: 0
|
|
arena chunks: 1
|
|
arena overflow: 2
|
|
arena a1: 42
|
|
arena a3: 99
|
|
arena reset idx: 0
|
|
arena reset gpa: 1
|
|
arena deinit: 0
|
|
buf pos: 48
|
|
buf overflow: 0
|
|
buf reset: 0
|
|
1 == (1)
|
|
(1) == 1
|
|
1 == 1
|
|
--- optionals ---
|
|
opt x: 42
|
|
opt y: null
|
|
unwrap: 10
|
|
coalesce a: 42
|
|
coalesce b: 99
|
|
chained ?? c: 42
|
|
chained ?? d: 99
|
|
chained ?? e: 0
|
|
if-bind x: 7
|
|
if-bind y: none
|
|
match some: 55
|
|
match none: 0
|
|
wrap pos: 5
|
|
wrap neg: null
|
|
opt field default: null
|
|
opt field set: 42
|
|
opt param a: 42
|
|
opt param b: 0
|
|
opt param 7: 7
|
|
opt reassign: 42.500000
|
|
opt compute assign: 15.000000
|
|
opt re-null: 99.000000
|
|
generic opt 1: 5
|
|
generic opt 2: 7
|
|
generic opt 3: null
|
|
chain some: 10
|
|
chain none: 0
|
|
chain print: 20
|
|
chain null: null
|
|
deep chain 1: 99
|
|
deep chain 2: 0
|
|
narrow x: 42
|
|
narrow y else: null
|
|
narrow else x: 42
|
|
guard some: 42
|
|
guard none: 0
|
|
and both: 10 20
|
|
and one null
|
|
or guard: 7
|
|
or guard null: 0
|
|
nested narrow: 10 20
|
|
guard loop: 3
|
|
block-lambda: 50
|
|
block-lambda: 0
|
|
block-lambda: 100
|
|
hello block
|
|
named-fn-type: 7
|
|
xx-fnptr: 142
|
|
closure-type: fn_ptr-nonnull=true
|
|
closure-type: env-null=true
|
|
closure-call: 15
|
|
auto-promote: 20
|
|
auto-promote-var: 10
|
|
closure-capture: 52
|
|
closure-snapshot: 15
|
|
closure-nocap: 14
|
|
closure-multi: 33
|
|
closure-block: 60
|
|
closure-block: 0
|
|
closure-block: 100
|
|
[LOG] hello
|
|
closure-hof: 30
|
|
closure-hof-bare: 20
|
|
closure-f32: 10.000000
|
|
closure-bool: hello
|
|
closure-2p: 107
|
|
closure-3p: 61
|
|
closure-mix: Alice is 35
|
|
closure-rbool: false true
|
|
closure-reduce: 115
|
|
closure-factory: 105 110
|
|
closure-struct: 10 20
|
|
closure-compose: 30
|
|
closure-indep: 15 50
|
|
opt-closure: none
|
|
opt-closure: 15
|
|
opt-closure-btn: 1 99
|
|
opt-closure-btn: null
|
|
closure-ptr: 3
|
|
closure-enum: 2
|
|
closure-rstr: [INFO] ok
|
|
closure-rstruct: 11 22
|
|
closure-linear: 37
|
|
closure-clamp: 0 100 255
|
|
closure-compose2: 12
|
|
closure-chain: 22
|
|
closure-map: 3 6 9 12 15
|
|
closure-filter: 3 [3 4 5]
|
|
closure-sort: 5 4 3 2 1
|
|
closure-fe: item 0=10
|
|
closure-fe: item 1=20
|
|
closure-fe: item 2=30
|
|
closure-find: 2
|
|
closure-any: false true
|
|
closure-struct-field: -5
|
|
closure-btn: 1 99
|
|
closure-counter: 1 2 3
|
|
closure-acc: 105 115
|
|
closure-loop: 115
|
|
closure-reassign: 11
|
|
closure-reassign: 20
|
|
closure-snapstruct: 15
|
|
closure-cap-promoted: 11
|
|
closure-iife: 15
|
|
closure-toggle: none
|
|
closure-toggle: true
|
|
closure-panel: main 800x600
|
|
closure-chain-call: true
|
|
closure-cond: 10
|
|
closure-form: submitted
|
|
closure-form: no cancel
|
|
closure-null-env: true
|
|
closure-slice: 10 20 30
|
|
closure-arena: 15
|
|
closure-gpa: 17 allocs=0
|
|
closure-opt: 42
|
|
closure-ropt: 50
|
|
closure-ropt: none
|
|
closure-mixed: 10
|
|
closure-mixed: 15
|
|
closure-mixed: 25
|
|
closure-factory-indep: 20 30 40
|
|
closure-deep-chain: 122
|
|
closure-8cap: 36
|
|
closure-4param: 10
|
|
closure-shared-ptr: 7
|
|
closure-f64: true
|
|
closure-zerocap: 49 true
|
|
closure-struct-method: 7
|
|
closure-multi-factory: 10
|
|
closure-multi-factory: 20
|
|
closure-multi-factory: 30
|
|
closure-bool-cap: true false
|
|
closure-as-arg: 142
|
|
closure-strfmt: hello world
|
|
closure-ptr-before: 10
|
|
closure-ptr-after: 42
|
|
closure-neg: -70
|
|
closure-proto-cap: true
|
|
closure-chain-factory: 37
|
|
closure-while-cond: 3
|
|
closure-infer: 7
|
|
closure-infer-arg: 15
|
|
closure-infer-block: 12
|
|
closure-infer-cap: 105
|
|
closure-infer-factory: 35
|
|
closure-infer-compose: 11
|
|
closure-infer-void: 42
|
|
=== Protocols ===
|
|
P1.1: 3
|
|
P1.2: 30
|
|
P2.1: 42
|
|
P2.2: 150
|
|
P2.3: 5 10
|
|
P3.1: 5
|
|
P3.2: 12
|
|
hi hi
|
|
P4.1: 2
|
|
yo yo
|
|
P4.2: 2
|
|
P4.3: 6 2
|
|
P5.1: true false
|
|
P5.2: 10 20
|
|
P5.5: true false
|
|
P5.3: true false
|
|
P6.1: true false
|
|
P6.2: true false
|
|
P6.3: true false
|
|
P6.4: 40
|
|
P6.5: 20
|
|
P7.1: 30
|
|
P7.2: 10 300
|
|
P2.6: 5 10
|
|
P2.7: 15
|
|
P3.3: 102
|
|
=== Auto Type Erasure ===
|
|
AE1: 12
|
|
AE2: 8
|
|
AE3: 102
|
|
AE4: 51
|
|
AE5: 15
|
|
=== Struct Constants ===
|
|
gravity: 9.810000
|
|
max speed: 100
|
|
p.y: 9.810000
|
|
=== Init Blocks ===
|
|
IB1: 60 3
|
|
IB2: 142 2
|
|
IB3: 5 1
|
|
IB4: 100
|
|
IB5: 52
|
|
--- struct static method shorthand ---
|
|
SM1: 16.000000 8.000000
|
|
SM1: 5.000000 5.000000
|
|
SM2: 10 20
|
|
--- optional if-else coercion ---
|
|
opt-if1: 99.000000
|
|
opt-if2: 10.000000
|
|
opt-if3: 10.000000
|
|
opt-if4: 0.000000
|
|
opt-if5: 42.000000
|
|
usize: 42
|
|
isize: -7
|
|
usize+8: 50
|
|
s32->usize: 10
|
|
usize->s64: 42
|
|
=== inline if ===
|
|
64-bit
|
|
not wasm
|
|
known os
|
|
desktop 64-bit
|
|
pointer size via if: 8
|
|
=== Trailing Commas ===
|
|
trailing commas ok
|
|
=== DONE ===
|