✓
Passing This code compiles and runs correctly.
Code
// GREEN (2026-06-14, fixed same day it was pinned): `std/control:if` as the
// body of a VALUE-returning subflow — now works, including under recursion.
//
// THE FIX (visitor_emitter.zig, the subflow-handler inline_body path): a
// statement-style template head (`//@koru:inline_stmt`) with named value-return
// continuations was emitted as `const result = <template body>; switch(result)`
// — which left the template's `__koru_continue_N` markers raw and tried to
// switch on an `if` statement. The handler path now detects the inline_stmt
// marker and delegates to the shared `emitInlineBodyNode` (the same path
// `emitFlow` uses for top-level inline templates), which resolves the markers
// into the continuation bodies inline — the continuation-RETURN model, no outer
// switch. The `__KORU_INLINE__` expression form is untouched.
//
// Original pin notes, for the record:
// `if` is a |template| proc that emits
// `{{ continuations["then"].continue }}` / `["else"].continue`. For the common
// EFFECT-dispatch case (250_007) those resolve to effect calls. But when the
// continuation is a `=>` value-return (`=> total 0`), the template mints a
// `__koru_continue_N` marker that the Zig emitter's
// emitInlineCodeResolvingSplices does NOT substitute in subflow-body position,
// so the generated Zig is malformed:
//
// const result = if (n <= 0) { __koru_continue_0 } else { __koru_continue_1 };
//
// (raw placeholders, missing `;`, and `if` emitted as statement-blocks rather
// than a value expression).
//
// ROOT (grounded in emitter_helpers.zig): the value-return subflow path
// (~line 2192, `return switch (result) { ... }`) treats the head invocation as
// a VALUE-PRODUCING EVENT — it binds `const result = <head>` and switches on
// its Output union. But `if` is a `|template|`, which produces its value by
// continuation HAND-OFF (`__koru_continue_N` markers), not a union. The two
// models collide: the markers are never routed through
// emitInlineCodeResolvingSplices AND the outer `switch (result)` is redundant
// for a template head. So the fix is NOT "also call the resolver" — it's a real
// seam: a `|template|` head in value-return position must lower via the
// continuation-return model (the if's arms directly yield the mapped returns),
// with no outer switch. Ties into the "for/if are templates over
// continuation-return" architecture. Fixing this lets `if` replace the [expand]
// template in 320_095. sum-to(5) = 15.
~import std/io
~import std/control
~event sum-to { n: i64 }
| total i64
~sum-to = std/control:if(n <= 0)
| then => total 0
| else |> sum-to(n: n - 1)
| total t => total t + n
~sum-to(n: 5)
| total r |> std/io:print.ln("{{ r:d }}")
Actual
15
Expected output
15
Test Configuration
MUST_RUN