Files
AILang/examples/fieldtest/rawbuf_1_score_table.ail
T
Brummel 8e9f0f06a6 fieldtest: raw-buf — 4 examples, 6 findings (refs #7)
Downstream field test of the raw-buf surface as a consumer with only
the public interface (design ledger + `ail` CLI; no crate source).
Four fixtures under examples/fieldtest/, spec at
docs/specs/0058-fieldtest-raw-buf.md. Every recorded outcome
reproduced independently before commit.

Findings (3 bug, 1 spec_gap, 2 working):

- B1 [bug] `borrow (RawBuf a)` receiver is unusable. A fn taking
  `borrow (RawBuf Int)` that calls RawBuf.get/.size on the parameter
  even once is rejected `consume-while-borrowed` — the diagnostic
  names the wrong cause (the receiver is not consumed). The ledger
  advertises get/size as borrow-receiver ops, so the documented use
  is unreachable from any helper-function decomposition. This blocks
  Series (#8) — the milestone's own downstream raison d'être —
  whose at/total_count read through a `borrow (Series a)`.

- B2 [bug] An owned RawBuf threaded through a `loop` binder passes
  check but panics codegen `unknown variable: b` at build. A plain
  Int loop binder builds fine, so the fault is specific to an owned
  RawBuf loop binder. Breaks the check↔codegen agreement.

- B3 [bug] `param-not-in-restricted-set` omits the allowed set.
  design/models/0007 §4 promises the diagnostic "names the offending
  type and the allowed set"; the shipped message names the type, the
  type-var, and the home type but not `{Int, Float, Bool}`. A
  downstream author is told what is wrong but not what is right.

- B4 [spec_gap] RawBuf.set receiver double-consume is not enforced;
  two set calls on the same owned buffer silently alias the slab.
  Not raw-buf-specific (owned Str/ADT behave identically) — a
  pre-existing linearity-non-enforcement property. Recorded because
  RawBuf's own→own mutation is the first place a silent alias yields
  a mutated-in-place surprise. Ratify in the ledger or open a backlog
  item for full linear enforcement.

- W1 [working] `(new …)` sugar + inference-from-use across
  Int/Float/Bool builds and runs first try (sensor_pair prints 5.0);
  the Bool i1/i8 packing edge round-trips. The milestone's cleanest win.

- W2 [working] param-in reject fires for both Str and a user TypeDef.

Net: B1+B2 mean the only RawBuf programs that build today are
straight-line owned let-threading with literal indices — the
shipped-fixture shape. Helper decomposition (B1) and runtime-length
loop fill (B2) both fail. fieldtest does not self-resolve; routing
is the orchestrator's call.
2026-05-30 16:26:48 +02:00

57 lines
2.4 KiB
Plaintext

; Fieldtest raw-buf.1 (Axis 1, RawBuf as mutable indexed storage).
; Task an LLM author is naturally given: "build a small score table of
; N slots, fill slot i with a computed score (i*i + 1), then read every
; slot back and total the scores." The natural shape uses RawBuf.size to
; drive the read loop rather than hard-coding the bound, which the spec's
; worked program (literal 0/1/2 indices) never exercises.
;
; Fill loop: thread the owned buffer through RawBuf.set in a (loop ...)
; with a recur per slot. Read loop: borrow the buffer, drive i from 0 to
; (RawBuf.size buf) accumulating (RawBuf.get buf i).
;
; Expected stdout: scores 1, 2, 5, 10, 17 -> sum 35.
;
; OUTCOME: does NOT check. `ail check` reports, for the `total` fn:
; [consume-while-borrowed] total: `buf` is consumed while a borrow of
; it is still live
; even though `total` takes `borrow (RawBuf Int)` and only calls
; RawBuf.size / RawBuf.get on it (both `borrow`-receiver ops per the
; ledger). A single get/size on a borrow-mode receiver triggers it.
; (The `fill` loop, if `total` were removed, would in turn fail at
; `build` with `unknown variable: b` — see rawbuf_2_running_max.ail.)
; First authoring attempt also hit two papercuts: a `loop` binder may
; not carry an `own` mode ("`own` may only appear inside fn-type params
; or ret"), and the prelude comparison is `ge`, not `gte`.
(module rawbuf_1_score_table
(fn fill
(doc "Fill slots 0..n of buf with (i*i + 1). Linear: own in, own out.")
(type (fn-type (params (own (con RawBuf (con Int))) (con Int)) (ret (own (con RawBuf (con Int))))))
(params buf n)
(body
(loop (b (con RawBuf (con Int)) buf) (i (con Int) 0)
(if (app ge i n)
b
(recur (app RawBuf.set b i (app + (app * i i) 1))
(app + i 1))))))
(fn total
(doc "Sum every slot of buf, driving the bound off RawBuf.size.")
(type (fn-type (params (borrow (con RawBuf (con Int)))) (ret (con Int))))
(params buf)
(body
(loop (acc (con Int) 0) (i (con Int) 0)
(if (app ge i (app RawBuf.size buf))
acc
(recur (app + acc (app RawBuf.get buf i))
(app + i 1))))))
(fn main
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body
(let buf (new RawBuf (con Int) 5)
(let buf (app fill buf 5)
(app print (app total buf)))))))