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        final(r1).loc() == old(r1).loc(),
102        final(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        final(r).loc() == old(r).loc(),
118        other.loc() == old(r).loc(),
119        final(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        final(r).loc() == old(r).loc(),
136        final(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        final(r1).loc() == old(r1).loc(),
157        final(r2).loc() == old(r1).loc(),
158        final(r1).value() == v1,
159        final(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        final(r1).loc() == old(r1).loc(),
184        final(r2).loc() == old(r1).loc(),
185        final(r1).value() == v1,
186        final(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|
220            #![trigger final(m).dom().contains(i)]
221            0 <= i < values.len() ==> !final(m).dom().contains(i),
222        all.loc() == loc,
223        all.value() == combine_values::<P>(values.skip(offset)),
224    decreases values.len() - offset,
225{
226    assert(m.dom().contains(offset));
227    assert(m[offset].loc() == loc && m[offset].value() == values[offset]);
228    let tracked p = m.tracked_remove(offset);
229    if offset == values.len() - 1 {
230        assert(combine_values::<P>(values.skip(offset)) == values[offset]) by {
231            lemma_pcm_properties::<P>();  // needed to show that combining with unit is locentity
232            reveal_with_fuel(combine_values, 2);
233        };
234        p
235    } else {
236        assert(combine_values::<P>(values.skip(offset)) == P::op(
237            values[offset],
238            combine_values::<P>(values.skip(offset + 1)),
239        )) by {
240            assert(values[offset] == values.skip(offset)[0]);
241            assert(values.skip(offset + 1) == values.skip(offset).skip(1));
242        }
243        assert forall|i|
244            #![trigger m.dom().contains(i)]
245            offset + 1 <= i < values.len() implies m.dom().contains(i) && m[i].loc() == loc
246            && m[i].value() == values[i] by {
247            assert(m.dom().contains(i));
248            assert(m[i].loc() == loc && m[i].value() == values[i]);
249        }
250        let tracked most = aggregate_resources_from_map_starting_at_offset(
251            m,
252            loc,
253            values,
254            offset + 1,
255        );
256        assert(most.loc() == loc);
257        assert(most.value() == combine_values::<P>(values.skip(offset + 1)));
258        p.join(most)
259    }
260}
261
262// This is a helper function used by `validate_multiple_resources` but
263// not meant for public export.
264//
265// This function restores the resources in the map to their original values.
266//
267// This function is given a map `m`, and the original carrier values in values.
268// Moreover, the aggregated resource is `p`. It splits the aggregated resource and restores the
269// original resources in map
270proof fn store_resources_into_map_starting_at_offset<P: PCM>(
271    tracked m: &mut Map<int, Resource<P>>,
272    values: Seq<P>,
273    offset: int,
274    tracked p: Resource<P>,
275)
276    requires
277        0 <= offset < values.len(),
278        forall|i| #![trigger old(m).dom().contains(i)] 0 <= i < offset ==> old(m).dom().contains(i),
279        forall|i|
280            #![trigger old(m)[i]]
281            0 <= i < offset ==> old(m)[i].loc() == p.loc() && old(m)[i].value() == values[i],
282        forall|i|
283            #![trigger old(m).dom().contains(i)]
284            offset <= i < values.len() ==> !old(m).dom().contains(i),
285        p.value() == combine_values::<P>(values.skip(offset)),
286    ensures
287        forall|i|
288            #![trigger final(m).dom().contains(i)]
289            0 <= i < values.len() ==> final(m).dom().contains(i),
290        forall|i|
291            #![trigger final(m)[i]]
292            0 <= i < values.len() ==> final(m)[i].loc() == p.loc() && final(m)[i].value()
293                == values[i],
294    decreases values.len() - offset,
295{
296    assert(combine_values::<P>(values.skip(offset)) == P::op(
297        values[offset],
298        combine_values::<P>(values.skip(offset + 1)),
299    )) by {
300        assert(values[offset] == values.skip(offset)[0]);
301        assert(values.skip(offset + 1) == values.skip(offset).skip(1));
302    }
303    let tracked (p_first, p_rest) = p.split(
304        values[offset],
305        combine_values::<P>(values.skip(offset + 1)),
306    );
307    m.tracked_insert(offset, p_first);
308    if offset < values.len() - 1 {
309        store_resources_into_map_starting_at_offset(m, values, offset + 1, p_rest);
310    }
311}
312
313/// Validates that a given sequence of resources has values that
314/// combine to form a valid value. Although that sequence consists of
315/// mutable references, none of those resources change. (They change
316/// in the middle of the function, but are restored by the time it
317/// completes.) The sequence of resources is specified using the
318/// following input parameters:
319///
320/// `m` -- a map from integers to resources, mapping 0 to the first
321/// resource, 1 to the second, etc.
322///
323/// `loc` -- the `loc()` shared by all the resources in `m`
324///
325/// `values` -- the sequence of resources
326// The proof goes as follows:
327// - join all the mutable resources, with aggregate_resources_from_map_starting_at_offset
328// - validate the joined resource with the shared reference with validate_2
329// - restore the mutable resources with store_resources_into_map_starting_at_offset
330pub proof fn validate_multiple<P: PCM>(
331    tracked m: &mut Map<int, Resource<P>>,
332    values: Seq<P>,
333    tracked shared: &Resource<P>,
334)
335    requires
336        values.len() > 0,
337        forall|i|
338            #![trigger old(m).dom().contains(i)]
339            0 <= i < values.len() ==> old(m).dom().contains(i),
340        forall|i|
341            #![trigger old(m)[i]]
342            0 <= i < values.len() ==> old(m)[i].loc() == shared.loc() && old(m)[i].value()
343                == values[i],
344    ensures
345        forall|i|
346            #![trigger final(m).dom().contains(i)]
347            0 <= i < values.len() ==> final(m).dom().contains(i),
348        forall|i|
349            #![trigger final(m)[i]]
350            0 <= i < values.len() ==> final(m)[i].loc() == shared.loc() && final(m)[i].value()
351                == values[i],
352        P::op(combine_values::<P>(values), shared.value()).valid(),
353{
354    let tracked mut agg = aggregate_resources_from_map_starting_at_offset(
355        m,
356        shared.loc(),
357        values,
358        0,
359    );
360    assert(agg.value() == combine_values::<P>(values)) by {
361        assert(values == values.skip(0));
362    }
363    agg.validate_2(shared);
364    store_resources_into_map_starting_at_offset(m, values, 0, agg);
365}
366
367/// Validates that the three given resources have values that combine to form a valid value.
368/// Although `r1` and `r2` are mutable, they don't change. (They change during the function but
369/// are restored to the way they were by the time the function returns.)
370pub proof fn validate_3<P: PCM>(
371    tracked r1: &mut Resource<P>,
372    tracked r2: &mut Resource<P>,
373    tracked r3: &Resource<P>,
374)
375    requires
376        old(r1).loc() == r3.loc(),
377        old(r2).loc() == r3.loc(),
378    ensures
379        final(r1).loc() == r3.loc(),
380        final(r2).loc() == r3.loc(),
381        final(r1).value() == old(r1).value(),
382        final(r2).value() == old(r2).value(),
383        P::op(final(r1).value(), P::op(final(r2).value(), r3.value())).valid(),
384{
385    lemma_pcm_properties::<P>();
386    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
387    let values: Seq<P> = seq![r1.value(), r2.value()];
388    m.tracked_insert(0, r1.extract());
389    m.tracked_insert(1, r2.extract());
390    assert(combine_values::<P>(values) == P::op(old(r1).value(), old(r2).value())) by {
391        lemma_pcm_properties::<P>();
392        reveal_with_fuel(combine_values, 3);
393    }
394    validate_multiple(&mut m, values, r3);
395    let tracked mut new_r1 = m.tracked_remove(0);
396    let tracked mut new_r2 = m.tracked_remove(1);
397    tracked_swap(r1, &mut new_r1);
398    tracked_swap(r2, &mut new_r2);
399    assert(P::op(r1.value(), P::op(r2.value(), r3.value())).valid()) by {
400        lemma_pcm_associative::<P>();
401    };
402}
403
404/// Validates that the four given resources have values that combine to form a valid value.
405/// Although the inputs `r1`, `r2` and `r3` are mutable, they don't change. (They change during
406/// the function but are restored to the way they were by the time the function returns.)
407pub proof fn validate_4<P: PCM>(
408    tracked r1: &mut Resource<P>,
409    tracked r2: &mut Resource<P>,
410    tracked r3: &mut Resource<P>,
411    tracked r4: &Resource<P>,
412)
413    requires
414        old(r1).loc() == r4.loc(),
415        old(r2).loc() == r4.loc(),
416        old(r3).loc() == r4.loc(),
417    ensures
418        final(r1).loc() == r4.loc(),
419        final(r2).loc() == r4.loc(),
420        final(r3).loc() == r4.loc(),
421        final(r1).value() == old(r1).value(),
422        final(r2).value() == old(r2).value(),
423        final(r3).value() == old(r3).value(),
424        P::op(
425            final(r1).value(),
426            P::op(final(r2).value(), P::op(final(r3).value(), r4.value())),
427        ).valid(),
428{
429    lemma_pcm_properties::<P>();
430    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
431    let values: Seq<P> = seq![r1.value(), r2.value(), r3.value()];
432    m.tracked_insert(0, r1.extract());
433    m.tracked_insert(1, r2.extract());
434    m.tracked_insert(2, r3.extract());
435    assert(combine_values::<P>(values) == P::op(
436        old(r1).value(),
437        P::op(old(r2).value(), old(r3).value()),
438    )) by {
439        lemma_pcm_properties::<P>();
440        reveal_with_fuel(combine_values, 4);
441    }
442    validate_multiple(&mut m, values, r4);
443    let tracked mut new_r1 = m.tracked_remove(0);
444    let tracked mut new_r2 = m.tracked_remove(1);
445    let tracked mut new_r3 = m.tracked_remove(2);
446    tracked_swap(r1, &mut new_r1);
447    tracked_swap(r2, &mut new_r2);
448    tracked_swap(r3, &mut new_r3);
449    assert(P::op(r1.value(), P::op(r2.value(), P::op(r3.value(), r4.value()))).valid()) by {
450        lemma_pcm_associative::<P>();
451    }
452}
453
454/// Validates that the five given resources have values that combine to form a valid value.
455/// Although the inputs `r1`, `r2`, `r3` and `r4` are mutable, they don't change. (They change
456/// during the function but are restored to the way they were by the time the function returns.)
457pub proof fn validate_5<P: PCM>(
458    tracked r1: &mut Resource<P>,
459    tracked r2: &mut Resource<P>,
460    tracked r3: &mut Resource<P>,
461    tracked r4: &mut Resource<P>,
462    tracked r5: &Resource<P>,
463)
464    requires
465        old(r1).loc() == r5.loc(),
466        old(r2).loc() == r5.loc(),
467        old(r3).loc() == r5.loc(),
468        old(r4).loc() == r5.loc(),
469    ensures
470        final(r1).loc() == r5.loc(),
471        final(r2).loc() == r5.loc(),
472        final(r3).loc() == r5.loc(),
473        final(r4).loc() == r5.loc(),
474        final(r1).value() == old(r1).value(),
475        final(r2).value() == old(r2).value(),
476        final(r3).value() == old(r3).value(),
477        final(r4).value() == old(r4).value(),
478        P::op(
479            final(r1).value(),
480            P::op(
481                final(r2).value(),
482                P::op(final(r3).value(), P::op(final(r4).value(), r5.value())),
483            ),
484        ).valid(),
485{
486    lemma_pcm_properties::<P>();
487    let tracked mut m: Map<int, Resource<P>> = Map::<int, Resource<P>>::tracked_empty();
488    let values: Seq<P> = seq![r1.value(), r2.value(), r3.value(), r4.value()];
489    assert(combine_values::<P>(values) == P::op(
490        old(r1).value(),
491        P::op(old(r2).value(), P::op(old(r3).value(), old(r4).value())),
492    )) by {
493        lemma_pcm_properties::<P>();
494        reveal_with_fuel(combine_values, 5);
495    }
496    m.tracked_insert(0, r1.extract());
497    m.tracked_insert(1, r2.extract());
498    m.tracked_insert(2, r3.extract());
499    m.tracked_insert(3, r4.extract());
500    validate_multiple(&mut m, values, r5);
501    let tracked mut new_r1 = m.tracked_remove(0);
502    let tracked mut new_r2 = m.tracked_remove(1);
503    let tracked mut new_r3 = m.tracked_remove(2);
504    let tracked mut new_r4 = m.tracked_remove(3);
505    tracked_swap(r1, &mut new_r1);
506    tracked_swap(r2, &mut new_r2);
507    tracked_swap(r3, &mut new_r3);
508    tracked_swap(r4, &mut new_r4);
509    assert(P::op(
510        r1.value(),
511        P::op(r2.value(), P::op(r3.value(), P::op(r4.value(), r5.value()))),
512    ).valid()) by {
513        lemma_pcm_associative::<P>();
514    }
515}
516
517} // verus!