-
Notifications
You must be signed in to change notification settings - Fork 18
/
custom_ra.v
283 lines (241 loc) · 8.19 KB
/
custom_ra.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
From iris.algebra Require Import cmra.
From iris.heap_lang.lib Require Export par.
From iris.heap_lang Require Import proofmode notation.
(* ################################################################# *)
(** * Custom Resource Algebra *)
(* ================================================================= *)
(** ** A Motivating Example *)
(**
In this chapter, we will define a new resource algebra from scratch.
To motivate the resource algebra, we will look at the following
program:
*)
Definition prog : expr :=
let: "l" := ref #4 in
("l" <- !"l" + #1) ||| ("l" <- !"l" + #1);;
!"l".
(**
We wish to show that the program terminates with a number that is at
least [5]. To do this, we will use an invariant separated into the
different abstract states of our program. At the outset, the location
will map to [4]. During the execution of the threads, the location
will be changed to either [5] or [6]. However, we just care about it
becoming greater than [4]. As such, we will have the following states.
*)
Inductive state :=
(** The starting state. *)
| Start
(** The state after the location has been increased. *)
| Final
(**
An invalid state we will use to restrict the operations on the
state.
*)
| Invalid.
(**
The [state] data structure will be our carrier for the RA. To use
[state] as a resource algebra, we need to turn it into a resource
algebra, meaning we need it to adhere to [RAMixin]. As such, we must
define an equivalence relation, composition, the core, and valid
elements, and prove that these definitions satisfy the fields in
[RAMixin].
*)
(* ================================================================= *)
(** ** Defining the State RA *)
Section state.
(**
First, we define an equivalence relation on the state. We just want
states to be equivalent exactly when they are equal.
*)
Global Instance state_equiv_instance : Equiv state := eq.
Global Instance state_equiv_equivalence : Equivalence (≡@{state}) := _.
(**
To help convert between equivalence and equality, we can tell Iris
that they coincide, which in this case is trivial.
*)
Global Instance state_leibniz_equiv : LeibnizEquiv state := _.
(**
Recall that resource algebras are discrete cameras and that cameras
build on OFEs (Ordered family of equivalences). As such, to define a
resources algebra, we must first define its OFE. An OFE encodes a kind
of time dependence, but when the camera is _discrete_ and hence a
resource algebra, it does not depend on time. To quickly define a
discrete OFE, we can use [discrete_ofe_mixin]. This will produce a
warning because we are reusing a definition in a canonical projection.
However, this warning can be safely ignored in this case.
*)
Canonical stateO := Ofe state (discrete_ofe_mixin _).
(**
Next, we define how elements of [state] can be combined. We define
[Final ⋅ Final] as [Final], and make every other combination
[Invalid]. In particular, this means that [Start] will be exclusive,
while [Final] can be shared.
*)
Local Instance state_op_instance : Op state := λ s1 s2,
match s1, s2 with
| Final, Final => Final
| _, _ => Invalid
end.
(**
The core then simply reflects that [Start] is exclusive.
*)
Local Instance state_pcore_instance : PCore state := λ s,
match s with
| Start => None
| _ => Some s
end.
(**
Finally, we have to define which elements of [state] are valid.
Naturally, we define everything except for [Invalid] as valid.
*)
Local Instance state_valid_instance : Valid state := λ s,
match s with
| Start | Final => True
| Invalid => False
end.
(**
With everything defined, we can move on to prove [RAMixin] for
[state].
*)
Lemma state_ra_mixin : RAMixin state.
Proof.
split.
- solve_proper.
- naive_solver.
- solve_proper.
- by intros [] [] [].
- by intros [] [].
- by intros [] [].
- by intros [] [].
- intros [] _ [] [[] ->] e; try done.
all: eexists; split; first done.
all: try by exists Invalid.
by exists Final.
- by intros [] [].
Qed.
(**
The final step is to package this into a CMRA structure, allowing us
to use the resource algebra in proofs (using Coq's Context mechanism).
*)
Canonical Structure stateR := discreteR state state_ra_mixin.
(**
To help the Iris Proof Mode, we will register [stateR] as a discrete
CMRA.
*)
Global Instance state_cmra_discrete : CmraDiscrete state.
Proof. apply discrete_cmra_discrete. Qed.
End state.
(* ================================================================= *)
(** ** Properties of State *)
(**
To alleviate working with the State RA, we here show some useful facts
about the resource algebra. Firstly, [Start] is exclusive and [Final]
is shareable.
*)
Global Instance Start_exclusive : Exclusive Start.
Proof. by intros []. Qed.
Global Instance Final_core_id : CoreId Final.
Proof. red. done. Qed.
(**
We want the ability to change the state from [Start] to [Final].
However, we will instead prove a more general fact: That any state can
update to [Final].
*)
Lemma state_update s : s ~~> Final.
Proof.
(**
As we are working with a discrete CMRA, we can simplify this
statement as follows.
*)
rewrite cmra_discrete_update.
intros mz H.
by destruct s, mz as [[| |]|].
Qed.
(**
Next, we give lemmas that help working with the State RA _in Iris_.
*)
Section properties.
Context `{!inG Σ stateR}.
(**
Our first lemma shows how a new State resource is created. We can
create new resources via the basic update modality [|==> P]. This
operation states that P holds after an update of resources. To work
with the update modality, we can use two facts:
- [P ⊢ |==> P]
- [(P ⊢ |==> Q) ⊢ (|==> P ⊢ |==> Q)]
Rather than working with these facts directly, we can use
[iModIntro] and [iMod] respectively.
*)
Lemma alloc_Start : ⊢ |==> ∃ γ, own γ Start.
Proof.
(** To allocate a new resource, we use [own_alloc]. *)
iApply own_alloc.
(**
We are naturally only allowed to allocate valid resources, but since
[Start] is valid, this is no problem.
*)
done.
Qed.
(**
Likewise, owning a resource means that it is valid.
A quick note: [✓ _] and [⌜✓ _⌝] are different – they only coincide
when the CMRA is discrete.
*)
Lemma state_valid γ (s : state) : own γ s ⊢ ⌜✓ s⌝.
Proof.
iIntros "H".
iPoseProof (own_valid with "H") as "%H".
done.
Qed.
(**
Next, we can lift [state_update] to ownership using [own_update].
As a shorthand for [_ -∗ |==> _] we write [_ ==∗ _].
*)
Lemma state_bupd γ (s : state) : own γ s ==∗ own γ Final.
Proof.
iApply own_update.
apply state_update.
Qed.
End properties.
(* ================================================================= *)
(** ** Using the State RA in Proofs *)
(**
Let us return to the motivating example from the first section of this
chapter. We will be using the State RA to prove that [prog] terminates
with a value that is at least [5].
*)
Section proofs.
Context `{!heapGS Σ, !spawnG Σ, !inG Σ stateR}.
Let N := nroot .@ "state".
(**
We can now define the invariant we hinted at in the beginning.
*)
Definition state_inv γ (l : loc) (x : Z) : iProp Σ :=
∃ y : Z, l ↦ #y ∗ (own γ Start ∗ ⌜x = y⌝ ∨ own γ Final ∗ ⌜x < y⌝%Z).
(**
Rather than proving the entire program at once, we will split it into
three pieces, starting with the two threads.
Note that WP contains an update modality, meaning we can update
resources in between steps.
*)
Lemma thread_spec γ l (x : Z) : {{{inv N (state_inv γ l x)}}} #l <- !#l + #1 {{{RET #(); own γ Final}}}.
Proof.
(* exercise *)
Admitted.
Lemma body_spec l (x : Z) : {{{l ↦ #x}}} (#l <- !#l + #1) ||| (#l <- !#l + #1);; !#l {{{(y : Z), RET #y; ⌜x < y⌝%Z}}}.
Proof.
(* exercise *)
Admitted.
Lemma prog_spec : {{{True}}} prog {{{(y : Z), RET #y; ⌜5 ≤ y⌝%Z}}}.
Proof.
(* exercise *)
Admitted.
End proofs.
(**
Iris ships with a bunch of different CMRAs that you can use out of the
box. Rather than creating new resources for every new situation, we
can instead compose existing CMRAs. For instance, our State resource
algebra corresponds to [csum (excl ()) (agree ())].
Some of the available CMRAs can be found here:
<<https://gitlab.mpi-sws.org/iris/iris/-/tree/master/iris/algebra>>
*)