# Lecture 11: A brief introduction to constraint-based verification.

There is a general approach to verification where a program is automatically converted into a logical statement of the form $\forall x. Q(x)$ such that if the statement is valid (so $Q$ is indeed true for all $x$), then the program is correct. In this section we describe this process in some detail, and then in the next section we will see how synthesis can be made to interact with this process in order to generate verified programs.
Following the style of Winskell, we will be explaining the key concepts in this lecture
using a simple imperative language
IMP shown below.

## Program Logics

In order to understand this style of verification, we need to provide some background on the formalism of Program Logics, also known in the literature as Axiomatic Semantics or Hoare-Floyd style verification.
The main construction in this form of verification is the Hoare triple:
\[
\{ A \} cmd \{ B \}
\]
which indicates that for all states that satisfy some predicate $A$, if we execute command $cmd$
starting from this state **and it terminates** then the resulting state will satisfy predicate $B$.
It is important to note that this is a **partial correctness** assertion, meaning that
the Hoare triple makes no claim as to whether or not $cmd$ actually terminates, it just says
that if it does terminate, and you started from a state satisfying $A$, then you are guaranteed
to end in a state satisfyng $B$.
There is a stronger version of the Hoare triple that is usually written in terms of square brackets
\[
[ A ] cmd [ B ]
\]
which additionally implies that $cmd$ will terminate on all inputs that satisfy the predicate A.

For example, the rule for while loop is saying that if you can show that any state that satisfies both $A$ and $b$ is always mapped to another state that satisfies $A$, then starting from a state satisfying $A$, if the loop ever terminates, then when it does the state will still satisfy $A$, but will also satisfy $\neg b$.

## Example

The following example illustrates how the rules can be used to prove a property for a given program. The figure shows a program with a pre and post condition that we wish to prove and illustrates the process of constructing the proof. Notice that every time we apply a rule, the premises of the rule create new proof obligations that need to be discharged by using other deductive rules.The figure illustrates the first few steps of the proof. The proof proceeds very mechanically until we get to a proof obligation involving loops. This proof obligation is problematic because the rule for loops has a very particular form. It is given in terms of a predicate ${A}$ that must be preserved by each iteration of the loop. However, the pre and post-conditions of the while loop in the example do not match this form. This means that in order to make the proof work, we need to discover a predicate $A$ that is then connected to the pre and post conditions using the rule of consequence. From the figure, we can see that the use of the rule of consequence together with the rule for loops leaves us with three proof obligations.

- The precondition must imply $A$.
- $A$ and the negation of the loop condition must imply the postcondition.
- When the loop condition is true, $A$ must be preserved by the loop body.

## From partial to total correctness

*Rank function*$F$, also refered in the literature as a

*Variant function*. The Rank function allows us to construct an inductive argument for the termination of the loop. The Rank function maps the state to an integer value and imposes two conditions:

- It is greater than or equal to zero for any state where the invariant and the loop condition hold.
- It decreases with every iteration of the loop

## Weakest Preconditions and Verification Conditions

To explain this approach, first we need to introduce the concept of a *weakest precondition*. The weakest precondition $wpc(cmd, B)$
is a precondition such that the Hoare triple $\{wpc(cmd, B)\} cmd \{B\}$ is valid and any other valid precondition $A$ is stronger,
i.e. $\vdash \{A\} cmd \{B\}$ valid iff $A \Rightarrow wpc(cmd, B)$.

Now, suppose we had an algorithmic way of computing $wpc(cmd, B)$ for any command $cmd$ and any postcondition $B$. Then checking
whether a Hoare triple $\{A\} cmd \{B\}$ is valid or not would reduce to checking whether the implication $A \Rightarrow wpc(cmd, B)$ holds.
For loop free programs, it is possible and relatively easy to compute $wpc$. The rules for computing it are shown in the figure.
As one might expect, though, it is in general not possible to compute $wpc$ algorithmically for programs with loops. Instead, we are going
to settle for a *Verification Condition* $vc(cmd, B)$. The verification condition $vc(cmd, B)$ is a valid precondition, so
$\{vc(cmd, B)\} cmd \{B\}$ is guaranteed to be valid. However, it is not guaranteed to be the weakest precondition.
This means that if $A \Rightarrow vc(cmd, B)$, then we can be sure that $\vdash \{A\} cmd \{B\}$, just from the rule of consequence.
However, if $A \Rightarrow vc(cmd, B)$ does not hold, then we have no way of knowing whether it was because the Hoare triple
$\{A\} cmd \{B\}$ is not valid, or whether the triple is valid, but $vc(cmd, B)$ is just not good enough to prove it.

The rules for computing the $vc$ for constructs other than loops are the same as those for computing the $wpc$. For loops, the rule is shown in the figure. Note that the rule is given in terms of a loop invariant $I$, that we expect to annotate the loop. It is interesting to note that the property that $\vdash \{vc(cmd, B)\} cmd \{B\}$ will hold regardless of what invariant we provide. We could even use a random expression generator to generate an invariant, and the property would still hold. This means that we do not have to trust whoever provides the loop invariant, as long as $A \Rightarrow vc(cmd, B)$, then we will know that $\vdash \{A\} cmd \{B\}$. The catch, of course, is that if we provide a garbage invariant, $vc(cmd, B)$ is likely to evaluate to $false$. It turns out $false$ is a valid precondition for any postcondition, just not a very useful one when it comes to proving things.