050 union accepts either state

✗ Failing This test is currently failing.

Failed: backend-exec

Failure Output

Showing last 10 of 44 lines
  --> phantom_semantic_check:30:0

❌ Compiler coordination error: Phantom semantic validation failed
error: CompilerCoordinationFailed
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_050_union_accepts_either_state/backend.zig:9937:17: 0x100a0a4af in emit (backend)
                return error.CompilerCoordinationFailed;
                ^
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_050_union_accepts_either_state/backend.zig:10021:28: 0x100a0b2b7 in main (backend)
    const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
                           ^

Code

// Test 330_050: Union accepts either state
// Tests that phantom state unions accept any matching member state
// Demonstrates: [paused|active] accepts both paused AND active states

~import "$app/connection"

// Test 1: resume() accepts 'active' state (second member of union)
~app.connection:create()
| created c1 |> app.connection:connect(conn: c1)
    | connected c2 |> app.connection:resume(conn: c2)  // active -> resume accepts it!
        | resumed c3 |> app.connection:close(conn: c3)
            | closed |> _

// Test 2: resume() accepts 'paused' state (first member of union)
~app.connection:create()
| created c1 |> app.connection:connect(conn: c1)
    | connected c2 |> app.connection:pause(conn: c2)
        | paused c3 |> app.connection:resume(conn: c3)  // paused -> resume accepts it!
            | resumed c4 |> app.connection:close(conn: c4)
                | closed |> _

// Test 3: close() accepts 'active' directly (union member)
~app.connection:create()
| created c1 |> app.connection:connect(conn: c1)
    | connected c2 |> app.connection:close(conn: c2)  // active -> close accepts it!
        | closed |> _

// Test 4: close() accepts 'paused' (other union member)
~app.connection:create()
| created c1 |> app.connection:connect(conn: c1)
    | connected c2 |> app.connection:pause(conn: c2)
        | paused c3 |> app.connection:close(conn: c3)  // paused -> close accepts it!
            | closed |> _
input.kz

Expected

Creating connection
Connecting: 1
Resuming/continuing: 1
Closing: 1
Creating connection
Connecting: 1
Pausing: 1
Resuming/continuing: 1
Closing: 1
Creating connection
Connecting: 1
Closing: 1
Creating connection
Connecting: 1
Pausing: 1
Closing: 1

Imported Files

// Library module: connection
// Demonstrates phantom state unions - events that accept multiple states

const std = @import("std");

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

// Create a new connection - starts in 'idle' state
~pub event create {}
| created *Connection[idle]

~proc create {
    std.debug.print("Creating connection\n", .{});
    const allocator = std.heap.page_allocator;
    const c = allocator.create(Connection) catch unreachable;
    c.* = Connection{ .id = 1 };
    return .{ .created = c };
}

// Connect - transitions from idle to active
~pub event connect { conn: *Connection[idle] }
| connected *Connection[active]

~proc connect {
    std.debug.print("Connecting: {d}\n", .{conn.id});
    return .{ .connected = conn };
}

// Pause - transitions from active to paused
~pub event pause { conn: *Connection[active] }
| paused *Connection[paused]

~proc pause {
    std.debug.print("Pausing: {d}\n", .{conn.id});
    return .{ .paused = conn };
}

// Resume - accepts EITHER paused OR active (union!) - always returns active
// This demonstrates the core union feature: flexible input acceptance
~pub event resume { conn: *Connection[paused|active] }
| resumed *Connection[active]

~proc resume {
    std.debug.print("Resuming/continuing: {d}\n", .{conn.id});
    return .{ .resumed = conn };
}

// Close - accepts active OR paused (union!) - cleans up connection
~pub event close { conn: *Connection[active|paused] }
| closed

~proc close {
    std.debug.print("Closing: {d}\n", .{conn.id});
    return .{ .closed = .{} };
}
connection.kz

Test Configuration

MUST_RUN