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::*;
14use vstd_extra::trans_macros::*;
15
16use crate::mm::frame::meta::{
17    META_SLOT_SIZE, REF_COUNT_UNIQUE,
18    mapping::{frame_to_meta, meta_to_frame},
19};
20use crate::mm::kspace::FRAME_METADATA_RANGE;
21use crate::specs::arch::*;
22use crate::specs::mm::frame::{
23    linked_list::linked_list_owners::*,
24    mapping::{frame_to_index, group_page_meta, max_meta_slots, meta_addr},
25    meta_owners::{MetaSlotOwner, Metadata},
26    meta_region_owners::MetaRegionOwners,
27    unique::UniqueFrameOwner,
28};
29
30use super::{
31    MetaSlot, mapping,
32    meta::{AnyFrameMeta, get_slot},
33    unique::UniqueFrame,
34};
35use crate::{
36    arch::mm::PagingConsts,
37    mm::{Paddr, Vaddr},
38    //panic::abort,
39};
40use core::{
41    ops::{Deref, DerefMut},
42    ptr::NonNull,
43    sync::atomic::{AtomicU64, Ordering},
44};
45
46verus! {
47
48/// A linked list of frames.
49///
50/// Two key features that [`LinkedList`] is different from
51/// [`alloc::collections::LinkedList`] is that:
52///  1. It is intrusive, meaning that the links are part of the frame metadata.
53///     This allows the linked list to be used without heap allocation. But it
54///     disallows a frame to be in multiple linked lists at the same time.
55///  2. The linked list exclusively own the frames, meaning that it takes
56///     unique pointers [`UniqueFrame`]. And other bodies cannot
57///     [`from_in_use`] a frame that is inside a linked list.
58///  3. We also allow creating cursors at a specific frame, allowing $O(1)$
59///     removal without iterating through the list at a cost of some checks.
60///
61/// # Example
62///
63/// To create metadata types that allows linked list links, wrap the metadata
64/// type in [`Link`]:
65///
66/// ```rust
67/// use ostd::{
68///     mm::{frame::{linked_list::{Link, LinkedList}, Frame}, FrameAllocOptions},
69///     impl_untyped_frame_meta_for,
70/// };
71///
72/// #[derive(Debug)]
73/// struct MyMeta { mark: usize }
74///
75/// type MyFrame = Frame<Link<MyMeta>>;
76///
77/// impl_untyped_frame_meta_for!(MyMeta);
78///
79/// let alloc_options = FrameAllocOptions::new();
80/// let frame1 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 1 })).unwrap();
81/// let frame2 = alloc_options.alloc_frame_with(Link::new(MyMeta { mark: 2 })).unwrap();
82///
83/// let mut list = LinkedList::new();
84/// list.push_front(frame1.try_into().unwrap());
85/// list.push_front(frame2.try_into().unwrap());
86///
87/// let mut cursor = list.cursor_front_mut();
88/// assert_eq!(cursor.current_meta().unwrap().mark, 2);
89/// cursor.move_next();
90/// assert_eq!(cursor.current_meta().unwrap().mark, 1);
91/// ```
92///
93/// [`from_in_use`]: super::Frame::from_in_use
94///
95/// # Verified Properties
96/// ## Verification Design
97/// The linked list is abstractly represented by a [`LinkedListOwner`]:
98/// ```rust
99/// tracked struct LinkedListOwner<M: AnyFrameMeta + Repr<MetaSlotStorage>> {
100///     pub list: Seq<LinkOwner>,
101///     pub list_id: u64,
102/// }
103/// ```
104/// The pointer permission for each link is parked in the global
105/// [`MetaRegionOwners`] (`regions.slots[idx]` paired with
106/// `regions.slot_owners[idx].inner_perms`); cursor accessors borrow it on
107/// demand via `borrow_typed_perm`/`borrow_mut_typed_perm` rather than carrying
108/// an owned `Map<int, _>` inside `LinkedListOwner`.
109/// ## Invariant
110/// The linked list uniquely owns the raw frames that it contains, so they cannot be used by other
111/// data structures. The frame metadata field `in_list` is equal to `list_id` for all links in the list.
112/// The per-link well-formedness against the region (pptr/inner_perms wiring,
113/// `next`/`prev` pointer chain) is captured by
114/// [`LinkedListOwner::relate_region`] (opaque, with per-position
115/// [`LinkedListOwner::relate_region_at`]). The cursor exposes this via
116/// [`CursorOwner::wf_with_region`] and [`CursorMut::wf_region`].
117/// ## Safety
118/// A given linked list can only have one cursor at a time, so there are no data races.
119/// The `prev` and `next` fields of the metadata for each link always points to valid
120/// links in the list, so the structure is memory safe (will not read or write invalid memory).
121pub struct LinkedList<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
122    pub front: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
123    pub back: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
124    /// The number of frames in the list.
125    pub size: usize,
126    /// A lazily initialized ID, used to check whether a frame is in the list.
127    /// 0 means uninitialized.
128    pub list_id: u64,
129}
130
131/// A cursor that can mutate the linked list links.
132///
133/// The cursor points to either a frame or the "ghost" non-element. It points
134/// to the "ghost" non-element when the cursor surpasses the back of the list.
135pub struct CursorMut<'a, M: AnyFrameMeta + Repr<MetaSlotSmall>> {
136    pub list: &'a mut LinkedList<M>,
137    pub current: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
138}
139
140impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M> {
141    /// Creates a new linked list.
142    pub const fn new() -> Self {
143        Self { front: None, back: None, size: 0, list_id: 0 }
144    }
145}
146
147impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Default for LinkedList<M> {
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153#[verus_verify]
154impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M> {
155    /// Gets the number of frames in the linked list.
156    #[verus_spec(s =>
157        with
158            Tracked(owner): Tracked<LinkedListOwner<M>>,
159        requires
160            self.wf(owner),
161            owner.inv(),
162        ensures
163            s == owner@.list.len(),
164    )]
165    pub fn size(&self) -> usize {
166        proof {
167            LinkedListOwner::<M>::view_preserves_len(owner.list);
168        }
169        self.size
170    }
171
172    /// Tells if the linked list is empty.
173    #[verus_spec(b =>
174        with
175            Tracked(owner): Tracked<LinkedListOwner<M>>,
176        requires
177            self.wf(owner),
178            owner.inv(),
179        ensures
180            b ==> self.size == 0 && self.front is None && self.back is None,
181            !b ==> self.size > 0 && self.front is Some && self.back is Some,
182    )]
183    pub fn is_empty(&self) -> bool {
184        let is_empty = self.size == 0;
185        is_empty
186    }
187
188    /// Pushes a frame to the front of the linked list.
189    /// # Verified Properties
190    /// ## Preconditions
191    /// The list must be well-formed, with the pointers to its links' metadata slots
192    /// matching the tracked permission objects. The new frame must be active, so that it is
193    /// valid to call `into_raw` on it inside of `insert_before`.
194    /// ## Postconditions
195    /// The new frame is inserted at the front of the list, and the cursor is moved to the new frame.
196    /// The list invariants are preserved.
197    /// ## Safety
198    /// See [`insert_before`] for the safety guarantees.
199    #[verus_spec(
200        with
201            Tracked(regions): Tracked<&mut MetaRegionOwners>,
202            Tracked(owner): Tracked<&mut LinkedListOwner<M>>,
203            Tracked(frame_own): Tracked<&mut UniqueFrameOwner<Link<M>>>,
204        requires
205            old(self).wf_region(*old(owner), *old(regions)),
206            old(owner).relate_region(*old(regions)),
207            old(frame_own).inv(),
208            old(frame_own).global_inv(*old(regions)),
209            frame.wf(*old(frame_own)),
210            old(frame_own).frame_link_inv(*old(regions)),
211            old(regions).inv(),
212        ensures
213            final(owner).relate_region(*final(regions)),
214            final(regions).inv(),
215            final(owner).list == old(owner).list.insert(0, final(frame_own).meta_own),
216            old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
217            final(owner).list_id != 0,
218            final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
219            final(frame_own).meta_own.in_list == final(owner).list_id,
220    )]
221    pub fn push_front(&mut self, frame: UniqueFrame<Link<M>>) {
222        let current = self.front;
223        let tracked mut cursor_own = CursorOwner::tracked_front_owner(*owner);
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 `has_safe_slot(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            !has_safe_slot(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(!has_safe_slot(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                borrow_field!(current_md => next, Meta(meta_perm))
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                borrow_field!(current_md => prev, Meta(meta_perm))
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 == meta_addr(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: usize|
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: usize| #![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 = borrow_field!(current_md => next, Meta(tp));
934        let prev_ptr = borrow_field!(current_md => prev, Meta(tp));
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            update_field!(prev => next <- next_ptr, Meta(prev_perm));
952
953            proof {
954                assert(regions.inv());
955                assert(regions.slots.dom() == regions0.slots.dom());
956                assert forall|j: usize| #![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: usize| #![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            update_field!(next => prev <- prev_ptr, Meta(next_perm));
994
995            proof {
996                assert(regions.inv());
997                assert(regions.slots.dom() == regions0.slots.dom());
998                assert forall|j: usize| #![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: usize| #![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        update_field!(current_md => next <- None, Meta(frame_perm));
1025
1026        let tracked frame_perm = regions.borrow_mut_typed_perm::<Link<M>>(idx);
1027        update_field!(current_md => prev <- None, Meta(frame_perm));
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: usize| #![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: usize = 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 = borrow_field!(current_md => prev, Meta(tp));
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                update_field!(prev => next <- Some(frame_ptr_as_link), Meta(perm));
1192
1193                let tracked perm = regions.borrow_mut_typed_perm::<Link<M>>(frame_idx_g);
1194                update_field!(frame_ptr => prev <- Some(prev_link), Meta(perm));
1195
1196                let tracked perm = regions.borrow_mut_typed_perm::<Link<M>>(frame_idx_g);
1197                update_field!(frame_ptr => next <- Some(current), Meta(perm));
1198
1199                let tracked perm = regions.borrow_mut_typed_perm::<Link<M>>(
1200                    frame_to_index(meta_to_frame(current.addr())),
1201                );
1202                update_field!(current_md => prev <- Some(frame_ptr_as_link), Meta(perm));
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                update_field!(frame_ptr => next <- Some(current), Meta(perm));
1224
1225                let tracked perm = regions.borrow_mut_typed_perm::<Link<M>>(
1226                    frame_to_index(meta_to_frame(current.addr())),
1227                );
1228                update_field!(current_md => prev <- Some(frame_ptr_as_link), Meta(perm));
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                update_field!(back_md => next <- Some(frame_ptr_as_link), Meta(perm));
1240
1241                let tracked perm = regions.borrow_mut_typed_perm::<Link<M>>(frame_idx_g);
1242                update_field!(frame_ptr => prev <- Some(back), Meta(perm));
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 Key = u64;
1368
1369    open spec fn key(self) -> Self::Key {
1370        self.list_id
1371    }
1372
1373    open spec fn constructor_requires(self, s: Self::State) -> bool {
1374        true
1375    }
1376
1377    open spec fn constructor_ensures(
1378        self,
1379        s0: Self::State,
1380        s1: Self::State,
1381        obl_key: Self::Key,
1382    ) -> bool {
1383        &&& s0 =~= s1
1384        &&& obl_key == self.list_id
1385    }
1386
1387    proof fn constructor_spec(self, tracked s: &mut Self::State) -> (tracked obl: DropObligation<
1388        Self::Key,
1389    >) {
1390        DropObligation::tracked_mint(self.list_id)
1391    }
1392
1393    open spec fn drop_requires(self, s: Self::State) -> bool {
1394        &&& self.wf(s.0)
1395        &&& s.0.inv()
1396        &&& s.1.inv()
1397        &&& forall|i: int|
1398            #![trigger s.0.list[i]]
1399            0 <= i < s.0.list.len() ==> s.1.slot_owners.contains_key(
1400                frame_to_index(meta_to_frame(s.0.list[i].paddr)),
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.slots.contains_key(idx)
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.slot_owners[idx].inner_perms.ref_count.value() == REF_COUNT_UNIQUE
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.frame_obligations.count(idx) == 0
1419            }
1420        &&& forall|i: int|
1421            #![trigger s.0.list[i]]
1422            0 <= i < s.0.list.len() ==> {
1423                let idx = frame_to_index(meta_to_frame(s.0.list[i].paddr));
1424                s.1.slot_owners[idx].paths_in_pt.is_empty()
1425            }
1426        &&& forall|i: int, j: int|
1427            #![trigger s.0.list[i], s.0.list[j]]
1428            0 <= i < j < s.0.list.len() ==> frame_to_index(meta_to_frame(s.0.list[i].paddr))
1429                != frame_to_index(meta_to_frame(s.0.list[j].paddr))
1430        &&& s.0.relate_region(s.1)
1431    }
1432
1433    open spec fn drop_ensures(self, s0: Self::State, s1: Self::State, obl_key: Self::Key) -> 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: usize|
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    open spec fn consume_requires(self, s: Self::State, obl_key: Self::Key) -> bool {
1458        // The token must match this list's identity — prevents a token
1459        // forged for a different list from discharging this one.
1460        obl_key == self.list_id
1461    }
1462
1463    open spec fn consume_ensures(
1464        self,
1465        s0: Self::State,
1466        s1: Self::State,
1467        obl_key: Self::Key,
1468    ) -> bool {
1469        s0 =~= s1
1470    }
1471
1472    proof fn consume_obligation(
1473        self,
1474        tracked s: &mut Self::State,
1475        tracked obl: DropObligation<Self::Key>,
1476    ) {
1477        // No-op on the ledger; the per-instance discipline lives in
1478        // `LinkedListOwner`'s state-side invariants.
1479    }
1480}
1481
1482impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Drop for LinkedList<M> {
1483    #[verifier::spinoff_prover]
1484    fn drop(
1485        self,
1486        Tracked(s): Tracked<&mut Self::State>,
1487        Tracked(obl): Tracked<DropObligation<u64>>,
1488    ) {
1489        // Single redeem path: route through `consume_obligation` before
1490        // running the destructor body.
1491        proof {
1492            self.consume_obligation(s, obl);
1493        }
1494
1495        proof_decl! {
1496            let tracked mut list_own: LinkedListOwner<M>;
1497        }
1498        let ghost original_list = s.0.list;
1499        let ghost original_list_id = s.0.list_id;
1500        let ghost n = original_list.len();
1501        let ghost original_regions = s.1;
1502        proof {
1503            list_own = LinkedListOwner::<M>::tracked_take(&mut s.0);
1504        }
1505        let tracked regions: &mut MetaRegionOwners = &mut s.1;
1506        let mut this = self;
1507
1508        #[verus_spec(with Tracked(list_own))]
1509        let cursor_pair = this.cursor_front_mut();
1510        let (mut cursor, Tracked(mut cursor_own)) = cursor_pair;
1511
1512        proof {
1513            if n > 0 {
1514                cursor_own.list_own.relate_region_at_facts(*regions, 0);
1515                cursor_own.list_own.relate_region_at_facts(*regions, n - 1);
1516            }
1517        }
1518
1519        let ghost mut k: int = 0;
1520
1521        loop
1522            invariant_except_break
1523                cursor.wf_region(cursor_own, *regions),
1524                cursor.current.is_some() <==> k < n,
1525            invariant
1526                cursor_own.wf_with_region(*regions),
1527                cursor_own.list_own.list_id == original_list_id,
1528                cursor_own.index == 0,
1529                regions.inv(),
1530                cursor_own.list_own.list.len() == n - k,
1531                0 <= k <= n,
1532                // The remaining list is a suffix of the original
1533                forall|j: int|
1534                    #![trigger cursor_own.list_own.list[j]]
1535                    0 <= j < n - k ==> cursor_own.list_own.list[j] == original_list[j + k],
1536                // Elements already taken have their in-list obligation redeemed (count 0)
1537                forall|j: int|
1538                    #![trigger original_list[j]]
1539                    0 <= j < k ==> {
1540                        let idx = frame_to_index(meta_to_frame(original_list[j].paddr));
1541                        regions.frame_obligations.count(idx) == 0
1542                    },
1543                // slots values inside the original_list.
1544                forall|idx: usize|
1545                    #![trigger regions.slot_owners[idx]]
1546                    (forall|j: int|
1547                        #![trigger original_list[j]]
1548                        0 <= j < n ==> idx != frame_to_index(meta_to_frame(original_list[j].paddr)))
1549                        ==> {
1550                        &&& regions.frame_obligations.count(idx)
1551                            == original_regions.frame_obligations.count(idx)
1552                        &&& regions.slot_owners[idx].usage
1553                            == original_regions.slot_owners[idx].usage
1554                        &&& regions.slot_owners[idx].slot_vaddr
1555                            == original_regions.slot_owners[idx].slot_vaddr
1556                        &&& regions.slot_owners[idx].paths_in_pt
1557                            == original_regions.slot_owners[idx].paths_in_pt
1558                    },
1559                regions.slots.dom() == original_regions.slots.dom(),
1560                // `paths_in_pt.is_empty()` precondition).
1561                forall|j: int|
1562                    #![trigger original_list[j]]
1563                    k <= j < n ==> {
1564                        let idx = frame_to_index(meta_to_frame(original_list[j].paddr));
1565                        &&& regions.frame_obligations.count(idx)
1566                            == original_regions.frame_obligations.count(idx)
1567                        &&& regions.slot_owners[idx].paths_in_pt
1568                            == original_regions.slot_owners[idx].paths_in_pt
1569                    },
1570                // Each remaining element's slot is in slot_owners
1571                forall|j: int|
1572                    #![trigger original_list[j]]
1573                    k <= j < n ==> regions.slot_owners.contains_key(
1574                        frame_to_index(meta_to_frame(original_list[j].paddr)),
1575                    ),
1576                // Distinct slot indices in original list (from drop_requires)
1577                forall|i: int, j: int|
1578                    #![trigger original_list[i], original_list[j]]
1579                    0 <= i < j < n ==> frame_to_index(meta_to_frame(original_list[i].paddr))
1580                        != frame_to_index(meta_to_frame(original_list[j].paddr)),
1581                forall|j: int|
1582                    #![trigger original_list[j]]
1583                    0 <= j < n ==> {
1584                        let idx = frame_to_index(meta_to_frame(original_list[j].paddr));
1585                        &&& original_regions.slot_owners.contains_key(idx)
1586                        &&& original_regions.slots.contains_key(idx)
1587                        &&& original_regions.frame_obligations.count(idx) == 0
1588                        &&& original_regions.slot_owners[idx].paths_in_pt.is_empty()
1589                        &&& original_regions.slot_owners[idx].inner_perms.ref_count.value()
1590                            == REF_COUNT_UNIQUE
1591                    },
1592            ensures
1593                k == n,
1594                cursor_own.list_own.list.len() == 0,
1595            decreases n - k,
1596        {
1597            #[verus_spec(with Tracked(regions), Tracked(&mut cursor_own))]
1598            let entry = cursor.take_current();
1599
1600            if let Some(current) = entry {
1601                let (mut frame, frame_own_tracked) = current;
1602                let tracked frame_own = frame_own_tracked.get();
1603                let ghost regions_pre_drop = *regions;
1604
1605                // Drop the frame, returning its slot to regions
1606                #[verus_spec(with Tracked(frame_own), Tracked(regions))]
1607                frame.drop();
1608
1609                proof {
1610                    assert forall|i: int|
1611                        #![trigger cursor_own.list_own.list[i]]
1612                        0 <= i < cursor_own.list_own.list.len() implies ({
1613                        let idx = cursor_own.list_own.slot_index_at(i);
1614                        &&& regions.slot_owners.contains_key(idx)
1615                        &&& regions.slot_owners[idx] == regions_pre_drop.slot_owners[idx]
1616                        &&& regions.frame_obligations.count(idx)
1617                            == regions_pre_drop.frame_obligations.count(idx)
1618                    }) by {
1619                        let idx = cursor_own.list_own.slot_index_at(i);
1620                        let ghost _trig_k = original_list[k as int];
1621                        let ghost _trig_ik = original_list[i + k + 1];
1622                        assert(cursor_own.list_own.list[i] == original_list[i + k + 1]);
1623
1624                        cursor_own.list_own.relate_region_at_facts(regions_pre_drop, i);
1625                    };
1626                    cursor_own.list_own.relate_region_preserved_external_change(
1627                        regions_pre_drop,
1628                        *regions,
1629                    );
1630
1631                    assert forall|j: int|
1632                        #![trigger cursor_own.list_own.list[j]]
1633                        0 <= j < n - k - 1 implies cursor_own.list_own.list[j] == original_list[j
1634                        + k + 1] by {};
1635
1636                    assert forall|j: int| #![trigger original_list[j]] 0 <= j < k implies ({
1637                        let idx = frame_to_index(meta_to_frame(original_list[j].paddr));
1638                        regions.frame_obligations.count(idx) == 0
1639                    }) by {
1640                        let ghost _a = original_list[j as int];
1641                        let ghost _b = original_list[k as int];
1642                    };
1643
1644                    k = k + 1;
1645                }
1646            } else {
1647                break;
1648            }
1649        }
1650
1651        // `s.1` is already updated in place via the re-borrow `regions`;
1652        // restore `s.0` to the cursor's final (empty) `list_own`.
1653        proof {
1654            let tracked mut final_list_own = cursor_own.list_own;
1655            vstd::modes::tracked_swap(&mut s.0, &mut final_list_own);
1656            final_list_own.tracked_destroy_empty();
1657        }
1658    }
1659}
1660
1661// SAFETY: `Link<M>` is `Send` and `Sync` if `M` is `Send` and `Sync` because
1662// we only access these unsafe cells when the frame is not shared. This is
1663// enforced by `UniqueFrame`.
1664// #[verifier::external]
1665// unsafe impl<M> Send for LinkedList<M> where Link<M>: AnyFrameMeta {}
1666// #[verifier::external]
1667// unsafe impl<M> Sync for LinkedList<M> where Link<M>: AnyFrameMeta {}
1668/// A link in the linked list.
1669pub struct Link<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
1670    pub next: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
1671    pub prev: Option<ReprPtr<MetaSlot, MetadataAsLink<M>>>,
1672    pub meta: M,
1673}
1674
1675impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Deref for Link<M> {
1676    type Target = M;
1677
1678    fn deref(&self) -> &Self::Target {
1679        &self.meta
1680    }
1681}
1682
1683impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> DerefMut for Link<M> {
1684    fn deref_mut(&mut self) -> &mut Self::Target {
1685        &mut self.meta
1686    }
1687}
1688
1689impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Link<M> {
1690    /// Creates a new linked list metadata.
1691    pub const fn new(meta: M) -> Self {
1692        Self { next: None, prev: None, meta }
1693    }
1694}
1695
1696// SAFETY: If `M::on_drop` reads the page using the provided `VmReader`,
1697// the safety is upheld by the one who implements `AnyFrameMeta` for `M`.
1698unsafe impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> AnyFrameMeta for Link<M> {
1699    open spec fn on_drop_pre(
1700        &self,
1701        reader: crate::mm::VmReader<'_, crate::mm::Infallible>,
1702        regions: crate::specs::mm::frame::meta_region_owners::MetaRegionOwners,
1703        vm_io_owner: crate::specs::mm::io::VmIoOwner,
1704    ) -> bool {
1705        self.meta.on_drop_pre(reader, regions, vm_io_owner)
1706    }
1707
1708    fn on_drop(
1709        &mut self,
1710        reader: &mut crate::mm::VmReader<crate::mm::Infallible>,
1711        regions: Tracked<&mut crate::specs::mm::frame::meta_region_owners::MetaRegionOwners>,
1712        vm_io_owner: Tracked<&mut crate::specs::mm::io::VmIoOwner>,
1713    ) {
1714        self.meta.on_drop(reader, regions, vm_io_owner);
1715    }
1716
1717    fn is_untyped(&self) -> bool {
1718        self.meta.is_untyped()
1719    }
1720
1721    uninterp spec fn vtable_ptr(&self) -> usize;
1722}
1723
1724} // verus!