✗
Failing This test is currently failing.
Failed: must-fail-passed
Failure Output
🎯 Compiler coordination: Passes: 15 (flow-based: frontend, analysis, emission) Code
// PIN (checker gap — RED): a borrowed obligation is dropped from conservation
// checking when a pipeline CONTINUATION follows the borrow.
//
// `make` mints <owned!> on h0. `touch` BORROWS it (bare <owned>, no consume,
// no re-issue — the obligation stays with the caller). `dispose` is the ONLY
// discharge (<!owned>). The flow borrows h0 and then continues the pipeline to
// `report` — h0 is NEVER disposed, so it leaks.
//
// The checker SHOULD reject: h0 still owes <owned!> at end of scope. It does
// NOT — with a continuation step after the borrow, conservation loses the
// still-held obligation and the program compiles and runs clean.
//
// CONTRAST — the ONLY difference that hides the leak is the continuation:
// caught : | ok h0 |> touch(h: h0) // arm ENDS on the borrow
// NOT : | ok h0 |> touch(h: h0) |> report(n: 0) // a step FOLLOWS it
// Removing the trailing `|> report(...)` makes KORU030 fire on h0. So the bug
// is isolated to the post-borrow continuation dropping the held obligation.
//
// Neighbors that do NOT cover this facet:
// 335_040 — ownership handed INTO a void subflow that drops it (now caught)
// 335_006 — leak on an | err branch
// 335_042 — bare []const u8 (no phantom at all)
// This is the borrow-with-caller-retained-obligation case: distinct and open.
// It blocks migrating the koru-libs / std corpus to the bare-borrow idiom,
// because a forgotten discharge after a borrow-then-continue silently leaks.
//
// Grounding:
// bare <owned> borrow as input — 330_073_label_fold_borrow_outer_own/input.kz
// <owned!> issue / <!owned> consume — koru_std/list.kz directionality
// inline |zig procs, const std — 335_040 uses the same pattern
const std = @import("std");
const Handle = struct { n: i32 };
~event make {}
| ok *Handle<owned!>
~proc make|zig {
const h = std.heap.page_allocator.create(Handle) catch unreachable;
h.* = .{ .n = 0 };
return .{ .ok = h };
}
// BORROW: bare <owned>, no consume — the obligation stays with the caller.
~event touch { h: *Handle<owned> }
~proc touch|zig { h.n += 1; }
~event report { n: i32 }
~proc report|zig { std.debug.print("n={}\n", .{n}); }
// Discharge — the ONLY consumer of <!owned>.
~event dispose { h: *Handle<!owned> }
~proc dispose|zig { std.heap.page_allocator.destroy(h); }
// h0 is borrowed by `touch`, then the pipeline CONTINUES to `report`.
// h0 is never disposed -> leak. MUST be rejected; currently is not.
~make()
| ok h0 |> touch(h: h0) |> report(n: 0)
Must contain:
was not dischargedError Verification
Actual Compiler Output
🎯 Compiler coordination: Passes: 15 (flow-based: frontend, analysis, emission)Flows
flow ~make click a branch to expand · @labels scroll to their anchor
make
Test Configuration
MUST_FAIL