Practical guide

State what remains true while a loop runs

A loop invariant summarizes the part of the work already completed. It lets a verifier reason about every iteration without treating a few unrolled iterations as the whole loop.

Three questions make an invariant useful

Does it hold before the loop starts? Does one iteration preserve it? At exit, does it imply the required result? The Dafny tutorial explains this role of invariants and the separate need to reason about termination. An invariant that is always true may be preserved but too weak to justify the result.

Illustrative example: checking a prefix

i = 0
while i < length(items) and items[i] is acceptable:
    i = i + 1

Invariant:
  0 <= i <= length(items)
  every item before i is acceptable

The prefix is empty at entry, so the claim holds. An iteration advances only after checking the current item, preserving the accepted prefix. At exit, either every item was accepted or the current item is the first unacceptable one. This is pseudocode and a mathematical argument, not a verified production implementation.

Termination and arithmetic remain separate

The nonnegative quantity length(items) - i decreases on each iteration in this model, which supplies a termination argument. Check that the actual language's index and arithmetic semantics support the reasoning. For an accumulator, “sum stays nonnegative” can be false under signed overflow even when every added value is positive. State numeric bounds or use the appropriate arithmetic model.

Use supported source annotations deliberately

Proof supports reqproof:invariant directives for supported loop forms. Its documented path establishes the invariant, checks preservation and uses it in the exit reasoning for the lemma. Translation support depends on the loop shape and language features. Consult the installed guidance before rewriting code around an unsupported construct.

proof help loop-invariants
proof verify-lemma ./internal/scan

The package is illustrative. Inspect the solver verdict and diagnostics; an unsupported translation or timeout is not a proof. Do not assume that adding a comment automatically makes the loop analyzable.

Review the invariant against the requirement

Bounds alone may establish memory safety while saying nothing about which item the scan returns. A prefix property may establish the right result but omit whether mutation is permitted. Keep the claim strong enough for the requirement, then execute integration tests for the callers and environment that the local proof does not model.

Follow the evidence

Describe the completed prefix, accumulated result or preserved resource at the top of one loop.

Review one loop invariant. Bring the loop and its required result so we can assess whether the invariant supports the exit claim and which arithmetic or termination assumptions need evidence.

Understand inductive arguments · Connect claims to source