✓
Passing This code compiles and runs correctly.
Code
// BOUNDARY (MUST_FAIL): you may NOT discharge an outer-scope obligation inside a
// scope. `h0 <owned!>` is owned OUTSIDE the `for`; `take(h: h0)` consumes it
// INSIDE the @scope. Correctly rejected with KORU032.
//
// THE RULE — obligations and scopes, in full (this test pins clause 1):
// 1. Inside a scope you may NOT discharge an obligation owned OUTSIDE the scope.
// (This test — rejected.)
// 2. Inside a scope you MAY issue obligations and discharge them locally.
// 3. From an effect branch OR a continuation branch you MAY let an issued
// obligation ESCAPE the scope — but then the event owning that branch must
// handle it: discharge it in its own implementation, or pass it on to
// another continuation/effect branch.
//
// WHY THIS MUST BE REJECTED (it is NOT a checker gap — see 330_072 history):
// A `for`/effect body is a 0-to-N scope. Consuming the OUTER `h0` "once" only
// type-checks if the scope runs exactly once; on any re-fire it is a
// double-consume of an already-poisoned binding. Re-issuing `<owned!>` on the
// popped `v` does NOT rehabilitate `h0` — `v` is a DIFFERENT obligation on a
// DIFFERENT binding. There is no "conservation" that licenses the outer
// consume; the earlier conserved-move reading was simply wrong.
//
// THE SOUND WAY to carry an obligation through a loop is GREEN 330_074: ISSUE it
// from a leaf inside the scope and let it ESCAPE to an outside disposer (clause 3)
// — never consume an outer binding in-scope. GREEN 330_083 shuttles by issuing
// FRESH handles each lap. Both honor the rule; this test deliberately breaks it.
//
// EXPECTED: KORU032 "Cannot discharge outer-scope resource ... inside @scope".
const std = @import("std");
const Handle = struct { n: i32 };
~event make {}
| made *Handle<owned!>
~proc make|zig {
const h = std.heap.page_allocator.create(Handle) catch unreachable;
h.* = .{ .n = 7 };
return .{ .made = h };
}
// Take the owned handle "out of A": consume the incoming hold, reissue it on the
// popped value (the collection-memory pop-reissue move, hand-rolled).
~event take { h: *Handle<!owned> }
| popped *Handle<owned!>
~proc take|zig {
return .{ .popped = h };
}
// Put the owned handle "into B": consume the obligation.
~event put { h: *Handle<!owned> }
~proc put|zig {
std.debug.print("moved n={}\n", .{h.n});
std.heap.page_allocator.destroy(h);
}
~make()
| made h0 |> for(0..1)
! each _ |> take(h: h0)
| popped v |> put(h: v)
| done |> _
Must contain:
Cannot discharge outer-scope resourceError Verification
Actual Compiler Output
error[KORU032]: Cannot discharge outer-scope resource 'h0' inside @scope boundary. Handle outside the scope or escape via branch constructor.
--> auto_discharge:55:0
❌ Compiler coordination error: Auto-discharge failed (multiple disposal options or no disposal event)
error: CompilerCoordinationFailed
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_082_element_obligation_move_in_for/backend.zig:94:13: 0x1007eb20b in emit (backend)
return error.CompilerCoordinationFailed;
^
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_082_element_obligation_move_in_for/backend.zig:190:28: 0x1007ebef7 in main (backend)
const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
^Flows
flow ~make click a branch to expand · @labels scroll to their anchor
make
Test Configuration
MUST_FAIL