// Phase 1.3 — the closure env-buffer heap-copy in `lowerLambda` must // dispatch through `context.allocator`, not `.heap_alloc` directly. // So when a `push Context.{ allocator = tracer }` block is active, a // capturing closure created inside it MUST allocate its env through // the tracker. // // Mirrors the shape of `130-xx-value-routes-through-context-allocator.sx` // for the protocol-erasure heap path — same Tracer, same install via // `push Context`, same `Tracer.count = 1` assertion. Different // allocation site (closure env vs xx-value heap copy). #import "modules/std.sx"; Tracer :: struct { count: s64; init :: () -> *Tracer { t : *Tracer = xx libc_malloc(size_of(Tracer)); t.count = 0; t } } impl Allocator for Tracer { alloc :: (self: *Tracer, size: s64) -> *void { self.count += 1; return libc_malloc(size); } dealloc :: (self: *Tracer, ptr: *void) { libc_free(ptr); } } main :: () -> s32 { tracer := Tracer.init(); push Context.{ allocator = xx tracer, data = null } { // Capturing closure. lowerLambda allocates an env struct on the // stack, copies the captures in, then heap-copies the env via // `allocViaContext` — which dispatches through the installed // tracer's `alloc`. captured : s64 = 100; add_capture := closure((y: s64) -> s64 => y + captured); _ = add_capture(1); } print("Tracer.count = {}\n", tracer.count); 0 }