Open and answer this issue on GitHub.
ready · opened 2026-10-19 · updated 2026-10-19 · 0 comments
Part III C.6 and Part II 12.3 say that taking a lock while a lock is held is always a compile-time error (NK2203), written nested, reached through a chain of calls, or opened as a scope inside the block. The compiler does not refuse it. The roadmap row for ADR-281 says the same (NK2201 and NK2203 “are not written”), and the touches_a_lock column is inferred but read by nothing (crates/nikaia/tests/lock_column.rs).
Found while checking the examples for the website’s “What Nikaia checks” section: the page had claimed NK2203 works, and it does not.
Built from develop at ccf982f, target/release/nikaia check:
struct Account {
balance: i64,
}
fn main() {
let a = SharedMut(Account { balance: 100 })
let b = SharedMut(Account { balance: 100 })
a.update fn(mut from) {
from.balance -= 30
b.update fn(mut to) { to.balance += 30 }
}
}
Result: Checked. Expected: error[NK2203]: `update` takes a lock, but you're already holding one here. (the message in Part III C.6).
The same lock twice, a.access fn(x) { a.update fn(mut y) { y.balance += 1 } }, is also accepted.
What is refused today: a pause inside a door (NK2202, time::sleep under update), and update_all(a, b) builds.
NK2203 for a door (access, update, set, access_all, update_all) opened directly inside another door’s block, with the message and help of C.6.touches_a_lock column: a call whose answer is touches a lock is refused, one whose answer is undecided is not (Part III C.4: never refuse a correct program).task::scope opened inside a door’s block (the scope waits for its tasks).update_all way out accepted, and the probes above as refusals.examples/ before it lands: no program there may newly fail.index.html, “Tasks that get stuck”); change that sentence when this lands. The commit that finishes step 6 writes Fixes #677.