Skip to main content

vstd/resource/
lib.rs

1use super::super::map::*;
2use super::super::modes::*;
3use super::super::prelude::*;
4use super::super::seq::*;
5use super::Loc;
6use super::pcm::Resource;
7use super::pcm::*;
8use super::relations::*;
9
10verus! {
11
12broadcast use super::super::group_vstd_default;
13
14/// Combines a list of values into one value using P::op().
15pub open spec fn combine_values<P: PCM>(values: Seq<P>) -> P
16    decreases values.len(),
17{
18    if values.len() == 0 {
19        P::unit()
20    } else {
21        P::op(values[0], combine_values(values.skip(1)))
22    }
23}
24
25/// Provides four quantified facts about a partially commutative
26/// monoid: that it's closed under inclusion, that it's commutative,
27/// that it's a monoid, and that its unit element is valid.
28///
29/// Note that, to avoid trigger loops, it doesn't provide associativity and thus
30/// should not be used in the same context as [`lemma_pcm_associative`]
31pub proof fn lemma_pcm_properties<P: PCM>()
32    ensures
33        forall|a: P, b: P| (#[trigger] P::op(a, b)).valid() ==> a.valid(),
34        forall|a: P, b: P| (#[trigger] P::op(a, b)) == P::op(b, a),
35        forall|a: P| (#[trigger] P::op(a, P::unit())) == a,
36        P::valid(P::unit()),
37{
38    assert forall|a: P, b: P| (#[trigger] P::op(a, b)).valid() implies a.valid() by {
39        P::valid_op(a, b);
40    }
41    assert forall|a: P, b: P| (#[trigger] P::op(a, b)) == P::op(b, a) by {
42        P::commutative(a, b);
43    }
44    assert forall|a: P| P::op(a, P::unit()) == a by {
45        P::op_unit(a);
46    }
47    assert(P::valid(P::unit())) by {
48        P::unit_valid();
49    }
50}
51
52/// Provides a quantified associativity fact about a partially commutative
53/// monoid.
54///
55/// Note that, to avoid trigger loops, it doesn't provide associativity and thus
56/// should not be used in the same context as [`lemma_pcm_properties`]
57pub proof fn lemma_pcm_associative<P: PCM>()
58    ensures
59        forall|a: P, b: P, c: P| (#[trigger] P::op(a, P::op(b, c))) == P::op(P::op(a, b), c),
60{
61    assert forall|a: P, b: P, c: P| (#[trigger] P::op(a, P::op(b, c))) == P::op(P::op(a, b), c) by {
62        P::associative(a, b, c);
63    }
64}
65
66/// Produces a new resource with value `new_value` given a shared
67/// reference to a resource `r` whose value has a duplicable part
68/// `new_value`. More precisely, produces a resource with value
69/// `new_value` given that `r.value() == P::op(r.value(), new_value)`.
70pub proof fn copy_duplicable_part<P: PCM>(tracked r: &Resource<P>, new_value: P) -> (tracked out:
71    Resource<P>)
72    requires
73        r.value() == P::op(r.value(), new_value),
74    ensures
75        out.loc() == r.loc(),
76        out.value() == new_value,
77{
78    lemma_pcm_properties::<P>();
79    r.validate();
80    r.duplicate_previous(new_value)
81}
82
83/// Duplicates `r`, returning an identical resource. The value of
84/// `r` must be duplicable, i.e., `r.value()` must be equal to
85/// `P::op(r.value(), r.value())`.
86pub proof fn duplicate<P: PCM>(tracked r: &Resource<P>) -> (tracked other: Resource<P>)
87    requires
88        P::op(r.value(), r.value()) == r.value(),
89    ensures
90        other.loc() == r.loc(),
91        other.value() == r.value(),
92{
93    copy_duplicable_part(r, r.value())
94}
95
96/// Incorporates the resources of `r2` into `r1`, consuming `r2`.
97pub proof fn incorporate<P: PCM>(tracked r1: &mut Resource<P>, tracked r2: Resource<P>)
98    requires
99        old(r1).loc() == r2.loc(),
100    ensures
101        r1.loc() == old(r1).loc(),
102        r1.value() == P::op(old(r1).value(), r2.value()),
103{
104    let tracked mut r = r1.extract();
105    let tracked mut joined_r = r.join(r2);
106    tracked_swap(r1, &mut joined_r);
107}
108
109/// Splits the value of `r` into `left` and `right`. At the end, `r`
110/// ends up with `left` as its value and the function returns a new
111/// resource with value `right`.
112pub proof fn split_mut<P: PCM>(tracked r: &mut Resource<P>, left: P, right: P) -> (tracked other:
113    Resource<P>)
114    requires
115        old(r).value() == P::op(left, right),
116    ensures
117        r.loc() == old(r).loc(),
118        other.loc() == old(r).loc(),
119        r.value() == left,
120        other.value() == right,
121{
122    let tracked mut r_value = r.extract();
123    let tracked (mut r_left, r_right) = r_value.split(left, right);
124    tracked_swap(r, &mut r_left);
125    r_right
126}
127
128/// Updates `r` to have new value `new_value`. This must be a
129/// frame-preserving update. That is, `new_value` must be compatible
130/// with all frames `old(r).value()` was compatible with.
131pub proof fn update_mut<P: PCM>(tracked r: &mut Resource<P>, new_value: P)
132    requires
133        frame_preserving_update(old(r).value(), new_value),
134    ensures
135        r.loc() == old(r).loc(),
136        r.value() == new_value,
137{
138    let tracked mut r_value = r.extract();
139    let tracked mut r_upd = r_value.update(new_value);
140    tracked_swap(r, &mut r_upd);
141}
142
143/// Redistribute the values held by resources `r1` and `r2` such that they
144/// have the same combination as before. The new value of `r1` will be `v1`
145/// and the new value of `r2` will be `v2`.
146pub proof fn redistribute<P: PCM>(
147    tracked r1: &mut Resource<P>,
148    tracked r2: &mut Resource<P>,
149    v1: P,
150    v2: P,
151)
152    requires
153        old(r1).loc() == old(r2).loc(),
154        P::op(old(r1).value(), old(r2).value()) == P::op(v1, v2),
155    ensures
156        r1.loc() == old(r1).loc(),
157        r2.loc() == old(r1).loc(),
158        r1.value() == v1,
159        r2.value() == v2,
160{
161    lemma_pcm_properties::<P>();
162    let tracked r2_extracted = r2.extract();
163    incorporate(r1, r2_extracted);
164    let tracked mut r2_new = split_mut(r1, v1, v2);
165    tracked_swap(r2, &mut r2_new);
166}
167
168/// Update the values held by resources `r1` and `r2` such that their
169/// values' combination is updated in a frame-preserving way (i.e.,
170/// that combination must be updatable in a frame-preserving way to
171/// the combination of `v1` and `v2`). The new value of `r1` will be
172/// `v1` and the new value of `r2` will be `v2`.
173pub proof fn update_and_redistribute<P: PCM>(
174    tracked r1: &mut Resource<P>,
175    tracked r2: &mut Resource<P>,
176    v1: P,
177    v2: P,
178)
179    requires
180        old(r1).loc() == old(r2).loc(),
181        frame_preserving_update(P::op(old(r1).value(), old(r2).value()), P::op(v1, v2)),
182    ensures
183        r1.loc() == old(r1).loc(),
184        r2.loc() == old(r1).loc(),
185        r1.value() == v1,
186        r2.value() == v2,
187{
188    lemma_pcm_properties::<P>();
189    let tracked r2_extracted = r2.extract();
190    incorporate(r1, r2_extracted);
191    update_mut(r1, P::op(v1, v2));
192    let tracked mut r2_new = split_mut(r1, v1, v2);
193    tracked_swap(r2, &mut r2_new);
194}
195
196// This is a helper function used by `validate_multiple_resources` but
197// not meant for public export.
198//
199// This function is given a map of resources and removes them one by one, accumulating them in a
200// resource by `join`ing them
201proof fn aggregate_resources_from_map_starting_at_offset<P: PCM>(
202    tracked m: &mut Map<int, Resource<P>>,
203    loc: Loc,
204    values: Seq<P>,
205    offset: int,
206) -> (tracked all: Resource<P>)
207    requires
208        0 <= offset < values.len(),
209        forall|i|
210            #![trigger old(m).dom().contains(i)]
211            0 <= i < offset ==> !old(m).dom().contains(i),
212        forall|i|
213            #![trigger old(m).dom().contains(i)]
214            offset <= i < values.len() ==> old(m).dom().contains(i),
215        forall|i|
216            #![trigger old(m)[i]]
217            offset <= i < values.len() ==> old(m)[i].loc() == loc && old(m)[i].value() == values[i],
218    ensures
219        forall|i| #![trigger m.dom().contains(i)] 0 <= i < values.len() ==> !m.dom().contains(i),
220        all.loc() == loc,
221        all.value() == combine_values::<P>(values.skip(offset)),
222    decreases values.len() - offset,
223{
224    assert(m.dom().contains(offset));
225    assert(m[offset].loc() == loc && m[offset].value() == values[offset]);
226    let tracked p = m.tracked_remove(offset);
227    if offset == values.len() - 1 {
228        assert(combine_values::<P>(values.skip(offset)) == values[offset]) by {
229            lemma_pcm_properties::<P>();  // needed to show that combining with unit is locentity
230            reveal_with_fuel(combine_values, 2);
231        };
232        p
233    } else {
234        assert(combine_values::<P>(values.skip(offset)) == P::op(
235            values[offset],
236            combine_values::<P>(values.skip(offset + 1)),
237        )) by {
238            assert(values[offset] == values.skip(offset)[0]);
239            assert(values.skip(offset + 1) == values.skip(offset).skip(1));
240        }
241        assert forall|i|
242            #![trigger m.dom().contains(i)]
243            offset + 1 <= i < values.len() implies m.dom().contains(i) && m[i].loc() == loc
244            && m[i].value() == values[i] by {
245            assert(m.dom().contains(i));
246            assert(m[i].loc() == loc && m[i].value() == values[i]);
247        }
248        let tracked most = aggregate_resources_from_map_starting_at_offset(
249            m,
250            loc,
251            values,
252            offset + 1,
253        );
254        assert(most.loc() == loc);
255        assert(most.value() == combine_values::<P>(values.skip(offset + 1)));
256        p.join(most)
257    }
258}
259
260// This is a helper function used by `validate_multiple_resources` but
261// not meant for public export.
262//
263// This function restores the resources in the map to their original values.
264//
265// This function is given a map `m`, and the original carrier values in values.
266// Moreover, the aggregated resource is `p`. It splits the aggregated resource and restores the
267// original resources in map
268proof fn store_resources_into_map_starting_at_offset<P: PCM>(
269    tracked m: &mut Map<int, Resource<P>>,
270    values: Seq<P>,
271    offset: int,
272    tracked p: Resource<P>,
273)
274    requires
275        0 <= offset < values.len(),
276        forall|i| #![trigger old(m).dom().contains(i)] 0 <= i < offset ==> old(m).dom().contains(i),
277        forall|i|
278            #![trigger old(m)[i]]
279            0 <= i < offset ==> old(m)[i].loc() == p.loc() && old(m)[i].value() == values[i],
280        forall|i|
281            #![trigger old(m).dom().contains(i)]
282            offset <= i < values.len() ==> !old(m).dom().contains(i),
283        p.value() == combine_values::<P>(values.skip(offset)),
284    ensures
285        forall|i| #![trigger m.dom().contains(i)] 0 <= i < values.len() ==> m.dom().contains(i),
286        forall|i|
287            #![trigger m[i]]
288            0 <= i < values.len() ==> m[i].loc() == p.loc() && m[i].value() == values[i],
289    decreases values.len() - offset,
290{
291    assert(combine_values::<P>(values.skip(offset)) == P::op(
292        values[offset],
293        combine_values::<P>(values.skip(offset + 1)),
294    )) by {
295        assert(values[offset] == values.skip(offset)[0]);
296        assert(values.skip(offset + 1) == values.skip(offset).skip(1));
297    }
298    let tracked (p_first, p_rest) = p.split(
299        values[offset],
300        combine_values::<P>(values.skip(offset + 1)),
301    );
302    m.tracked_insert(offset, p_first);
303    if offset < values.len() - 1 {
304        store_resources_into_map_starting_at_offset(m, values, offset + 1, p_rest);
305    }
306}
307
308/// Validates that a given sequence of resources has values that
309/// combine to form a valid value. Although that sequence consists of
310/// mutable references, none of those resources change. (They change
311/// in the middle of the function, but are restored by the time it
312/// completes.) The sequence of resources is specified using the
313/// following input parameters:
314///
315/// `m` -- a map from integers to resources, mapping 0 to the first
316/// resource, 1 to the second, etc.
317///
318/// `loc` -- the `loc()` shared by all the resources in `m`
319///
320/// `values` -- the sequence of resources
321// The proof goes as follows:
322// - join all the mutable resources, with aggregate_resources_from_map_starting_at_offset
323// - validate the joined resource with the shared reference with validate_2
324// - restore the mutable resources with store_resources_into_map_starting_at_offset
325pub proof fn validate_multiple<P: PCM>(
326    tracked m: &mut Map<int, Resource<P>>,
327    values: Seq<P>,
328    tracked shared: &Resource<P>,
329)
330    requires
331        values.len() > 0,
332        forall|i|
333            #![trigger old(m).dom().contains(i)]
334            0 <= i < values.len() ==> old(m).dom().contains(i),
335        forall|i|
336            #![trigger old(m)[i]]
337            0 <= i < values.len() ==> old(m)[i].loc() == shared.loc() && old(m)[i].value()
338                == values[i],
339    ensures
340        forall|i| #![trigger m.dom().contains(i)] 0 <= i < values.len() ==> m.dom().contains(i),
341        forall|i|
342            #![trigger m[i]]
343            0 <= i < values.len() ==> m[i].loc() == shared.loc() && m[i].value() == values[i],
344        P::op(combine_values::<P>(values), shared.value()).valid(),
345{
346    let tracked mut agg = aggregate_resources_from_map_starting_at_offset(
347        m,
348        shared.loc(),
349        values,
350        0,
351    );
352    assert(agg.value() == combine_values::<P>(values)) by {
353        assert(values == values.skip(0));
354    }
355    agg.validate_2(shared);
356    store_resources_into_map_starting_at_offset(m, values, 0, agg);
357}
358
359/// Validates that the three given resources have values that combine to form a valid value.
360/// Although `r1` and `r2` are mutable, they don't change. (They change during the function but
361/// are restored to the way they were by the time the function returns.)
362pub proof fn validate_3<P: PCM>(
363    tracked r1: &mut Resource<P>,
364    tracked r2: &mut Resource<P>,
365    tracked r3: &Resource<P>,
366)
367    requires
368        old(r1).loc() == r3.loc(),
369        old(r2).loc() == r3.loc(),
370    ensures
371        r1.loc() == r3.loc(),
372        r2.loc() == r3.loc(),
373        r1.value() == old(r1).value(),
374        r2.value() == old(r2).value(),
375        P::op(r1.value(), P::op(r2.value(), r3.value())).valid(),
376{
377    lemma_pcm_properties::<P>();
378    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
379    let values: Seq<P> = seq![r1.value(), r2.value()];
380    m.tracked_insert(0, r1.extract());
381    m.tracked_insert(1, r2.extract());
382    assert(combine_values::<P>(values) == P::op(old(r1).value(), old(r2).value())) by {
383        lemma_pcm_properties::<P>();
384        reveal_with_fuel(combine_values, 3);
385    }
386    validate_multiple(&mut m, values, r3);
387    let tracked mut new_r1 = m.tracked_remove(0);
388    let tracked mut new_r2 = m.tracked_remove(1);
389    tracked_swap(r1, &mut new_r1);
390    tracked_swap(r2, &mut new_r2);
391    assert(P::op(r1.value(), P::op(r2.value(), r3.value())).valid()) by {
392        lemma_pcm_associative::<P>();
393    };
394}
395
396/// Validates that the four given resources have values that combine to form a valid value.
397/// Although the inputs `r1`, `r2` and `r3` are mutable, they don't change. (They change during
398/// the function but are restored to the way they were by the time the function returns.)
399pub proof fn validate_4<P: PCM>(
400    tracked r1: &mut Resource<P>,
401    tracked r2: &mut Resource<P>,
402    tracked r3: &mut Resource<P>,
403    tracked r4: &Resource<P>,
404)
405    requires
406        old(r1).loc() == r4.loc(),
407        old(r2).loc() == r4.loc(),
408        old(r3).loc() == r4.loc(),
409    ensures
410        r1.loc() == r4.loc(),
411        r2.loc() == r4.loc(),
412        r3.loc() == r4.loc(),
413        r1.value() == old(r1).value(),
414        r2.value() == old(r2).value(),
415        r3.value() == old(r3).value(),
416        P::op(r1.value(), P::op(r2.value(), P::op(r3.value(), r4.value()))).valid(),
417{
418    lemma_pcm_properties::<P>();
419    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
420    let values: Seq<P> = seq![r1.value(), r2.value(), r3.value()];
421    m.tracked_insert(0, r1.extract());
422    m.tracked_insert(1, r2.extract());
423    m.tracked_insert(2, r3.extract());
424    assert(combine_values::<P>(values) == P::op(
425        old(r1).value(),
426        P::op(old(r2).value(), old(r3).value()),
427    )) by {
428        lemma_pcm_properties::<P>();
429        reveal_with_fuel(combine_values, 4);
430    }
431    validate_multiple(&mut m, values, r4);
432    let tracked mut new_r1 = m.tracked_remove(0);
433    let tracked mut new_r2 = m.tracked_remove(1);
434    let tracked mut new_r3 = m.tracked_remove(2);
435    tracked_swap(r1, &mut new_r1);
436    tracked_swap(r2, &mut new_r2);
437    tracked_swap(r3, &mut new_r3);
438    assert(P::op(r1.value(), P::op(r2.value(), P::op(r3.value(), r4.value()))).valid()) by {
439        lemma_pcm_associative::<P>();
440    }
441}
442
443/// Validates that the five given resources have values that combine to form a valid value.
444/// Although the inputs `r1`, `r2`, `r3` and `r4` are mutable, they don't change. (They change
445/// during the function but are restored to the way they were by the time the function returns.)
446pub proof fn validate_5<P: PCM>(
447    tracked r1: &mut Resource<P>,
448    tracked r2: &mut Resource<P>,
449    tracked r3: &mut Resource<P>,
450    tracked r4: &mut Resource<P>,
451    tracked r5: &Resource<P>,
452)
453    requires
454        old(r1).loc() == r5.loc(),
455        old(r2).loc() == r5.loc(),
456        old(r3).loc() == r5.loc(),
457        old(r4).loc() == r5.loc(),
458    ensures
459        r1.loc() == r5.loc(),
460        r2.loc() == r5.loc(),
461        r3.loc() == r5.loc(),
462        r4.loc() == r5.loc(),
463        r1.value() == old(r1).value(),
464        r2.value() == old(r2).value(),
465        r3.value() == old(r3).value(),
466        r4.value() == old(r4).value(),
467        P::op(
468            r1.value(),
469            P::op(r2.value(), P::op(r3.value(), P::op(r4.value(), r5.value()))),
470        ).valid(),
471{
472    lemma_pcm_properties::<P>();
473    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
474    let values: Seq<P> = seq![r1.value(), r2.value(), r3.value(), r4.value()];
475    assert(combine_values::<P>(values) == P::op(
476        old(r1).value(),
477        P::op(old(r2).value(), P::op(old(r3).value(), old(r4).value())),
478    )) by {
479        lemma_pcm_properties::<P>();
480        reveal_with_fuel(combine_values, 5);
481    }
482    m.tracked_insert(0, r1.extract());
483    m.tracked_insert(1, r2.extract());
484    m.tracked_insert(2, r3.extract());
485    m.tracked_insert(3, r4.extract());
486    validate_multiple(&mut m, values, r5);
487    let tracked mut new_r1 = m.tracked_remove(0);
488    let tracked mut new_r2 = m.tracked_remove(1);
489    let tracked mut new_r3 = m.tracked_remove(2);
490    let tracked mut new_r4 = m.tracked_remove(3);
491    tracked_swap(r1, &mut new_r1);
492    tracked_swap(r2, &mut new_r2);
493    tracked_swap(r3, &mut new_r3);
494    tracked_swap(r4, &mut new_r4);
495    assert(P::op(
496        r1.value(),
497        P::op(r2.value(), P::op(r3.value(), P::op(r4.value(), r5.value()))),
498    ).valid()) by {
499        lemma_pcm_associative::<P>();
500    }
501}
502
503} // verus!