Lecture notes 20190531

More Semantics 4 Termination

Require Import Coq.Relations.Relation_Operators.
Require Import Coq.Relations.Relation_Definitions.
Require Export Coq.ZArith.ZArith.
Require Export Coq.Strings.String.
Require Export Coq.Logic.Classical.

Arguments clos_refl_trans {A} _ _ _.
Arguments clos_refl_trans_1n {A} _ _ _.
Arguments clos_refl_trans_n1 {A} _ _ _.

Open Scope Z.

Termination

We have already learnt 3 different program semantics in this course: axiomatic semantics, denotational semantics and small step semantics.
For denotational semantics, we define ceval to describe how a program behave. Given starting state st1, a program c will terminate if there is a state st2 such that ceval c st1 st2 holds.
For small step semantics, we define cstep to describe what will be the next step and we call its reflexive and transitive closure multi_cstep. Given starting state st1, a program c will terminate if there is a state st2 such that multi_cstep (c, st1) (CSkip, st2).
But you may notice that Hoare logic can not yet be used to reason about termination. That is our main topic today.

Total Correctness

Hoare rules that only talk about what happens when commands terminate (without proving that they do) are often said to describe a logic of partial correctness. It is also possible to give Hoare rules for total correctness, which build in the fact that the commands terminate. We mainly focused on partial correctness in this course but we will briefly introduce total correctness today.

Unchanged rules

The following rules are not changed:
Axiom hoare_skip : P,
  {{P}Skip {{P}}.

Axiom hoare_seq : (P Q RAssertion) (c1 c2com),
  {{P}c1 {{Q}} →
  {{Q}c2 {{R}} →
  {{P}c1;;c2 {{R}}.

Axiom hoare_if : P Q b c1 c2,
  {P AND [[b]}c1 {Q }} →
  {P AND NOT [[b]}c2 {Q }} →
  {P }If b Then c1 Else c2 EndIf {Q }}.

Axiom hoare_consequence : (P P' Q Q' : Assertionc,
  P ⊢ P' →
  {{P'}c {{Q'}} →
  Q' ⊢ Q →
  {{P}c {{Q}}.

Axiom hoare_asgn_fwd : P (XvarE,
  {P }}
  X ::= E
  {EXISTS xP [X ⟼ xAND
               [[X]] == [E [X ⟼ x]}}.

Axiom hoare_asgn_bwd : P (XvarE,
  {P [ X ⟼ E}X ::= E {P }}.
We can check them one by one and see whether they are actually sound.

While loop rule

A while loop might be not terminating even if its loop body always terminates. Thus, original proof rule for partial correctness will not work for total correctness.
Axiom hoare_while_partial : I b c,
  {I AND [[b]}c {I }} →
  {I }While b Do c EndWhile {I AND NOT [[b]}}.
The following proof rule for total correctness includes not only a loop invariant I but also a loop variant V.
Axiom hoare_while_total : I V b c,
  (T:Z,
    {I AND [[b]AND V == T }c {I AND 0 ≤ V AND V < T }}) →
  {I }While b Do c EndWhile {I AND NOT [[b]}}.
Here, the loop variable V is a integer term in the assertion language that talks about program variables. This rule says, if the loop variant strictly decreases in every execution of loop body, the loop will always terminate.

Examples

Example 1:
       {{ 0 < [[X]}}
       While !(X == 0) Do
         X ::= X - 1
       EndWhile
       {[[X]] == 0 }}.
Example 2:
    {{ 0 ≤ m }}
    X ::= m;;
    Y ::= p;;
    While !(X == 0) Do
      Y ::= Y - 1;;
      X ::= X - 1
    EndWhile
    {[[Y]] - [[X]] == p - m AND [[X]] == 0 }}.
Example 3:
       {{ 0 ≤ m }}
       X ::= m;;
       Y ::= 0;;
       While n ≤ X Do
         X ::= X - n;;
         Y ::= Y + 1
       EndWhile
       {n * [[Y]] + [[X]] == m AND 0 ≤ [[X]AND [[X]] < n }}.

Programs With Addresses And Dereferences

Now, we start to consider programming languages with addresses. In C language, people can use a pointer to represent an address in memory. Suppose E is a C pointer-type expressions, then * E represents the value stored in that address in memory. Moreover, if E talks about the value store on some address, then & E represents that specific address. In the following AST of expressions, we use ADeref E and AAddr E to represent * E and & E.
Definition var: Type := nat.

Inductive aexp : Type :=
  | ANum (n : Z)
  | AId (X : var)
  | APlus (a1 a2 : aexp)
  | AMinus (a1 a2 : aexp)
  | AMult (a1 a2 : aexp)
  | ADeref (a1: aexp) (* <-- new *)
  | AAddr (a1: aexp). (* <-- new *)

Inductive bexp : Type :=
  | BTrue
  | BFalse
  | BEq (a1 a2 : aexp)
  | BLe (a1 a2 : aexp)
  | BNot (b : bexp)
  | BAnd (b1 b2 : bexp).

Inductive com : Type :=
  | CSkip
  | CAss (a1 a2 : aexp) (* <-- new *)
  | CSeq (c1 c2 : com)
  | CIf (b : bexp) (c1 c2 : com)
  | CWhile (b : bexp) (c : com).
We assume that the value of variables of 0, 1, ... are stored at address 1, 2, ... etc. Address 0 is for null pointer.
Definition var2addr (X: var): Z := Z.of_nat X + 1.

Program States With Permission

One critical problem of pointer programs is dangling pointer. That is, dereferencing a null pointer is a run time error. For any address that the program has not read/write permission, dereferencing it is a run time error.
Due to this consideration, the definition of program states must talks about permission. Here is a simple approach.
Definition state: Type := Zoption Z.
For a state st and an address p, st p = Some _ means that we have read/write permission of p. Otherwise, st p = None and we do not have read or write permission of it.

Denotational Semantics

Now, we try to write down its denotational semantics. We start from integer expressions.
Module FirstTry_aeval.

Inductive aeval : aexpstateZProp :=
  | E_ANum n st:
      aeval (ANum n) st n
  | E_AId X st n:
      st (var2addr X) = Some n
      aeval (AId X) st n
  | E_APlus (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aeval e1 st n1)
      (H2 : aeval e2 st n2) :
      aeval (APlus e1 e2) st (n1 + n2)
  | E_AMinus (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aeval e1 st n1)
      (H2 : aeval e2 st n2) :
      aeval (AMinus e1 e2) st (n1 - n2)
  | E_AMult (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aeval e1 st n1)
      (H2 : aeval e2 st n2) :
      aeval (AMult e1 e2) st (n1 * n2)
  | E_ADeref (e1: aexp) (n1 n2: Z) st
      (H1 : aeval e1 st n1)
      (H2 : st n1 = Some n2) :
      aeval (ADeref e1) st n2.

End FirstTry_aeval.
The problem here is that we have no way to talk about AAddr here. Because the value of AAddr E is not depended on E's value but about E's address. Thus, we need to define two evaluation relation mutually inductively.
Inductive aevalR : aexpstateZProp :=
  | E_ANum n st:
      aevalR (ANum n) st n
  | E_AId X st n:
      st (var2addr X) = Some n
      aevalR (AId X) st n
  | E_APlus (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2) :
      aevalR (APlus e1 e2) st (n1 + n2)
  | E_AMinus (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2) :
      aevalR (AMinus e1 e2) st (n1 - n2)
  | E_AMult (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2) :
      aevalR (AMult e1 e2) st (n1 * n2)
  | E_ADeref (e1: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : st n1 = Some n2) :
      aevalR (ADeref e1) st n2
  | E_AAddre (e1: aexp) (n1: Z) st
      (H1 : aevalL e1 st n1):
      aevalR (AAddr e1) st n1
with aevalL : aexpstateZProp :=
  | EA_AId X st:
      aevalL (AId X) st (var2addr X)
  | EA_ADeref (e1: aexp) (n1: Z) st
      (H1 : aevalR e1 st n1) :
      aevalL (ADeref e1) st n1.
Then, we can easily define bexp's denotations.
Inductive beval : bexpstateboolProp :=
  | E_BTrue st:
      beval BTrue st true
  | E_BFalse st:
      beval BFalse st false
  | E_BEqTrue (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2)
      (H3 : n1 = n2) :
      beval (BEq e1 e2) st true
  | E_BEqFalse (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2)
      (H3 : n1n2) :
      beval (BEq e1 e2) st false
  | E_BLeTrue (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2)
      (H3 : n1 < n2) :
      beval (BLe e1 e2) st true
  | E_BLeFalse (e1 e2: aexp) (n1 n2: Z) st
      (H1 : aevalR e1 st n1)
      (H2 : aevalR e2 st n2)
      (H3 : n1n2) :
      beval (BLe e1 e2) st false
  | E_BNot (e: bexp) (b: bool) st
      (H1 : beval e st b):
      beval (BNot e) st (negb b)
  | E_BAnd (e1 e2: bexp) (b1 b2: bool) st
      (H1 : beval e1 st b1)
      (H2 : beval e2 st b2):
      beval (BAnd e1 e2) st (andb b1 b2).

(* Sat Jun 8 04:35:31 UTC 2019 *)