✓
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(c1.conn)
| ok c2 |> commit(c2.tx)
| err msg |> report-err(stage: "commit", msg)
| err msg |> report-err(stage: "begin", msg)
| err msg |> report-err(stage: "connect", msg)
Error Verification
Actual Compiler Output
error[KORU030]: Resource 'c2' <active!> was not discharged. Call: commit
--> auto_discharge:53: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_062_reject_identity_struct_mismatch/backend.zig:95:13: 0x10445b9d3 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:202:28: 0x10445cb63 in main (backend)
const generated_code = try RuntimeEmitter.emit(compile_allocator, final_ast);
^Flows
flow ~connect click a branch to expand · @labels scroll to their anchor
connect (host: "localhost")
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