|
| 1 | +======================================================================== |
| 2 | +Tactic: `swap` |
| 3 | +======================================================================== |
| 4 | + |
| 5 | +The `swap` tactic applies to program-logic goals by rewriting the program |
| 6 | +into a semantically equivalent form where two consecutive, independent |
| 7 | +program fragments are exchanged. |
| 8 | + |
| 9 | +In a nutshell, `swap` permutes commands when doing so does not change the |
| 10 | +program’s behavior (typically because the swapped fragments do not |
| 11 | +interfere, e.g., they write to disjoint variables and neither reads what the |
| 12 | +other writes). |
| 13 | + |
| 14 | +Applying `swap` replaces the current goal by the same goal, but with the |
| 15 | +selected commands swapped in the program. This is useful to expose a more |
| 16 | +convenient program structure, for example to align programs in relational |
| 17 | +proofs or to bring related statements closer together. |
| 18 | + |
| 19 | +.. contents:: |
| 20 | + :local: |
| 21 | + |
| 22 | +------------------------------------------------------------------------ |
| 23 | +Syntax |
| 24 | +------------------------------------------------------------------------ |
| 25 | + |
| 26 | +The `swap` tactic comes in several forms: |
| 27 | + |
| 28 | +.. admonition:: Syntax |
| 29 | + |
| 30 | + - `swap {side}? {codepos1}` |
| 31 | + - `swap {side}? {codepos1} {codeoffset1}` |
| 32 | + - `swap {side}? [{codepos1}..{codepos1}] {codeoffset1}`` |
| 33 | + |
| 34 | +Here: |
| 35 | + |
| 36 | +- `{side}` is optional and is either `1` or `2`. It selects the left or |
| 37 | + right program in relational goals. If omitted, the tactic applies to the |
| 38 | + single program under consideration. |
| 39 | + |
| 40 | +- `{codepos1}` denotes a *top-level code position*. |
| 41 | + |
| 42 | +- A `{codeoffset1}` is either: |
| 43 | + |
| 44 | + - a signed integer (`n` or `-n`), denoting a relative position, or |
| 45 | + - an absolute code position written `@ {codepos1}`. |
| 46 | + |
| 47 | +The meaning of these forms is as follows: |
| 48 | + |
| 49 | +- `swap {side}? {codepos1}` |
| 50 | + |
| 51 | + swaps the two adjacent commands starting at the top-level position |
| 52 | + `{codepos1}`. |
| 53 | + |
| 54 | +- `swap {side}? {codepos1} {codeoffset1}` |
| 55 | + |
| 56 | + swaps the command at top-level position `{codepos1}` with the command |
| 57 | + at the position designated by `{codeoffset1}`. |
| 58 | + |
| 59 | +- `swap {side}? [{codepos1}..{codepos1}] {codeoffset1}` |
| 60 | + |
| 61 | + swaps a whole sequence of commands delimited by `[{codepos1}..{codepos1}]` |
| 62 | + with the commands starting at the position designated by `{codeoffset1}`. |
| 63 | + |
| 64 | +In all cases, the swap is only valid when the exchanged fragments are |
| 65 | +independent, so that the transformation preserves the program semantics. |
| 66 | + |
| 67 | +------------------------------------------------------------------------ |
| 68 | +Example (single statement) |
| 69 | +------------------------------------------------------------------------ |
| 70 | + |
| 71 | +The following example swaps two adjacent assignments that do not interfere. |
| 72 | +The returned result is unchanged, but the rewritten program may be more |
| 73 | +convenient for subsequent proof steps. |
| 74 | + |
| 75 | +.. ecproof:: |
| 76 | + |
| 77 | + require import AllCore. |
| 78 | + |
| 79 | + module M = { |
| 80 | + proc reorder(x : int) : int = { |
| 81 | + var a, b : int; |
| 82 | + a <- x + 1; |
| 83 | + b <- x + 2; |
| 84 | + return a + b; |
| 85 | + } |
| 86 | + }. |
| 87 | + |
| 88 | + lemma reorder_correct (n : int) : |
| 89 | + hoare [ M.reorder : x = n ==> res = (n + 1) + (n + 2) ]. |
| 90 | + proof. |
| 91 | + proc. |
| 92 | + |
| 93 | + (*$*) (* Swap the command at position 1 with the next command (offset +1). *) |
| 94 | + swap 1 1. |
| 95 | +
|
| 96 | + (* The goal is the same, but with the program rewritten. *) |
| 97 | + admit. |
| 98 | + qed. |
| 99 | +
|
| 100 | +------------------------------------------------------------------------ |
| 101 | +Example (swapping a block) |
| 102 | +------------------------------------------------------------------------ |
| 103 | + |
| 104 | +The following example illustrates the block form |
| 105 | +`swap [{codepos1}..{codepos1}] {codeoffset1}`. We swap a block of two |
| 106 | +commands with a later, independent command. |
| 107 | + |
| 108 | +.. ecproof:: |
| 109 | + |
| 110 | + require import AllCore. |
| 111 | + |
| 112 | + module M = { |
| 113 | + proc swap_block(x : int) : int = { |
| 114 | + var a, b, c : int; |
| 115 | + |
| 116 | + a <- x + 1; (* 1 *) |
| 117 | + b <- x + 2; (* 2 *) |
| 118 | + c <- x + 3; (* 3 *) |
| 119 | + a <- a + 10; (* 4 *) |
| 120 | +
|
| 121 | + return a + b + c; |
| 122 | + } |
| 123 | + }. |
| 124 | + |
| 125 | + lemma swap_block_correct (n : int) : |
| 126 | + hoare [ M.swap_block : x = n ==> res = (n + 1 + 10) + (n + 2) + (n + 3) ]. |
| 127 | + proof. |
| 128 | + proc. |
| 129 | + (*$*) |
| 130 | + (* Swap the block [2..3] (b <- x+2; c <- x+3) with the following command |
| 131 | + a <- a + 10. These fragments are independent since: |
| 132 | + - the block assigns b and c, and |
| 133 | + - the command updates a using only a. *) |
| 134 | + swap [2..3] 1. |
| 135 | +
|
| 136 | + (* The goal is the same, but with the program rewritten. *) |
| 137 | + admit. |
| 138 | + qed. |
| 139 | +
|
| 140 | +------------------------------------------------------------------------ |
| 141 | +Example (invalid swap) |
| 142 | +------------------------------------------------------------------------ |
| 143 | + |
| 144 | +The following example shows a swap attempt that fails because the two |
| 145 | +commands are not independent: the second command reads the value written by |
| 146 | +the first one. |
| 147 | + |
| 148 | +.. ecproof:: |
| 149 | + |
| 150 | + require import AllCore. |
| 151 | + |
| 152 | + module M = { |
| 153 | + proc bad_swap(x : int) : int = { |
| 154 | + var a, b : int; |
| 155 | + a <- x + 1; (* 1 *) |
| 156 | + b <- a + 2; (* 2 *) |
| 157 | + return b; |
| 158 | + } |
| 159 | + }. |
| 160 | +
|
| 161 | + lemma bad_swap_demo (n : int) : |
| 162 | + hoare [ M.bad_swap : x = n ==> res = n + 3 ]. |
| 163 | + proof. |
| 164 | + proc. |
| 165 | + |
| 166 | + (*$*)(* This swap is invalid: b depends on a. *) |
| 167 | + fail swap 1 1. |
| 168 | +
|
| 169 | + admit. |
| 170 | + qed. |
0 commit comments