Owned RawBuf threaded through a loop binder passes check but panics codegen unknown variable #47

Closed
opened 2026-05-30 16:31:27 +02:00 by Brummel · 0 comments
Owner

Summary

A loop binder holding an owned RawBuf value threaded through
recur passes ail check but fails at ail build with
unknown variable. A plain Int loop binder of the same name builds
and runs fine, so the fault is specific to an owned RawBuf value in
a loop binder. This breaks check↔codegen agreement: a program that
passes check must be one codegen can lower.

Reproduction (verified)

ail check  examples/fieldtest/rawbuf_2_running_max.ail   # → ok (33 symbols across 3 modules)
ail build  examples/fieldtest/rawbuf_2_running_max.ail   # → Error: module ...: def `main`: unknown variable: `b`

Minimal shape:

(let buf (new RawBuf (con Int) 3)
  (loop (b (con RawBuf (con Int)) buf) (i (con Int) 0)
    (if (app ge i 3) b
      (recur (app RawBuf.set b i 0) (app + i 1)))))

The owned-RawBuf loop binder b is not registered in the codegen
scope.

Impact

Filling a buffer whose length is not a fixed number of literal
indices is the natural way to use RawBuf for runtime-sized data;
that path does not build today. Together with the borrow-receiver
reject (#46), the only RawBuf programs that build are straight-line
owned let-threading with literal indices.

Related papercut (not this bug): a loop binder rejects an own
mode annotation (own may only appear inside fn-type params or ret),
so there is currently no annotated way to thread an owned RawBuf
through a loop either.

Surfaced by the raw-buf fieldtest (finding B2), spec
docs/specs/0058-fieldtest-raw-buf.md. Commit 8e9f0f0.

Acceptance

  • The fill-loop fixture builds without unknown variable
  • RED test pinning the build failure before the fix
## Summary A `loop` binder holding an owned `RawBuf` value threaded through `recur` passes `ail check` but fails at `ail build` with `unknown variable`. A plain `Int` loop binder of the same name builds and runs fine, so the fault is specific to an owned `RawBuf` value in a loop binder. This breaks check↔codegen agreement: a program that passes `check` must be one codegen can lower. ## Reproduction (verified) ``` ail check examples/fieldtest/rawbuf_2_running_max.ail # → ok (33 symbols across 3 modules) ail build examples/fieldtest/rawbuf_2_running_max.ail # → Error: module ...: def `main`: unknown variable: `b` ``` Minimal shape: ``` (let buf (new RawBuf (con Int) 3) (loop (b (con RawBuf (con Int)) buf) (i (con Int) 0) (if (app ge i 3) b (recur (app RawBuf.set b i 0) (app + i 1))))) ``` The owned-RawBuf loop binder `b` is not registered in the codegen scope. ## Impact Filling a buffer whose length is not a fixed number of literal indices is the natural way to use `RawBuf` for runtime-sized data; that path does not build today. Together with the borrow-receiver reject (#46), the only RawBuf programs that build are straight-line owned `let`-threading with literal indices. Related papercut (not this bug): a `loop` binder rejects an `own` mode annotation (`own may only appear inside fn-type params or ret`), so there is currently no annotated way to thread an owned RawBuf through a loop either. Surfaced by the raw-buf fieldtest (finding B2), spec `docs/specs/0058-fieldtest-raw-buf.md`. Commit `8e9f0f0`. ## Acceptance - [ ] The fill-loop fixture builds without `unknown variable` - [ ] RED test pinning the build failure before the fix
Brummel added the bug label 2026-05-30 16:31:27 +02:00
Sign in to join this conversation.