✓
Passing This code compiles and runs correctly.
Code
// NEGATIVE TEST. This pins that `~` inside a Koru flow is ILLEGAL and the
// compiler MUST reject it. It is NOT a feature awaiting a fix.
//
// The offending line is the subflow RHS below:
//
// ~decide = ~if(ctx.flag and ctx.n > 0)
// ^ second `~` — ILLEGAL
//
// `~` is parser mode, not a call operator. It switches the parser from Zig
// into Koru. Once you are in Koru you STAY in Koru until the flow ends — you
// never write `~` again inside it. The `~decide =` already opened Koru, so the
// `~if` on the RHS is a `~` inside a flow. This is the FIRST rule in the Koru
// handbook ("The `~` prefix is parser mode, not a call operator"). The legal
// form, if this were a real subflow, would drop the second tilde: `~decide =
// if(...)`. `~if(...)` at FLOW-STATEMENT position is fine (see 320_002) — what
// is illegal is `~if` as the RHS of a flow that is already in Koru.
//
// HISTORY (do not repeat): two ADD loops on 2026-05-31 mistook this rejection
// for a bug. The first (b5a4d4de) pinned it as "should work, fix later"; the
// second (2e986a27) made parser.zig STRIP the leading `~` so the illegal code
// compiled — teaching the compiler to ACCEPT what the language forbids, the
// exact lax-compiler anti-pattern. Both were premise-absorption failures: the
// commission was never checked against the language standard. Reverted; this
// test now correctly stands as MUST_FAIL.
//
// CURRENT REJECTION: the parser records the literal name `~if`, which Stage C
// fails to resolve -> `error[KORU040]: unknown event 'input:~if'`
// (BACKEND_EXEC_ERROR). That message is technically a rejection but POOR — it
// blames an "unknown event" instead of naming the `~`-inside-flow violation.
// Improving the diagnostic to say so directly is legitimate future work, to be
// done by TIGHTENING (a clear early-layer parser error), NEVER by accepting the
// code. If that work lands, update expected_error.txt to the new message; the
// test stays MUST_FAIL either way.
//
// NOTE on the arms: `yes ctx` / `no ctx` use the canonical braceless
// identity-branch constructor (not `yes(ctx)`, which `parser.zig:5984` would
// read as an invocation). That part is correct and incidental — the rejection
// fires on the `~if` long before the arms matter.
~import std/io
const std = @import("std");
pub const Ctx = struct {
flag: bool,
n: usize,
};
~pub event decide { ctx: Ctx }
| yes Ctx
| no Ctx
~decide = ~if(ctx.flag and ctx.n > 0)
| then => yes ctx
| else => no ctx
~pub event report { text: []const u8 }
~proc report|zig {
std.io.getStdOut().writer().print("{s}\n", .{text}) catch {};
}
~pub event run { ctx: Ctx }
~run = decide(ctx)
| yes _ |> report(text: "YES")
| no _ |> report(text: "NO")
pub fn main() void {
const c = Ctx{ .flag = true, .n = 3 };
~run(ctx: c)
}
Output must match:
BACKEND_EXEC_ERRORError Verification
Actual Compiler Output
error[KORU040]: unknown event 'input:~if'
--> tests/regression/300_ADVANCED_FEATURES/320_STDLIB/320_047_if_subflow_enclosing_branches/input.kz:57:0
❌ Compiler coordination error: Unknown event referenced
error: CompilerCoordinationFailed
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/320_STDLIB/320_047_if_subflow_enclosing_branches/backend.zig:94:13: 0x104c761b7 in emit (backend)
return error.CompilerCoordinationFailed;
^
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/320_STDLIB/320_047_if_subflow_enclosing_branches/backend.zig:190:28: 0x104c76ea3 in main (backend)
const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
^Test Configuration
MUST_FAIL
Expected Error:
error[KORU040]: unknown event 'input:~if'