Part 3 · 1 chapters · ~8 min

Decidability and the Halting Problem

Decidable, semi-decidable and undecidable problems, the halting problem proved by diagonalisation, reductions between undecidable problems, Rice's theorem (every non-trivial property of program behaviour is undecidable), and what that means for linters, type checkers, static analysers and AI code review.

5

The proof, and what it means for tools

code
// suppose this existed and always answered correctly
declare function halts(program: string, input: string): boolean;

function trouble(p: string) {
  if (halts(p, p)) { while (true) {} }      // predicted to halt? then loop
  return;                                   // predicted to loop? then halt
}
// trouble(source of trouble): whatever halts() answers is wrong → halts() cannot exist
toolhow it copes with undecidability
type checkers (TypeScript, Rust)sound-ish and conservative: reject some correct programs, require annotations
linters and static analysersheuristics: report likely bugs, accept false positives and negatives
termination checkers (Coq, Lean, Agda)accept only programs whose recursion visibly shrinks
test suites and fuzzerscheck behaviour on chosen inputs; never prove absence of bugs
timeoutsgive up after a budget: the practical answer to "might not halt"

Rice's theorem: any non-trivial question about what a program does (does it ever divide by zero? does it leak this secret? is it equivalent to that program?) is undecidable in general. Every analysis tool you use is an approximation by necessity, not by laziness.

WHY NO PROGRAM CAN DECIDE HALTING
proof by contradiction, in code
halts(p, x)assumed to always answertrouble(p)if halts(p, p) loop forever else returntrouble(trouble)?contradictioneither answer is wrong
swipe the figure sideways, or tap expand for full screen
1/4
assume
Suppose a function halts(program, input) always returns true or false correctly.
assume a perfect deciderfor contradiction