Skip to main content

ostd/mm/frame/
linked_list.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Enabling linked lists of frames without heap allocation.
3//!
4//! This module leverages the customizability of the metadata system (see
5//! [super::meta]) to allow any type of frame to be used in a linked list.
6use vstd::prelude::*;
7
8use vstd::seq_lib::*;
9use vstd::simple_pptr::*;
10
11use vstd_extra::cast_ptr::*;
12use vstd_extra::drop_tracking::{Drop, DropObligation, TrackDrop};
13use vstd_extra::ownership::*;
14
15use crate::mm::frame::meta::{
16    META_SLOT_SIZE, REF_COUNT_UNIQUE,
17    mapping::{frame_to_meta, meta_to_frame},
18};
19use crate::mm::kspace::FRAME_METADATA_RANGE;
20use crate::specs::arch::*;
21use crate::specs::mm::frame::{
22    linked_list::linked_list_owners::*,
23    mapping::{frame_to_index, group_page_meta, index_to_meta, meta_to_index},
24    meta_owners::{
25        MetaSlotOwner, MetaSlotStorage, borrow_meta, borrow_meta_mut, typed_meta_value,
26        typed_meta_wf,
27    },
28    meta_region_owners::MetaRegionOwners,
29    unique::UniqueFrameOwner,
30};
31
32use super::{
33    MetaSlot, mapping,
34    meta::{AnyFrameMeta, get_slot},
35    unique::UniqueFrame,
36};
37use crate::{
38    arch::mm::PagingConsts,
39    mm::{Paddr, Vaddr},
40    //panic::abort,
41};
42use core::{
43    ops::{Deref, DerefMut},
44    ptr::NonNull,
45    sync::atomic::{AtomicU64, Ordering},
46};
47
48verus! {
49
50/// A linked list of frames.
51///
52/// Two key features that [`LinkedList`] is different from
53/// [`alloc::collections::LinkedList`] is that:
54///  1. It is intrusive, meaning that the links are part of the frame metadata.
55///     This allows the linked list to be used without heap allocation. But it
56///     disallows a frame to be in multiple linked lists at the same time.
57///  2. The linked list exclusively own the frames, meaning that it takes
58///     unique pointers [`UniqueFrame`]. And other bodies cannot
59///     [`from_in_use`] a frame that is inside a linked list.
60///  3. We also allow creating cursors at a specific frame, allowing $O(1)$
61///     removal without iterating through the list at a cost of some checks.
62///
63/// # Example
64///
65/// To create metadata types that allows linked list links, wrap the metadata
66/// type in [`Link`]:
67///
68/// ```rust
69/// use ostd::{
70///     mm::{frame::{linked_list::{Link, LinkedList}, Frame}, FrameAllocOptions},
71///     impl_untyped_frame_meta_for,
72/// };
73///
74/// #[derive(Debug)]
75/// struct MyMeta { mark: usize }
76///
77/// type MyFrame = Frame<Link<MyMeta>>;
78///
79/// impl_untyped_frame_meta_for!(MyMeta);
80///
81/// let alloc_options = FrameAllocOptions::new();
82/// let frame1 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 1 })).unwrap();
83/// let frame2 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 2 })).unwrap();
84///
85/// let mut list = LinkedList::new();
86/// list.push_front(frame1.try_into().unwrap());
87/// list.push_front(frame2.try_into().unwrap());
88///
89/// let mut cursor = list.cursor_front_mut();
90/// assert_eq!(cursor.current_meta().unwrap().mark, 2);
91/// cursor.move_next();
92/// assert_eq!(cursor.current_meta().unwrap().mark, 1);
93/// ```
94///
95/// [`from_in_use`]: super::Frame::from_in_use
96///
97/// # Verified Properties
98/// ## Verification Design
99/// The linked list is abstractly represented by a [`LinkedListOwner`]:
100/// ```rust
101/// tracked struct LinkedListOwner<M: AnyFrameMeta + Repr<MetaSlotStorage>> {
102///     pub list: Seq<LinkOwner>,
103///     pub list_id: u64,
104/// }
105/// ```
106/// The raw slot and storage permissions for each link are parked in the global
107/// [`MetaRegionOwners`], while [`LinkedListOwner`] owns the corresponding
108/// type-specific `Link<M>::ReprPerm`. Cursor accessors borrow these independent
109/// components together when projecting a `Link<M>`.
110/// ## Invariant
111/// The linked list uniquely owns the raw frames that it contains, so they cannot be used by other
112/// data structures. The frame metadata field `in_list` is equal to `list_id` for all links in the list.
113/// The per-link well-formedness against the region (pointer/permission wiring,
114/// `next`/`prev` pointer chain) is captured by
115/// [`LinkedListOwner::relate_region`] (opaque, with per-position
116/// [`LinkedListOwner::relate_region_at`]). The cursor exposes this via
117/// [`CursorOwner::wf_with_region`] and [`CursorMut::wf_region`].
118/// ## Safety
119/// A given linked list can only have one cursor at a time, so there are no data races.
120/// The `prev` and `next` fields of the metadata for each link always points to valid
121/// links in the list, so the structure is memory safe (will not read or write invalid memory).
122pub struct LinkedList<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
123    pub front: Option<ReprPtr<MetaSlotStorage, Link<M>>>,
124    pub back: Option<ReprPtr<MetaSlotStorage, Link<M>>>,
125    /// The number of frames in the list.
126    pub size: usize,
127    /// A lazily initialized ID, used to check whether a frame is in the list.
128    /// 0 means uninitialized.
129    pub list_id: u64,
130}
131
132/// A cursor that can mutate the linked list links.
133///
134/// The cursor points to either a frame or the "ghost" non-element. It points
135/// to the "ghost" non-element when the cursor surpasses the back of the list.
136pub struct CursorMut<'a, M: AnyFrameMeta + Repr<MetaSlotSmall>> {
137    pub list: &'a mut LinkedList<M>,
138    pub current: Option<ReprPtr<MetaSlotStorage, Link<M>>>,
139}
140
141#[verifier::spinoff_prover]
142proof fn lemma_meta_region_inv_at(regions: MetaRegionOwners, i: int)
143    requires
144        regions.inv(),
145        regions.contains(i),
146    ensures
147        regions.slot_owners[i].inv(),
148        regions.slots[i].is_init(),
149        regions.slots[i].addr() == index_to_meta(i),
150        regions.slots[i].value().wf(regions.slot_owners[i]),
151        regions.slot_owners[i].slot_vaddr == regions.slots[i].addr(),
152{
153}
154
155/// Localizes the "no existing slot aliases the inserted frame" universal fact
156/// into its own prover query, so the `MetaRegionOwners::inv` and map-insert
157/// quantifiers do not get over-instantiated inside `insert_before`'s body.
158#[verifier::spinoff_prover]
159proof fn lemma_insert_before_slot_distinct<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
160    owner0: LinkedListOwner<M>,
161    regions0: MetaRegionOwners,
162    frame_idx: int,
163    nn: int,
164)
165    requires
166        owner0.relate_region(regions0),
167        regions0.inv(),
168        regions0.contains(frame_idx),
169        regions0.slot_owners[frame_idx].in_list_perm.value() == 0,
170        0 <= nn <= owner0.list.len() as int,
171    ensures
172        forall|p: int|
173            #![trigger regions0.slot_owners[meta_to_index(owner0.list[p].paddr)]]
174            (0 <= p < owner0.list.len() as int) ==> frame_idx != meta_to_index(
175                owner0.list[p].paddr,
176            ),
177{
178    assert forall|p: int|
179        #![trigger regions0.slot_owners[meta_to_index(owner0.list[p].paddr)]]
180        0 <= p < owner0.list.len() as int implies frame_idx != meta_to_index(
181        owner0.list[p].paddr,
182    ) by {
183        owner0.relate_region_at_facts(regions0, p);
184        if frame_idx == meta_to_index(owner0.list[p].paddr) {
185            assert(regions0.slot_owners[meta_to_index(owner0.list[p].paddr)].in_list_perm.value()
186                == owner0.list_id);
187        }
188    }
189}
190
191impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M> {
192    /// Creates a new linked list.
193    pub const fn new() -> Self {
194        Self { front: None, back: None, size: 0, list_id: 0 }
195    }
196}
197
198impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Default for LinkedList<M> {
199    fn default() -> Self {
200        Self::new()
201    }
202}
203
204#[verus_verify]
205impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M> {
206    /// Gets the number of frames in the linked list.
207    #[verus_spec(s =>
208        with
209            Tracked(owner): Tracked<LinkedListOwner<M>>,
210        requires
211            self.wf(owner),
212            owner.inv(),
213        ensures
214            s == owner@.list.len(),
215    )]
216    pub fn size(&self) -> usize {
217        proof {
218            LinkedListOwner::<M>::view_preserves_len(owner.list);
219        }
220        self.size
221    }
222
223    /// Tells if the linked list is empty.
224    #[verus_spec(b =>
225        with
226            Tracked(owner): Tracked<LinkedListOwner<M>>,
227        requires
228            self.wf(owner),
229            owner.inv(),
230        ensures
231            b ==> self.size == 0 && self.front is None && self.back is None,
232            !b ==> self.size > 0 && self.front is Some && self.back is Some,
233    )]
234    pub fn is_empty(&self) -> bool {
235        let is_empty = self.size == 0;
236        is_empty
237    }
238
239    /// Pushes a frame to the front of the linked list.
240    /// # Verified Properties
241    /// ## Preconditions
242    /// The list must be well-formed, with the pointers to its links' metadata slots
243    /// matching the tracked permission objects. The new frame must be active, so that it is
244    /// valid to call `into_raw` on it inside of `insert_before`.
245    /// ## Postconditions
246    /// The new frame is inserted at the front of the list, and the cursor is moved to the new frame.
247    /// The list invariants are preserved.
248    /// ## Safety
249    /// See [`insert_before`] for the safety guarantees.
250    #[verus_spec(
251        with
252            Tracked(regions): Tracked<&mut MetaRegionOwners>,
253            Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
254            Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>,
255        requires
256            old(self).wf_region(*old(owner), *old(regions)),
257            old(owner).relate_region(*old(regions)),
258            old(frame_own).inv(),
259            old(frame_own).global_inv(*old(regions)),
260            frame.wf(*old(frame_own)),
261            old(frame_own).frame_link_inv(*old(regions)),
262            old(regions).inv(),
263        ensures
264            final(owner).relate_region(*final(regions)),
265            final(regions).inv(),
266            final(owner).list == old(owner).list.insert(0, final(frame_own).meta_own),
267            old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
268            final(owner).list_id != 0,
269            final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
270            final(frame_own).meta_own.in_list == final(owner).list_id,
271    )]
272    pub fn push_front(&mut self, frame: UniqueFrame<Link<M>>) {
273        let current = self.front;
274        let tracked owner0 = LinkedListOwner::tracked_take(owner);
275        let tracked mut cursor_own = CursorOwner::tracked_front_owner(owner0);
276        let mut cursor = CursorMut { list: self, current };
277
278        #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own), Tracked(frame_own))]
279        cursor.insert_before(frame);
280
281        proof {
282            *owner = cursor_own.list_own;
283        }
284    }
285
286    /// Pops a frame from the front of the linked list.
287    /// # Verified Properties
288    /// ## Preconditions
289    /// The list must be well-formed, with the pointers to its links' metadata slots
290    /// matching the tracked permission objects. The list must be non-empty, so that the
291    /// current frame is valid.
292    /// ## Postconditions
293    /// The front frame is removed from the list, and the cursor is moved to the next frame.
294    /// The list invariants are preserved.
295    /// ## Safety
296    /// See [`take_current`] for the safety guarantees.
297    #[verus_spec(r =>
298        with
299            Tracked(regions): Tracked<&mut MetaRegionOwners>,
300            Tracked(owner): Tracked<LinkedListOwner<M>>,
301            Tracked(frame_own): Tracked<UniqueFrameOwner<Link<M>>>,
302        requires
303            old(regions).inv(),
304            old(self).wf_region(owner, *old(regions)),
305            owner.relate_region(*old(regions)),
306        ensures
307            owner.list.len() == 0 ==> r.is_none(),
308            r.is_some() ==> (r->0).1@@.meta == owner.list[0]@,
309            r.is_some() ==> (r->0).1@.frame_link_inv(*final(regions)),
310    )]
311    pub fn pop_front(&mut self) -> Option<
312        (UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>),
313    > {
314        let tracked mut cursor_own = CursorOwner::tracked_front_owner(owner);
315        let current = self.front;
316        let mut cursor = CursorMut { list: self, current };
317
318        proof {
319            if owner.list.len() > 0 {
320                owner.relate_region_at_facts(*regions, 0);
321            }
322        }
323
324        #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own))]
325        cursor.take_current()
326    }
327
328    /// Pushes a frame to the back of the linked list.
329    /// # Verified Properties
330    /// ## Preconditions
331    /// The list must be well-formed, with the pointers to its links' metadata slots
332    /// matching the tracked permission objects. The new frame must be active, so that it is
333    /// valid to call `into_raw` on it inside of `insert_before`.
334    /// ## Postconditions
335    /// - The new frame is inserted at the back of the list, and the cursor is moved to the new frame.
336    /// - The list invariants are preserved.
337    /// ## Safety
338    /// See [`insert_before`] for the safety guarantees.
339    #[verus_spec(
340        with
341            Tracked(regions): Tracked<&mut MetaRegionOwners>,
342            Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
343            Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>,
344        requires
345            old(self).wf_region(*old(owner), *old(regions)),
346            old(owner).relate_region(*old(regions)),
347            old(frame_own).inv(),
348            old(frame_own).global_inv(*old(regions)),
349            frame.wf(*old(frame_own)),
350            old(frame_own).frame_link_inv(*old(regions)),
351            old(regions).inv(),
352        ensures
353            final(owner).relate_region(*final(regions)),
354            final(regions).inv(),
355            old(owner).list.len() > 0 ==> final(owner).list == old(owner).list.insert(
356                old(owner).list.len() - 1, final(frame_own).meta_own),
357            old(owner).list.len() == 0 ==> final(owner).list == old(owner).list.insert(
358                0, final(frame_own).meta_own),
359            // Id preserved when already minted; a fresh (empty) list adopts a
360            // non-zero id.
361            old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
362            final(owner).list_id != 0,
363            final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
364            final(frame_own).meta_own.in_list == final(owner).list_id,
365    )]
366    pub fn push_back(&mut self, frame: UniqueFrame<Link<M>>) {
367        let current = self.back;
368        let tracked owner0 = LinkedListOwner::tracked_take(owner);
369        let tracked mut cursor_own = CursorOwner::tracked_back_owner(owner0);
370        let mut cursor = CursorMut { list: self, current };
371
372        #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own), Tracked(frame_own))]
373        cursor.insert_before(frame);
374
375        proof {
376            *owner = cursor_own.list_own;
377        }
378    }
379
380    /// Pops a frame from the back of the linked list.
381    /// # Verified Properties
382    /// ## Preconditions
383    /// - The list must be well-formed, with the pointers to its links' metadata slots
384    /// matching the tracked permission objects.
385    /// - The list must be non-empty, so that the
386    /// current frame is valid.
387    /// ## Postconditions
388    /// - The back frame is removed from the list, and the cursor is moved to the "ghost" non-element.
389    /// - The list invariants are preserved.
390    /// ## Safety
391    /// See [`take_current`] for the safety guarantees.
392    #[verus_spec(r =>
393        with
394            Tracked(regions): Tracked<&mut MetaRegionOwners>,
395            Tracked(owner): Tracked<LinkedListOwner<M>>,
396            Tracked(frame_own): Tracked<UniqueFrameOwner<Link<M>>>,
397        requires
398            old(regions).inv(),
399            old(self).wf_region(owner, *old(regions)),
400            owner.relate_region(*old(regions)),
401        ensures
402            owner.list.len() == 0 ==> r.is_none(),
403            r.is_some() ==> (r->0).1@@.meta == owner.list[owner.list.len() - 1]@,
404            r.is_some() ==> (r->0).1@.frame_link_inv(*final(regions)),
405    )]
406    pub fn pop_back(&mut self) -> Option<
407        (UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>),
408    > {
409        let current = self.back;
410        let tracked mut cursor_own = CursorOwner::tracked_back_owner(owner);
411        let mut cursor = CursorMut { list: self, current };
412
413        proof {
414            if owner.list.len() > 0 {
415                owner.relate_region_at_facts(*regions, owner.list.len() - 1);
416            }
417        }
418
419        #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own))]
420        cursor.take_current()
421    }
422
423    /// Tells if a frame is in the list.
424    /// # Verified Properties
425    /// ## Preconditions
426    /// - The list must be well-formed, with the pointers to its links' metadata slots
427    /// matching the tracked permission objects.
428    /// - The frame must be a valid, active frame.
429    /// ## Postconditions
430    /// The function returns `true` if the frame is in the list, `false` otherwise.
431    /// ## Safety
432    /// - `lazy_get_id` uses atomic memory accesses, so there are no data races.
433    /// - We assume that the ID allocator has an available ID if the list previously didn't have one,
434    /// but the consequence if that is not the case is a failsafe panic.
435    /// - Everything else conforms to the safe interface.
436    #[verus_spec(r =>
437        with
438            Tracked(regions): Tracked<&mut MetaRegionOwners>,
439            Tracked(slot_own): Tracked<&MetaSlotOwner>,
440            Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
441        requires
442            slot_own.inv(),
443            old(regions).inv(),
444        ensures
445            old(owner).list_id != 0 ==> *final(owner) == *old(owner),
446    )]
447    pub fn contains(&mut self, frame: Paddr) -> bool {
448        proof_decl! {
449        let ghost idx = frame_to_index(frame);
450            if valid_frame_paddr(frame) {
451                regions.lemma_contains_valid_frame_paddr(frame);
452            }
453        let tracked slot_perm = if valid_frame_paddr(frame) {
454            Some(*regions.slots.tracked_borrow(idx))
455        } else {
456            None
457        };
458        }
459        let Ok(slot) = (#[verus_spec(with Tracked(slot_perm))]
460        crate::mm::frame::meta::get_slot(frame)) else {
461            return false;
462        };
463
464        let tracked mut slot_own = regions.slot_owners.tracked_borrow_mut(idx);
465
466        slot.in_list.load(Tracked(&mut slot_own.in_list_perm)) == #[verus_spec(with Tracked(owner))]
467        self.lazy_get_id()
468    }
469
470    /// Gets a cursor at the specified frame if the frame is in the list.
471    ///
472    /// This method fails if the frame is not in the list.
473    /// # Verified Properties
474    /// ## Preconditions
475    /// - The list must be well-formed, with the pointers to its links' metadata slots
476    /// matching the tracked permission objects.
477    /// - The frame should be raw (because it is owned by the list)
478    /// ## Postconditions
479    /// - This functions post-conditions are incomplete due to refactoring of the permission model.
480    /// When complete, it will guarantee that the cursor is well-formed and points to the matching
481    /// element in the list.
482    /// ## Safety
483    /// - `lazy_get_id` uses atomic memory accesses, so there are no data races.
484    /// - We assume that the ID allocator has an available ID if the list previously didn't have one,
485    /// but the consequence if that is not the case is a failsafe panic.
486    /// - Everything else conforms to the safe interface.
487    #[verus_spec(r =>
488        with
489            Tracked(regions): Tracked<&mut MetaRegionOwners>,
490            Tracked(owner): Tracked<LinkedListOwner<M>>,
491            -> cursor_owner: Tracked<Option<CursorOwner<M>>>,
492        requires
493            old(regions).inv(),
494        ensures
495            !valid_frame_paddr(frame) ==> r is None,
496            final(regions).inv(),
497            final(regions).slots == old(regions).slots,
498            final(regions).slot_owners.dom() == old(regions).slot_owners.dom(),
499    )]
500    pub fn cursor_mut_at(&mut self, frame: Paddr) -> Option<CursorMut<'_, M>> {
501        proof_decl! {
502            let ghost idx = frame_to_index(frame);
503            if valid_frame_paddr(frame) {
504                regions.lemma_contains_valid_frame_paddr(frame);
505            }
506
507            let tracked slot_perm = if valid_frame_paddr(frame) {
508                Some(*regions.slots.tracked_borrow(idx))
509            } else {
510                None
511            };
512        }
513        let Ok(slot) = (#[verus_spec(with Tracked(slot_perm))]
514        crate::mm::frame::meta::get_slot(frame)) else {
515            return {
516                proof_with!(|= Tracked(None));
517                None
518            };
519        };
520
521        let tracked mut slot_own = regions.slot_owners.tracked_borrow_mut(idx);
522        let contains = slot.in_list.load(Tracked(&mut slot_own.in_list_perm))
523            == #[verus_spec(with Tracked(&owner))]
524        self.lazy_get_id();
525
526        if contains {
527            proof_decl!{
528                let ghost link = owner.list.filter(|link: LinkOwner| link.paddr == frame).first();
529                let ghost index = owner.list.index_of(link);
530                let tracked cursor_owner = CursorOwner::tracked_cursor_mut_at_owner(owner, index);
531            }
532
533            let meta_ptr = ReprPtr::<MetaSlotStorage, Link<M>>::from_pptr(
534                PPtr::<MetaSlotStorage>::from_addr(frame_to_meta(frame)),
535            );
536            proof_with!(|= Tracked(Some(cursor_owner)));
537            Some(CursorMut { list: self, current: Some(meta_ptr) })
538        } else {
539            proof_with!(|= Tracked(None));
540            None
541        }
542    }
543
544    /// Gets a cursor at the front that can mutate the linked list links.
545    ///
546    /// If the list is empty, the cursor points to the "ghost" non-element.
547    /// # Verified Properties
548    /// ## Preconditions
549    /// - The list must be well-formed, with the pointers to its links' metadata slots
550    /// matching the tracked permission objects.
551    /// ## Postconditions
552    /// - The cursor is well-formed, with the pointers to its links' metadata slots
553    /// matching the tracked permission objects. The list invariants are preserved.
554    /// - See [`CursorOwner::front_owner`] for the precise specification.
555    /// ## Safety
556    /// - This function only uses the list permission, so there are no illegal memory accesses.
557    /// - No data races are possible.
558    #[verus_spec(r =>
559        with
560            Tracked(owner): Tracked<LinkedListOwner<M>>,
561        requires
562            old(self).wf(owner),
563            owner.inv(),
564        ensures
565            r.0.wf(r.1@),
566            r.1@.inv(),
567            r.1@ == CursorOwner::front_owner(owner),
568    )]
569    pub fn cursor_front_mut(&mut self) -> (CursorMut<'_, M>, Tracked<CursorOwner<M>>) {
570        let current = self.front;
571
572        (CursorMut { list: self, current }, Tracked(CursorOwner::tracked_front_owner(owner)))
573    }
574
575    /// Gets a cursor at the back that can mutate the linked list links.
576    ///
577    /// If the list is empty, the cursor points to the "ghost" non-element.
578    /// # Verified Properties
579    /// ## Preconditions
580    /// - The list must be well-formed, with the pointers to its links' metadata slots
581    /// matching the tracked permission objects.
582    /// ## Postconditions
583    /// - The cursor is well-formed, with the pointers to its links' metadata slots
584    /// matching the tracked permission objects. The list invariants are preserved.
585    /// See [`CursorOwner::back_owner`] for the precise specification.
586    /// ## Safety
587    /// - This function only uses the list permission, so there are no illegal memory accesses.
588    /// - No data races are possible.
589    #[verus_spec(
590        with
591            Tracked(owner): Tracked<LinkedListOwner<M>>,
592    )]
593    pub fn cursor_back_mut(&mut self) -> (res: (CursorMut<'_, M>, Tracked<CursorOwner<M>>))
594        requires
595            old(self).wf(owner),
596            owner.inv(),
597        ensures
598            res.0.wf(res.1@),
599            res.1@.inv(),
600            res.1@ == CursorOwner::back_owner(owner),
601    {
602        let current = self.back;
603
604        (CursorMut { list: self, current }, Tracked(CursorOwner::tracked_back_owner(owner)))
605    }
606
607    /// Gets a cursor at the "ghost" non-element that can mutate the linked list links.
608    #[verus_spec(
609        with Tracked(owner): Tracked<&mut LinkedListOwner<M>>
610    )]
611    fn cursor_at_ghost_mut(&mut self) -> CursorMut<'_, M> {
612        CursorMut { list: self, current: None }
613    }
614
615    /// # Verification Assumption
616    /// We assume that there is an available ID for `lazy_get_id` to return.
617    /// This is safe because it will panic if the ID allocator is exhausted.
618    #[verifier::external_body]
619    #[verus_spec(
620        with Tracked(owner): Tracked<& LinkedListOwner<M>>
621    )]
622    fn lazy_get_id(&mut self) -> (id: u64)
623        ensures
624            owner.list_id != 0 ==> id == owner.list_id,
625            final(self).size == old(self).size,
626            final(self).front == old(self).front,
627            final(self).back == old(self).back,
628            old(self).list_id != 0 ==> final(self).list_id == old(self).list_id,
629            id != 0,
630            final(self).list_id == id,
631    {
632        unimplemented!()/*        // FIXME: Self-incrementing IDs may overflow, while `core::pin::Pin`
633        // is not compatible with locks. Think about a better solution.
634        static LIST_ID_ALLOCATOR: AtomicU64 = AtomicU64::new(1);
635        const MAX_LIST_ID: u64 = i64::MAX as u64;
636
637        if self.list_id == 0 {
638            let id = LIST_ID_ALLOCATOR.fetch_add(1, Ordering::Relaxed);
639            if id >= MAX_LIST_ID {
640//                log::error!("The frame list ID allocator has exhausted.");
641//                abort();
642                unimplemented!()
643            }
644            self.list_id = id;
645            id
646        } else {
647            self.list_id
648        }*/
649
650    }
651}
652
653impl<'a, M: AnyFrameMeta + Repr<MetaSlotSmall>> CursorMut<'a, M> {
654    /// Moves the cursor to the next frame towards the back.
655    ///
656    /// If the cursor is pointing to the "ghost" non-element then this will
657    /// move it to the first element of the [`LinkedList`]. If it is pointing
658    /// to the last element of the LinkedList then this will move it to the
659    /// "ghost" non-element.
660    #[verus_spec(
661        with Tracked(owner): Tracked<CursorOwner<M>>,
662            Tracked(regions): Tracked<&MetaRegionOwners>,
663    )]
664    pub fn move_next(&mut self)
665        requires
666            owner.wf_with_region(*regions),
667            old(self).wf_region(owner, *regions),
668        ensures
669            owner.move_next_owner_spec()@ == owner@.move_next_spec(),
670            owner.move_next_owner_spec().wf_with_region(*regions),
671            final(self).wf_region(owner.move_next_owner_spec(), *regions),
672    {
673        proof {
674            if self.current is Some {
675                owner.list_own.relate_region_at_facts(*regions, owner.index);
676            }
677            if owner.index < owner.length() - 1 {
678                owner.list_own.relate_region_at_facts(*regions, owner.index + 1);
679            }
680        }
681
682        self.current = match self.current {
683            // SAFETY: The cursor is pointing to a valid element.
684            Some(current) => {
685                proof_decl!{
686                    let ghost idx = meta_to_index(current.addr());
687                    let tracked points_to = regions.slots.tracked_borrow(idx);
688                    let tracked slot_owner = regions.slot_owners.tracked_borrow(idx);
689                    let tracked repr_perm = owner.list_own.repr_perms.tracked_borrow(owner.index);
690                }
691                proof {
692                    assert(regions.contains(idx));
693                }
694                let link = borrow_meta(
695                    current,
696                    Tracked(points_to),
697                    Tracked(&slot_owner.metadata_perm),
698                    Tracked(repr_perm),
699                );
700                link.next
701            },
702            None => self.list.front,
703        };
704
705        proof {
706            LinkedListOwner::<M>::view_preserves_len(owner.list_own.list);
707            assert(owner.move_next_owner_spec()@.fore == owner@.move_next_spec().fore);
708            assert(owner.move_next_owner_spec()@.rear == owner@.move_next_spec().rear);
709        }
710    }
711
712    /// Moves the cursor to the previous frame towards the front.
713    ///
714    /// If the cursor is pointing to the "ghost" non-element then this will
715    /// move it to the last element of the [`LinkedList`]. If it is pointing
716    /// to the first element of the LinkedList then this will move it to the
717    /// "ghost" non-element.
718    #[verus_spec(
719        with Tracked(owner): Tracked<CursorOwner<M>>,
720            Tracked(regions): Tracked<&MetaRegionOwners>,
721    )]
722    pub fn move_prev(&mut self)
723        requires
724            owner.wf_with_region(*regions),
725            old(self).wf_region(owner, *regions),
726        ensures
727            owner.move_prev_owner_spec()@ == owner@.move_prev_spec(),
728            owner.move_prev_owner_spec().wf_with_region(*regions),
729            final(self).wf_region(owner.move_prev_owner_spec(), *regions),
730    {
731        proof {
732            if self.current is Some {
733                owner.list_own.relate_region_at_facts(*regions, owner.index);
734            }
735            if 0 < owner.index {
736                owner.list_own.relate_region_at_facts(*regions, owner.index - 1);
737            }
738        }
739
740        self.current = match self.current {
741            // SAFETY: The cursor is pointing to a valid element.
742            Some(current) => {
743                proof_decl!{
744                    let ghost idx = meta_to_index(current.addr());
745                    let tracked points_to = regions.slots.tracked_borrow(idx);
746                    let tracked slot_owner = regions.slot_owners.tracked_borrow(idx);
747                    let tracked repr_perm = owner.list_own.repr_perms.tracked_borrow(owner.index);
748                }
749                proof {
750                    assert(regions.contains(idx));
751                }
752
753                let link = borrow_meta(
754                    current,
755                    Tracked(points_to),
756                    Tracked(&slot_owner.metadata_perm),
757                    Tracked(repr_perm),
758                );
759                link.prev
760            },
761            None => self.list.back,
762        };
763
764        proof {
765            LinkedListOwner::<M>::view_preserves_len(owner.list_own.list);
766
767            if owner@.list_model.list.len() > 0 {
768                if owner@.fore.len() > 0 {
769                    assert(owner.move_prev_owner_spec()@.fore == owner@.move_prev_spec().fore);
770                    assert(owner.move_prev_owner_spec()@.rear == owner@.move_prev_spec().rear);
771                    if owner@.rear.len() > 0 {
772                        owner.list_own.relate_region_at_facts(*regions, owner.index);
773                    }
774                } else {
775                    owner.list_own.relate_region_at_facts(*regions, owner.index);
776                    assert(owner.move_prev_owner_spec()@.rear == owner@.move_prev_spec().rear);
777                    assert(owner@.rear == owner@.list_model.list);
778                }
779            }
780        }
781    }
782
783    /// Gets the mutable reference to the current frame's metadata.
784    ///
785    /// # Verified Properties
786    /// ## Preconditions
787    /// The cursor must be well-formed with respect to the tracked `CursorOwner`.
788    /// ## Postconditions
789    /// If the cursor is on an element, returns `Some(&mut meta)` borrowing the
790    /// current link's metadata. The cursor state and list shape are otherwise
791    /// unchanged; the current metadata permission remains borrowed while the
792    /// returned reference is live.
793    /// ## Safety
794    /// The `&mut self` guarantees exclusive access to the cursor; the tracked
795    /// `CursorOwner` guarantees the perm for the current link is live.
796    #[verus_spec(
797        with Tracked(owner): Tracked<&'b mut CursorOwner<M>>,
798            Tracked(regions): Tracked<&'b mut MetaRegionOwners>,
799    )]
800    pub fn current_meta<'b>(&'b mut self) -> (res: Option<&'b mut M>)
801        requires
802            old(self).wf_region(*old(owner), *old(regions)),
803            old(owner).wf_with_region(*old(regions)),
804            old(regions).inv(),
805        ensures
806            final(owner).index == old(owner).index,
807            final(owner).list_own.list == old(owner).list_own.list,
808            final(owner).list_own.list_id == old(owner).list_own.list_id,
809            *final(self) == *old(self),
810            res.is_some() == (0 <= final(owner).index < final(owner).length()),
811            final(regions).slots.dom() == old(regions).slots.dom(),
812            final(regions).slot_owners.dom() == old(regions).slot_owners.dom(),
813    {
814        // Verus does not support option.map very well.
815        // self.current.map(|current| {
816        //     let link_mut = unsafe { &mut *(current.ptr.addr() as *mut Link<M>) };
817        //     &mut link_mut.meta
818        // })
819        match self.current {
820            Some(current) => {
821                proof {
822                    owner.list_own.relate_region_at_facts(*regions, owner.index);
823                }
824                let ghost idx = meta_to_index(current.addr());
825                proof {
826                    assert(regions.contains(idx));
827                }
828                let tracked points_to = regions.slots.tracked_borrow(idx);
829                let tracked slot_owner = regions.slot_owners.tracked_borrow_mut(idx);
830                let tracked repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(owner.index);
831                Some(
832                    &mut borrow_meta_mut(
833                        current,
834                        Tracked(points_to),
835                        Tracked(slot_owner),
836                        Tracked(repr_perm),
837                    ).meta,
838                )
839            },
840            None => None,
841        }
842    }
843
844    /// Takes the current pointing frame out of the linked list.
845    ///
846    /// If successful, the frame is returned and the cursor is moved to the
847    /// next frame. If the cursor is pointing to the back of the list then it
848    /// is moved to the "ghost" non-element.
849    /// # Verified Properties
850    /// ## Preconditions
851    /// The cursor must be well-formed, with the pointers to its links' metadata slots
852    /// matching the tracked permission objects. The list must be non-empty, so that the
853    /// current frame is valid.
854    /// ## Postconditions
855    /// The current frame is removed from the list, and the cursor is moved to the next frame.
856    /// The list invariants are preserved.
857    /// ## Safety
858    /// This function calls `from_raw` on the frame, but we guarantee that the frame is forgotten
859    /// if it is in the list. So, double-free will not occur. All loads and stores are through track
860    /// tracked permissions, so there are no illegal memory accesses. No data races are possible.
861    #[verus_spec(
862        with Tracked(regions) : Tracked<&mut MetaRegionOwners>,
863            Tracked(owner) : Tracked<&mut CursorOwner<M>>
864    )]
865    #[verifier::spinoff_prover]
866    #[verifier::rlimit(200)]
867    pub fn take_current(&mut self) -> (res: Option<
868        (UniqueFrame<Link<M>>, Tracked<UniqueFrameOwner<Link<M>>>),
869    >)
870        requires
871            old(self).wf_region(*old(owner), *old(regions)),
872            old(owner).wf_with_region(*old(regions)),
873            old(regions).inv(),
874        ensures
875            old(owner).length() == 0 ==> res.is_none(),
876            old(self).current.is_some() ==> res.is_some(),
877            res.is_some() ==> (res->0).1@@.meta == old(owner).list_own.list[old(owner).index]@,
878            res.is_some() ==> final(owner)@ == old(owner)@.remove(),
879            res.is_some() ==> (res->0).1@.frame_link_inv(*final(regions)),
880            // Invariant preservation
881            res.is_some() ==> final(owner).wf_with_region(*final(regions)),
882            res.is_some() ==> final(self).wf_region(*final(owner), *final(regions)),
883            res.is_none() ==> *final(owner) == *old(owner),
884            final(regions).inv(),
885            // Structural: remove_owner_spec
886            res.is_some() ==> final(owner).index == old(owner).index,
887            res.is_some() ==> final(owner).list_own.list == old(owner).list_own.list.remove(
888                old(owner).index,
889            ),
890            final(owner).list_own.list_id == old(owner).list_own.list_id,
891            res.is_some() ==> {
892                let paddr = old(self).current->0.addr();
893                let idx = meta_to_index(paddr);
894                &&& final(regions).slots.dom() == old(regions).slots.dom()
895                &&& final(regions).slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
896                &&& final(regions).slot_owners[idx].in_list_perm.value() == 0
897                &&& final(regions).slot_owners[idx].storage_perm().is_init()
898                &&& final(regions).slot_owners[idx].vtable_ptr_perm().is_init()
899                &&& final(regions).slot_owners[idx].slot_vaddr == index_to_meta(idx)
900                &&& final(regions).slot_owners[idx].paths_in_pt == old(
901                    regions,
902                ).slot_owners[idx].paths_in_pt
903            },
904            res.is_some() ==> forall|j: int|
905                #![trigger final(regions).slot_owners[j]]
906                j != meta_to_index(old(self).current->0.addr()) ==> {
907                    &&& final(regions).slot_owners[j].usage == old(regions).slot_owners[j].usage
908                    &&& final(regions).slot_owners[j].slot_vaddr == old(
909                        regions,
910                    ).slot_owners[j].slot_vaddr
911                    &&& final(regions).slot_owners[j].paths_in_pt == old(
912                        regions,
913                    ).slot_owners[j].paths_in_pt
914                },
915            res.is_none() ==> *final(regions) == *old(regions),
916            // Properties of the returned frame needed for UniqueFrame::drop
917            res.is_some() ==> (res->0).0.wf((res->0).1@),
918            res.is_some() ==> (res->0).1@.inv(),
919            res.is_some() ==> (res->0).1@.slot_index == meta_to_index(old(self).current->0.addr()),
920            res.is_some() ==> (res->0).0.ptr.addr() == old(self).current->0.addr(),
921            res.is_some() ==> final(regions).frame_obligations == old(
922                regions,
923            ).frame_obligations.insert(meta_to_index(old(self).current->0.addr())),
924    {
925        let ghost owner0 = *owner;
926        let ghost regions0 = *regions;
927
928        let current = self.current?;
929
930        proof {
931            owner.list_own.relate_region_at_facts(*regions, owner.index);
932            if owner.index > 0 {
933                owner.list_own.relate_region_at_facts(*regions, owner.index - 1);
934            }
935            if owner.index < owner.list_own.list.len() - 1 {
936                owner.list_own.relate_region_at_facts(*regions, owner.index + 1);
937            }
938        }
939
940        let meta_ptr = current.addr();
941        let paddr = meta_to_frame(meta_ptr);
942        let ghost idx = frame_to_index(paddr);
943
944        let tracked mut cur_own = owner.list_own.list.tracked_remove(owner.index);
945        let tracked cur_repr_perm = owner.list_own.repr_perms.tracked_remove(owner.index);
946
947        let (mut frame, Tracked(mut frame_own)) = unsafe {
948            // SAFETY: The frame was forgotten when inserted into the linked list.
949            #[verus_spec(with Tracked(regions), Tracked(cur_own), Tracked(cur_repr_perm))]
950            UniqueFrame::<Link<M>>::from_raw(paddr)
951        };
952
953        proof {
954            assert(regions.slots.dom() == regions0.slots.dom());
955            assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
956                &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
957                &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
958                &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
959            } by {}
960        }
961
962        let next_ptr = (#[verus_spec(with Tracked(&frame_own), Tracked(&*regions))]
963        frame.meta()).next;
964        let prev_ptr = (#[verus_spec(with Tracked(&frame_own), Tracked(&*regions))]
965        frame.meta()).prev;
966
967        if let Some(prev) = prev_ptr {
968            let ghost prev_idx = meta_to_index(owner.list_own.list[owner.index - 1].paddr);
969            let tracked prev_points_to = regions.slots.tracked_borrow(prev_idx);
970            let tracked prev_slot_owner = regions.slot_owners.tracked_borrow_mut(prev_idx);
971            let tracked prev_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(
972                owner.index - 1,
973            );
974            let prev_meta = borrow_meta_mut(
975                prev,
976                Tracked(prev_points_to),
977                Tracked(prev_slot_owner),
978                Tracked(prev_repr_perm),
979            );
980            prev_meta.next = next_ptr;
981
982            proof {
983                assert(regions.inv());
984                assert(regions.slots.dom() == regions0.slots.dom());
985                assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
986                    &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
987                    &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
988                    &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
989                } by {
990                    if j == meta_to_index(prev.addr()) {
991                    }
992                }
993            }
994
995        } else {
996            self.list.front = next_ptr;
997            proof {
998                assert(regions.slots.dom() == regions0.slots.dom());
999                assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
1000                    &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
1001                    &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
1002                    &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
1003                } by {}
1004            }
1005        }
1006
1007        if let Some(next) = next_ptr {
1008            let ghost next_idx = meta_to_index(owner.list_own.list[owner.index].paddr);
1009            let tracked next_points_to = regions.slots.tracked_borrow(next_idx);
1010            let tracked next_slot_owner = regions.slot_owners.tracked_borrow_mut(next_idx);
1011            let tracked next_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(owner.index);
1012            let next_meta = borrow_meta_mut(
1013                next,
1014                Tracked(next_points_to),
1015                Tracked(next_slot_owner),
1016                Tracked(next_repr_perm),
1017            );
1018            next_meta.prev = prev_ptr;
1019
1020            proof {
1021                assert(regions.inv());
1022                assert(regions.slots.dom() == regions0.slots.dom());
1023                assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
1024                    &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
1025                    &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
1026                    &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
1027                } by {
1028                    if j == meta_to_index(next.addr()) {
1029                    }
1030                }
1031            }
1032
1033            self.current = Some(next);
1034        } else {
1035            self.list.back = prev_ptr;
1036
1037            self.current = None;
1038            proof {
1039                assert(regions.slots.dom() == regions0.slots.dom());
1040                assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
1041                    &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
1042                    &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
1043                    &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
1044                } by {}
1045            }
1046        }
1047
1048        (#[verus_spec(with Tracked(&mut frame_own), Tracked(regions))]
1049        frame.meta_mut()).next = None;
1050        (#[verus_spec(with Tracked(&mut frame_own), Tracked(regions))]
1051        frame.meta_mut()).prev = None;
1052
1053        let tracked frame_outer = regions.slots.tracked_borrow(idx);
1054        let tracked mut frame_so = regions.slot_owners.tracked_borrow_mut(idx);
1055        #[verus_spec(with Tracked(&frame_outer))]
1056        let slot = frame.slot();
1057        slot.in_list.store(Tracked(&mut frame_so.in_list_perm), 0);
1058        proof {
1059            assert(regions.inv());
1060            assert(regions.slots.dom() == regions0.slots.dom());
1061            assert(regions.slot_owners[idx].paths_in_pt == regions0.slot_owners[idx].paths_in_pt);
1062            assert forall|j: int| #![trigger regions0.slot_owners[j]] j != idx implies {
1063                &&& regions.slot_owners[j].usage == regions0.slot_owners[j].usage
1064                &&& regions.slot_owners[j].slot_vaddr == regions0.slot_owners[j].slot_vaddr
1065                &&& regions.slot_owners[j].paths_in_pt == regions0.slot_owners[j].paths_in_pt
1066            } by {}
1067        }
1068
1069        self.list.size = self.list.size - 1;
1070
1071        proof {
1072            owner0.remove_owner_spec_implies_model_spec(*owner);
1073            let ghost oldl = owner0.list_own;
1074            let ghost nn = owner0.index as int;
1075            assert forall|p: int|
1076                #![trigger meta_to_index(oldl.list[p].paddr)]
1077                (0 <= p < oldl.list.len() && p != nn) implies ({
1078                let i = meta_to_index(oldl.list[p].paddr);
1079                let np = if p < nn {
1080                    p
1081                } else {
1082                    p - 1
1083                };
1084                let fp = owner.list_own.meta_value_at(*regions, np);
1085                &&& regions.contains(i)
1086                &&& regions.slots[i].addr() == oldl.list[p].paddr
1087                &&& regions.slots[i].pptr() == regions0.slots[i].pptr()
1088                &&& regions.slot_owners[i].ref_count() == REF_COUNT_UNIQUE
1089                &&& regions.slot_owners[i].usage is Frame
1090                &&& regions.slot_owners[i].in_list_perm.value() == owner.list_own.list_id
1091                &&& owner.list_own.meta_wf_at(*regions, np)
1092                &&& regions.slots[i].addr() % META_SLOT_SIZE == 0
1093                &&& FRAME_METADATA_RANGE.start <= regions.slots[i].addr()
1094                    < FRAME_METADATA_RANGE.start + MAX_NR_PAGES * META_SLOT_SIZE
1095                &&& (p == nn - 1 ==> fp.next == oldl.meta_value_at(regions0, nn).next)
1096                &&& (p != nn - 1 ==> fp.next == oldl.meta_value_at(regions0, p).next)
1097                &&& (p == nn + 1 ==> fp.prev == oldl.meta_value_at(regions0, nn).prev)
1098                &&& (p != nn + 1 ==> fp.prev == oldl.meta_value_at(regions0, p).prev)
1099            }) by {
1100                let i = meta_to_index(oldl.list[p].paddr);
1101                let np = if p < nn {
1102                    p
1103                } else {
1104                    p - 1
1105                };
1106                let fp = owner.list_own.meta_value_at(*regions, np);
1107                oldl.relate_region_at_facts(regions0, p);
1108                oldl.relate_region_at_facts(regions0, nn);
1109                assert(regions.contains(i));
1110            }
1111            LinkedListOwner::pop_preserves_relate_region(
1112                oldl,
1113                regions0,
1114                owner.list_own,
1115                *regions,
1116                nn,
1117            );
1118        }
1119        Some((frame, Tracked(frame_own)))
1120    }
1121
1122    /// Inserts a frame before the current frame.
1123    ///
1124    /// If the cursor is pointing at the "ghost" non-element then the new
1125    /// element is inserted at the back of the [`LinkedList`].
1126    /// # Verified Properties
1127    /// ## Preconditions
1128    /// The cursor must be well-formed, with the pointers to its links' metadata slots matching the tracked permission objects.
1129    /// - The new frame must be active, so that it is valid to call `into_raw` on it.
1130    /// ## Postconditions
1131    /// - The new frame is inserted into the list, immediately before the current index.
1132    /// - The list invariants are preserved.
1133    /// ## Safety
1134    /// - This function calls `into_raw` on the frame, so the caller must ensure that the frame is active and
1135    /// has not been forgotten already to avoid a memory leak. If the caller attempts to insert a forgotten frame,
1136    /// the invariant around `into_raw` and `from_raw` will be violated. But, it is the safe failure case in that
1137    /// it will not cause a double-free. (Note: we should be able to move this requirement into the `UniqueFrame` invariants.)
1138    #[verus_spec(
1139        with Tracked(regions): Tracked<&mut MetaRegionOwners>,
1140            Tracked(owner): Tracked<&mut CursorOwner<M>>,
1141            Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>
1142    )]
1143    #[verifier::spinoff_prover]
1144    #[verifier::rlimit(200)]
1145    pub fn insert_before(&mut self, mut frame: UniqueFrame<Link<M>>)
1146        requires
1147            old(self).wf_region(*old(owner), *old(regions)),
1148            old(owner).wf_with_region(*old(regions)),
1149            old(regions).inv(),
1150            old(frame_own).inv(),
1151            old(frame_own).global_inv(*old(regions)),
1152            frame.wf(*old(frame_own)),
1153            old(frame_own).frame_link_inv(*old(regions)),
1154        ensures
1155            final(owner).wf_with_region(*final(regions)),
1156            final(self).wf_region(*final(owner), *final(regions)),
1157            final(regions).inv(),
1158            final(owner).list_own.list == old(owner).list_own.list.insert(
1159                old(owner).index,
1160                final(frame_own).meta_own,
1161            ),
1162            // The id is preserved when it was already minted; a `list_id == 0`
1163            // (necessarily empty) list adopts a freshly-minted non-zero id.
1164            old(owner).list_own.list_id != 0 ==> final(owner).list_own.list_id == old(
1165                owner,
1166            ).list_own.list_id,
1167            final(owner).list_own.list_id != 0,
1168            final(owner).index == old(owner).index + 1,
1169            final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
1170            final(frame_own).meta_own.in_list == final(owner).list_own.list_id,
1171            final(owner)@ == old(owner)@.insert(final(frame_own).meta_own@),
1172    {
1173        hide(LinkedListOwner::relate_region);
1174        hide(<MetaRegionOwners as Inv>::inv);
1175        let ghost owner0 = *owner;
1176        let ghost regions0 = *regions;
1177        let ghost nn = owner.index as int;
1178
1179        proof {
1180            assert(owner0.list_own.repr_perms.len() == owner0.list_own.list.len()) by {
1181                reveal(LinkedListOwner::relate_region);
1182            };
1183            assert(owner0.list_own.list.len() > 0 ==> owner0.list_own.list_id != 0) by {
1184                reveal(LinkedListOwner::relate_region);
1185            };
1186            assert(regions0.contains(frame_own.slot_index));
1187            lemma_meta_region_inv_at(regions0, frame_own.slot_index);
1188            owner0.list_own.length_lt_usize_max(regions0);
1189            if nn > 0 {
1190                assert(owner0.list_own.relate_region_at(regions0, nn - 1)) by {
1191                    reveal(LinkedListOwner::relate_region);
1192                };
1193                owner.list_own.relate_region_at_facts(*regions, nn - 1);
1194                lemma_meta_region_inv_at(
1195                    regions0,
1196                    meta_to_index(owner0.list_own.list[nn - 1].paddr),
1197                );
1198            }
1199            if nn < owner.list_own.list.len() {
1200                assert(owner0.list_own.relate_region_at(regions0, nn)) by {
1201                    reveal(LinkedListOwner::relate_region);
1202                };
1203                owner.list_own.relate_region_at_facts(*regions, nn);
1204                lemma_meta_region_inv_at(regions0, meta_to_index(owner0.list_own.list[nn].paddr));
1205            }
1206            assert forall|p: int|
1207                #![trigger
1208                    regions0.slot_owners[meta_to_index(owner0.list_own.list[p].paddr)]]
1209                0 <= p < owner0.list_own.list.len() implies frame_own.slot_index != meta_to_index(
1210                owner0.list_own.list[p].paddr,
1211            ) by {
1212                lemma_insert_before_slot_distinct(
1213                    owner0.list_own,
1214                    regions0,
1215                    frame_own.slot_index,
1216                    nn,
1217                );
1218            }
1219        }
1220
1221        let frame_ptr = ReprPtr::<MetaSlotStorage, Link<M>>::from_pptr(
1222            PPtr::<MetaSlotStorage>::from_addr(frame.ptr.addr()),
1223        );
1224
1225        if let Some(current) = self.current {
1226            proof_decl!{
1227                let ghost idx = meta_to_index(current.addr());
1228                let tracked points_to = regions.slots.tracked_borrow(idx);
1229                let tracked slot_owner = regions.slot_owners.tracked_borrow(idx);
1230                let tracked repr_perm = owner.list_own.repr_perms.tracked_borrow(owner.index);
1231            }
1232
1233            // Read current's prev pointer.
1234            let opt_prev_link: Option<ReprPtr<MetaSlotStorage, Link<M>>> = borrow_meta(
1235                current,
1236                Tracked(points_to),
1237                Tracked(&slot_owner.metadata_perm),
1238                Tracked(repr_perm),
1239            ).prev;
1240
1241            if let Some(prev_link) = opt_prev_link {
1242                let prev = prev_link;
1243
1244                (#[verus_spec(with Tracked(frame_own), Tracked(regions))]
1245                frame.meta_mut()).prev = Some(prev_link);
1246                (#[verus_spec(with Tracked(frame_own), Tracked(regions))]
1247                frame.meta_mut()).next = Some(current);
1248
1249                let ghost prev_idx = meta_to_index(owner.list_own.list[nn - 1].paddr);
1250                proof {
1251                    assert(prev_idx != idx) by {
1252                        reveal(LinkedListOwner::relate_region);
1253                    };
1254                }
1255                let tracked prev_points_to = regions.slots.tracked_borrow(prev_idx);
1256                let tracked prev_slot_owner = regions.slot_owners.tracked_borrow_mut(prev_idx);
1257                let tracked prev_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(nn - 1);
1258                let prev_meta = borrow_meta_mut(
1259                    prev,
1260                    Tracked(prev_points_to),
1261                    Tracked(prev_slot_owner),
1262                    Tracked(prev_repr_perm),
1263                );
1264                prev_meta.next = Some(frame_ptr);
1265
1266                let ghost current_idx = meta_to_index(owner.list_own.list[nn].paddr);
1267                let tracked current_points_to = regions.slots.tracked_borrow(current_idx);
1268                let tracked current_slot_owner = regions.slot_owners.tracked_borrow_mut(
1269                    current_idx,
1270                );
1271                let tracked current_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(nn);
1272                let current_meta = borrow_meta_mut(
1273                    current,
1274                    Tracked(current_points_to),
1275                    Tracked(current_slot_owner),
1276                    Tracked(current_repr_perm),
1277                );
1278                current_meta.prev = Some(frame_ptr);
1279            } else {
1280                (#[verus_spec(with Tracked(frame_own), Tracked(regions))]
1281                frame.meta_mut()).next = Some(current);
1282
1283                let ghost current_idx = meta_to_index(owner.list_own.list[nn].paddr);
1284                let tracked current_points_to = regions.slots.tracked_borrow(current_idx);
1285                let tracked current_slot_owner = regions.slot_owners.tracked_borrow_mut(
1286                    current_idx,
1287                );
1288                let tracked current_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(nn);
1289                let current_meta = borrow_meta_mut(
1290                    current,
1291                    Tracked(current_points_to),
1292                    Tracked(current_slot_owner),
1293                    Tracked(current_repr_perm),
1294                );
1295                current_meta.prev = Some(frame_ptr);
1296                self.list.front = Some(frame_ptr);
1297            }
1298        } else {
1299            if let Some(back) = self.list.back {
1300                (#[verus_spec(with Tracked(frame_own), Tracked(regions))]
1301                frame.meta_mut()).prev = Some(back);
1302
1303                let ghost back_idx = meta_to_index(owner.list_own.list[nn - 1].paddr);
1304                proof {
1305                    assert(0 <= nn - 1 < owner.list_own.repr_perms.len());
1306                }
1307                let tracked back_points_to = regions.slots.tracked_borrow(back_idx);
1308                let tracked back_slot_owner = regions.slot_owners.tracked_borrow_mut(back_idx);
1309                let tracked back_repr_perm = owner.list_own.repr_perms.tracked_borrow_mut(nn - 1);
1310                let back_meta = borrow_meta_mut(
1311                    back,
1312                    Tracked(back_points_to),
1313                    Tracked(back_slot_owner),
1314                    Tracked(back_repr_perm),
1315                );
1316                back_meta.next = Some(frame_ptr);
1317                self.list.back = Some(frame_ptr);
1318            } else {
1319                // EMPTY list: just point both ends at the inserted frame.
1320                self.list.front = Some(frame_ptr);
1321                self.list.back = Some(frame_ptr);
1322            }
1323        }
1324
1325        #[verus_spec(with Tracked(&owner.list_own))]
1326        let list_id = self.list.lazy_get_id();
1327
1328        proof {
1329            assert(owner0.list_own.list.len() > 0 ==> list_id == owner0.list_own.list_id);
1330        }
1331        let tracked frame_outer = regions.slots.tracked_borrow_mut(frame_own.slot_index);
1332        let tracked mut frame_so = regions.slot_owners.tracked_borrow_mut(frame_own.slot_index);
1333        #[verus_spec(with Tracked(frame_outer))]
1334        let slot = frame.slot();
1335        slot.in_list.store(Tracked(&mut frame_so.in_list_perm), list_id);
1336        proof {
1337            assert(regions.inv()) by {
1338                reveal(<MetaRegionOwners as Inv>::inv);
1339            };
1340        }
1341
1342        #[verus_spec(with Tracked(&*frame_own), Tracked(regions))]
1343        let _ = frame.into_raw();
1344
1345        self.list.size = self.list.size + 1;
1346
1347        proof {
1348            let tracked frame_repr_perm = frame_own.repr_perm.tracked_take();
1349            CursorOwner::<M>::tracked_list_insert(
1350                owner,
1351                &mut frame_own.meta_own,
1352                frame_repr_perm,
1353                list_id,
1354            );
1355
1356            let oldl = owner0.list_own;
1357            let nn = owner0.index as int;
1358            let flink = frame_own.meta_own;
1359            let ins = frame_own.slot_index;
1360
1361            assert(owner.list_own.relate_region(*regions)) by {
1362                assert forall|p: int|
1363                    #![trigger
1364                        owner.list_own.insert_old_slot_post_at(
1365                            *regions,
1366                            oldl,
1367                            regions0,
1368                            nn,
1369                            flink,
1370                            p,
1371                        )]
1372                    (0 <= p < oldl.list.len()) implies owner.list_own.insert_old_slot_post_at(
1373                    *regions,
1374                    oldl,
1375                    regions0,
1376                    nn,
1377                    flink,
1378                    p,
1379                ) by {
1380                    reveal(LinkedListOwner::insert_old_slot_post_at);
1381                    assert(oldl.relate_region_at(regions0, p)) by {
1382                        reveal(LinkedListOwner::relate_region);
1383                    };
1384                    oldl.relate_region_at_facts(regions0, p);
1385                    if nn - 1 >= 0 && nn - 1 < oldl.list.len() && p != nn - 1 {
1386                        assert(meta_to_index(oldl.list[p].paddr) != meta_to_index(
1387                            oldl.list[nn - 1].paddr,
1388                        )) by {
1389                            reveal(LinkedListOwner::relate_region);
1390                        };
1391                    }
1392                    if nn >= 0 && nn < oldl.list.len() && p != nn {
1393                        assert(meta_to_index(oldl.list[p].paddr) != meta_to_index(
1394                            oldl.list[nn].paddr,
1395                        )) by {
1396                            reveal(LinkedListOwner::relate_region);
1397                        };
1398                    }
1399                }
1400
1401                LinkedListOwner::insert_preserves_relate_region(
1402                    oldl,
1403                    regions0,
1404                    owner.list_own,
1405                    *regions,
1406                    nn,
1407                    flink,
1408                );
1409            };
1410
1411            owner0.insert_owner_spec_implies_model_spec(flink, *owner);
1412        }
1413    }
1414
1415    /// Provides a reference to the linked list.
1416    pub fn as_list(&self) -> &LinkedList<M> {
1417        self.list
1418    }
1419}
1420
1421impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> TrackDrop for LinkedList<M> {
1422    type State = (LinkedListOwner<M>, MetaRegionOwners);
1423
1424    /// Real key: the list's `list_id`. The token carries the identity of
1425    /// the list it belongs to, so a token forged for one list can't be
1426    /// used to discharge another (the `consume_requires` key match
1427    /// refuses the mismatch). A multiset ledger over `list_id` is not
1428    /// added because every live `LinkedList` already has a unique
1429    /// `LinkedListOwner` in scope — the per-instance discipline is
1430    /// state-side, not ledger-side.
1431    type Obligation = DropObligation<u64>;
1432
1433    open spec fn tracked_redeem_requires(self, s: Self::State) -> bool {
1434        true
1435    }
1436
1437    open spec fn tracked_redeem_ensures(
1438        self,
1439        s0: Self::State,
1440        s1: Self::State,
1441        obl: Self::Obligation,
1442    ) -> bool {
1443        &&& s0 =~= s1
1444        &&& obl.value() == self.list_id
1445    }
1446
1447    proof fn tracked_redeem(self, tracked s: &mut Self::State) -> (tracked obl: Self::Obligation) {
1448        DropObligation::tracked_mint(self.list_id)
1449    }
1450
1451    open spec fn drop_requires(self, s: Self::State, obl: Self::Obligation) -> bool {
1452        &&& self.wf(s.0)
1453        &&& s.0.inv()
1454        &&& s.1.inv()
1455        &&& forall|i: int|
1456            #![trigger s.0.list[i]]
1457            0 <= i < s.0.list.len() ==> s.1.contains(meta_to_index(s.0.list[i].paddr))
1458        &&& forall|i: int|
1459            #![trigger s.0.list[i]]
1460            0 <= i < s.0.list.len() ==> {
1461                let idx = meta_to_index(s.0.list[i].paddr);
1462                s.1.contains(idx)
1463            }
1464        &&& forall|i: int|
1465            #![trigger s.0.list[i]]
1466            0 <= i < s.0.list.len() ==> {
1467                let idx = meta_to_index(s.0.list[i].paddr);
1468                s.1.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
1469            }
1470        &&& forall|i: int|
1471            #![trigger s.0.list[i]]
1472            0 <= i < s.0.list.len() ==> {
1473                let idx = meta_to_index(s.0.list[i].paddr);
1474                s.1.frame_obligations.count(idx) == 0
1475            }
1476        &&& forall|i: int|
1477            #![trigger s.0.list[i]]
1478            0 <= i < s.0.list.len() ==> {
1479                let idx = meta_to_index(s.0.list[i].paddr);
1480                s.1.slot_owners[idx].paths_in_pt.is_empty()
1481            }
1482        &&& forall|i: int, j: int|
1483            #![trigger s.0.list[i], s.0.list[j]]
1484            0 <= i < j < s.0.list.len() ==> meta_to_index(s.0.list[i].paddr) != meta_to_index(
1485                s.0.list[j].paddr,
1486            )
1487        &&& s.0.relate_region(s.1)
1488        &&& obl.value() == self.list_id
1489    }
1490
1491    open spec fn drop_ensures(
1492        self,
1493        s0: Self::State,
1494        s1: Self::State,
1495        obl: Self::Obligation,
1496    ) -> bool {
1497        &&& s1.0.list.len() == 0
1498        &&& forall|i: int|
1499            #![trigger s0.0.list[i]]
1500            0 <= i < s0.0.list.len() ==> {
1501                let idx = meta_to_index(s0.0.list[i].paddr);
1502                s1.1.frame_obligations.count(idx) == s0.1.frame_obligations.count(idx)
1503            }
1504        &&& forall|idx: int|
1505            #![trigger s1.1.slot_owners[idx]]
1506            (forall|i: int|
1507                #![trigger s0.0.list[i]]
1508                0 <= i < s0.0.list.len() ==> idx != meta_to_index(s0.0.list[i].paddr)) ==> {
1509                &&& s1.1.frame_obligations.count(idx) == s0.1.frame_obligations.count(idx)
1510                &&& s1.1.slot_owners[idx].usage == s0.1.slot_owners[idx].usage
1511                &&& s1.1.slot_owners[idx].slot_vaddr == s0.1.slot_owners[idx].slot_vaddr
1512                &&& s1.1.slot_owners[idx].paths_in_pt == s0.1.slot_owners[idx].paths_in_pt
1513            }
1514        &&& s1.1.slots.dom() =~= s0.1.slots.dom()
1515        &&& s1.1.inv()
1516    }
1517}
1518
1519impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Drop for LinkedList<M> {
1520    #[verifier::spinoff_prover]
1521    fn drop(
1522        self,
1523        Tracked(s): Tracked<&mut Self::State>,
1524        Tracked(obl): Tracked<DropObligation<u64>>,
1525    ) {
1526        proof_decl! {
1527            let tracked mut list_own: LinkedListOwner<M>;
1528        }
1529        let ghost original_list = s.0.list;
1530        let ghost original_list_id = s.0.list_id;
1531        let ghost n = original_list.len();
1532        let ghost original_regions = s.1;
1533        proof {
1534            list_own = LinkedListOwner::<M>::tracked_take(&mut s.0);
1535        }
1536        let tracked regions: &mut MetaRegionOwners = &mut s.1;
1537        let mut this = self;
1538
1539        #[verus_spec(with Tracked(list_own))]
1540        let cursor_pair = this.cursor_front_mut();
1541        let (mut cursor, Tracked(mut cursor_own)) = cursor_pair;
1542
1543        proof {
1544            if n > 0 {
1545                cursor_own.list_own.relate_region_at_facts(*regions, 0);
1546                cursor_own.list_own.relate_region_at_facts(*regions, n - 1);
1547            }
1548        }
1549
1550        let ghost mut k: int = 0;
1551
1552        loop
1553            invariant_except_break
1554                cursor.wf_region(cursor_own, *regions),
1555                cursor.current.is_some() <==> k < n,
1556            invariant
1557                cursor_own.wf_with_region(*regions),
1558                cursor_own.list_own.list_id == original_list_id,
1559                cursor_own.index == 0,
1560                regions.inv(),
1561                cursor_own.list_own.list.len() == n - k,
1562                0 <= k <= n,
1563                // The remaining list is a suffix of the original
1564                forall|j: int|
1565                    #![trigger cursor_own.list_own.list[j]]
1566                    0 <= j < n - k ==> cursor_own.list_own.list[j] == original_list[j + k],
1567                // Elements already taken have their in-list obligation redeemed (count 0)
1568                forall|j: int|
1569                    #![trigger original_list[j]]
1570                    0 <= j < k ==> {
1571                        let idx = meta_to_index(original_list[j].paddr);
1572                        regions.frame_obligations.count(idx) == 0
1573                    },
1574                // slots values inside the original_list.
1575                forall|idx: int|
1576                    #![trigger regions.slot_owners[idx]]
1577                    (forall|j: int|
1578                        #![trigger original_list[j]]
1579                        0 <= j < n ==> idx != meta_to_index(original_list[j].paddr)) ==> {
1580                        &&& regions.frame_obligations.count(idx)
1581                            == original_regions.frame_obligations.count(idx)
1582                        &&& regions.slot_owners[idx].usage
1583                            == original_regions.slot_owners[idx].usage
1584                        &&& regions.slot_owners[idx].slot_vaddr
1585                            == original_regions.slot_owners[idx].slot_vaddr
1586                        &&& regions.slot_owners[idx].paths_in_pt
1587                            == original_regions.slot_owners[idx].paths_in_pt
1588                    },
1589                regions.slots.dom() == original_regions.slots.dom(),
1590                // `paths_in_pt.is_empty()` precondition).
1591                forall|j: int|
1592                    #![trigger original_list[j]]
1593                    k <= j < n ==> {
1594                        let idx = meta_to_index(original_list[j].paddr);
1595                        &&& regions.frame_obligations.count(idx)
1596                            == original_regions.frame_obligations.count(idx)
1597                        &&& regions.slot_owners[idx].paths_in_pt
1598                            == original_regions.slot_owners[idx].paths_in_pt
1599                    },
1600                // Each remaining element's slot is in slot_owners
1601                forall|j: int|
1602                    #![trigger original_list[j]]
1603                    k <= j < n ==> regions.contains(meta_to_index(original_list[j].paddr)),
1604                // Distinct slot indices in original list (from drop_requires)
1605                forall|i: int, j: int|
1606                    #![trigger original_list[i], original_list[j]]
1607                    0 <= i < j < n ==> meta_to_index(original_list[i].paddr) != meta_to_index(
1608                        original_list[j].paddr,
1609                    ),
1610                forall|j: int|
1611                    #![trigger original_list[j]]
1612                    0 <= j < n ==> {
1613                        let idx = meta_to_index(original_list[j].paddr);
1614                        &&& original_regions.contains(idx)
1615                        &&& original_regions.frame_obligations.count(idx) == 0
1616                        &&& original_regions.slot_owners[idx].paths_in_pt.is_empty()
1617                        &&& original_regions.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
1618                    },
1619            ensures
1620                k == n,
1621                cursor_own.list_own.list.len() == 0,
1622            decreases n - k,
1623        {
1624            #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own))]
1625            let entry = cursor.take_current();
1626
1627            if let Some(current) = entry {
1628                let (mut frame, frame_own_tracked) = current;
1629                let tracked frame_own = frame_own_tracked.get();
1630                let ghost regions_pre_drop = *regions;
1631
1632                // Drop the frame, returning its slot to regions
1633                #[verus_spec(with Tracked(frame_own), Tracked(regions))]
1634                frame.drop();
1635
1636                proof {
1637                    assert forall|i: int|
1638                        #![trigger cursor_own.list_own.list[i]]
1639                        0 <= i < cursor_own.list_own.list.len() implies ({
1640                        let idx = meta_to_index(cursor_own.list_own.list[i].paddr);
1641                        &&& regions.contains(idx)
1642                        &&& regions.slot_owners[idx] == regions_pre_drop.slot_owners[idx]
1643                        &&& regions.frame_obligations.count(idx)
1644                            == regions_pre_drop.frame_obligations.count(idx)
1645                    }) by {
1646                        let idx = meta_to_index(cursor_own.list_own.list[i].paddr);
1647                        let ghost _trig_k = original_list[k as int];
1648                        let ghost _trig_ik = original_list[i + k + 1];
1649                        assert(cursor_own.list_own.list[i] == original_list[i + k + 1]);
1650
1651                        cursor_own.list_own.relate_region_at_facts(regions_pre_drop, i);
1652                    };
1653                    cursor_own.list_own.relate_region_preserved_external_change(
1654                        regions_pre_drop,
1655                        *regions,
1656                    );
1657
1658                    assert forall|j: int|
1659                        #![trigger cursor_own.list_own.list[j]]
1660                        0 <= j < n - k - 1 implies cursor_own.list_own.list[j] == original_list[j
1661                        + k + 1] by {};
1662
1663                    assert forall|j: int| #![trigger original_list[j]] 0 <= j < k implies ({
1664                        let idx = meta_to_index(original_list[j].paddr);
1665                        regions.frame_obligations.count(idx) == 0
1666                    }) by {
1667                        let ghost _a = original_list[j as int];
1668                        let ghost _b = original_list[k as int];
1669                    };
1670
1671                    k = k + 1;
1672                }
1673            } else {
1674                break;
1675            }
1676        }
1677
1678        // `s.1` is already updated in place via the re-borrow `regions`;
1679        // restore `s.0` to the cursor's final (empty) `list_own`.
1680        proof {
1681            let tracked mut final_list_own = cursor_own.list_own;
1682            vstd::modes::tracked_swap(&mut s.0, &mut final_list_own);
1683            final_list_own.tracked_destroy_empty();
1684        }
1685    }
1686}
1687
1688// SAFETY: `Link<M>` is `Send` and `Sync` if `M` is `Send` and `Sync` because
1689// we only access these unsafe cells when the frame is not shared. This is
1690// enforced by `UniqueFrame`.
1691// #[verifier::external]
1692// unsafe impl<M> Send for LinkedList<M> where Link<M>: AnyFrameMeta {}
1693// #[verifier::external]
1694// unsafe impl<M> Sync for LinkedList<M> where Link<M>: AnyFrameMeta {}
1695/// A link in the linked list.
1696pub struct Link<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
1697    pub next: Option<ReprPtr<MetaSlotStorage, Link<M>>>,
1698    pub prev: Option<ReprPtr<MetaSlotStorage, Link<M>>>,
1699    pub meta: M,
1700}
1701
1702impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Deref for Link<M> {
1703    type Target = M;
1704
1705    fn deref(&self) -> &Self::Target {
1706        &self.meta
1707    }
1708}
1709
1710impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> DerefMut for Link<M> {
1711    fn deref_mut(&mut self) -> &mut Self::Target {
1712        &mut self.meta
1713    }
1714}
1715
1716impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Link<M> {
1717    /// Creates a new linked list metadata.
1718    pub const fn new(meta: M) -> Self {
1719        Self { next: None, prev: None, meta }
1720    }
1721}
1722
1723// SAFETY: If `M::on_drop` reads the page using the provided `VmReader`,
1724// the safety is upheld by the one who implements `AnyFrameMeta` for `M`.
1725unsafe impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> AnyFrameMeta for Link<M> {
1726    open spec fn on_drop_pre(
1727        &self,
1728        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1729        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
1730        vm_io_owner: crate::specs::mm::io::VmIoOwner,
1731    ) -> bool {
1732        self.meta.on_drop_pre(reader, regions, vm_io_owner)
1733    }
1734
1735    fn on_drop(
1736        &mut self,
1737        reader: &mut crate::mm::VmReader<crate::mm::Infallible>,
1738        regions: Tracked<&mut crate::specs::mm::frame::meta_region_owners::MetaRegionOwners>,
1739        vm_io_owner: Tracked<&mut crate::specs::mm::io::VmIoOwner>,
1740    ) {
1741        self.meta.on_drop(reader, regions, vm_io_owner);
1742    }
1743
1744    fn is_untyped(&self) -> bool {
1745        self.meta.is_untyped()
1746    }
1747
1748    uninterp spec fn vtable_ptr(&self) -> usize;
1749}
1750
1751} // verus!