module Chapter.Imp.VerificationConditions where

Imports

open import Library.Bool
open import Library.Nat
open import Library.Logic
open import Library.Equality
open import Library.Equality.Reasoning
open import Chapter.Imp.AexpBexp
open import Chapter.Imp.BigStep
open import Chapter.Imp.HoareLogic

The verification of the program sum-prog in chapter Example of derivation with Hoare logic makes it apparent how difficult may be to directly use Hoare logic for program verification without the help of some mechanical procedure. From a quick insepection of that proof one realizes that most of the effort resides in establishing rather obvious implications among assertions occurring in the rule H-Conseq or its variants. At least in this example, these consist of simple facts from arithmetic, involving sum and order, stated using quantifier-free formulas; but such formulas could be proved authomatically using e.g. tools based on satisfiability-modulo-theories, SMT.

To make such a remark effective, it would be of great advantage to factor the arithmetic part of the proofs out of the remaining inferences using other rules of Hoare logic. In fact, the latters essentially follow the structure of the program and seem to be computable starting with the post-condition. This is suggested by the the main properties of the predicate wp in chapter Relative Completeness of Hoare logic, namely

 Fact : ∀ {P c Q} -> |= [ P ] c [ Q ] -> (∀ s -> P s -> wp c Q s)
 wp-lemma : ∀ c {Q : Assn} -> |- [ wp c Q ] c [ Q ]

Indeed, suppose that we have to verify the program c against the pre-condition P and post-condition Q; this amounts to prove that |= [ P ] c [ Q ] which by the soundness of Hoare logic is consequence of the derivability of |- [ P ] c [ Q ]. By computing wp c Q we have that ∀ s -> P s -> wp c Q s by Fact and |- [ wp c Q ] c [ Q ] by the wp-lemma, from which the desired proof of |- [ P ] c [ Q ] follows by rule H-Str:

      ∀ s -> P s -> wp c Q s     |- [ wp c Q ] c [ Q ]
H-Str ------------------------------------------------
                   |- [ P ] c [ Q ]

However the definition of wp is semantic, while we need a syntactic definition to compute the weakest precondition wp c Q. Let us try to define wp c Q by induction over the command c. The first cases are rather obvious:

wp SKIP     Q s = Q s
wp (x := a) Q s = Q (s [ x ::= aval a s ])

From the proof of lemma wp-lemma in case of the command c₁ :: c₂ we see that we can define:

wp (c₁ :: c₂) Q s = wp c₁ (wp c₂ Q) s 

The case of IF command is easy:

wp (IF b THEN c₁ ELSE c₂) Q s = wp c₁ Q s     if aval b s == true
                              = wp c₂ Q s     if aval b s == false

Where the attempt fails is in case of a WHILE command. Indeed, by exploting the equivalence

(WHILE b DO c) ∼ (IF b THEN (c :: (WHILE b DO c)) ELSE SKIP)

(see chapter Big-step operational semantics, exercise 3) we could set

pre (WHILE b DO c) Q s = wp (c :: (WHILE b DO c)) Q s    if bval b s == true
                       = Q s                             if bval b s == false

so that in case bval b s == true we have

wp (c :: (WHILE b DO c)) Q s = wp c (wp (WHILE b DO c) Q) s

But then the definition of pre (WHILE b DO c) Q depends on itself.

Verification conditions

Let us look at the rule H-While:

         |- [ (λ s -> P s ∧ bval b s == true) ] c [ P ]
 H-While ------------------------------------------------------------
         |- [ P ] (WHILE b DO c) [ (λ s -> P s ∧ bval b s == false) ]

Then the pre-condition P in the conclusion is the invariant assertion of the command WHILE b DO c and it is the desired wp (WHILE b DO c) Q provided that P s ∧ bval b s == false implies Q s for all s.

Now, a way out of the impasse in the inductive definition of wp in case of the WHILE command is to ask the user to provide herself an invariant I for each loop in her program, and then to verify that such I’s are the correct choice by generating a formula expressing that they are actual invariants of the respective WHILE commands: these are the verification conditions.

We begin by defining the grammar of annotated commands, namely IMP commands where each WHILE command carries a (candidate) invariant assertion.

data Acom : Set₁ where
  SKIP  :  Acom
  _:=_  :  Vname  Aexp  Acom           
  _::_  :  Acom  Acom  Acom   
  IF_THEN_ELSE_ : Bexp  Acom  Acom  Acom 
  WHILE[_]_DO_  : Assn  Bexp  Acom  Acom

To recover the IMP command corresponding to C : Acom there is an obvious erasure map called strip:

strip : Acom  Com
strip SKIP = SKIP
strip (x := a) = x := a
strip (C₁ :: C₂) = strip C₁ :: strip C₂
strip (IF b THEN C₁ ELSE C₂) = IF b THEN strip C₁ ELSE strip C₂
strip (WHILE[ I ] b DO C) = WHILE b DO strip C

Inspired to the tentative syntactic definition of wp, we define the assertion pre C Q as follows:

pre : Acom  Assn  Assn
pre SKIP Q = Q
pre (x := a) Q s =  Q (s [ x ::= aval a s ])
pre (C₁ :: C₂) Q = pre C₁ (pre C₂ Q)
pre (IF b THEN C₁ ELSE C₂) Q s with bval b s
...   | true  = pre C₁ Q s
...   | false = pre C₂ Q s
pre (WHILE[ I ] b DO C) Q = I

Then, given an annotated command C : Acom and the post-condition Q, we define the verification condition vc C Q asserting that all the invariants I occurring in some subexpression WHILE[ I ] b DO C' of C are actual invariants of the command WHILE b DO (strip C'), so that pre C Q can be regarded as a valid pre-condition of the command strip C with respect to the post-condition Q:

vc : Acom  Assn  Set
vc SKIP Q = 
vc (x := a) Q = 
vc (C₁ :: C₂) Q = vc C₁ (pre C₂ Q)  vc C₂ Q
vc (IF b THEN C₁ ELSE C₂) Q = vc C₁ Q  vc C₂ Q
vc (WHILE[ I ] b DO C) Q =
   (∀ s  I s  bval b s == true  pre C I s) 
   (∀ s  I s  bval b s == false  Q s) 
   vc C I

Now, we have to prove that if the verification condition vc C Q is satisfied then we can derive |- [ pre C Q ] strip C [ Q ] in Hoare logic, and hence |= [ pre C Q ] strip C [ Q ], namely the triple [ pre C Q ] strip C [ Q ] is valid.

vc-sound :  {Q : Assn} C  vc C Q  |- [ pre C Q ] strip C [ Q ]
vc-sound SKIP hyp = H-Skip
vc-sound (x := a) hyp = H-Loc
vc-sound {Q} (C₁ :: C₂) (hyp1 , hyp2) = H-Comp IH1 IH2
  where
    IH1 : |- [ pre C₁ (pre C₂ Q) ] strip C₁ [ pre C₂ Q ]
    IH1 = vc-sound C₁ hyp1

    IH2 : |- [ pre C₂ Q ] strip C₂ [ Q ]
    IH2 = vc-sound C₂ hyp2

vc-sound {Q} (IF b THEN C₁ ELSE C₂) (hyp1 , hyp2) = H-If if-True if-False
  where
    IH1 : |- [ pre C₁ Q ] strip C₁ [ Q ]
    IH1 = vc-sound C₁ hyp1

    case-True :  s  pre (IF b THEN C₁ ELSE C₂) Q s  bval b s == true  pre C₁ Q s
    case-True s (hyp3 , hyp4) rewrite hyp4 = hyp3
    
    -- hyp4 : bval b s == true
    -- hyp3 : pre (IF b THEN c₁ ELSE c₂) Q s | bval b s

    if-True : |- [  s  pre (IF b THEN C₁ ELSE C₂) Q s  bval b s == true) ] strip C₁ [ Q ]
    if-True = H-Str case-True IH1

    IH2 : |- [ pre C₂ Q ] strip C₂ [ Q ]
    IH2 = vc-sound C₂ hyp2

    case-False :  s  pre (IF b THEN C₁ ELSE C₂) Q s  bval b s == false  pre C₂ Q s
    case-False s (hyp5 , hyp6) rewrite hyp6 = hyp5
    
    -- hyp6 : bval b s == false
    -- hyp5 : pre (IF b THEN C₁ ELSE C₂) Q s | bval b s

    if-False : |- [  s  pre (IF b THEN C₁ ELSE C₂) Q s  bval b s == false) ] strip C₂ [ Q ]
    if-False = H-Str case-False IH2

vc-sound {Q} (WHILE[ I ] b DO C) (hyp7 , hyp8 , hyp9) = H-Weak While-conclusion hyp8

  -- hyp7 : ∀ s → I s ∧ bval b s == true → pre C I s
  -- hyp8 : ∀ s → I s ∧ bval b s == false → Q s
  -- hyp9 : vc C I
  
  where
    IH : |- [ pre C I ] strip C [ I ]
    IH = vc-sound C hyp9

    While-premise : |- [  s  I s  bval b s == true) ] strip C [ I ]
    While-premise = H-Str hyp7 IH

    While-conclusion : |- [ I ] WHILE b DO (strip C) [  s  I s  bval b s == false) ]
    While-conclusion = H-While While-premise

Completeness of the verification conditions method

Should it be the case that |- [ pre C Q ] strip C [ Q ] implies that vc C Q is valid? The answer is obviously not; indeed this would be clearly false if the invariants in C are ill choosen, say because they are just trivial assertions like ⊤'. However it is true that any proof of |- [ P ] c [ Q ] implicitly includes its own verification condition, so that we can show that for any such triple there exists a properly annotated command C such that c == strip C, the assertion vc C Q is valid and P implies pre C Q.

Toward the proof of such a theorem, we prove two lemmas stating that if P implies P' then both pre C P implies pre C P' and vc C P implies vc C P'.

pre-mono : ∀{P P' : Assn} {s : State} (C : Acom)
            (∀ s'  P s'  P' s')  pre C P s  pre C P' s
           
pre-mono {s = s''} SKIP hyp1 hyp2 = hyp1 s'' hyp2
-- {s = s''} is short for {P} {P'} {s''} where just the binding of s to s'' matters
pre-mono {s = s''} (x := a) hyp1 hyp2 = hyp1 (s'' [ x ::= aval a s'' ]) hyp2
pre-mono {P} {P'} {s} (C₁ :: C₂) hyp1 hyp2 = thesis
  
  -- hyp1 : ∀ s' → P s' → P' s'
  -- hyp2 : pre C₁ (pre C₂ P) s
  
  where
    pre-C₂ :  s  pre C₂ P s  pre C₂ P' s
    pre-C₂ s = pre-mono C₂ hyp1

    thesis : pre C₁ (pre C₂ P') s
    thesis = pre-mono C₁ pre-C₂ hyp2

pre-mono {s = s} (IF b THEN C₁ ELSE C₂) hyp1 hyp2 
  -- hyp1 : ∀ s' → P s' → P' s'
  -- hyp2 : pre (IF b THEN C₁ ELSE C₂) P s | bval b s
  with bval b s
... | true  = pre-mono C₁ hyp1 hyp2
... | false = pre-mono C₂ hyp1 hyp2

pre-mono (WHILE[ I ] b DO C) hyp1 hyp2 = hyp2
  -- hyp1 : ∀ s' → P s' → P' s'
  -- hyp2 : I s


vc-mono :  {P P' : Assn} (C : Acom) 
               (∀ s -> P s  P' s)  vc C P  vc C P'

vc-mono SKIP hyp1 hyp2 = hyp2

vc-mono (x := a) hyp1 hyp2 = hyp2

vc-mono {P} {P'} (C₁ :: C₂) hyp1 (hyp2 , hyp3) = IH1 , IH2
  -- hyp1 : ∀ s → P s → P' s
  -- hyp2 : vc C₁ (pre C₂ P)
  -- hyp3 : vc C₂ P
  where
    pre-C₂ : (∀ s  pre C₂ P s  pre C₂ P' s)
    pre-C₂ = λ _  pre-mono C₂ hyp1            -- λ _ is dummy for λ (s : State)

    IH1 : vc C₁ (pre C₂ P')
    IH1 = vc-mono C₁ pre-C₂ hyp2

    IH2 : vc C₂ P'
    IH2 = vc-mono C₂ hyp1 hyp3
vc-mono {P} {P'} (IF b THEN C₁ ELSE C₂) hyp (hyp1 , hyp2) = IH1 , IH2
    -- hyp  : ∀ s → P s → P' s
    -- hyp1 : vc C₁ P
    -- hyp2 : vc C₂ P
    where
      IH1 : vc C₁ P'
      IH1 = vc-mono C₁ hyp hyp1

      IH2 : vc C₂ P'
      IH2 = vc-mono C₂ hyp hyp2
    
vc-mono {P} {P'} (WHILE[ I ] b DO C) hyp (hyp1 , hyp2 , hyp3) = th1 , th2 , th3
    -- hyp  : ∀ s → P s → P' s
    -- hyp1 : ∀ s → I s ∧ bval b s == true → pre C I s
    -- hyp2 : ∀ s → I s ∧ bval b s == false → P s
    -- hyp3 : vc C I
    where
      th1 :  s  I s  bval b s == true  pre C I s
      th1 = hyp1

      th2 :  s  I s  bval b s == false  P' s
      th2 s h = hyp s (hyp2 s h)

      th3 : vc C I
      th3 = hyp3


We are now in place to prove the completeness theorem of verification conditions by induction over the derivation of |- [ P ] c [ Q ].

vc-completeness :  {P Q : Assn} c 
            |- [ P ] c [ Q ] 
            ∃[ C ] ( c == strip C  vc C Q  (∀ s  P s  pre C Q s) )

vc-completeness SKIP H-Skip = SKIP , refl , (<> ,  s h  h))
vc-completeness (x := a) H-Loc = (x := a) , (refl , (<> , λ s h  h))
vc-completeness (c₁ :: c₂) (H-Comp {P = P} {R} {Q} hyp₁ hyp₂) =
                (C₁ :: C₂) , (A , D , h)
                
  -- hyp₁ : |- [ P ] c₁ [ R ]
  -- hyp₂ : |- [ R ] c₂ [ Q ]

  where
    IH₁ : ∃[ C₁ ] ( c₁ == strip C₁  vc C₁ R  (∀ s  P s  pre C₁ R s) )
    IH₁ = vc-completeness {P} {R} c₁ hyp₁

    IH₂ : ∃[ C₂ ] ( c₂ == strip C₂  vc C₂ Q  (∀ s  R s  pre C₂ Q s) )
    IH₂ = vc-completeness {R} {Q} c₂ hyp₂
  
    C₁ : Acom
    C₁ = fst IH₁

    C₂ : Acom
    C₂ = fst IH₂

    Matr₁ : c₁ == strip C₁  vc C₁ R  (∀ s  P s  pre C₁ R s)
    Matr₁ = snd IH₁

    Matr₂ : c₂ == strip C₂  vc C₂ Q  (∀ s  R s  pre C₂ Q s)
    Matr₂ = snd IH₂

    A : (c₁ :: c₂) == strip (C₁ :: C₂)
    A = begin c₁ :: c₂             ==⟨ cong  z  z :: c₂)       (fst Matr₁) 
              strip C₁ :: c₂       ==⟨ cong  z  strip C₁ :: z) (fst Matr₂) 
              strip C₁ :: strip C₂
        end

    D : vc (C₁ :: C₂) Q  -- vc C₁ (pre C₂ Q) ∧ vc C₂ Q
    D = vc-mono C₁ (snd (snd Matr₂)) (fst (snd Matr₁)) ,
        fst (snd Matr₂)

    f :  s  P s  pre C₁ R s
    f = snd (snd Matr₁)

    g :  s  R s  pre C₂ Q s
    g = snd (snd Matr₂)

    h :  s  P s  pre (C₁ :: C₂) Q s  --  ∀ s  → P s → pre C₁ (pre C₂ Q) s
    h s p = pre-mono {s = s} C₁ g (f s p)

vc-completeness (IF b THEN c₁ ELSE c₂) (H-If {P = P} {Q = Q} hyp₁ hyp₂) =
     (IF b THEN C₁ ELSE C₂) , A , D , h
     
  -- hyp₁ : |- [ (λ s → P s ∧ bval b s == true)  ] c₁ [ Q ]
  -- hyp₂ : |- [ (λ s → P s ∧ bval b s == false) ] c₂ [ Q ]

  where

    IH₁ : ∃[ C₁ ] ( c₁ == strip C₁  vc C₁ Q  (∀ s  P s  bval b s == true  pre C₁ Q s) )
    IH₁ = vc-completeness c₁ hyp₁

    IH₂ : ∃[ C₂ ] ( c₂ == strip C₂  vc C₂ Q  (∀ s  P s  bval b s == false  pre C₂ Q s) )
    IH₂ = vc-completeness c₂ hyp₂

    C₁ : Acom
    C₁ = fst IH₁

    C₂ : Acom
    C₂ = fst IH₂

    Matr₁ : c₁ == strip C₁  vc C₁ Q  (∀ s  P s  bval b s == true  pre C₁ Q s)
    Matr₁ = snd IH₁

    Matr₂ : c₂ == strip C₂  vc C₂ Q  (∀ s  P s  bval b s == false  pre C₂ Q s)
    Matr₂ = snd IH₂

    A : (IF b THEN c₁ ELSE c₂) == (IF b THEN strip C₁ ELSE strip C₂)
    A = begin
          IF b THEN c₁ ELSE c₂       ==⟨ cong  z  IF b THEN z ELSE c₂) (fst Matr₁) 
          IF b THEN strip C₁ ELSE c₂ ==⟨ cong  z  IF b THEN strip C₁ ELSE z) (fst Matr₂)
          IF b THEN strip C₁ ELSE strip C₂
        end

    D : vc C₁ Q  vc C₂ Q
    D = (fst (snd Matr₁)) , (fst (snd Matr₂))

    f :  s  P s  bval b s == true  pre C₁ Q s
    f = snd (snd Matr₁)

    g :  s  P s  bval b s == false  pre C₂ Q s
    g = snd (snd Matr₂)

    if-pre : ∀{Q C₁ C₂} b s
          (bval b s == true   pre C₁ Q s)
          (bval b s == false  pre C₂ Q s)
           pre (IF b THEN C₁ ELSE C₂) Q s
    if-pre b s f g with bval b s
    ... | true  = f refl
    ... | false = g refl

    h :  s  P s  pre (IF b THEN C₁ ELSE C₂) Q s
    h s p = if-pre b s
                 x  f s (p , x))
                 y  g s (p , y))

vc-completeness (WHILE b DO c) (H-While {P} hyp) =
                (WHILE[ P ] b DO C) , (A , (D , (E , F)) , G)

  -- hyp : |- [ (λ s → P s ∧ bval b s == true) ] c [ P ]

  where

    IH : ∃[ C ] (c == strip C  vc C P  (∀ s  P s  bval b s == true  pre C P s))
    IH = vc-completeness c hyp

    C : Acom
    C = fst IH

    Matr : c == strip C  vc C P  (∀ s  P s  bval b s == true  pre C P s)
    Matr = snd IH

    A : (WHILE b DO c) == (WHILE b DO strip C)
    A = begin
          WHILE b DO c ==⟨ cong  z  WHILE b DO z) (fst Matr)  -- by ind. hyp.
          WHILE b DO strip C
        end

    D :  s  P s  bval b s == true  pre C P s
    D = snd (snd Matr)  -- by ind. hyp.

    E :  s  P s  bval b s == false  P s  bval b s == false
    E s h = h

    F : vc C P
    F = fst (snd Matr)  -- by ind. hyp.

    G :  s  P s  P s
    G s p = p


vc-completeness {P} {Q} c (H-Conseq {P'} {Q'} hyp₁ hyp hyp₂) = C , A , (D , E)

  -- hyp₁ : ∀ s → P s → P' s
  -- hyp  : |- [ P' ] c [ Q' ]
  -- hyp₂ : ∀ s → Q' s → Q s

  where
    IH : ∃[ C ] (c == strip C  vc C Q'  (∀ s  P' s  pre C Q' s) )
    IH = vc-completeness c hyp
  
    C : Acom
    C = fst IH

    Matr : c == strip C  vc C Q'  (∀ s  P' s  pre C Q' s)
    Matr = snd IH

    A : c == strip C
    A = fst Matr

    D : vc C Q
    D = vc-mono C hyp₂ (fst (snd Matr))

    f :  s  P' s  pre C Q' s
    f = snd (snd Matr)

    E :  s  P s  pre C Q s
    E s p = pre-mono C hyp₂ (f s (hyp₁ s p))