Xo is experimental. The source is now public on GitHub, or try Xo in your browser.
Menu · XO0538

Docs

XO0538: a variable written after a closure captured it

warning · reported by the vet

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.