✓
Passing This code compiles and runs correctly.
Code
// PINS: an effect payload and a capture nested inside its own handler are
// real shadowing, and Koru refuses it.
//
// ! item x |> classify(v: x) <- x bound here…
// | lo x |> sink(v: x) <- …still live when this binds
//
// The two are NOT siblings. `| lo` hangs off `classify`, which is inside the
// `! item` handler body, so the payload's `x` is in scope when the capture
// binds and the inner name hides the outer one. That is nesting, and it is the
// whole difference from `| lo x` / `| hi x`, which share a name legally because
// disjoint arms never see each other.
//
// Ruled by Lars 2026-07-27. This test previously asserted the opposite — that
// the two names denote one value flowing through, that "Koru has no rule
// against it", and that the EMITTER should alpha-rename one of them. That
// reading was wrong: the language forbids shadowing, and the pun machinery
// rests on it ("no shadowing means the in-scope match is always unique",
// ast_transform.zig). Allowing it would have meant a scope stack in the
// desugar, where today a nested duplicate is skipped and a pun inside the inner
// scope silently resolves to the OUTER binding.
//
// What changed is the SENTENCE, not the verdict. This program has always been
// refused; it was refused by Zig — `capture 'x' shadows function parameter
// from outer scope` — a host error standing in for a language one, pointing
// into a file the author never opened. KORU106 names the binding, both sites,
// and what to do instead.
//
// The fix an author writes is to rename one of them. Sibling arms are
// untouched: `| lo x` and `| hi x` still share `x` legally, and this wall does
// not reach them.
const std = @import("std");
~pub tor run { n: u64 }
! item u64
~pub tor classify { v: u64 }
| lo u64
| hi u64
~pub tor sink { v: u64 }
~proc run|zig {
var i: u64 = 0;
while (i < n) : (i += 1) {
item(i);
}
}
~proc classify|zig {
if (v < 10) return .{ .lo = v };
return .{ .hi = v };
}
~proc sink|zig {
std.debug.print("sink {d}\n", .{v});
}
// The collision: effect binding `x` and branch captures `x` share a name.
~run(n: 1)
! item x |> classify(v: x)
| lo x |> sink(v: x)
| hi x |> sink(v: x)
Must fail at frontend compile:
Parsing or type-checking must reject the program.
Flows
flow ~run click a branch to expand · @labels scroll to their anchor
run (n: 1)
Test Configuration
Expected Error:
KORU106