✓
Passing This code compiles and runs correctly.
Code
// NEGATIVE TEST: Identity branch decl + struct-shape proc return.
//
// `connect` declares `| ok Connection[open!]` — an identity branch whose
// payload IS a Connection. But its proc returns `.{ .ok = .{ .conn = ... } }`
// — a struct shape. The two don't match.
//
// The shape checker should reject this with a clear error pointing at the
// mismatch between the branch declaration and the constructor.
//
// Today this input segfaults the phantom semantic checker at
// findDisposalEventsForState because phantom_state slices in BindingContext
// go stale once the malformed state path is propagated through recursive
// validateContinuation frames. The compiler should never segfault on user
// input — a clean shape error is the correct outcome.
const std = @import("std");
const Connection = struct { handle: i32 };
const Transaction = struct { conn_handle: i32 };
~pub event connect { host: []const u8 }
| ok Connection[open!]
| err []const u8
~proc connect|zig {
_ = host;
return .{ .ok = .{ .conn = .{ .handle = 1 } } };
}
~pub event begin { conn: Connection[!open] }
| ok Transaction[active!]
| err []const u8
~proc begin|zig {
return .{ .ok = .{ .tx = .{ .conn_handle = conn.handle } } };
}
~[!]pub event commit { tx: Transaction[!active] }
| err []const u8
~proc commit|zig {
_ = tx;
return .ok;
}
~event report_err { stage: []const u8, msg: []const u8 }
~proc report_err|zig {
std.debug.print("{s} err: {s}\n", .{ stage, msg });
}
~connect(host: "localhost")
| ok c1 |> begin(conn: c1.conn)
| ok c2 |> commit(tx: c2.tx)
| err msg |> report_err(stage: "commit", msg: msg)
| err msg |> report_err(stage: "begin", msg: msg)
| err msg |> report_err(stage: "connect", msg: msg)
Error Verification
Actual Compiler Output
error[KORU030]: Phantom state mismatch: argument 'conn' has no tracked phantom state, but event requires '[!open]' (consumption). Did you mean to pass a value with state 'input:open'?
--> phantom_semantic_check:53:0
error[KORU030]: Phantom state mismatch: argument 'tx' has no tracked phantom state, but event requires '[!active]' (consumption). Did you mean to pass a value with state 'input:active'?
--> phantom_semantic_check:53:0
error[KORU030]: Resource 'c1' [open!] was not discharged. Call: input:begin
--> phantom_semantic_check:53:0
❌ Compiler coordination error: Phantom semantic validation failed
error: CompilerCoordinationFailed
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_062_reject_identity_struct_mismatch/backend.zig:94:13: 0x10078900b in emit (backend)
return error.CompilerCoordinationFailed;
^
/Users/larsde/src/koru/tests/regression/300_ADVANCED_FEATURES/330_PHANTOM_TYPES/330_062_reject_identity_struct_mismatch/backend.zig:184:28: 0x100789c6b in main (backend)
const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
^Test Configuration
MUST_FAIL
Post-validation Script:
#!/bin/bash
# Custom validation for 330_062.
#
# The proc body declares `~event connect | ok Connection[open!]` (identity
# branch), but `~proc connect` returns `.{ .ok = .{ .conn = ... } }` — a
# struct shape that doesn't match the declared identity-branch payload type.
#
# Per koru/CLAUDE.md "Koru is emit-only with the host language," shape_checker
# does NOT introspect proc body Zig. So the diagnostic for this mismatch must
# come from downstream: when c1 is bound from the malformed return, its tracked
# phantom state is inconsistent, and the phantom checker catches it as soon as
# c1.conn is passed to `begin`.
#
# This script verifies that:
# 1. The compiler produced KORU-coded errors (didn't crash with a Zig stack
# trace from a segfault on the malformed state propagation path).
# 2. The diagnostic identifies the bound binding's bad shape (phantom state
# mismatch / not-discharged), proving Koru caught the mismatch downstream
# rather than letting it propagate to Zig.
# 3. The error message is meaningful (mentions the bound name and what's
# wrong), not just a generic panic.
#
# A regex pin in expected_patterns.txt was the old approach. It demanded the
# error mention "identity" and come from shape_checker — both unreachable under
# the emit-only principle. post.sh expresses the actual invariant instead.
set -e
[ -s backend.err ] || { echo "FAIL: backend.err is missing or empty"; exit 1; }
# Must produce a KORU error code, not a raw Zig stack trace from a crash.
if ! grep -qE 'error\[KORU[0-9]+\]' backend.err; then
echo "FAIL: no KORU error code in backend.err — compiler may have crashed"
cat backend.err
exit 1
fi
# Must not contain segfault / panic / general protection markers.
if grep -qE 'general protection exception|panic:|Segmentation fault' backend.err; then
echo "FAIL: compiler crashed instead of producing a clean diagnostic"
cat backend.err
exit 1
fi
# Must identify the bound-binding shape mismatch. Phantom checker catches this
# as either "no tracked phantom state" on the argument or "not discharged" on
# the bound resource — either is acceptable evidence the mismatch was caught.
if ! grep -qE 'no tracked phantom state|was not discharged' backend.err; then
echo "FAIL: diagnostic doesn't reference the bound-shape mismatch"
cat backend.err
exit 1
fi
# Must reference at least one of the bindings from the test flow.
if ! grep -qE "'(c1|c2|conn|tx)'" backend.err; then
echo "FAIL: diagnostic doesn't name any of the involved bindings"
cat backend.err
exit 1
fi
echo "PASS: malformed identity-branch return diagnosed downstream without segfault"
exit 0