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