✓
Passing Passing: the compiler rejects this program as expected.
Code
// The World in Koru — entry 4, the negative twin of 834_mutex_unlock.
// Doorway: Go's `mu.Unlock(); mu.Unlock()` — releasing a lock you've already
// released. In Go (and C) that's undefined behaviour: a runtime panic if you're
// lucky, silent corruption if you're not — `defer` does nothing to stop it.
//
// In Koru the released token's TYPE no longer carries `<held!>`, so a second
// `unlock` has no obligation to consume — the phantom checker rejects the reuse
// at compile time. Use-after-release is unrepresentable, not merely unlucky.
import std/io
pub tor lock { name: string } -> string<held!>
lock -> name
pub tor unlock { name: string<!held> }
unlock = std/io:print.ln("unlock {{ name:s }}")
// Release once (discharges <!held>), then try to release the same token again.
lock(name: "m"): m |> unlock(name: m) |> unlock(name: m)
Actual compiler output
error[KORU030]: Use-after-discharge: binding 'm' was already discharged and cannot be used
--> tests/regression/830_THE_WORLD/835_double_unlock/input.k:18:0
❌ Compiler coordination error: Phantom semantic validation failed
(set KORU_BACKEND_TRACE=1 for the backend return trace)Must fail at runtime with:
CONTAINS Use-after-dischargeFlows
subflow ~unlock click a branch to expand · @labels scroll to their anchor
print.ln (expr: "unlock {{ name:s }}")
flow ~lock click a branch to expand · @labels scroll to their anchor
lock (name: "m")
Test Configuration
MUST_ERROR