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 the values of the variables that occur in S.)

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

  1. 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 liklihood that {P} S1 {R} is valid.

  2. If S is skip, show [P ⇒ Q].
  3. If S is abort, show [P ≡ false].
  4. If S is an assignment x:=E, show [P ⇒ Q(x:=E)].
  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, if it is executed in a state in which all its guards are false, a selection command acts like an abort command (see above) (i.e., it causes execution to "abort" (or "hang")). The purpose of (b) is to show that any guarded command that is a candidate to be chosen for execution (i.e., any one whose guard is true) is such that, if executed, it is guaranteed to terminate with the postcondition Q being true.

    Before considering the repetition command, it is worth noting that, 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))], each of guidelines (2) through (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 R (the loop invariant) and a "bound" function t (mapping the state space to the integers) and show each of the following:
    (i) [P ⇒ R]    (basis of invariance) (in practice, we show {P} Sinit {R}; see below)
    (ii) {R ∧ Bi} Si {R} (for all i satisfying 0≤i≤r)     (induction step of invariance)
    (iii) [R ∧ ¬BB  ⇒  Q]    (adequacy of invariant)
    (iv) [R ∧ BB  ⇒  t > 0] (or, equivalently, [R ⇒ (t≤0 ⇒ ¬BB)]) (boundedness)
    (v) {R ∧ 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 R holds just before the first iteration of the loop. The purpose of (ii) is to show that if R 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 R is true immediately before and immediately after each iteration of the loop (which is exactly what we mean when we refer to R 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 R is true. Thus, if R ∧ ¬BB ⇒ Q holds, not only R &and ¬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, your sequence can never grow beyond one of length 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 (1), we are to choose an "intermediate" predicate R and show the validity of both {P} Sinit {R} and {R} DO {Q}. As R we choose the loop invariant. Thus, (i) boils down to showing [R ⇒ R], which holds, trivially! We are then left with the task of showing {P} Sinit {R}. Hence, in practice we let this Hoare Triple (rather than the trivial [R ⇒ R]) play the role of (i) in the correctness proof.