SE 504   Formal Methods and Models
Guidelines for proving the correctness of a program in Dijkstra's Guarded Command Language

Let S be a program and let P and Q be predicates over the state space of S. (In other words, the values of P and Q depend upon only variables that occur in S.)

To show the validity of the Hoare triple {P} S {Q}, follow these rules:

  1. If S is skip, show [P Q].
  2. If S is abort, show [P ≡ false].
  3. If S is an assignment x:=E, show [P Q(x:=E)].
    (In cases where E might not be well-defined, show [P def.E ∧ Q(x:=E)].)
  4. If S is a catenation (also known as a sequential composition) S1;S2 (where each of S1 and S2 is a sequence of one or more commands), develop a predicate R and show the validity of both {P} S1 {R} and {R} S2 {Q}.

    If the form of S2 makes calculation of wp.S2.Q not unduly burdensome, it would be wise to choose R to be that predicate. That way, you get {R} S2 {Q} "for free" and you get R to be as weak as possible, increasing the likelihood that {P} S1 {R} is valid.

  5. If S is a selection (sometimes called "alternative") command

    if B0 ---> S0
    [] B1 ---> S1
    .
    .
    [] Br ---> Sr
    fi

    show
    (a) [P BB], where BB is an abbreviation for the disjunction of the guards (i.e., B0 ∨ B1 ∨ ... ∨ Br)
    (b) {P ∧ Bi} Si {Q} (for all i satisfying 0≤i≤r)

    The purpose of (a) is to show that at least one guard is true. (Recall that —in Dijkstra's Guarded Command Language— if a selection command is executed in a state in which all its guards are false, it acts like an abort command (see above), which is to say that its behaviour is totally unpredictable.) The purpose of (b) is to show that every guarded command Si that is a candidate to be chosen for execution (i.e., each one whose guard Bi is true) is such that, if executed, it is guaranteed to terminate with the postcondition Q being true.

    By virtue of the facts that [wp.skip.Q = Q], [wp.abort.Q = false], [wp.(x:=E).Q = Q(x:=E)], and [wp.IF.Q = (BB ∧ (i | 0≤i≤r : Bi wp.Si.Q))], guidelines 1-3 and 5-6 above correspond to showing {P} S {Q} by demonstrating the equivalent [P wp.S.Q].

  6. If S is a loop (also called "repetition") command

    do B0 ---> S0
    [] B1 ---> S1
    .
    .
    [] Br ---> Sr
    od

    then develop a predicate I (the loop invariant) and a "bound" function t (mapping the state space to the integers) and show each of the following:
    (i) [P I]    (basis proof of invariance) (in practice, we show {P} Sinit {I}; see below)
    (ii) {I ∧ Bi} Si {I} (for all i satisfying 0≤i≤r)     (induction step of proof of invariance)
    (iii) [I ∧ ¬BB    Q]    (adequacy of invariant) (BB is the disjunction B0 ∨ ... ∨ Br)
    (iv) [I ∧ BB    t > 0] (or, equivalently, [I ∧ t≤0 ⇒ ¬BB]) (boundedness) (BB is as in (iii))
    (v) {I ∧ Bi ∧ t=C} Si {t<C} (for all i satisfying 0≤i≤r)    (progress toward termination)

    The purpose of (i) is to show that that I holds just before the first iteration of the loop. The purpose of (ii) is to show that if I holds at the beginning of a particular iteration, it also holds at the end of that iteration. Thus, (i) and (ii) together constitute an inductive proof that I is true immediately before and immediately after each iteration of the loop (which is exactly what we mean when we refer to I as a loop invariant!).

    The purpose of (iii) is to show that, when the loop terminates (assuming that it does), Q holds. When the loop terminates, it must be that BB is false (otherwise the loop wouldn't have terminated) and, by (i) and (ii), that I is true. Thus, if I ∧ ¬BB Q holds, not only I ∧ ¬BB, but also Q, must be true upon termination of the loop.

    By proving (i), (ii), and (iii), one establishes what is called partial correctness, which corresponds to the idea that, if the loop terminates, the postcondition will have been established. However, the truth of (i), (ii), and (iii) does not guarantee that the loop will terminate.

    The purpose of (iv) and (v) is to show that the loop necessarily terminates (after finitely many iterations). This, in conjunction with the proof of partial correctness embodied in (i), (ii), and (iii), yields a proof of total correctenss. Specifically, (v) shows that each loop iteration causes the value of t to decrease. (More precisely, the value of t in the state existing immediately after an iteration is less than the value of t in the state existing immediately before that iteration.) What (iv) shows is that, as long as more iterations are to be performed, t>0. (Note that any constant can serve in place of zero here, but zero is usually the most natural choice.)

    To understand why (iv) and (v) together guarantee that the number of loop iterations is finite, consider the following process by which to generate a sequence of integers: Choose some integer k, and write it down. Now write down a smaller integer. Then write down another which is yet smaller. And so on and so forth. Once you have written a number that is zero or less, you must stop. (You may stop even before you reach zero.) Is it possible for your sequence of numbers to keep growing, without end? No! Indeed, the length of your sequence can never exceed k+1. (A longest possible sequence is k, k-1, k-2, ..., 1, 0.) In other words, the number of times that you may "write a smaller integer" is bounded above by your original choice of k. By the same reasoning, (iv) and (v) guarantee that the number of loop iterations is bounded above by the value of t immediately before the first iteration. More generally, the value of t immediately before any particular iteration is an upper bound on the number of iterations that remain!

    Almost every loop is immediately preceded by a short chunk of "initialization" code whose purpose is to truthify the loop invariant, typically by assigning values to one or more variables. Suppose we have such a chunk of code, Sinit, and a selection command DO, and that we want to show

    {P} Sinit ; DO {Q}

    Following the advice in guideline (4) above, we are to choose an "intermediate" predicate R and show the validity of both {P} Sinit {R} and {R} DO {Q}. By choosing R to be I, (i) boils down to showing [I I], which holds trivially! We are then left with the task of showing {P} Sinit {I}. Hence, in practice we let this Hoare Triple (rather than the trivial [I I]) play the role of (i) in the correctness proof.