Skip to content

Commit 495bc20

Browse files
committed
[documentation]: tactic swap
1 parent c148172 commit 495bc20

1 file changed

Lines changed: 159 additions & 0 deletions

File tree

doc/tactics/swap.rst

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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 {codepos1}`
31+
- `swap {codepos1} {codeoffset1}`
32+
- `swap [{codepos1}..{codepos1}] {codeoffset1}`
33+
34+
Where `{codepos1}` denotes a *top-level code position* and `{codeoffset1}`
35+
is either:
36+
37+
- a signed integer (`n` or `-n`), denoting a relative position, or
38+
39+
- an absolute code position written `@ {codepos1}`.
40+
41+
The meaning of these forms is as follows:
42+
43+
- `swap {codepos1}` swaps the two adjacent commands starting at the
44+
top-level position `{codepos1}`.
45+
46+
- `swap {codepos1} {codeoffset1}` swaps the command at top-level position
47+
`{codepos1}` with the command designated by `{codeoffset1}`.
48+
49+
- `swap [{codepos1}..{codepos1}] {codeoffset1}` swaps a whole block of
50+
commands delimited by `[{codepos1}..{codepos1}]` with the block
51+
designated by `{codeoffset1}`.
52+
53+
In all cases, the swap is only valid when the exchanged fragments are
54+
independent, so that the transformation preserves the program semantics.
55+
56+
------------------------------------------------------------------------
57+
Example (Hoare logic)
58+
------------------------------------------------------------------------
59+
60+
The following example swaps two adjacent assignments that do not interfere.
61+
The returned result is unchanged, but the rewritten program may be more
62+
convenient for subsequent proof steps.
63+
64+
.. ecproof::
65+
66+
require import AllCore.
67+
68+
module M = {
69+
proc reorder(x : int) : int = {
70+
var a, b : int;
71+
a <- x + 1;
72+
b <- x + 2;
73+
return a + b;
74+
}
75+
}.
76+
77+
lemma reorder_correct (n : int) :
78+
hoare [ M.reorder : x = n ==> res = (n + 1) + (n + 2) ].
79+
proof.
80+
proc.
81+
82+
(*$*) (* Swap the command at position 1 with the next command (offset +1). *)
83+
swap 1 1.
84+
85+
(* The goal is the same, but with the program rewritten. *)
86+
admit.
87+
qed.
88+
89+
------------------------------------------------------------------------
90+
Example (swapping a block)
91+
------------------------------------------------------------------------
92+
93+
The following example illustrates the block form
94+
`swap [{codepos1}..{codepos1}] {codeoffset1}`. We swap a block of two
95+
commands with a later, independent command.
96+
97+
.. ecproof::
98+
99+
require import AllCore.
100+
101+
module M = {
102+
proc swap_block(x : int) : int = {
103+
var a, b, c : int;
104+
105+
a <- x + 1; (* 1 *)
106+
b <- x + 2; (* 2 *)
107+
c <- x + 3; (* 3 *)
108+
a <- a + 10; (* 4 *)
109+
110+
return a + b + c;
111+
}
112+
}.
113+
114+
lemma swap_block_correct (n : int) :
115+
hoare [ M.swap_block : x = n ==> res = (n + 1 + 10) + (n + 2) + (n + 3) ].
116+
proof.
117+
proc.
118+
(*$*)
119+
(* Swap the block [2..3] (b <- x+2; c <- x+3) with the following command
120+
a <- a + 10. These fragments are independent since:
121+
- the block assigns b and c, and
122+
- the command updates a using only a. *)
123+
swap [2..3] 1.
124+
125+
(* The goal is the same, but with the program rewritten. *)
126+
admit.
127+
qed.
128+
129+
------------------------------------------------------------------------
130+
Example (invalid swap)
131+
------------------------------------------------------------------------
132+
133+
The following example shows a swap attempt that fails because the two
134+
commands are not independent: the second command reads the value written by
135+
the first one.
136+
137+
.. ecproof::
138+
139+
require import AllCore.
140+
141+
module M = {
142+
proc bad_swap(x : int) : int = {
143+
var a, b : int;
144+
a <- x + 1; (* 1 *)
145+
b <- a + 2; (* 2 *)
146+
return b;
147+
}
148+
}.
149+
150+
lemma bad_swap_demo (n : int) :
151+
hoare [ M.bad_swap : x = n ==> res = n + 3 ].
152+
proof.
153+
proc.
154+
155+
(*$*)(* This swap is invalid: b depends on a. *)
156+
fail swap 1 1.
157+
158+
admit.
159+
qed.

0 commit comments

Comments
 (0)