diff --git a/doc/tactics/swap.rst b/doc/tactics/swap.rst new file mode 100644 index 000000000..6ba623871 --- /dev/null +++ b/doc/tactics/swap.rst @@ -0,0 +1,170 @@ +======================================================================== +Tactic: `swap` +======================================================================== + +The `swap` tactic applies to program-logic goals by rewriting the program +into a semantically equivalent form where two consecutive, independent +program fragments are exchanged. + +In a nutshell, `swap` permutes commands when doing so does not change the +program’s behavior (typically because the swapped fragments do not +interfere, e.g., they write to disjoint variables and neither reads what the +other writes). + +Applying `swap` replaces the current goal by the same goal, but with the +selected commands swapped in the program. This is useful to expose a more +convenient program structure, for example to align programs in relational +proofs or to bring related statements closer together. + +.. contents:: + :local: + +------------------------------------------------------------------------ +Syntax +------------------------------------------------------------------------ + +The `swap` tactic comes in several forms: + +.. admonition:: Syntax + + - `swap {side}? {codepos1}` + - `swap {side}? {codepos1} {codeoffset1}` + - `swap {side}? [{codepos1}..{codepos1}] {codeoffset1}`` + +Here: + +- `{side}` is optional and is either `1` or `2`. It selects the left or + right program in relational goals. If omitted, the tactic applies to the + single program under consideration. + +- `{codepos1}` denotes a *top-level code position*. + +- A `{codeoffset1}` is either: + + - a signed integer (`n` or `-n`), denoting a relative position, or + - an absolute code position written `@ {codepos1}`. + +The meaning of these forms is as follows: + +- `swap {side}? {codepos1}` + + swaps the two adjacent commands starting at the top-level position + `{codepos1}`. + +- `swap {side}? {codepos1} {codeoffset1}` + + swaps the command at top-level position `{codepos1}` with the command + at the position designated by `{codeoffset1}`. + +- `swap {side}? [{codepos1}..{codepos1}] {codeoffset1}` + + swaps a whole sequence of commands delimited by `[{codepos1}..{codepos1}]` + with the commands starting at the position designated by `{codeoffset1}`. + +In all cases, the swap is only valid when the exchanged fragments are +independent, so that the transformation preserves the program semantics. + +------------------------------------------------------------------------ +Example (single statement) +------------------------------------------------------------------------ + +The following example swaps two adjacent assignments that do not interfere. +The returned result is unchanged, but the rewritten program may be more +convenient for subsequent proof steps. + +.. ecproof:: + + require import AllCore. + + module M = { + proc reorder(x : int) : int = { + var a, b : int; + a <- x + 1; + b <- x + 2; + return a + b; + } + }. + + lemma reorder_correct (n : int) : + hoare [ M.reorder : x = n ==> res = (n + 1) + (n + 2) ]. + proof. + proc. + + (*$*) (* Swap the command at position 1 with the next command (offset +1). *) + swap 1 1. + + (* The goal is the same, but with the program rewritten. *) + admit. + qed. + +------------------------------------------------------------------------ +Example (swapping a block) +------------------------------------------------------------------------ + +The following example illustrates the block form +`swap [{codepos1}..{codepos1}] {codeoffset1}`. We swap a block of two +commands with a later, independent command. + +.. ecproof:: + + require import AllCore. + + module M = { + proc swap_block(x : int) : int = { + var a, b, c : int; + + a <- x + 1; (* 1 *) + b <- x + 2; (* 2 *) + c <- x + 3; (* 3 *) + a <- a + 10; (* 4 *) + + return a + b + c; + } + }. + + lemma swap_block_correct (n : int) : + hoare [ M.swap_block : x = n ==> res = (n + 1 + 10) + (n + 2) + (n + 3) ]. + proof. + proc. + (*$*) + (* Swap the block [2..3] (b <- x+2; c <- x+3) with the following command + a <- a + 10. These fragments are independent since: + - the block assigns b and c, and + - the command updates a using only a. *) + swap [2..3] 1. + + (* The goal is the same, but with the program rewritten. *) + admit. + qed. + +------------------------------------------------------------------------ +Example (invalid swap) +------------------------------------------------------------------------ + +The following example shows a swap attempt that fails because the two +commands are not independent: the second command reads the value written by +the first one. + +.. ecproof:: + + require import AllCore. + + module M = { + proc bad_swap(x : int) : int = { + var a, b : int; + a <- x + 1; (* 1 *) + b <- a + 2; (* 2 *) + return b; + } + }. + + lemma bad_swap_demo (n : int) : + hoare [ M.bad_swap : x = n ==> res = n + 3 ]. + proof. + proc. + + (*$*)(* This swap is invalid: b depends on a. *) + fail swap 1 1. + + admit. + qed.