Skip to main content

creusot_std/logic/
ra.rs

1//! Definitions of Resource Algebras
2
3pub mod agree;
4pub mod auth;
5pub mod excl;
6pub mod fmap;
7mod int;
8pub mod lattice;
9mod nat;
10pub mod option;
11mod positive;
12mod positive_real;
13pub mod prod;
14pub mod sum;
15pub mod update;
16pub mod view;
17
18#[cfg(creusot)]
19use crate::logic::such_that;
20use crate::{logic::Set, prelude::*};
21
22/// Define a _Resource Algebra_.
23///
24/// Resource algebras are a concept inspired by [Iris](https://iris-project.org/). Used in
25/// conjunction with [`Resource`](crate::ghost::resource::Resource)s, they unlock new reasonings.
26///
27/// # Notes on the definition of resource algebras
28///
29/// Our definition of resource algebras differs from the one in Iris in that it
30/// does not require RAs to define a "core" function. Instead, we follow "Idempotent
31/// Resources in Separation Logic --- The Heart of core in Iris" by Gratzer, Møller &
32/// Birkedal (GMB), and require RAs to satisfy a "maximal idempotent" axiom.
33pub trait RA: Sized {
34    /// The operation of this resource algebra.
35    ///
36    /// This is the core of the trait. This operation will be used to [`join`](crate::Resource::join)
37    /// and [`split`](crate::ghost::Resource::split) resources.
38    ///
39    /// It must be [associative](Self::associative) and [commutative](Self::commutative)
40    /// (among others).
41    #[logic]
42    fn op(self, other: Self) -> Option<Self>;
43
44    // Derived notions: `factor`, `incl`, `idemp`.
45    // We allow the implementor to give a custom definition, that is possibly
46    // simpler than the generic one. The custom definition is the one that
47    // will be used to prove the RA laws.
48
49    #[logic(open, inline)]
50    #[ensures(result == (self == other))]
51    fn eq(self, other: Self) -> bool {
52        self == other
53    }
54
55    /// Inclusion of RA.
56    ///
57    /// This asserts that `other` is, in a sense, 'bigger' than `self`.
58    ///
59    /// # Notes on reflexivity
60    ///
61    /// Following Iris, our definition of `incl` is not reflexive.
62    /// We could define it to be `self == other || ...`, but doing that
63    /// loses the following desirable property for the product RA:
64    ///
65    /// ```text
66    /// (x, y).incl((x', y')) == x.incl(x') && y.incl(y').
67    /// ```
68    ///
69    /// If you need the reflexive closure of the inclusion relation, then
70    /// you can use `Some(x).incl(Some(y))`. Indeed, `incl` on the Option RA
71    /// has the following property:
72    ///
73    /// ```text
74    /// Some(x).incl(Some(y)) == (x == y || x.incl(y))
75    /// ```
76    ///
77    /// Note that the paper on the maximal idempotent axiom (GMB) uses the
78    /// reflexive definition of `incl` on paper, but not in its accompanying
79    /// Iris formalization, where it uses the non-reflexive definition (as
80    /// we do here).
81    #[logic]
82    #[ensures(result == (exists<factor> self.op(factor) == Some(other)))]
83    fn incl(self, other: Self) -> bool;
84
85    #[logic(law)]
86    #[requires(self.op(other) == Some(comb))]
87    #[ensures(self.incl(comb))]
88    fn incl_op(self, other: Self, comb: Self) {}
89
90    #[logic(open, sealed)]
91    fn incl_eq(self, other: Self) -> bool {
92        self.eq(other) || self.incl(other)
93    }
94
95    #[logic(open, sealed)]
96    fn incl_eq_op(a: Self, b: Self, x: Self) -> bool {
97        match a.op(b) {
98            None => false,
99            Some(ab) => ab.incl_eq(x),
100        }
101    }
102
103    /// Factorizing elements of the RA
104    ///
105    /// Given `a` and `c` such that `c.incl(a)`, this returns an element `b` such that `a = b.c`,
106    #[logic]
107    #[requires(factor.incl(self))]
108    #[ensures(factor.op(result) == Some(self))]
109    fn factor(self, factor: Self) -> Self {
110        such_that(|r| factor.op(r) == Some(self))
111    }
112
113    /// Ensures that we can go from `self` to `x` without making composition with the frame invalid.
114    ///
115    /// This is used in [`Resource::update`](crate::resource::Resource::update).
116    #[logic(open, sealed)]
117    fn update(self, x: Self) -> bool {
118        pearlite! {
119            forall<y: Self> self.op(y) != None ==> x.op(y) != None
120        }
121    }
122
123    /// This is used in [`Resource::update_nondet`](crate::resource::Resource::update_nondet).
124    #[logic(open, sealed)]
125    fn update_nondet(self, s: Set<Self>) -> bool {
126        pearlite! {
127            forall<y: Self> self.op(y) != None ==>
128                exists<x: Self> s.contains(x) && x.op(y) != None
129        }
130    }
131
132    // Laws
133
134    /// [`Self::op`] is commutative.
135    #[logic(law)]
136    #[ensures(a.op(b) == b.op(a))]
137    fn commutative(a: Self, b: Self);
138
139    /// [`Self::op`] is associative.
140    ///
141    /// This version uses `and_then_logic` for brevity, but is not easily used by provers.
142    /// Thus, we have [`Self::associative_none`] and  [`Self::associative_some`] as laws,
143    /// which are more friendly to provers.
144    #[logic]
145    #[ensures(a.op(b).and_then_logic(|ab: Self| ab.op(c)) == b.op(c).and_then_logic(|bc| a.op(bc)))]
146    fn associative(a: Self, b: Self, c: Self);
147
148    /// Specialized version of [`Self::associative`], in the case where `a.op(b) == None`.
149    ///
150    /// By commutativity, it also covers the case where `b.op(c) == None`.
151    #[logic(law)]
152    #[requires(a.op(b) == None)]
153    #[requires(b.op(c) == Some(bc))]
154    #[ensures(a.op(bc) == None)]
155    fn associative_none(a: Self, b: Self, c: Self, bc: Self) {
156        Self::associative(a, b, c);
157    }
158
159    /// Specialized version of [`Self::associative`], in the case where `a.op(b)` and `b.op(c)`
160    /// are both valid.
161    ///
162    /// By commutativity, it also covers the case where `b.op(c) == None`.
163    #[logic(law)]
164    #[requires(a.op(b) == Some(ab))]
165    #[requires(b.op(c) == Some(bc))]
166    #[ensures(a.op(bc) == ab.op(c))]
167    fn associative_some(a: Self, b: Self, c: Self, ab: Self, bc: Self) {
168        Self::associative(a, b, c);
169    }
170
171    /// [`RA::incl`] is transitive.
172    #[logic(law)]
173    #[requires(a.incl(b))]
174    #[requires(b.incl(c))]
175    #[ensures(a.incl(c))]
176    fn incl_transitive(a: Self, b: Self, c: Self) {
177        let _ = Self::associative;
178    }
179
180    /// The core of an element, when it exists, is included in that element,
181    /// and idempotent. Note that the statement `c.op(self) == Some(self)` is
182    /// equivalent to `c.incl(self)` for idempotent elements.
183    ///
184    /// The specification of this function is not part of an ensures clause,
185    /// because it has a tendency to make the provers loop.
186    #[logic]
187    fn core(self) -> Option<Self>;
188
189    /// The specification of [`core`].
190    #[logic]
191    #[requires(self.core() != None)]
192    #[ensures({
193        let c = self.core().unwrap_logic();
194        c.op(c) == Some(c)
195    })]
196    #[ensures(self.core().unwrap_logic().op(self) == Some(self))]
197    fn core_idemp(self);
198
199    /// The core maximal among idempotent elements included in self
200    #[logic]
201    #[requires(i.op(i) == Some(i))]
202    #[requires(i.op(self) == Some(self))]
203    #[ensures(match self.core() {
204        Some(c) => i.incl(c),
205        None => false,
206    })]
207    fn core_is_maximal_idemp(self, i: Self);
208
209    /// Cancelation of resource algebra elements.
210    ///
211    /// An element `e` is said to be _cancelable_ if it can be removed from a
212    /// composition: `∀ x y , e · x = e · y → x = y`.
213    #[logic]
214    #[ensures(result == (forall<x, y> self.op(x) != None ==>
215        self.op(x) == self.op(y) ==> x == y))]
216    fn cancelable(self) -> bool {
217        pearlite! { forall<x, y> self.op(x) != None ==>
218            self.op(x) == self.op(y) ==> x == y
219        }
220    }
221}
222
223/// Unitary RAs are RA with a neutral element.
224pub trait UnitRA: RA {
225    /// The unit element
226    #[logic]
227    #[ensures(forall<x: Self> #[trigger(x.op(result))] x.op(result) == Some(x))]
228    fn unit() -> Self;
229
230    /// In unitary RAs, the inclusion relation is reflexive
231    #[logic(law)]
232    #[ensures(forall<x: Self> x.incl(x))]
233    fn incl_refl() {
234        let _ = Self::unit();
235    }
236
237    /// In unitary RAs, the core is a total function. For better automation, it
238    /// is given a simpler, total definition.
239    #[logic(open)]
240    #[ensures(self.core() == Some(result))]
241    fn core_total(self) -> Self {
242        self.core_is_maximal_idemp(Self::unit());
243        self.core().unwrap_logic()
244    }
245
246    /// The specification of [`core_total`]
247    #[logic]
248    #[ensures(self.core_total().op(self.core_total()) == Some(self.core_total()))]
249    #[ensures(self.core_total().op(self) == Some(self))]
250    fn core_total_idemp(self);
251
252    /// The unit is its own core
253    #[logic(law)]
254    #[ensures(Self::unit().core_total() == Self::unit())]
255    fn unit_core() {
256        Self::unit().core_idemp()
257    }
258}