056 union per member consume marker

✗ Failing This test is currently failing.

Failed: backend-exec

Failure Output

Showing last 10 of 11 lines
  --> auto_discharge:9: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_056_union_per_member_consume_marker/backend.zig:9546:17: 0x1040f24af in emit (backend)
                return error.CompilerCoordinationFailed;
                ^
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_056_union_per_member_consume_marker/backend.zig:9630:28: 0x1040f32b7 in main (backend)
    const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
                           ^

Code

// Test 330_056: Union with per-member consume markers
// Tests that [!opened|!closing] discharges the correct obligation from either state
// Demonstrates: per-member ! syntax where each union member carries its own discharge marker

~import "$app/handle"

// Test 1: finalize() from opened state - discharges opened! obligation
~app.handle:open()
| opened h1 |> app.handle:finalize(h: h1.h)
    | done |> _

// Test 2: finalize() from closing state - discharges closing! obligation
~app.handle:open()
| opened h1 |> app.handle:start_close(h: h1.h)
    | closing h2 |> app.handle:finalize(h: h2.h)
        | done |> _
input.kz

Expected

Opening handle
Finalizing handle: 42
Opening handle
Starting close: 42
Finalizing handle: 42

Imported Files

// Library module: handle
// Demonstrates per-member consume markers in phantom state unions
// Each union member carries its own ! prefix to indicate obligation discharge

const std = @import("std");

// Handle type with phantom states
const Handle = struct {
    id: i32,
};

// Open a handle - produces obligation (must be finalized)
~pub event open {}
| opened *Handle[opened!]

~proc open {
    std.debug.print("Opening handle\n", .{});
    const allocator = std.heap.page_allocator;
    const h = allocator.create(Handle) catch unreachable;
    h.* = Handle{ .id = 42 };
    return .{ .opened = h };
}

// Start closing - explicitly discharges opened! and produces closing! obligation
~pub event start_close { h: *Handle[!opened] }
| closing *Handle[closing!]

~proc start_close {
    std.debug.print("Starting close: {d}\n", .{h.id});
    return .{ .closing = h };
}

// Finalize - per-member consume markers: !opened discharges opened!, !closing discharges closing!
~pub event finalize { h: *Handle[!opened|!closing] }
| done

~proc finalize {
    std.debug.print("Finalizing handle: {d}\n", .{h.id});
    return .{ .done = .{} };
}
handle.kz

Test Configuration

MUST_RUN