diff --git a/CHANGELOG.md b/CHANGELOG.md index bfa50834cea8066800bb39ef9c8dfd3121c7e31c..c4153ca2215e1ae640a043b8fe1dc48074f5c6fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,9 +27,9 @@ Changes in the theory of Iris itself: the number of forked-off threads, and have a global fixed proposition that describes the postcondition of each forked-off thread (instead of it being `True`). -* `[#]` A stronger adequacy statement for weakest preconditions that involves - the final state, involves the post-condition of forked-off threads, and also - applies if the main-thread has not terminated. +* A stronger adequacy statement for weakest preconditions that involves + the final state, the post-condition of forked-off threads, and also applies if + the main-thread has not terminated. * The user-chosen functor used to instantiate the Iris logic now goes from COFEs to Cameras (it was OFEs to Cameras). diff --git a/docs/program-logic.tex b/docs/program-logic.tex index 7f1c6a59fe8cb1505b1704506b801938d9cb15b0..354dda8b2e66d410ce2f0848efd3e5e0cebc4f9c 100644 --- a/docs/program-logic.tex +++ b/docs/program-logic.tex @@ -250,48 +250,77 @@ This basically just copies the second branch (the non-value case) of the definit \paragraph{Adequacy of weakest precondition.} +\newcommand\traceprop{\Sigma} + The purpose of the adequacy statement is to show that our notion of weakest preconditions is \emph{realistic} in the sense that it actually has anything to do with the actual behavior of the program. -There are two properties we are looking for: First of all, the postcondition should reflect actual properties of the values the program can terminate with. -Second, a proof of a weakest precondition with any postcondition should imply that the program is \emph{safe}, \ie that it does not get stuck. - -\begin{defn}[Adequacy] - A program $\expr$ in some initial state $\state$ is \emph{adequate} for stuckness $\stuckness$ and a set $V \subseteq \Val \times \State$ of legal return-value-final-state combinations (written $\expr, \state \vDash_\stuckness V$) if for all $\tpool', \state'$ such that $([\expr], \state) \tpsteps[\vec\obs] (\tpool', \state')$ we have -\begin{enumerate} -\item Safety: If $\stuckness = \NotStuck$, then for any $\expr' \in \tpool'$ we have that either $\expr'$ is a - value, or \(\red(\expr'_i,\state')\): - \[ \stuckness = \NotStuck \Ra \All\expr'\in\tpool'. \toval(\expr') \neq \bot \lor \red(\expr', \state') \] - Notice that this is stronger than saying that the thread pool can reduce; we actually assert that \emph{every} non-finished thread can take a step. -\item Legal return value: If $\tpool'_1$ (the main thread) is a value $\val'$, then $\val' \in V$: - \[ \All \val',\tpool''. \tpool' = [\val'] \dplus \tpool'' \Ra (\val',\state') \in V \] -\end{enumerate} -\end{defn} - -To express the adequacy statement for functional correctness, we assume that the signature $\Sig$ adds a predicate $\pred$ to the logic: -\[ \pred : \Val \times \State \to \Prop \in \SigFn \] -Furthermore, we assume that the \emph{interpretation} $\Sem\pred$ of $\pred$ reflects some set $V$ of legal return values and final states into the logic (also see \Sref{sec:model}): +The most general form of the adequacy statement is about proving properties of arbitrary program executions. +That is, the goal is to prove a statement of the form +\[ +\All \expr_0, \state_0, \vec\obs, \tpool_1, \state_1. ([\expr_0], \state_0) \tpsteps[\vec\obs] (\tpool_1, \state_1) \Ra (\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \in \traceprop +\] +for some \emph{meta-level} relation $\traceprop$ characterizing the ``trace property'' we are interested in. + +To state the adequacy theorem, we need a way to talk about $\traceprop$ inside Iris. +To this end, we assume that the signature $\Sig$ contains some predicate $\hat{\traceprop}$: +\[ \hat{\traceprop} : \Expr \times \State \times \List(\Obs) \times \List(\Expr) \times \State \to \Prop \in \SigFn \] +Furthermore, we assume that the \emph{interpretation} $\Sem{\hat{\traceprop}}$ of $\hat{\traceprop}$ reflects $\traceprop$ (also see \Sref{sec:model}): \[\begin{array}{rMcMl} - \Sem\pred &:& \Sem{\Val\times\State\,} \nfn \Sem\Prop \\ - \Sem\pred &\eqdef& \Lam (\val,\state). \Lam \any. \setComp{n}{(v,\state) \in V} + \Sem{\hat{\traceprop}} &:& \Sem{\Expr \times \State \times \List(\Obs) \times \List(\Expr) \times \State\,} \nfn \Sem\Prop \\ + \Sem{\hat{\traceprop}} &\eqdef& \Lam (\expr_0, \state_0, \vec\obs, \tpool_1, \state_1). \Lam \any. \setComp{n}{(\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \in \traceprop} \end{array}\] -The signature can of course state arbitrary additional properties of $\pred$, as long as they are proven sound. +The signature can of course state arbitrary additional properties of $\hat{\traceprop}$, as long as they are proven sound. + The adequacy statement now reads as follows: \begin{align*} - &\All \mask, \expr, \state. - \\&( \TRUE \proves \All\vec\obs. \pvs[\mask] \Exists \stateinterp, \pred_F. \stateinterp(\state,\vec\obs,0) * \wpre[\stateinterp;\pred_F]{\expr}[\stuckness;\mask]{x.\; \All \state, m. \stateinterp(\state', (), m) \vsW[\top][\emptyset] \pred(x,\state')}) \Ra - \\&\expr, \state \vDash_\stuckness V + &\All \expr_0, \state_0, \vec\obs, \tpool_1, \state_1.\\ + &( \TRUE \proves \pvs[\top] \Exists \stuckness, \stateinterp, \pred_F, \pred. {}\\ + &\quad\stateinterp(\state_0,\vec\obs,0) * \wpre[\stateinterp;\pred_F]{\expr_0}[\stuckness;\top]{x.\; \pred(x)} * {}\\ + &\quad(\All \expr_1, \tpool_1'. \tpool_1 = [\expr_1] \dplus \tpool_1' \wand {}\\ + &\quad\quad (s = \NotStuck \Ra \All \expr \in \tpool_1. \toval(\expr) \neq \bot \lor \red(\expr, \state_1) ) \wand {}\\ + &\quad\quad \stateinterp(\state_1, (), |\tpool_1'|) \wand{}\\ + &\quad\quad (\toval(\expr_1) \ne \bot \wand \pred(\toval(\expr_1))) \wand{}\\ + &\quad\quad (\Sep[\expr \in \tpool_1'] \toval(\expr) \ne \bot \wand \pred_F(\toval(\expr))) \wand{}\\ + &\quad\quad \pvs[\top,\emptyset] \hat{\traceprop}(\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \\ + &\quad ) \Ra{}\\ + &([\expr_0], \state_0) \tpsteps[\vec\obs] (\tpool_1, \state_1) \Ra (\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \in \traceprop \end{align*} -Notice that the state invariant $S$ used by the weakest precondition is chosen \emph{after} doing a fancy update, which allows it to depend on the names of ghost variables that are picked in that initial fancy update. -Also, notice that the proof of $\expr$ must be performed with a universally quantified list of observations $\vec\obs$, but the \emph{entire} list is known to the proof from the beginning. +In other words, to show that $\traceprop$ holds for all possible executions of the program, we have to prove an entailment in Iris that, starting from the empty context, proves that the initial state interpretation holds, proves a weakest precondition, \emph{and} proves that $\hat{\traceprop}$ holds under the following assumptions: +\begin{itemize} +\item The final thread-pool $\tpool_1$ contains the final state of the main thread $\expr_1$, and any number of additional threads in $\tpool_1'$. +\item If this is a stuck-free weakest precondition, then all threads in the final thread-pool are either values or are reducible in the final state $\state_1$. +\item The state interpretation holds for the final state. +\item If the main thread reduced to a value, the post-condition $\pred$ of the weakest precondition holds for that value. +\item If any other thread reduced to a value, the forked-thread post-condition $\pred_F$ holds for that value. +\end{itemize} +Notice also that the adequacy statement quantifies over the program trace only once, so it can be easily specialized to, say, some particular initial state $\state_0$. +This lets us show properties that only hold for some executions. +Furthermore, the state invariant $S$ used by the weakest precondition is chosen \emph{after} doing a fancy update, which allows it to depend on the names of ghost variables that are picked in that initial fancy update. + +As an example for how to use this adequacy theorem, let us say we wanted to prove that a program $\expr_0$ for which we derived a $\NotStuck$ weakest-precondition cannot get stuck. +We would pick +\[ +\traceprop(\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \eqdef \All \expr \in \tpool_1. \toval(\expr) \neq \bot \lor \red(\expr, \state_1) +\] +and we would show the following in Iris: +\[ +\TRUE \proves \All \state_0, \vec\obs. \pvs[\top] \Exists \stateinterp, \pred_F, \pred. \stateinterp(\state_0,\vec\obs,0) * \wpre[\stateinterp;\pred_F]{\expr_0}[\NotStuck;\top]{x.\; \pred(x)} +\] +The adequacy theorem would then give us: +\[ +\All \state_0, \vec\obs, \tpool_1, \state_1. ([\expr_0], \state_0) \tpsteps[\vec\obs] (\tpool_1, \state_1) \Ra \All \expr \in \tpool_1. \toval(\expr) \neq \bot \lor \red(\expr, \state_1) +\] -The following variant of adequacy also allows exploiting the second parameter of $\stateinterp$, the number of threads, but only applies when \emph{all} threads have reduced to a value: +Similarly, if we wanted to show that the final value of the main thread is always in some set $V \subseteq \Val$, we could pick +\[ +\traceprop(\expr_0, \state_0, \vec\obs, \tpool_1, \state_1) \eqdef \All\val_1, \tpool_1'. \tpool_1 = [\ofval(\val_1)] \dplus \tpool_1' \Ra \val_1 \in \Val +\] +and then we could derive the following from the main adequacy theorem: \begin{align*} - &\All \mask, \expr, \state, \vec\obs, \val, \vec\val, \state'. - \\&( \TRUE \proves \pvs[\mask] \Exists \stateinterp, \pred_F. \stateinterp(\state,\vec\obs,0) * \wpre[\stateinterp;\pred_F]{\expr}[\stuckness;\mask]% - {x.\; \stateinterp(\state',(),|\vec\val|) * \Sep_{y \in \vec\val} \pred_F(y) \vsW[\top][\emptyset] \pred(x,\state')}) \Ra - \\&([\expr], \state) \tpsteps[\vec\obs] (\val :: \vec\val, \state') \Ra - \\&(\val,\state') \in V + &(\TRUE \proves \All\state_0, \vec\obs. \pvs[\top] \Exists \stuckness, \stateinterp, \pred_F. \stateinterp(\state_0,\vec\obs,0) * \wpre[\stateinterp;\pred_F]{\expr_0}[\stuckness;\top]{x.\; x \in V}) \Ra{}\\ + &\All \state_0, \vec\obs, \val_1, \tpool_1, \state_1. ([\expr_0], \state_0) \tpsteps[\vec\obs] ([\ofval(\val_1)] \dplus \tpool_1, \state_1) \Ra \val_1 \in V \end{align*} + \paragraph{Hoare triples.} It turns out that weakest precondition is actually quite convenient to work with, in particular when performing these proofs in Coq. Still, for a more traditional presentation, we can easily derive the notion of a Hoare triple: