Download code/notes/src/0303-hoare-refinement.tex from viai957/align: direct link, hf CLI and curl.
- Browser
- Download file 21.9 kB
-
https://huggingface.co/viai957/align/resolve/main/code/notes/src/0303-hoare-refinement.tex
- Command line
-
hf download hf://viai957/align/code/notes/src/0303-hoare-refinement.tex
-
curl -L -o 0303-hoare-refinement.tex https://huggingface.co/viai957/align/resolve/main/code/notes/src/0303-hoare-refinement.tex
21.9 kB
| \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 $<k$ iterations''. (Proof of the union formula: a terminating run takes some finite $k$ iterations, and then $\sigma\in W_{k+1}$ by induction on $k$ reading the evaluation rules; conversely $\sigma\in W_k$ exhibits a run.) The operator $F(X)=(\neg b\wedge Q)\vee(b\wedge\wpc(c,X))$ is monotone (as $\wpc(c,\cdot)$ is), so $W_k\subseteq W_{k+1}$ by induction. It is also \emph{continuous}: $\wpc(c,\bigcup_kX_k)=\bigcup_k\wpc(c,X_k)$ for an increasing chain, because $c$ is deterministic ($\sigma\in\wpc(c,X)$ means the unique end state $\sigma'$ lies in $X$, and $\sigma'\in\bigcup_kX_k$ means $\sigma'\in X_k$ for some $k$). Hence $F(\bigcup_kW_k)=\bigcup_kF(W_k)=\bigcup_kW_{k+1}=\bigcup_kW_k$ and, by Kleene's argument, $\bigcup_kW_k$ is the \emph{least} fixed point (any fixed point $Y$ contains $W_0=\emptyset$, and $W_k\subseteq Y\Rightarrow W_{k+1}=F(W_k)\subseteq F(Y)=Y$). No closed form exists in general (it can encode the halting problem), which is why we \emph{guess} an $I$. | |
| \begin{theorem}[Total-correctness invariant rule]\label{thm:inv} | |
| If (1) $I\wedge\neg b\Rightarrow Q$; (2) $I\Rightarrow V\ge0$; (3) $I\wedge b\wedge V{=}v_0\Rightarrow\wpc(c,I\wedge V{<}v_0)$ ($v_0$ fresh), then $I\Rightarrow\wpc(W,Q)$. | |
| \end{theorem} | |
| \emph{Proof.} Show: $\sigma\models I\Rightarrow\sigma\in W_{V(\sigma)+1}$, by strong induction on $v=V(\sigma)\ge0$. | |
| If $\sigma\not\models b$: by (1) $\sigma\models Q$, so $\sigma\in W_1\subseteq W_{v+1}$. | |
| If $\sigma\models b$: by (3) $c$ terminates in some $\sigma'$ with $\sigma'\models I$ and $V(\sigma')<v$. By the IH, $\sigma'\in W_{V(\sigma')+1}\subseteq W_v$ (monotone, as $V(\sigma')+1\le v$). Then $\sigma\in W_{v+1}$ by the definition of $W_{v+1}$. $\square$ | |
| \begin{theorem}[Partial invariant rule]\label{thm:pinv} | |
| If $\hoare{I\wedge b}{c}{I}$ then $\hoare{I}{W}{I\wedge\neg b}$. | |
| \end{theorem} | |
| \emph{Proof.} Let $\langle W,\sigma\rangle\Downarrow\sigma''$ with $\sigma\models I$. Induction on the derivation. Case $\sigma\not\models b$: $\sigma''=\sigma\models I\wedge\neg b$. Case $\sigma\models b$ with $\langle c,\sigma\rangle\Downarrow\sigma'$ and $\langle W,\sigma'\rangle\Downarrow\sigma''$: $\sigma\models I\wedge b$ so $\sigma'\models I$ (premise); the IH on the shorter derivation gives $\sigma''\models I\wedge\neg b$. $\square$ | |
| \subsection{Verification conditions} | |
| Annotate every loop with $(I,V)$. Define $\wpc'$ as $\wpc$ with $\wpc'(W,Q):=I$, and the side conditions | |
| \[ | |
| \mathrm{vc}(x{:=}e,Q)=\emptyset,\ \ \mathrm{vc}(S_1;S_2,Q)=\mathrm{vc}(S_1,\wpc'(S_2,Q))\cup\mathrm{vc}(S_2,Q),\ \ \mathrm{vc}(\texttt{if},Q)=\mathrm{vc}(S_1,Q)\cup\mathrm{vc}(S_2,Q), | |
| \] | |
| \[ | |
| \mathrm{vc}(W,Q)=\{\,I\wedge\neg b\Rightarrow Q,\ \ I\Rightarrow V\ge0,\ \ I\wedge b\wedge V{=}v_0\Rightarrow\wpc'(c,\,I\wedge V{<}v_0)\,\}\cup\mathrm{vc}\bigl(c,\,I\wedge V{<}v_0\bigr), | |
| \] | |
| with a \emph{fresh ghost} $v_0$ per loop, regarded as a logical constant the program never assigns (the VCs are valid iff they hold for every value of the ghosts; an inner loop's invariant must therefore record the outer ghost; the notebook's nested example does so with $n-i=g_1$). The body is verified against the \emph{single} postcondition $I\wedge V{<}v_0$, not against $I$ and $V{<}v_0$ separately: for a nested loop the separate form never generates the inner exit VC for $V{<}v_0$, and the termination argument of the outer loop is unchecked. (Reporting the $I$-part alone, $I\wedge b\wedge V{=}v_0\Rightarrow\wpc'(c,I)$, is convenient for diagnosis: the notebook calls it \emph{preserve}.) | |
| \begin{theorem}[VC soundness]\label{thm:vc} If every formula in $\{P\Rightarrow\wpc'(S,Q)\}\cup\mathrm{vc}(S,Q)$ is valid, then $[P]\,S\,[Q]$.\end{theorem} | |
| \emph{Proof.} Structural induction on $S$, showing $\wpc'(S,Q)\subseteq\wpc(S,Q)$ whenever $\mathrm{vc}(S,Q)$ is valid. Assign, \texttt{skip}: equality. Sequence and \texttt{if}: monotonicity of $\wpc$ plus IH. Loop: the IH for $c$ with postcondition $R=I\wedge V{<}v_0$ (its VCs $\mathrm{vc}(c,R)$ are valid) gives $\wpc'(c,R)\subseteq\wpc(c,R)$, so condition (3) of Theorem~\ref{thm:inv} holds; (1) and (2) are the first two VCs; hence $I\subseteq\wpc(W,Q)$. Finally $P\subseteq\wpc'(S,Q)\subseteq\wpc(S,Q)$. $\square$ | |
| \paragraph{Worked example (notebook A2).} $\{n\ge0\}\ i{:=}0;\,s{:=}0;\,\texttt{while }i<n\ \texttt{do }s{:=}s+i;\,i{:=}i+1\ \{2s=n(n-1)\}$ with | |
| $I\equiv0\le i\le n\wedge2s=i(i-1)$ and $V=n-i$. Check by hand: init: $\wpc'(i{:=}0;s{:=}0,I)=I[0/s][0/i]=(0\le0\le n\wedge0=0)$, implied by $n\ge0$. Preserve: $\wpc(s{:=}s{+}i;i{:=}i{+}1,I)=0\le i{+}1\le n\wedge2(s{+}i)=(i{+}1)i$; from $I\wedge i<n$: $2s=i(i-1)$ so $2(s+i)=i(i-1)+2i=i(i+1)$. \checkmark\ Exit: $i=n$ (from $i\le n$, $\neg i<n$) and $2s=n(n-1)$. \checkmark\ Variant: $n-i\ge0$, decreases by 1. | |
| \subsection{Five ways a verification attempt can fail, and what each means} | |
| \begin{center}\small\begin{tabular}{@{}P{4.2cm}P{4.8cm}P{6.5cm}@{}}\toprule | |
| Annotation & Failing VC & Meaning (notebook A3)\\\midrule | |
| $I'=0\le i\le n$ (too weak) & exit: counterexample $(n,i,s)=(0,0,-2)$ & program correct; the state is \emph{unreachable} from any $P$-state: a proof artefact, not a bug\\ | |
| $I''=\cdots\wedge i<n$ (too strong) & init and preserve & program correct; annotation wrong\\ | |
| Right $I$, code $s{:=}s{+}i{+}1$ & preserve: $(n,i,s)=(1,0,0)$ & \emph{real bug}: running from $n=1$ violates $Q$\\ | |
| Right $I$, post $2s=n(n{+}1)$ & exit & spec disagrees with code; run violates it too\\ | |
| Right $I$ and code, variant $V=i$ (grows) & variant-decrease & program correct and terminating; the \emph{termination argument} is wrong\\\bottomrule\end{tabular}\end{center} | |
| A failed VC is therefore a \emph{question}, not a verdict (note 0304 turns it into a decision procedure). | |
| \subsection{Refinement: derive code from the spec} | |
| View the specification statement $x{:}[\mathit{pre},\mathit{post}]$ as the predicate transformer $\wpc_{\mathrm{spec}}(Q)(\sigma)=\mathit{pre}(\sigma)\wedge\forall x'.\,\mathit{post}(\sigma,x')\Rightarrow Q(x')$. Refinement $S\sqsubseteq T$ means every postcondition guaranteed by $S$ is guaranteed by $T$. | |
| \begin{proposition}[Assignment refinement law]\label{prop:ref} | |
| $x{:}[\mathit{pre},\mathit{post}]\sqsubseteq x{:=}e\iff\mathit{pre}\Rightarrow\mathit{post}[e/x]$. | |
| \end{proposition} | |
| \emph{Proof.} ($\Leftarrow$) Let $\sigma\in\wpc_{\mathrm{spec}}(Q)$: then $\mathit{pre}(\sigma)$, so $\mathit{post}(\sigma,e(\sigma))$, and instantiating $x'=e(\sigma)$ gives $Q(e(\sigma))=\wpc(x{:=}e,Q)(\sigma)$ by Prop.~\ref{prop:asg}. | |
| ($\Rightarrow$) Take $Q:=\mathit{post}(\sigma,\cdot)$ at a fixed start state $\sigma$. Then $\wpc_{\mathrm{spec}}(Q)(\sigma)=\mathit{pre}(\sigma)$ (the quantified implication is trivially true), while $\wpc(x{:=}e,Q)(\sigma)=\mathit{post}(\sigma,e(\sigma))$; refinement forces $\mathit{pre}(\sigma)\Rightarrow\mathit{post}(\sigma,e(\sigma))$. $\square$ | |
| \paragraph{Functional specifications.} For a pure function $f$ with pre $P$ and post $Q$ (the VERINA format \cite{verina} \S3.1), ``$f$ meets the spec'' is $\forall x.\ P(x)\Rightarrow Q(x,f(x))$, i.e.\ the triple $\{P(x)\}\,y{:=}f(x)\,\{Q(x,y)\}$ with $x$ a frozen logical variable. CLEVER's form $\exists r.\ \mathrm{impl}\,x=r\wedge\mathrm{spec}\,r$ \cite{clever} Fig.~2 is the same statement with termination built in (total correctness): in Lean every \texttt{def} terminates, so $[P]\,S\,[Q]$ is the default and \texttt{partial def} (no termination proof) is opaque to the kernel. | |
| \subsection{A worked example in Lean 4: verified linear search} | |
| Dafny's \texttt{LinearSearch} (DafnyBench Fig.~2 \cite{dafnybench}) has postcondition $0\le n\le|a|$, $n=|a|\vee P(a[n])$, $\forall i<n,\ \neg P(a[i])$ and loop invariants $0\le n\le|a|$ and $\forall i<n,\ \neg P(a[i])$. Abstracting $a[i]$ by a predicate on indices: | |
| \begin{lstlisting} | |
| def search (P : Nat β Bool) (len n : Nat) : Nat := | |
| if n < len then (if P n then n else search P len (n + 1)) else n | |
| termination_by len - n -- the VARIANT; Lean demands it | |
| def Post (P : Nat β Bool) (len r : Nat) : Prop := | |
| r β€ len β§ (r = len β¨ P r = true) β§ β i, i < r β P i = false | |
| theorem search_inv (P : Nat β Bool) (len : Nat) : | |
| β n, n β€ len β (β i, i < n β P i = false) β Post P len (search P len n) | |
| -- proof: strong induction on len - n; exit branch: I β§ Β¬b β Q; recursive branch: I β§ b β§ Β¬P n β I[n+1/n] | |
| theorem search_correct (P : Nat β Bool) (len : Nat) : Post P len (search P len 0) | |
| \end{lstlisting} | |
| The invariant \emph{is} the induction hypothesis of \texttt{search\_inv}: its premise is $I$; the recursive call is the loop back-edge and needs $I[n{+}1/n]$ (preserve); the else-branch is the exit condition. The whole development compiles, and \texttt{\#print axioms search\_correct} gives $\{\texttt{propext},\texttt{Quot.sound}\}$ (notebook B). The off-by-one loop (\texttt{n + 1 < len}) with the \emph{same} proof script is refused: \texttt{omega could not prove the goal} at the exit branch, where $r=len$ would be required; and | |
| \texttt{\#eval searchBad (fun \_ => 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<lo\iff hi-lo<2^w$, which always holds. \emph{Hence the VC fails exactly when $lo+hi\ge2^w$} (for $lo\le hi$). Counting for $w=8$: ordered pairs with $lo+hi\ge256$: $\sum_{lo=1}^{255}lo=32640$; of these $128$ lie on the diagonal ($2lo\ge256$); unordered with $lo\le hi$: $(32640+128)/2=16384$. The safe midpoint $lo+\lfloor(hi-lo)/2\rfloor$ never overflows. | |
| \section{Boxed result} | |
| \begin{result} | |
| \textbf{R1.} $\wpc(x{:=}e,Q)=Q[e/x]$; $\wpc(S_1;S_2,Q)=\wpc(S_1,\wpc(S_2,Q))$; $\wpc(\texttt{if},Q)=(b\wedge\wpc(S_1,Q))\vee(\neg b\wedge\wpc(S_2,Q))$.\\ | |
| \textbf{R2.} $\wpc(\texttt{while }b\texttt{ do }c,Q)=\mathrm{lfp}\,X.\,(\neg b\wedge Q)\vee(b\wedge\wpc(c,X))$; invariant rule: $I\wedge\neg b\Rightarrow Q$, $I\Rightarrow V\ge0$, $I\wedge b\wedge V{=}v_0\Rightarrow\wpc(c,I\wedge V{<}v_0)\ \Longrightarrow\ I\Rightarrow\wpc(W,Q)$.\\ | |
| \textbf{R3.} VC soundness: all VCs valid $\Rightarrow[P]\,S\,[Q]$.\\ | |
| \textbf{R4.} $x{:}[\mathit{pre},\mathit{post}]\sqsubseteq x{:=}e\iff\mathit{pre}\Rightarrow\mathit{post}[e/x]$.\\ | |
| \textbf{R5.} $w$-bit midpoint VC fails iff $lo+hi\ge2^w$ ($lo\le hi$); count $=2^{2w-2}$ for $w=8$ ($16384$). | |
| \end{result} | |
| \paragraph{Numerical verification (notebook 0303, all cells executed).} | |
| \begin{center}\small\begin{tabular}{@{}P{8.4cm}P{3.5cm}P{3.6cm}@{}}\toprule | |
| Identity & size & max abs error / violations\\\midrule | |
| R1: $\wpc_{\mathrm{syn}}$ vs executing, 300 random loop-free programs $\times$ 49 states & 14,700 pairs & 0 mismatches\\ | |
| R2/R3: all VCs valid (ghost $v_0$ quantified over $[-12,12]$, so the variant-decrease VC is genuinely evaluated) $\Rightarrow$ semantic triple on 48 inputs; $I\wedge V\ge0$ states all terminate in $Q$ & 40 box states & 0 violations\\ | |
| A3: five failure modes classified (weak $I$: counterexample unreachable; buggy code: reproduced by a run; wrong variant: only the decrease VC fails) & 5 cases & as predicted 5/5\\ | |
| A5: nested loops $s=nm$: all 9 VCs valid on the box and 9 runs end in $Q$; a wrong inner variant is caught by the decrease VC & box $3{\times}3{\times}4{\times}4{\times}10$ & as predicted\\ | |
| Lean: \texttt{search\_correct} compiles; \texttt{\#print axioms} & --- & $\{\texttt{propext},\texttt{Quot.sound}\}$\\ | |
| Lean: off-by-one rejected; \texttt{\#eval} 9 vs 10; \texttt{decide (Post \dots)} false/true & --- & as predicted\\ | |
| R5: violating pairs, Python brute force vs closed form vs Lean \texttt{UInt8 \#eval}; safe midpoint & 32,896 pairs & 16384 = 16384 = 16384; safe 0\\ | |
| R5: 32-bit: \texttt{(3000000000+3000000000)/2} in \texttt{UInt32}; \texttt{decide} of $\neg(lo\le mid)$ & --- & mid $=852516352<lo$\\ | |
| R4: law vs brute force over all $2^{11}$ postconditions & 80 random pairs (71 refined) & 0 disagreements\\\bottomrule\end{tabular}\end{center} | |
| (All quantities are integers or Booleans; ``max abs error'' is $0$ throughout.) Approximation named: the VC checks on a box (A2) are \emph{refutation-complete inside the box only}; they are evidence, not proof. The proofs are the derivations above and the Lean theorem. | |
| \section{What breaks} | |
| \begin{center}\small\begin{tabular}{@{}P{3.5cm}P{6.4cm}P{5.5cm}@{}}\toprule | |
| Failure & Mechanism & Assumption violated\\\midrule | |
| Overflow / wrap-around & VC holds over $\Z$, fails for $\mathrm{int}_w$ (R5). Mistral's Leanstral report \cite{leanstral} (company-reported, primary) describes a real overflow in the zigzag-decoding \texttt{sign} function of \texttt{datrs/varinteger}: \texttt{value + 1} overflows on \texttt{Std.U64.MAX} (secondary coverage: \cite{decoder}) & the model's arithmetic equals the machine's\\ | |
| Partial vs total & $\hoare{\mathit{true}}{\texttt{while true do skip}}{\mathit{false}}$ is valid: a diverging loop satisfies every partial spec & termination is required; Lean's \texttt{termination\_by} enforces it, \texttt{partial def} is opaque\\ | |
| Vacuous precondition & if $P$ is unsatisfiable every triple holds & $P$ is satisfiable (check by witness, 0302)\\ | |
| Weak spec & a spec the wrong code also meets (0305) & $Q$ captures intent\\ | |
| Annotation errors & too-weak/too-strong $I$ yields failed VCs on correct code (A3) & the failed VC is a question (0304)\\ | |
| Model $\ne$ code & abstracting $a[i]$ to $P(i)$, ignoring aliasing, I/O, floats, concurrency & the abstraction is faithful; heap needs separation logic (not covered)\\ | |
| Cheating the harness & DafnyBench counts success only if \texttt{requires}/\texttt{ensures} are preserved and no \texttt{assume false}/\texttt{\{:verify false\}} is used \cite{dafnybench} \S3.2 & the spec given is the spec proved\\\bottomrule\end{tabular}\end{center} | |
| \nextnote{0304: when a proof fails, how to search for a counterexample (\texttt{decide}, property testing, model finding), why a proof is sound but a failed proof is not complete evidence, and how to turn a counterexample into a confirmed bug.} | |
| \begin{small} | |
| \begin{thebibliography}{99} | |
| \bibitem{dafnybench} C.~Loughridge et al., \emph{DafnyBench}, arXiv:2406.08467, Fig.~2 (\texttt{LinearSearch} with \texttt{invariant}s), \S3.2 (the \texttt{fill\_hints} task: remove \texttt{assert}/\texttt{invariant}, success conditions 1--3). | |
| \bibitem{verina} Z.~Ye et al., \emph{VERINA}, arXiv:2505.23135, \S3.1 (a specification is a pre-condition and a post-condition); \S4.1 (soundness/completeness of $\hat P,\hat Q$). | |
| \bibitem{clever} A.~Thakur et al., \emph{CLEVER}, arXiv:2505.13938, Fig.~2 (the \texttt{$\exists$ result, implementation x = result $\wedge$ spec result} form; computable specs that leak the implementation). | |
| \bibitem{fvapps} Q.~Dougherty, R.~Mehta, \emph{Proving the Coding Interview (FVAPPS)}, arXiv:2502.05714, \S III.E (discussion of \texttt{Id.run do} solutions). | |
| \bibitem{leanstral} Mistral AI, \emph{Leanstral} technical report (\texttt{LeanstralReport.pdf}, repository \texttt{mistralai/LeanstralSafeVerify}) and the vendor post ``Leanstral 1.5: Proof Abundance for All'': 47 violated properties, 11 genuine bugs, 5 previously unreported on GitHub; the \texttt{datrs/varinteger} \texttt{sign} example (section numbers not recorded). Company-reported, not independently reproduced. | |
| \bibitem{decoder} The Decoder (4 Jul 2026), coverage of Leanstral~1.5: 5 previously unknown bugs in 57 repositories incl.\ a varint zigzag-decode overflow. Secondary, paywall-truncated in our vault copy; used only as corroboration. | |
| \bibitem{lean4lean} M.~Carneiro, \emph{Lean4Lean}, arXiv:2403.14064, \S4.1 (a soundness bug found by failing to prove a verification condition for \texttt{inferLambda}/\texttt{cheapBetaReduce}). | |
| \bibitem{hoare69} C.~A.~R.~Hoare, ``An axiomatic basis for computer programming'', CACM 12(10), 1969; R.~W.~Floyd, ``Assigning meanings to programs'' (1967); E.~W.~Dijkstra, ``Guarded commands, nondeterminacy and formal derivation of programs'', CACM 18(8), 1975 ($\wpc$); R.-J.~Back, J.~von Wright, \emph{Refinement Calculus} (1998). \emph{Not on arXiv; not fetched; cited for the named calculi. Every rule used above is derived from the big-step semantics in \S3.} | |
| \end{thebibliography} | |
| \end{small} | |
| \end{document} | |