XO0538: a variable written after a closure captured it
Reported by xo vet, not xo check. let root = fn(k) { .. up[k] .. } copies up when the closure is created. A later up[i] = x, up = .., or up.push(..) in the enclosing function changes up but not the closure’s copy, so every later call of root walks the old value. Found as the union find bug of the arm64 SLP pass (2026-10-21), where every pack stayed its own group. The check fires for a let name = fn(..) { .. } whose body reads a var local, a write to that local in a later statement of the same block, and a use of name after the write. Pass the variable as a parameter (fn(k, up)), or move the closure after the writes. See core 5.1.
Example
fn groups(n: Int) -> Int {
var up: List[Int] = [0, 1, 2]
let root = fn(k: Int) -> Int { up[k] }
up[0] = 2
root(0)
}Fix
fn groups(n: Int) -> Int {
var up: List[Int] = [0, 1, 2]
up[0] = 2
let root = fn(k: Int) -> Int { up[k] }
root(0)
}Run xo explain XO0538 for this text in a terminal, or see
section 10.6 of the specification.