vstd/
cell.rs

1#![allow(deprecated)]
2#![allow(unused_imports)]
3
4use core::cell::UnsafeCell;
5use core::marker;
6use core::{mem, mem::MaybeUninit};
7
8use super::invariant::*;
9use super::modes::*;
10use super::pervasive::*;
11use super::prelude::*;
12pub use super::raw_ptr::MemContents;
13use super::set::*;
14use super::*;
15
16verus! {
17
18broadcast use {super::map::group_map_axioms, super::set::group_set_axioms};
19// TODO implement: borrow_mut; figure out Drop, see if we can avoid leaking?
20
21/// `PCell<V>` (which stands for "permissioned call") is the primitive Verus `Cell` type.
22///
23/// Technically, it is a wrapper around
24/// `core::cell::UnsafeCell<core::mem::MaybeUninit<V>>`, and thus has the same runtime
25/// properties: there are no runtime checks (as there would be for Rust's traditional
26/// [`core::cell::RefCell`](https://doc.rust-lang.org/core/cell/struct.RefCell.html)).
27/// Its data may be uninitialized.
28///
29/// Furthermore (and unlike both
30/// [`core::cell::Cell`](https://doc.rust-lang.org/core/cell/struct.Cell.html) and
31/// [`core::cell::RefCell`](https://doc.rust-lang.org/core/cell/struct.RefCell.html)),
32/// a `PCell<V>` may be `Sync` (depending on `V`).
33/// Thanks to verification, Verus ensures that access to the cell is data-race-free.
34///
35/// `PCell` uses a _ghost permission token_ similar to [`simple_pptr::PPtr`] -- see the [`simple_pptr::PPtr`]
36/// documentation for the basics.
37/// For `PCell`, the associated type of the permission token is [`cell::PointsTo`].
38///
39/// ### Differences from `PPtr`.
40///
41/// The key difference is that, whereas [`simple_pptr::PPtr`] represents a fixed address in memory,
42/// a `PCell` has _no_ fixed address because a `PCell` might be moved.
43/// As such, the [`pcell.id()`](PCell::id) does not correspond to a memory address; rather,
44/// it is a unique identifier that is fixed for a given cell, even when it is moved.
45///
46/// The arbitrary ID given by [`pcell.id()`](PCell::id) is of type [`CellId`].
47/// Despite the fact that it is, in some ways, "like a pointer", note that
48/// `CellId` does not support any meangingful arithmetic,
49/// has no concept of a "null ID",
50/// and has no runtime representation.
51///
52/// Also note that the `PCell` might be dropped before the `PointsTo` token is dropped,
53/// although in that case it will no longer be possible to use the `PointsTo` in `exec` code
54/// to extract data from the cell.
55///
56/// ### Example (TODO)
57#[verifier::external_body]
58#[verifier::accept_recursive_types(V)]
59pub struct PCell<V> {
60    ucell: UnsafeCell<MaybeUninit<V>>,
61}
62
63/// `PCell` is _always_ safe to `Send` or `Sync`. Rather, it is the [`PointsTo`] object where `Send` and `Sync` matter.
64/// (It doesn't matter if you move the bytes to another thread if you can't access them.)
65#[verifier::external]
66unsafe impl<T> Sync for PCell<T> {
67
68}
69
70#[verifier::external]
71unsafe impl<T> Send for PCell<T> {
72
73}
74
75/// Permission object associated with a [`PCell<V>`].
76///
77/// See the documentation of [`PCell<V>`] for more information.
78// PointsTo<V>, on the other hand, needs to inherit both Send and Sync from the V,
79// which it does by default in the given definition.
80// (Note: this depends on the current behavior that #[verifier::spec] fields are still counted for marker traits)
81#[verifier::external_body]
82#[verifier::reject_recursive_types_in_ground_variants(V)]
83pub tracked struct PointsTo<V> {
84    phantom: marker::PhantomData<V>,
85    no_copy: NoCopy,
86}
87
88pub ghost struct PointsToData<V> {
89    pub pcell: CellId,
90    #[cfg_attr(not(verus_verify_core), deprecated = "use `pcell_points!`, or `mem_contents()` instead")]
91    pub value: Option<V>,
92}
93
94#[doc(hidden)]
95pub open spec fn option_from_mem_contents<V>(val: MemContents<V>) -> Option<V> {
96    match val {
97        MemContents::Init(v) => Some(v),
98        MemContents::Uninit => None,
99    }
100}
101
102#[doc(hidden)]
103#[macro_export]
104macro_rules! pcell_opt_internal {
105    [$pcell:expr => $val:expr] => {
106        $crate::vstd::cell::PointsToData {
107            pcell: $pcell,
108            value: $val,
109        }
110    };
111}
112
113#[cfg_attr(not(verus_verify_core), deprecated = "use pcell_points! instead")]
114#[macro_export]
115macro_rules! pcell_opt {
116    [$($tail:tt)*] => {
117        ::verus_builtin_macros::verus_proof_macro_exprs!(
118            $crate::vstd::cell::pcell_opt_internal!($($tail)*)
119        )
120    }
121}
122
123pub use pcell_opt_internal;
124pub use pcell_opt;
125
126#[doc(hidden)]
127#[macro_export]
128macro_rules! pcell_points_internal {
129    [$pcell:expr => $val:expr] => {
130        $crate::vstd::cell::PointsToData {
131            pcell: $pcell,
132            value: $crate::vstd::cell::option_from_mem_contents($val),
133        }
134    };
135}
136
137#[macro_export]
138macro_rules! pcell_points {
139    [$($tail:tt)*] => {
140        ::verus_builtin_macros::verus_proof_macro_exprs!(
141            $crate::vstd::cell::pcell_points_internal!($($tail)*)
142        )
143    }
144}
145
146pub use pcell_points_internal;
147pub use pcell_points;
148
149#[verifier::external_body]
150pub struct CellId {
151    id: int,
152}
153
154impl<V> PointsTo<V> {
155    /// The [`CellId`] of the [`PCell`] this permission is associated with.
156    pub uninterp spec fn id(&self) -> CellId;
157
158    /// The contents of the cell, either unitialized or initialized to some `V`.
159    pub uninterp spec fn mem_contents(&self) -> MemContents<V>;
160
161    pub open spec fn view(self) -> PointsToData<V> {
162        PointsToData { pcell: self.id(), value: option_from_mem_contents(self.mem_contents()) }
163    }
164
165    #[cfg_attr(not(verus_verify_core), deprecated = "use mem_contents() instead")]
166    pub open spec fn opt_value(&self) -> Option<V> {
167        match self.mem_contents() {
168            MemContents::Init(value) => Some(value),
169            MemContents::Uninit => None,
170        }
171    }
172
173    /// Is this cell initialized?
174    #[verifier::inline]
175    pub open spec fn is_init(&self) -> bool {
176        self.mem_contents().is_init()
177    }
178
179    /// Is this cell uninitialized?
180    #[verifier::inline]
181    pub open spec fn is_uninit(&self) -> bool {
182        self.mem_contents().is_uninit()
183    }
184
185    /// Value of the cell (if initialized)
186    #[verifier::inline]
187    pub open spec fn value(&self) -> V
188        recommends
189            self.is_init(),
190    {
191        self.mem_contents().value()
192    }
193}
194
195impl<V> PCell<V> {
196    /// A unique ID for the cell.
197    pub uninterp spec fn id(&self) -> CellId;
198
199    /// Return an empty ("uninitialized") cell.
200    #[inline(always)]
201    #[verifier::external_body]
202    pub const fn empty() -> (pt: (PCell<V>, Tracked<PointsTo<V>>))
203        ensures
204            pt.1@@ === pcell_points![ pt.0.id() => MemContents::Uninit ],
205    {
206        let p = PCell { ucell: UnsafeCell::new(MaybeUninit::uninit()) };
207        (p, Tracked::assume_new())
208    }
209
210    #[inline(always)]
211    #[verifier::external_body]
212    pub const fn new(v: V) -> (pt: (PCell<V>, Tracked<PointsTo<V>>))
213        ensures
214            pt.1@@ === pcell_points! [ pt.0.id() => MemContents::Init(v) ],
215    {
216        let p = PCell { ucell: UnsafeCell::new(MaybeUninit::new(v)) };
217        (p, Tracked::assume_new())
218    }
219
220    #[inline(always)]
221    #[verifier::external_body]
222    pub fn put(&self, Tracked(perm): Tracked<&mut PointsTo<V>>, v: V)
223        requires
224            old(perm)@ === pcell_points![ self.id() => MemContents::Uninit ],
225        ensures
226            perm@ === pcell_points![ self.id() => MemContents::Init(v) ],
227        opens_invariants none
228        no_unwind
229    {
230        unsafe {
231            *(self.ucell.get()) = MaybeUninit::new(v);
232        }
233    }
234
235    #[inline(always)]
236    #[verifier::external_body]
237    pub fn take(&self, Tracked(perm): Tracked<&mut PointsTo<V>>) -> (v: V)
238        requires
239            self.id() === old(perm)@.pcell,
240            old(perm).is_init(),
241        ensures
242            perm.id() === old(perm)@.pcell,
243            perm.mem_contents() === MemContents::Uninit,
244            v === old(perm).value(),
245        opens_invariants none
246        no_unwind
247    {
248        unsafe {
249            let mut m = MaybeUninit::uninit();
250            mem::swap(&mut m, &mut *self.ucell.get());
251            m.assume_init()
252        }
253    }
254
255    #[inline(always)]
256    #[verifier::external_body]
257    pub fn replace(&self, Tracked(perm): Tracked<&mut PointsTo<V>>, in_v: V) -> (out_v: V)
258        requires
259            self.id() === old(perm)@.pcell,
260            old(perm).is_init(),
261        ensures
262            perm.id() === old(perm)@.pcell,
263            perm.mem_contents() === MemContents::Init(in_v),
264            out_v === old(perm).value(),
265        opens_invariants none
266        no_unwind
267    {
268        unsafe {
269            let mut m = MaybeUninit::new(in_v);
270            mem::swap(&mut m, &mut *self.ucell.get());
271            m.assume_init()
272        }
273    }
274
275    // The reason for the the lifetime parameter 'a is
276    // that `self` actually contains the data in its interior, so it needs
277    // to outlive the returned borrow.
278    #[inline(always)]
279    #[verifier::external_body]
280    pub fn borrow<'a>(&'a self, Tracked(perm): Tracked<&'a PointsTo<V>>) -> (v: &'a V)
281        requires
282            self.id() === perm@.pcell,
283            perm.is_init(),
284        ensures
285            *v === perm.value(),
286        opens_invariants none
287        no_unwind
288    {
289        unsafe { (*self.ucell.get()).assume_init_ref() }
290    }
291
292    //////////////////////////////////
293    // Untrusted functions below here
294    #[inline(always)]
295    pub fn into_inner(self, Tracked(perm): Tracked<PointsTo<V>>) -> (v: V)
296        requires
297            self.id() === perm@.pcell,
298            perm.is_init(),
299        ensures
300            v === perm.value(),
301        opens_invariants none
302        no_unwind
303    {
304        let tracked mut perm = perm;
305        self.take(Tracked(&mut perm))
306    }
307    // TODO this should replace the external_body implementation of `new` above;
308    // however it requires unstable features: const_mut_refs and const_refs_to_cell
309    //#[inline(always)]
310    //pub const fn new(v: V) -> (pt: (PCell<V>, Tracked<PointsTo<V>>))
311    //    ensures (pt.1@@ === PointsToData{ pcell: pt.0.id(), value: MemContents::Init(v) }),
312    //{
313    //    let (p, Tracked(mut t)) = Self::empty();
314    //    p.put(Tracked(&mut t), v);
315    //    (p, Tracked(t))
316    //}
317
318}
319
320impl<V: Copy> PCell<V> {
321    #[inline(always)]
322    #[verifier::external_body]
323    pub fn write(&self, Tracked(perm): Tracked<&mut PointsTo<V>>, in_v: V)
324        requires
325            self.id() === old(perm)@.pcell,
326            old(perm).is_init(),
327        ensures
328            perm.id() === old(perm)@.pcell,
329            perm.mem_contents() === MemContents::Init(in_v),
330        opens_invariants none
331        no_unwind
332    {
333        let _out = self.replace(Tracked(&mut *perm), in_v);
334    }
335}
336
337struct InvCellPred {}
338
339impl<T> InvariantPredicate<(Set<T>, PCell<T>), PointsTo<T>> for InvCellPred {
340    closed spec fn inv(k: (Set<T>, PCell<T>), perm: PointsTo<T>) -> bool {
341        let (possible_values, pcell) = k;
342        {
343            &&& perm.is_init()
344            &&& possible_values.contains(perm.value())
345            &&& pcell.id() === perm@.pcell
346        }
347    }
348}
349
350#[verifier::reject_recursive_types(T)]
351pub struct InvCell<T> {
352    possible_values: Ghost<Set<T>>,
353    pcell: PCell<T>,
354    perm_inv: Tracked<LocalInvariant<(Set<T>, PCell<T>), PointsTo<T>, InvCellPred>>,
355}
356
357impl<T> InvCell<T> {
358    #[verifier::type_invariant]
359    closed spec fn wf(&self) -> bool {
360        &&& self.perm_inv@.constant() === (self.possible_values@, self.pcell)
361    }
362
363    pub closed spec fn inv(&self, val: T) -> bool {
364        &&& self.possible_values@.contains(val)
365    }
366
367    pub fn new(val: T, Ghost(f): Ghost<spec_fn(T) -> bool>) -> (cell: Self)
368        requires
369            f(val),
370        ensures
371            forall|v| f(v) <==> cell.inv(v),
372    {
373        let (pcell, Tracked(perm)) = PCell::new(val);
374        let ghost possible_values = Set::new(f);
375        let tracked perm_inv = LocalInvariant::new((possible_values, pcell), perm, 0);
376        InvCell { possible_values: Ghost(possible_values), pcell, perm_inv: Tracked(perm_inv) }
377    }
378}
379
380impl<T> InvCell<T> {
381    pub fn replace(&self, val: T) -> (old_val: T)
382        requires
383            self.inv(val),
384        ensures
385            self.inv(old_val),
386    {
387        proof {
388            use_type_invariant(self);
389        }
390        let r;
391        open_local_invariant!(self.perm_inv.borrow() => perm => {
392            r = self.pcell.replace(Tracked(&mut perm), val);
393        });
394        r
395    }
396}
397
398impl<T: Copy> InvCell<T> {
399    pub fn get(&self) -> (val: T)
400        ensures
401            self.inv(val),
402    {
403        proof {
404            use_type_invariant(self);
405        }
406        let r;
407        open_local_invariant!(self.perm_inv.borrow() => perm => {
408            r = *self.pcell.borrow(Tracked(&perm));
409        });
410        r
411    }
412}
413
414} // verus!