scriggo fork: go-render function index truncates at 256 (int8→uint8) — silent wrong render

Latent correctness bug in the vendored scriggo fork (gitlab.com/haproxy-haptic/scriggo), found while investigating #165 (closed). Not triggered by the bundled chart; filing so it isn't lost. The fix belongs in the fork.

Summary OpGoRender's function operand is an int8 set from int8(len(currFn.Functions)) and read back as Functions[uint8(a)]. A single compiled template function that references more than 256 distinct parallel-render (go) targets wraps the index modulo 256: targets 0–127 are fine, 128–255 survive the int8→uint8 round-trip, and 256..N wrap to 0..(N−256), rendering the wrong function's body. Output is full-length but silently wrong — no error.

Where (fork, pinned commit 780f57e0)

  • internal/compiler/emitter_func_store.go:66,74-75scriggoFnIndex returns int8(len(currFn.Functions)) and appends, no bound check.
  • internal/compiler/builder_instructions.go:361,364emitGoRender(f int8, …) stores it in Instruction.A.
  • internal/runtime/run.go:1077,1079case OpGoRender: fn := vm.fn.Functions[uint8(a)]. (OpGoRenderIndirect, run.go:1100, is register-based and unaffected.)

Reproduction (deterministic, no -race): a go render_glob "partials/*.txt" over 400 files emits 400 OpGoRenders into one function. Build 400 files partials/shard0000..0399.txt each MARKER_NNNN\n, index.txt = {{ go render_glob "partials/*.txt" }}, render once, count each marker. Result on 780f57e0: 144 markers missing, 144 duplicated of 400 — files 256–399 rendered files 0–143 again.

Impact Latent for the bundled HAPTIC chart — it renders every sharded section with a single macro invoked in a runtime for loop ({% for i … %}{{ go Macro(shard_slice(…)) }}{% end %}), one function index reused each iteration, never >256 distinct targets in one function. It bites any template that fans out go render_glob over >256 files, or writes >256 distinct go Macro() / go render "file" sites in one function/macro.

Fix sketch Widen the parallel-render function operand beyond int8 (the instruction already spans two words), or bound-check in scriggoFnIndex and fail the build with a clear "too many parallel-render targets in one function" error. A build-time hard error is the floor; the silent modulo is the dangerous part.