module Chapter.Imp.HoareLogicSoundness where

Imports

open import Library.Bool
open import Library.Nat
open import Library.Nat.Properties
open import Library.Logic
open import Library.Logic.Laws
open import Library.Equality
open import Library.Equality.Reasoning

open import Chapter.Imp.AexpBexp
open import Chapter.Imp.BigStep
open import Chapter.Imp.HoareLogic

Formalizing the partial correctness criterion into a definition, we say that the triple [ P ] c [ Q ] is valid if for all s, t ∈ State whenever P holds of s and ⦅ c , s ⦆ converges to t, Q holds of t. Formally, we define the following predicate:

|=_ : Triple -> Set
|= [ P ] c [ Q ] =  {s t} -> P s ->  c , s   t -> Q t

Hoare Logic is intended to provide a tool to establish when a triple is valid. To ensure that this is the case, we show that the system is sound i.e. that only valid triples are derivable. This is the content of theorem-Hoare-sound claiming the implication

                ∀ {P c Q} ->  |- [ P ] c [ Q ]  ->  |= [ P ] c [ Q ]

The strategy to prove the theorem is first to show that all instances of axioms H-Skip and H-Loc are valid triples, which is immediate. Then the actual task in the proof is to show that the remaining rules infer valid triples from valid ones instantiating the respective premises. All in all, this is a proof by structural induction on the derivation of |- [ P ] c [ Q ].

Going through the proof, we realise that the thesis is an easy consequence of the induction hypotheses in all cases but in case of rule H-While, which deserves a more careful inspection. In Hoare system the rule is written:

                              {P ∧ b} c {P}
                        -------------------------
                        {P} WHILE b DO c {P ∧ ¬ b}

where P is the loop invariant, which is assumed to hold before executing the command WHILE b DO c and to be preserved by the execution of the body c whenever the condition of the guard b is true. From such hypotheses and the hypothesis that the execution of WHILE b DO c terminates, the rule infers that the post-condition of this loop is P ∧ ¬ b, where ¬ b expresses the fact that for a WHILE command to terminate the guard must be eventually false.

In our system the above rule is encoded by

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

To simplify the proof of the theorem, we begin with two lemmas stating respectively that under the hypothesis that [ (λ s -> P s ∧ bval b s == true) ] c [ P ] is valid, the assertion P is a loop invariant for WHILE b DO c and that if its execution terminates in a state t then bval b t == false.

lemma-Hoare-inv :  {P : Assn} {s b c t} ->
                  (∀{s' t'} -> (P s'  bval b s' == true) ->
                                c , s'   t'
                               -> P t') ->
                  P s ->
                   WHILE b DO c , s   t ->
                  P t
                  
lemma-Hoare-inv {P} {s} {b} {c} {.s} hyp1 hyp2 (WhileFalse x) = hyp2
lemma-Hoare-inv {P} {s} {b} {c} {t} hyp1 hyp2
           (WhileTrue {.c} {.b} {.s} {s'} {s''} x hyp3 hyp4) = claim2 
     where
       claim1 = hyp1 {s} {s'} (hyp2 , x) hyp3               
       claim2 = lemma-Hoare-inv {P} {s'} {b} {c} {t}
                                  (hyp1 {_} {_}) claim1 hyp4

lemma-Hoare-loop-exit :  {b c s t}
             ->  WHILE b DO c , s   t -> bval b t == false 

lemma-Hoare-loop-exit (WhileFalse x) = x
lemma-Hoare-loop-exit (WhileTrue x hyp1 hyp2) =
                                  lemma-Hoare-loop-exit hyp2

Technically we observe the usage of implicit arguments in the proof of lemma-Hoare-inv and in particular of dot patterns Implicit arguments are necessary to distinguish the states s', s'' from s: indeed the formers are automatically generated by Agda when splitting the hypothesis ⦅ WHILE b DO c , s ⦆ ⇒ s'' of the inductive case into ⦅ c , s ⦆ ⇒ s' and ⦅ WHILE b DO c , s' ⦆ ⇒ s'', so that without passing the implicit arguments to WhileTrue they are not in scope and hence inaccessible.

Dot patterns are constraints used by Agda to enforce the same binding of several occurrences of a variable in a non linear pattern: for example the variable {.c} has to be bound to the same command as the previous variable {c}. For explanations about dot patterns see Function Definitions - Dot patterns in Agda documentation.

We are eventually in place to prove theorem-Hoare-sound which is a corollary of lemma-Hoare-sound that establishes the same thesis but in a more explicit form, which allows the access to those implicit parameters which are essential in the proof of the theorem.

lemma-Hoare-sound :  {P c Q s t} ->
             |- [ P ] c [ Q ] -> 
             P s -> 
              c , s   t -> 
             Q t

To facilitate the understanding of the proof we intersperse the cases with some comments drown from the interactive construction of the proof itself. As a first step in the proof we write

                 lemma-Hoare-sound hyp1 hyp2 hyp3 = ?

and then we hit C-c C-c twice splitting hyp1 : |- [ P ] c [ Q ] and hyp3 : ⦅ c , s ⦆ ⇒ t.

lemma-Hoare-sound H-Skip hyp2 Skip = hyp2

lemma-Hoare-sound H-Loc hyp2 Loc = hyp2

In cases of H-Skip and H-Loc the hypothesis hyp2 in the left-hand sides has the types P s and Q (s [ x ::= aval a s ]) respectively. These are the same as the types of the respective right-hand sides, hence they are both authomatically obtained typing C-c C-a while putting the cursor in the holes.

lemma-Hoare-sound (H-Comp hyp1 hyp4) hyp2 (Comp hyp3 hyp5) = IH2
      where
         IH1 = lemma-Hoare-sound hyp1 hyp2 hyp3
         IH2 = lemma-Hoare-sound hyp4 IH1 hyp5

In case of H-Comp, typing

      lemma-Hoare-sound (H-Comp hyp1 hyp4) hyp2 (Comp hyp3 hyp5) = ?

and then hitting C-c C-, with the cursor in the hole we obtain from Agda the reply (omitting parameters that are not in scope):

      Goal: Q t
      ———————————————————————————————————————————————————————
      hyp5 : ⦅ c₂ , s₂ ⦆ ⇒ t
      hyp3 : ⦅ c₁ , s ⦆ ⇒ s₂
      hyp2 : P s
      hyp4 : |- [ Q₁ ] c₂ [ Q ]
      hyp1 : |- [ P ] c₁ [ Q₁ ]

The induction hypothesis lemma-Hoare-sound hyp1 is a proof of |= [ P ] c₁ [ Q₁ ] that is applied to hyp2 : P s and to hyp3 : ⦅ c₁ , s ⦆ ⇒ s₂ yielding a proof IH1 : Q₁ s₂. On the other hand the induction hypothesis lemma-Hoare-sound hyp4 is a proof of |= [ Q₁ ] c₂ [ Q ], that when applied to IH1 : Q₁ s₂ and hyp5 : ⦅ c₂ , s₂ ⦆ ⇒ t returns a proof of Q t which is the goal.

lemma-Hoare-sound (H-If hyp1 hyp4) hyp2 (IfTrue x hyp3) = thesis
      where

        IH = lemma-Hoare-sound hyp1
        thesis = IH (hyp2 , x) hyp3

lemma-Hoare-sound (H-If hyp1 hyp4) hyp2 (IfFalse y hyp3) = thesis
      where

        IH = lemma-Hoare-sound hyp4
        thesis = IH (hyp2 , y) hyp3

Case H-If is split into two subcases according to the value of b from the command IF b THEN c₁ ELSE c₂ in the third hypothesis. By asking Agda about the context by means of C-c C-, we get

      Goal: Q t
      ——————————————————————————————————————————————————————
      hyp3 : ⦅ c₁ , s ⦆ ⇒ t
      x    : bval b s == true
      hyp2 : P s
      hyp4 : |- [ (λ s₁ -> P s₁ ∧ bval b s₁ == false) ] c₂ [ Q ]
      hyp1 : |- [ (λ s₁ -> P s₁ ∧ bval b s₁ == true) ] c₁ [ Q ]

In case of (IfTrue x hyp3) : ⦅ IF b THEN c₁ ELSE c₂ , s ⦆ ⇒ t the induction hypothesis IH = lemma-Hoare-sound hyp1 : |= [ (λ s₁ -> P s₁ ∧ bval b s₁ == true) ] c₁ [ Q ] is applied to (hyp2 , x) : P s ∧ bval b s == true and to hyp3 : ⦅ c₁ , s ⦆ ⇒ t yielding a proof thesis : Q t as desired.

The case (IfFalse y hyp3) : ⦅ IF b THEN c₁ ELSE c₂ , s ⦆ ⇒ t, where hyp3 : ⦅ c₂ , s ⦆ ⇒ t and y : bval b s == false is similar, using the induction hypothesis IH = lemma-Hoare-sound hyp4.

lemma-Hoare-sound {P} {_} {_} {s} {t}
                  (H-While {_} {b} hyp1) hyp2 hyp3 = Pt , b-false
      where

        Pt : P t
        Pt = lemma-Hoare-inv (lemma-Hoare-sound hyp1 ) hyp2 hyp3 

        b-false : bval b t == false
        b-false = lemma-Hoare-loop-exit hyp3

Case H-While is the most interesting one. Starting with

      lemma-Hoare-sound {P} {_} {_} {s} {t}
              (H-While {.P} {b} hyp1) hyp2 hyp3 = ?

and typing C-c C-l and then C-c C-, with the cursor in the hole we get the context:

      Goal: P t ∧ bval b t == false
      ———————————————————————————————————————————————————————
      hyp3 : ⦅ WHILE b DO c , s ⦆ ⇒ t
      hyp2 : P s
      hyp1 : |- [ (λ s₁ -> P s₁ ∧ bval b s₁ == true) ] c [ P ]

Since the goal is a conjunction, putting the cursor in the hole and typing C-c C-r to refine Agda’s guess about the filling of the hole, we get a pair of holes {!!} , {!!}; in the first hole it is expected a proof of P t, which is called Pt in the above proof, and in the second hole a proof of bval b == false is required, which is called b-false.

Now, by passing the induction hypothesis lemma-Hoare-sound hyp1 : |= [ (λ s₁ -> P s₁ ∧ bval b s₁ == true) ] c [ P ] to the lemma lemma-Hoare-inv and then the result to hyp2 and to hyp3 we get a proof of the fact that P is an invariant of the command WHILE b DO c, hence it holds for the final state t whenever ⦅ WHILE b DO c , s ⦆ ⇒ t; therefore we have established that P t holds as required.

On the other hand applying lemma-Hoare-loop-exit to hyp3 : ⦅ WHILE b DO c , s ⦆ ⇒ t we obtain a proof b-false : bval b t == false which ends the proof of this case.

lemma-Hoare-sound {_} {_} {_} {s} {t}
                               (H-Conseq x hyp1 y) hyp2 hyp3 = ths
      where

        P₁s = x s hyp2
        IH  = lemma-Hoare-sound hyp1
        Q₁t = IH P₁s hyp3
        ths = y t Q₁t

In the case H-Conseq we have the context:

      Goal: Q t
      ————————————————————————————————————————————————————
      hyp3 : ⦅ c , s ⦆ ⇒ t
      hyp2 : P s
      y    : (s₁ : State) -> Q₁ s₁ -> Q s₁
      hyp1 : |- [ P₁ ] c [ Q₁ ]
      x    : (s₁ : State) -> P s₁ -> P₁ s₁

Then the induction hypothesis lemma-Hoare-sound hyp1 : |= [ P₁ ] c [ Q₁ ] can be easily combined with P₁s = x s hyp2 : P₁ s and Q₁t = IH P₁s hyp3 : Q₁ t establishing the thesis ths = y t Q₁t : Q t as desired.

Eventually we conclude:

theorem-Hoare-sound :  {P c Q} ->
                    |- [ P ] c [ Q ] -> |= [ P ] c [ Q ] 

theorem-Hoare-sound hyp = lemma-Hoare-sound hyp

Remark. The functions theorem-Hoare-sound and lemma-Hoare-sound have different definitions, even if when applied to hyp yield the same value. The reason is that these functions are extensionally equivalent, that is they yield the same values for the same arguments. However the extensionality principle is not part of the Agda logic, rather it is just compatible with it and must be postulated for use in proofs.