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