\documentclass[11pt]{article} \usepackage{common} \begin{document} \notehead{0303}{Hoare logic and refinement: specifying and verifying code}{Pre/postconditions, verification conditions, loop invariants, and a verified linear search in Lean~4. Companion notebook: \texttt{notebooks/\allowbreak 0303-hoare-refinement.ipynb}.} \begin{problem} \question{How do we turn ``the code does what it should'' into finitely many statements a prover can check, loops included?} Notes 0301--0302 certify mathematical statements. A \emph{program} is not a statement: it is an object that does something. To ask a prover to verify code we must (i) turn ``what the code is supposed to do'' into a \emph{statement} (a specification), (ii) reduce ``the code meets the spec'' to statements about \emph{finitely many} points of the program (verification conditions), and (iii) handle loops, whose behaviour is an unbounded sequence of steps, with one finite object: an \emph{invariant}. Hoare logic does (i)--(iii). Refinement is the converse direction: derive code from a spec so that correctness holds by construction. Every benchmark in note 0306 is, in the end, a Hoare-triple generator and checker. \end{problem} \stepcounter{section}% the problem box counts as section 1: keeps every \S and notebook cross-reference stable \section{Notation} \begin{center}\small\begin{tabular}{@{}lP{10.4cm}@{}}\toprule Symbol & Meaning\\\midrule $\sigma$ & state: map from program variables to $\Z$\\ $\sem{e}\sigma$, $\sigma\models B$ & value of expression $e$ in $\sigma$; Boolean assertion $B$ true in $\sigma$\\ $\langle S,\sigma\rangle\Downarrow\sigma'$ & big-step evaluation: running $S$ from $\sigma$ ends in $\sigma'$\\ $\hoare PSQ$ & Hoare triple: \emph{partial} correctness $=$ every terminating run from a $P$-state ends in a $Q$-state\\ $[P]\,S\,[Q]$ & \emph{total} correctness $=$ additionally every run from a $P$-state terminates\\ $Q[e/x]$ & $Q$ with $e$ substituted for $x$\\ $\wpc(S,Q)$ & weakest precondition: $\{\sigma\mid\exists\sigma'.\ \langle S,\sigma\rangle\Downarrow\sigma'\wedge\sigma'\models Q\}$ (total)\\ $I$, $V$ & loop invariant; variant (a $\Z$-valued term, $\ge0$ and strictly decreasing)\\ $S\sqsubseteq T$ & $T$ refines $S$: $\wpc(S,Q)\subseteq\wpc(T,Q)$ for all $Q$\\ $x{:}[\mathit{pre},\mathit{post}]$ & specification statement: ``from a $\mathit{pre}$ state, change $x$ so that $\mathit{post}$ holds''\\\bottomrule\end{tabular}\end{center} \section{Derivation} \subsection{Language and semantics} $S::=\texttt{skip}\mid x:=e\mid S_1;S_2\mid\texttt{if }b\texttt{ then }S_1\texttt{ else }S_2\mid\texttt{while }b\texttt{ do }c$. Evaluation: \[ \langle x{:=}e,\sigma\rangle\Downarrow\sigma[x{\mapsto}\sem e\sigma],\qquad \frac{\sigma\not\models b}{\langle\texttt{while }b\texttt{ do }c,\sigma\rangle\Downarrow\sigma},\qquad \frac{\sigma\models b\ \ \langle c,\sigma\rangle\Downarrow\sigma'\ \ \langle\texttt{while }b\texttt{ do }c,\sigma'\rangle\Downarrow\sigma''}{\langle\texttt{while }b\texttt{ do }c,\sigma\rangle\Downarrow\sigma''} \] (and the obvious rules for \texttt{skip}, sequence, conditional). The language is deterministic: $\Downarrow$ is a partial function. A loop that never ends has no $\sigma''$. \subsection{The assignment axiom, derived} \begin{lemma}[Substitution]\label{lem:sub} $\sigma\models Q[e/x]\iff\sigma[x{\mapsto}\sem e\sigma]\models Q$.\end{lemma} \emph{Proof.} Induction on $Q$; the atomic case is induction on terms: $\sem{t[e/x]}\sigma=\sem t{\sigma[x\mapsto\sem e\sigma]}$ because a variable $y\neq x$ evaluates identically in both, and $x$ evaluates to $\sem e\sigma$ on the right and to $\sem{e}\sigma$ on the left. Connectives commute with substitution. $\square$ \begin{proposition}[Assignment]\label{prop:asg} $\wpc(x{:=}e,Q)=Q[e/x]$, hence $\hoare{Q[e/x]}{x:=e}{Q}$.\end{proposition} \emph{Proof.} $x{:=}e$ always terminates in $\sigma[x\mapsto\sem e\sigma]$; apply Lemma~\ref{lem:sub}. $\square$\\ (The easy-to-get-wrong direction is the arrow: the \emph{postcondition} is rewritten, working backwards. The ``forward'' pattern $\{P\}x{:=}e\{P\wedge x=e\}$ is unsound when $x$ occurs in $e$.) \subsection{Sequence and conditional} $\wpc(S_1;S_2,Q)=\wpc(S_1,\wpc(S_2,Q))$: from $\sigma$, $S_1$ ends in a unique $\sigma_1$ and then $S_2$ must end in $Q$, which is $\sigma_1\in\wpc(S_2,Q)$, which is $\sigma\in\wpc(S_1,\wpc(S_2,Q))$. For \texttt{if}: $\wpc=(b\wedge\wpc(S_1,Q))\vee(\neg b\wedge\wpc(S_2,Q))$ by splitting on $\sigma\models b$. The notebook checks both by running 300 random programs on 49 states each: $\wpc$ computed by these syntactic rules coincides with the set obtained by \emph{executing}: 0 mismatches over 14,700 pairs. \subsection{Loops: the weakest precondition is a least fixed point} Let $W=\texttt{while }b\texttt{ do }c$. Unfolding once: $\wpc(W,Q)=(\neg b\wedge Q)\vee(b\wedge\wpc(c,\wpc(W,Q)))$. This is an equation, not a definition, so it does not give a formula. Define the iterates \[ W_0=\emptyset,\qquad W_{k+1}=(\neg b\wedge Q)\vee\bigl(b\wedge\wpc(c,W_k)\bigr),\qquad\wpc(W,Q)=\bigcup_kW_k. \] $W_k$ is ``terminates in $Q$ after $ false) 10 0} returns \texttt{9}, \texttt{\#eval decide (Post \dots)} returns \texttt{false} (the correct loop returns \texttt{10}, \texttt{true}). FVAPPS \cite{fvapps} \S III.E observes the practical corollary: solutions written with imperative \texttt{Id.run do} loops hide the inductive structure the proof needs, so verifiable code is written as recursion whose shape mirrors its invariant. \subsection{Integers: when the spec lives in $\Z$ and the code in $\mathrm{int}_{w}$} Binary search computes $\mathit{mid}=(lo+hi)/2$ with VC $lo\le hi\Rightarrow lo\le\mathit{mid}\le hi$. This is valid over $\Z$. Over $w$-bit unsigned words, $lo+hi$ wraps to $lo+hi-2^w$ when $lo+hi\ge2^w$, so $\mathit{mid}=\lfloor(lo+hi-2^w)/2\rfloor