author | wenzelm |
Wed, 23 Jul 2025 14:53:21 +0200 | |
changeset 82898 | 89da4dcd1fa8 |
parent 81189 | 47a0dfee26ea |
permissions | -rw-r--r-- |
13903 | 1 |
(* Title: HOL/Hoare/Separation.thy |
2 |
Author: Tobias Nipkow |
|
3 |
Copyright 2003 TUM |
|
4 |
||
5 |
A first attempt at a nice syntactic embedding of separation logic. |
|
14074 | 6 |
Already builds on the theory for list abstractions. |
7 |
||
8 |
If we suppress the H parameter for "List", we have to hardwired this |
|
9 |
into parser and pretty printer, which is not very modular. |
|
10 |
Alternative: some syntax like <P> which stands for P H. No more |
|
11 |
compact, but avoids the funny H. |
|
13903 | 12 |
*) |
13 |
||
72990 | 14 |
section \<open>Separation logic\<close> |
15 |
||
16 |
theory Separation |
|
17 |
imports Hoare_Logic_Abort SepLogHeap |
|
18 |
begin |
|
13857 | 19 |
|
62042 | 20 |
text\<open>The semantic definition of a few connectives:\<close> |
13857 | 21 |
|
80914
d97fdabd9e2b
standardize mixfix annotations via "isabelle update -a -u mixfix_cartouches" --- to simplify systematic editing;
wenzelm
parents:
72990
diff
changeset
|
22 |
definition ortho :: "heap \<Rightarrow> heap \<Rightarrow> bool" (infix \<open>\<bottom>\<close> 55) |
38353 | 23 |
where "h1 \<bottom> h2 \<longleftrightarrow> dom h1 \<inter> dom h2 = {}" |
13857 | 24 |
|
38353 | 25 |
definition is_empty :: "heap \<Rightarrow> bool" |
68451 | 26 |
where "is_empty h \<longleftrightarrow> h = Map.empty" |
13857 | 27 |
|
38353 | 28 |
definition singl:: "heap \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> bool" |
29 |
where "singl h x y \<longleftrightarrow> dom h = {x} & h x = Some y" |
|
13857 | 30 |
|
38353 | 31 |
definition star:: "(heap \<Rightarrow> bool) \<Rightarrow> (heap \<Rightarrow> bool) \<Rightarrow> (heap \<Rightarrow> bool)" |
32 |
where "star P Q = (\<lambda>h. \<exists>h1 h2. h = h1++h2 \<and> h1 \<bottom> h2 \<and> P h1 \<and> Q h2)" |
|
13875 | 33 |
|
38353 | 34 |
definition wand:: "(heap \<Rightarrow> bool) \<Rightarrow> (heap \<Rightarrow> bool) \<Rightarrow> (heap \<Rightarrow> bool)" |
35 |
where "wand P Q = (\<lambda>h. \<forall>h'. h' \<bottom> h \<and> P h' \<longrightarrow> Q(h++h'))" |
|
13875 | 36 |
|
62042 | 37 |
text\<open>This is what assertions look like without any syntactic sugar:\<close> |
13903 | 38 |
|
13857 | 39 |
lemma "VARS x y z w h |
40 |
{star (%h. singl h x y) (%h. singl h z w) h} |
|
41 |
SKIP |
|
42 |
{x \<noteq> z}" |
|
43 |
apply vcg |
|
13875 | 44 |
apply(auto simp:star_def ortho_def singl_def) |
13857 | 45 |
done |
46 |
||
62042 | 47 |
text\<open>Now we add nice input syntax. To suppress the heap parameter |
13903 | 48 |
of the connectives, we assume it is always called H and add/remove it |
49 |
upon parsing/printing. Thus every pointer program needs to have a |
|
50 |
program variable H, and assertions should not contain any locally |
|
62042 | 51 |
bound Hs - otherwise they may bind the implicit H.\<close> |
13857 | 52 |
|
53 |
syntax |
|
80914
d97fdabd9e2b
standardize mixfix annotations via "isabelle update -a -u mixfix_cartouches" --- to simplify systematic editing;
wenzelm
parents:
72990
diff
changeset
|
54 |
"_emp" :: "bool" (\<open>emp\<close>) |
81189 | 55 |
"_singl" :: "nat \<Rightarrow> nat \<Rightarrow> bool" (\<open>(\<open>open_block notation=\<open>mixfix singl\<close>\<close>[_ \<mapsto> _])\<close>) |
56 |
"_star" :: "bool \<Rightarrow> bool \<Rightarrow> bool" (infixl \<open>**\<close> 60) |
|
57 |
"_wand" :: "bool \<Rightarrow> bool \<Rightarrow> bool" (infixl \<open>-*\<close> 60) |
|
58 |
||
59 |
syntax_consts |
|
60 |
"_emp" \<rightleftharpoons> is_empty and |
|
61 |
"_singl" \<rightleftharpoons> singl and |
|
62 |
"_star" \<rightleftharpoons> star and |
|
63 |
"_wand" \<rightleftharpoons> wand |
|
13857 | 64 |
|
17781 | 65 |
(* FIXME does not handle "_idtdummy" *) |
67444 | 66 |
ML \<open> |
67 |
\<comment> \<open>\<open>free_tr\<close> takes care of free vars in the scope of separation logic connectives: |
|
68 |
they are implicitly applied to the heap\<close> |
|
13875 | 69 |
fun free_tr(t as Free _) = t $ Syntax.free "H" |
67444 | 70 |
\<^cancel>\<open>| free_tr((list as Free("List",_))$ p $ ps) = list $ Syntax.free "H" $ p $ ps\<close> |
13875 | 71 |
| free_tr t = t |
72 |
||
69597 | 73 |
fun emp_tr [] = Syntax.const \<^const_syntax>\<open>is_empty\<close> $ Syntax.free "H" |
13875 | 74 |
| emp_tr ts = raise TERM ("emp_tr", ts); |
69597 | 75 |
fun singl_tr [p, q] = Syntax.const \<^const_syntax>\<open>singl\<close> $ Syntax.free "H" $ p $ q |
13857 | 76 |
| singl_tr ts = raise TERM ("singl_tr", ts); |
69597 | 77 |
fun star_tr [P,Q] = Syntax.const \<^const_syntax>\<open>star\<close> $ |
44241 | 78 |
absfree ("H", dummyT) (free_tr P) $ absfree ("H", dummyT) (free_tr Q) $ |
13875 | 79 |
Syntax.free "H" |
80 |
| star_tr ts = raise TERM ("star_tr", ts); |
|
69597 | 81 |
fun wand_tr [P, Q] = Syntax.const \<^const_syntax>\<open>wand\<close> $ |
44241 | 82 |
absfree ("H", dummyT) P $ absfree ("H", dummyT) Q $ Syntax.free "H" |
13875 | 83 |
| wand_tr ts = raise TERM ("wand_tr", ts); |
62042 | 84 |
\<close> |
13857 | 85 |
|
62042 | 86 |
parse_translation \<open> |
69597 | 87 |
[(\<^syntax_const>\<open>_emp\<close>, K emp_tr), |
88 |
(\<^syntax_const>\<open>_singl\<close>, K singl_tr), |
|
89 |
(\<^syntax_const>\<open>_star\<close>, K star_tr), |
|
90 |
(\<^syntax_const>\<open>_wand\<close>, K wand_tr)] |
|
62042 | 91 |
\<close> |
13857 | 92 |
|
62042 | 93 |
text\<open>Now it looks much better:\<close> |
13903 | 94 |
|
13857 | 95 |
lemma "VARS H x y z w |
96 |
{[x\<mapsto>y] ** [z\<mapsto>w]} |
|
97 |
SKIP |
|
98 |
{x \<noteq> z}" |
|
99 |
apply vcg |
|
13875 | 100 |
apply(auto simp:star_def ortho_def singl_def) |
101 |
done |
|
102 |
||
103 |
lemma "VARS H x y z w |
|
104 |
{emp ** emp} |
|
105 |
SKIP |
|
106 |
{emp}" |
|
107 |
apply vcg |
|
108 |
apply(auto simp:star_def ortho_def is_empty_def) |
|
13857 | 109 |
done |
110 |
||
62042 | 111 |
text\<open>But the output is still unreadable. Thus we also strip the heap |
112 |
parameters upon output:\<close> |
|
13903 | 113 |
|
62042 | 114 |
ML \<open> |
35113 | 115 |
local |
13857 | 116 |
|
13903 | 117 |
fun strip (Abs(_,_,(t as Const("_free",_) $ Free _) $ Bound 0)) = t |
118 |
| strip (Abs(_,_,(t as Free _) $ Bound 0)) = t |
|
67444 | 119 |
\<^cancel>\<open>| strip (Abs(_,_,((list as Const("List",_))$ Bound 0 $ p $ ps))) = list$p$ps\<close> |
13903 | 120 |
| strip (Abs(_,_,(t as Const("_var",_) $ Var _) $ Bound 0)) = t |
13875 | 121 |
| strip (Abs(_,_,P)) = P |
69597 | 122 |
| strip (Const(\<^const_syntax>\<open>is_empty\<close>,_)) = Syntax.const \<^syntax_const>\<open>_emp\<close> |
13875 | 123 |
| strip t = t; |
35113 | 124 |
|
13875 | 125 |
in |
35113 | 126 |
|
69597 | 127 |
fun is_empty_tr' [_] = Syntax.const \<^syntax_const>\<open>_emp\<close> |
128 |
fun singl_tr' [_,p,q] = Syntax.const \<^syntax_const>\<open>_singl\<close> $ p $ q |
|
129 |
fun star_tr' [P,Q,_] = Syntax.const \<^syntax_const>\<open>_star\<close> $ strip P $ strip Q |
|
130 |
fun wand_tr' [P,Q,_] = Syntax.const \<^syntax_const>\<open>_wand\<close> $ strip P $ strip Q |
|
35113 | 131 |
|
13875 | 132 |
end |
62042 | 133 |
\<close> |
13857 | 134 |
|
62042 | 135 |
print_translation \<open> |
69597 | 136 |
[(\<^const_syntax>\<open>is_empty\<close>, K is_empty_tr'), |
137 |
(\<^const_syntax>\<open>singl\<close>, K singl_tr'), |
|
138 |
(\<^const_syntax>\<open>star\<close>, K star_tr'), |
|
139 |
(\<^const_syntax>\<open>wand\<close>, K wand_tr')] |
|
62042 | 140 |
\<close> |
13903 | 141 |
|
62042 | 142 |
text\<open>Now the intermediate proof states are also readable:\<close> |
13857 | 143 |
|
144 |
lemma "VARS H x y z w |
|
145 |
{[x\<mapsto>y] ** [z\<mapsto>w]} |
|
13867 | 146 |
y := w |
13857 | 147 |
{x \<noteq> z}" |
148 |
apply vcg |
|
13875 | 149 |
apply(auto simp:star_def ortho_def singl_def) |
150 |
done |
|
151 |
||
152 |
lemma "VARS H x y z w |
|
153 |
{emp ** emp} |
|
154 |
SKIP |
|
155 |
{emp}" |
|
156 |
apply vcg |
|
157 |
apply(auto simp:star_def ortho_def is_empty_def) |
|
158 |
done |
|
159 |
||
62042 | 160 |
text\<open>So far we have unfolded the separation logic connectives in |
13903 | 161 |
proofs. Here comes a simple example of a program proof that uses a law |
62042 | 162 |
of separation logic instead.\<close> |
13903 | 163 |
|
67410 | 164 |
\<comment> \<open>a law of separation logic\<close> |
13875 | 165 |
lemma star_comm: "P ** Q = Q ** P" |
18447 | 166 |
by(auto simp add:star_def ortho_def dest: map_add_comm) |
13875 | 167 |
|
168 |
lemma "VARS H x y z w |
|
169 |
{P ** Q} |
|
170 |
SKIP |
|
171 |
{Q ** P}" |
|
172 |
apply vcg |
|
173 |
apply(simp add: star_comm) |
|
174 |
done |
|
175 |
||
14074 | 176 |
|
177 |
lemma "VARS H |
|
178 |
{p\<noteq>0 \<and> [p \<mapsto> x] ** List H q qs} |
|
179 |
H := H(p \<mapsto> q) |
|
180 |
{List H p (p#qs)}" |
|
181 |
apply vcg |
|
182 |
apply(simp add: star_def ortho_def singl_def) |
|
183 |
apply clarify |
|
184 |
apply(subgoal_tac "p \<notin> set qs") |
|
185 |
prefer 2 |
|
186 |
apply(blast dest:list_in_heap) |
|
187 |
apply simp |
|
188 |
done |
|
189 |
||
190 |
lemma "VARS H p q r |
|
191 |
{List H p Ps ** List H q Qs} |
|
192 |
WHILE p \<noteq> 0 |
|
193 |
INV {\<exists>ps qs. (List H p ps ** List H q qs) \<and> rev ps @ qs = rev Ps @ Qs} |
|
194 |
DO r := p; p := the(H p); H := H(r \<mapsto> q); q := r OD |
|
195 |
{List H q (rev Ps @ Qs)}" |
|
196 |
apply vcg |
|
197 |
apply(simp_all add: star_def ortho_def singl_def) |
|
198 |
||
44890
22f665a2e91c
new fastforce replacing fastsimp - less confusing name
nipkow
parents:
44241
diff
changeset
|
199 |
apply fastforce |
14074 | 200 |
|
201 |
apply (clarsimp simp add:List_non_null) |
|
202 |
apply(rename_tac ps') |
|
203 |
apply(rule_tac x = ps' in exI) |
|
204 |
apply(rule_tac x = "p#qs" in exI) |
|
205 |
apply simp |
|
206 |
apply(rule_tac x = "h1(p:=None)" in exI) |
|
207 |
apply(rule_tac x = "h2(p\<mapsto>q)" in exI) |
|
208 |
apply simp |
|
209 |
apply(rule conjI) |
|
210 |
apply(rule ext) |
|
211 |
apply(simp add:map_add_def split:option.split) |
|
212 |
apply(rule conjI) |
|
213 |
apply blast |
|
214 |
apply(simp add:map_add_def split:option.split) |
|
215 |
apply(rule conjI) |
|
216 |
apply(subgoal_tac "p \<notin> set qs") |
|
217 |
prefer 2 |
|
218 |
apply(blast dest:list_in_heap) |
|
219 |
apply(simp) |
|
220 |
apply fast |
|
221 |
||
44890
22f665a2e91c
new fastforce replacing fastsimp - less confusing name
nipkow
parents:
44241
diff
changeset
|
222 |
apply(fastforce) |
14074 | 223 |
done |
224 |
||
13875 | 225 |
end |