Skip to content

Commit 3b85034

Browse files
committed
[documentation]: tactic swap
1 parent 9557f1d commit 3b85034

1 file changed

Lines changed: 170 additions & 0 deletions

File tree

doc/tactics/swap.rst

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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

Comments
 (0)