Skip to main content

ostd/specs/mm/frame/linked_list/
linked_list_owners.rs

1use core::marker::PhantomData;
2
3use vstd::modes::tracked_swap;
4use vstd::prelude::*;
5
6use vstd::{atomic::*, seq_lib::*, set_lib::*, simple_pptr::*};
7use vstd_extra::{
8    cast_ptr::{Repr, ReprPtr},
9    ownership::*,
10};
11
12use crate::specs::{
13    arch::MAX_NR_PAGES,
14    mm::frame::{
15        mapping::{max_meta_slots, meta_to_index},
16        meta_owners::*,
17        meta_region_owners::MetaRegionOwners,
18        unique::UniqueFrameOwner,
19    },
20};
21
22use crate::mm::{
23    Paddr,
24    frame::{
25        AnyFrameMeta, CursorMut, Link, LinkedList, MetaSlot,
26        meta::{META_SLOT_SIZE, REF_COUNT_UNIQUE},
27    },
28    kspace::FRAME_METADATA_RANGE,
29};
30
31use super::*;
32
33verus! {
34
35pub struct MetaSlotSmall;
36
37/// Representation of a link as stored in the metadata slot.
38pub struct StoredLink {
39    pub next: Option<Paddr>,
40    pub prev: Option<Paddr>,
41    pub slot: MetaSlotSmall,
42}
43
44pub tracked struct LinkInnerPerms<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
45    pub storage: <M as Repr<MetaSlotSmall>>::ReprPerm,
46    pub ghost next_ptr: Option<PPtr<MetaSlotStorage>>,
47    pub ghost prev_ptr: Option<PPtr<MetaSlotStorage>>,
48}
49
50impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Repr<MetaSlotStorage> for Link<M> {
51    type ReprPerm = LinkInnerPerms<M>;
52
53    open spec fn wf(r: MetaSlotStorage, perm: LinkInnerPerms<M>) -> bool {
54        match r {
55            MetaSlotStorage::FrameLink(link) => {
56                &&& M::wf(link.slot, perm.storage)
57                &&& (link.next is Some) == (perm.next_ptr is Some)
58                &&& (link.prev is Some) == (perm.prev_ptr is Some)
59                &&& link.next is Some ==> link.next->0 == perm.next_ptr->0.addr()
60                &&& link.prev is Some ==> link.prev->0 == perm.prev_ptr->0.addr()
61            },
62            _ => false,
63        }
64    }
65
66    open spec fn to_repr_spec(self, perm: LinkInnerPerms<M>) -> (
67        MetaSlotStorage,
68        LinkInnerPerms<M>,
69    ) {
70        let (slot, storage) = self.meta.to_repr_spec(perm.storage);
71        (
72            MetaSlotStorage::FrameLink(
73                StoredLink {
74                    next: match self.next {
75                        Some(ptr) => Some(ptr.ptr.addr()),
76                        None => None,
77                    },
78                    prev: match self.prev {
79                        Some(ptr) => Some(ptr.ptr.addr()),
80                        None => None,
81                    },
82                    slot,
83                },
84            ),
85            LinkInnerPerms {
86                storage,
87                next_ptr: match self.next {
88                    Some(ptr) => Some(ptr.ptr),
89                    None => None,
90                },
91                prev_ptr: match self.prev {
92                    Some(ptr) => Some(ptr.ptr),
93                    None => None,
94                },
95            },
96        )
97    }
98
99    #[verifier::external_body]
100    fn to_repr(self, Tracked(perm): Tracked<&mut LinkInnerPerms<M>>) -> MetaSlotStorage {
101        unimplemented!()
102    }
103
104    open spec fn from_repr_spec(r: MetaSlotStorage, perm: LinkInnerPerms<M>) -> Self {
105        match r {
106            MetaSlotStorage::FrameLink(link) => Link {
107                next: match link.next {
108                    Some(addr) => Some(ReprPtr { ptr: perm.next_ptr->0, _T: PhantomData }),
109                    None => None,
110                },
111                prev: match link.prev {
112                    Some(addr) => Some(ReprPtr { ptr: perm.prev_ptr->0, _T: PhantomData }),
113                    None => None,
114                },
115                meta: M::from_repr_spec(link.slot, perm.storage),
116            },
117            _ => Link {
118                next: None,
119                prev: None,
120                meta: M::from_repr_spec(MetaSlotSmall, perm.storage),
121            },
122        }
123    }
124
125    #[verifier::external_body]
126    fn from_repr(r: MetaSlotStorage, Tracked(perm): Tracked<&LinkInnerPerms<M>>) -> Self {
127        unimplemented!()
128    }
129
130    #[verifier::external_body]
131    fn from_borrowed<'a>(
132        r: &'a MetaSlotStorage,
133        Tracked(perm): Tracked<&'a LinkInnerPerms<M>>,
134    ) -> &'a Self {
135        unimplemented!()
136    }
137
138    #[verifier::external_body]
139    fn from_borrowed_mut<'a>(
140        r: &'a mut MetaSlotStorage,
141        Tracked(perm): Tracked<&'a mut LinkInnerPerms<M>>,
142    ) -> &'a mut Self {
143        unimplemented!()
144    }
145
146    proof fn from_to_repr(self, perm: LinkInnerPerms<M>) {
147        <M as Repr<MetaSlotSmall>>::from_to_repr(self.meta, perm.storage);
148    }
149
150    proof fn to_from_repr(r: MetaSlotStorage, perm: LinkInnerPerms<M>) {
151        match r {
152            MetaSlotStorage::FrameLink(link) => {
153                M::to_from_repr(link.slot, perm.storage);
154            },
155            _ => {
156                assert(false);
157            },
158        }
159    }
160
161    proof fn to_repr_wf(self, perm: LinkInnerPerms<M>) {
162        <M as Repr<MetaSlotSmall>>::to_repr_wf(self.meta, perm.storage);
163    }
164}
165
166pub ghost struct LinkModel {
167    pub paddr: Paddr,
168}
169
170impl Inv for LinkModel {
171    open spec fn inv(self) -> bool {
172        true
173    }
174}
175
176pub tracked struct LinkOwner {
177    pub ghost paddr: Paddr,
178    pub ghost in_list: u64,
179}
180
181impl Inv for LinkOwner {
182    open spec fn inv(self) -> bool {
183        true
184    }
185}
186
187impl View for LinkOwner {
188    type V = LinkModel;
189
190    open spec fn view(&self) -> Self::V {
191        LinkModel { paddr: self.paddr }
192    }
193}
194
195impl InvView for LinkOwner {
196    proof fn view_preserves_inv(self) {
197    }
198}
199
200impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> OwnerOf for Link<M> {
201    type Owner = LinkOwner;
202
203    open spec fn wf(self, owner: Self::Owner) -> bool {
204        true
205        //        &&& owner.self_perm@.mem_contents().value() == self
206        //        &&& owner.next == self.next
207        //        &&& owner.prev == self.prev
208
209    }
210}
211
212pub ghost struct LinkedListModel {
213    pub list: Seq<LinkModel>,
214}
215
216impl LinkedListModel {
217    pub open spec fn front(self) -> Option<LinkModel> {
218        if self.list.len() > 0 {
219            Some(self.list[0])
220        } else {
221            None
222        }
223    }
224
225    pub open spec fn back(self) -> Option<LinkModel> {
226        if self.list.len() > 0 {
227            Some(self.list[self.list.len() - 1])
228        } else {
229            None
230        }
231    }
232}
233
234impl Inv for LinkedListModel {
235    open spec fn inv(self) -> bool {
236        true
237    }
238}
239
240pub tracked struct LinkedListOwner<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
241    pub list: Seq<LinkOwner>,
242    pub repr_perms: Seq<LinkInnerPerms<M>>,
243    pub ghost list_id: u64,
244    pub ghost _marker: core::marker::PhantomData<M>,
245}
246
247impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Inv for LinkedListOwner<M> {
248    open spec fn inv(self) -> bool {
249        // Weakened (our change): an EMPTY list may carry `list_id == 0` (the
250        // lazily-minted-id convention used by the list-store embedding); the
251        // id is only constrained non-zero once the list is non-empty.
252        &&& self.list.len() > 0 ==> self.list_id != 0
253        &&& self.repr_perms.len() == self.list.len()
254        &&& forall|i: int| 0 <= i < self.list.len() ==> self.inv_at(i)
255    }
256}
257
258impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedListOwner<M> {
259    pub open spec fn meta_addr_at(self, regions: MetaRegionOwners, i: int) -> usize {
260        regions.slots[meta_to_index(self.list[i].paddr)].addr()
261    }
262
263    pub open spec fn meta_pptr_at(self, regions: MetaRegionOwners, i: int) -> PPtr<MetaSlot> {
264        regions.slots[meta_to_index(self.list[i].paddr)].pptr()
265    }
266
267    /// Per-link structural invariant: the link's own `inv()` holds and its
268    /// `in_list` tag matches the list's `list_id`. The per-link metadata facts
269    /// (perm wf/is_init/pointer wiring) are tracked via `relate_region_at`
270    /// against the global `MetaRegionOwners`, NOT through this predicate.
271    pub open spec fn inv_at(self, i: int) -> bool {
272        &&& self.list[i].inv()
273        &&& self.list[i].in_list == self.list_id
274    }
275
276    pub open spec fn meta_wf_at(self, regions: MetaRegionOwners, i: int) -> bool {
277        let idx = meta_to_index(self.list[i].paddr);
278        typed_meta_wf::<Link<M>>(
279            *regions.slots[idx],
280            regions.slot_owners[idx].metadata_perm,
281            self.repr_perms[i],
282        )
283    }
284
285    pub open spec fn meta_value_at(self, regions: MetaRegionOwners, i: int) -> Link<M> {
286        let idx = meta_to_index(self.list[i].paddr);
287        typed_meta_value::<Link<M>>(regions.slot_owners[idx].metadata_perm, self.repr_perms[i])
288    }
289
290    /// The per-link invariant expressed directly over the region-owned slot and
291    /// storage permissions plus the list-owned representation permission.
292    #[verifier::opaque]
293    pub open spec fn relate_region_at(self, regions: MetaRegionOwners, i: int) -> bool {
294        let idx = meta_to_index(self.list[i].paddr);
295        let value = self.meta_value_at(regions, i);
296        &&& regions.contains(idx)
297        &&& regions.slots[idx].addr() == self.list[i].paddr
298        &&& regions.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
299        &&& regions.slot_owners[idx].usage is Frame
300        &&& regions.slot_owners[idx].in_list_perm.value() == self.list_id
301        &&& self.meta_wf_at(regions, i)
302        &&& regions.slots[idx].addr() % META_SLOT_SIZE == 0
303        &&& FRAME_METADATA_RANGE.start <= regions.slots[idx].addr() < FRAME_METADATA_RANGE.start
304            + MAX_NR_PAGES * META_SLOT_SIZE
305        &&& value.wf(self.list[i])
306        &&& i == 0 <==> value.prev is None
307        &&& i == self.list.len() - 1 <==> value.next is None
308        &&& 0 < i ==> {
309            &&& value.prev is Some
310            &&& value.prev->0.addr() == self.meta_addr_at(regions, i - 1)
311        }
312        &&& i < self.list.len() - 1 ==> {
313            &&& value.next is Some
314            &&& value.next->0.addr() == self.meta_addr_at(regions, i + 1)
315        }
316        &&& self.list[i].inv()
317        &&& self.list[i].in_list == self.list_id
318    }
319
320    /// The list-wide region relation: every link satisfies `relate_region_at`,
321    /// and distinct list positions map to distinct region slot indices (so a
322    /// frame appears at most once — required by the borrow model, where link
323    /// edits mutate `regions.slots[meta_to_index(self.list[i].paddr)]` and must not alias).
324    pub open spec fn relate_region(self, regions: MetaRegionOwners) -> bool {
325        &&& self.repr_perms.len() == self.list.len()
326        &&& forall|i: int|
327            #![trigger self.list[i]]
328            0 <= i < self.list.len() ==> self.relate_region_at(regions, i)
329        &&& forall|i: int, j: int|
330            #![trigger meta_to_index(self.list[i].paddr), meta_to_index(self.list[j].paddr)]
331            0 <= i < self.list.len() && 0 <= j < self.list.len() && i != j ==> meta_to_index(
332                self.list[i].paddr,
333            ) != meta_to_index(self.list[j].paddr)
334        &&& self.list.len() > 0 ==> self.list_id != 0
335    }
336
337    /// Pigeonhole bound: the list is no longer than the number of meta slots.
338    /// Each link occupies a region slot (`relate_region_at` ⟹
339    /// `slots.contains_key(meta_to_index(self.list[i].paddr))`, and `regions.inv()` ⟹
340    /// `meta_to_index(self.list[i].paddr) < max_meta_slots()`), and distinct positions occupy
341    /// distinct slots (`relate_region`'s injectivity). So the positions inject
342    /// into `[0, max_meta_slots())` and the length is capped by it.
343    pub proof fn length_le_max_meta_slots(self, regions: MetaRegionOwners)
344        requires
345            self.relate_region(regions),
346            regions.inv(),
347        ensures
348            self.list.len() <= max_meta_slots(),
349    {
350        let idxs = Seq::new(self.list.len(), |i: int| meta_to_index(self.list[i].paddr));
351
352        idxs.unique_seq_to_set();
353
354        let bound = set_int_range(0, max_meta_slots());
355        assert(idxs.to_set().subset_of(bound)) by {
356            assert forall|x: int|
357                #![trigger idxs.to_set().contains(x)]
358                idxs.to_set().contains(x) implies bound.contains(x) by {
359                let i = choose|i: int| 0 <= i < idxs.len() && idxs[i] == x;
360                self.relate_region_at_facts(regions, i);
361                // `regions.inv()`: the contained slot index is `< max_meta_slots()`.
362            }
363        }
364        lemma_int_range(0, max_meta_slots());
365        lemma_len_subset(idxs.to_set(), bound);
366    }
367
368    /// The list counter can never saturate: its length is capped by
369    /// `max_meta_slots()` (see [`Self::length_le_max_meta_slots`]), which is far
370    /// below `usize::MAX`. Lets `insert_before` discharge the `size + 1`
371    /// overflow check without a caller-supplied non-fullness precondition.
372    pub proof fn length_lt_usize_max(self, regions: MetaRegionOwners)
373        requires
374            self.relate_region(regions),
375            regions.inv(),
376        ensures
377            self.list.len() < usize::MAX,
378    {
379        self.length_le_max_meta_slots(regions);
380    }
381
382    /// Unfolds the opaque `relate_region_at` ONCE and exposes its clauses.
383    /// `relate_region_at` is opaque to avoid quantifier explosion at use sites;
384    /// this lemma localizes the reveal so callers get
385    /// the facts at a single index without re-exploding the SMT context.
386    pub proof fn relate_region_at_facts(self, regions: MetaRegionOwners, i: int)
387        requires
388            self.relate_region_at(regions, i),
389        ensures
390            ({
391                let idx = meta_to_index(self.list[i].paddr);
392                let value = self.meta_value_at(regions, i);
393                &&& regions.contains(idx)
394                &&& regions.slots[idx].addr() == self.list[i].paddr
395                &&& regions.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
396                &&& regions.slot_owners[idx].usage is Frame
397                &&& regions.slot_owners[idx].in_list_perm.value() == self.list_id
398                &&& self.meta_wf_at(regions, i)
399                &&& regions.slots[idx].addr() % META_SLOT_SIZE == 0
400                &&& FRAME_METADATA_RANGE.start <= regions.slots[idx].addr()
401                    < FRAME_METADATA_RANGE.start + MAX_NR_PAGES * META_SLOT_SIZE
402                &&& value.wf(self.list[i])
403                &&& (i == 0 <==> value.prev is None)
404                &&& (i == self.list.len() - 1 <==> value.next is None)
405                &&& (0 < i ==> {
406                    &&& value.prev is Some
407                    &&& value.prev->0.addr() == self.meta_addr_at(regions, i - 1)
408                })
409                &&& (i < self.list.len() - 1 ==> {
410                    &&& value.next is Some
411                    &&& value.next->0.addr() == self.meta_addr_at(regions, i + 1)
412                })
413                &&& self.list[i].inv()
414                &&& self.list[i].in_list == self.list_id
415            }),
416    {
417        reveal(LinkedListOwner::relate_region_at);
418    }
419
420    /// Constructor (inverse of [`relate_region_at_facts`]): establishes the
421    /// opaque `relate_region_at` from its unfolded clauses. Used by the pop/
422    /// insert "surgery" proofs, which assemble each clause for the new list and
423    /// then fold them back into the opaque predicate.
424    pub proof fn relate_region_at_from_clauses(self, regions: MetaRegionOwners, i: int)
425        requires
426            ({
427                let idx = meta_to_index(self.list[i].paddr);
428                let value = self.meta_value_at(regions, i);
429                &&& regions.contains(idx)
430                &&& self.repr_perms.len() == self.list.len()
431                &&& regions.slots[idx].addr() == self.list[i].paddr
432                &&& regions.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE
433                &&& regions.slot_owners[idx].usage is Frame
434                &&& regions.slot_owners[idx].in_list_perm.value() == self.list_id
435                &&& self.meta_wf_at(regions, i)
436                &&& regions.slots[idx].addr() % META_SLOT_SIZE == 0
437                &&& FRAME_METADATA_RANGE.start <= regions.slots[idx].addr()
438                    < FRAME_METADATA_RANGE.start + MAX_NR_PAGES * META_SLOT_SIZE
439                &&& value.wf(self.list[i])
440                &&& (i == 0 <==> value.prev is None)
441                &&& (i == self.list.len() - 1 <==> value.next is None)
442                &&& (0 < i ==> {
443                    &&& value.prev is Some
444                    &&& value.prev->0.addr() == self.meta_addr_at(regions, i - 1)
445                })
446                &&& (i < self.list.len() - 1 ==> {
447                    &&& value.next is Some
448                    &&& value.next->0.addr() == self.meta_addr_at(regions, i + 1)
449                })
450                &&& self.list[i].inv()
451                &&& self.list[i].in_list == self.list_id
452            }),
453        ensures
454            self.relate_region_at(regions, i),
455    {
456        reveal(LinkedListOwner::relate_region_at);
457    }
458
459    /// `relate_region` is preserved under a region change that doesn't touch
460    /// any of the list's slots. Used by `LinkedList::drop`'s loop body: after
461    /// `take_current` pops position 0, the popped slot is dropped via
462    /// `frame.drop`, which only modifies `regions.slot_owners[cur_idx]` and
463    /// leaves `regions.slots` fully untouched. Since the cursor's remaining
464    /// list never contains `cur_idx` (distinctness on the original list),
465    /// `relate_region` carries through.
466    pub proof fn relate_region_preserved_external_change(
467        self,
468        regions1: MetaRegionOwners,
469        regions2: MetaRegionOwners,
470    )
471        requires
472            self.relate_region(regions1),
473            regions2.slots == regions1.slots,
474            forall|i: int|
475                #![trigger self.list[i]]
476                0 <= i < self.list.len() ==> {
477                    let idx = meta_to_index(self.list[i].paddr);
478                    &&& regions2.contains(idx)
479                    &&& regions2.slot_owners[idx] == regions1.slot_owners[idx]
480                },
481        ensures
482            self.relate_region(regions2),
483    {
484        let llen = self.list.len() as int;
485        assert forall|k: int|
486            #![trigger self.relate_region_at(regions2, k)]
487            0 <= k < llen implies self.relate_region_at(regions2, k) by {
488            let _ = self.list[k];
489            self.relate_region_at_facts(regions1, k);
490            self.relate_region_at_from_clauses(regions2, k);
491        }
492    }
493
494    /// The list-rewiring "surgery" for popping the element at index `n`: given
495    /// the entry invariant `old.relate_region(r0)` and a characterization of the
496    /// post-pop region `fr` (every surviving slot keeps its local facts and
497    /// pointers, except the two neighbors whose `next`/`prev` were rewired to
498    /// bridge the gap), the shrunk list `new` satisfies `relate_region(fr)`.
499    ///
500    /// New position `k` maps to old position `p = (k < n ? k : k+1)`; the
501    /// neighbor of `k` maps to `p ± 1` except across the cut (new position `n-1`
502    /// reaches old `n+1`, new position `n` reaches old `n-1`), which is exactly
503    /// where the body rewired the link pointers.
504    #[verifier::spinoff_prover]
505    #[verifier::rlimit(60)]
506    pub proof fn pop_preserves_relate_region(
507        old: LinkedListOwner<M>,
508        r0: MetaRegionOwners,
509        new: LinkedListOwner<M>,
510        fr: MetaRegionOwners,
511        n: int,
512    )
513        requires
514            0 <= n < old.list.len(),
515            old.relate_region(r0),
516            new.list == old.list.remove(n),
517            new.repr_perms.len() == new.list.len(),
518            new.list_id == old.list_id,
519            forall|p: int|
520                #![trigger meta_to_index(old.list[p].paddr)]
521                (0 <= p < old.list.len() && p != n) ==> ({
522                    let i = meta_to_index(old.list[p].paddr);
523                    let np = if p < n {
524                        p
525                    } else {
526                        p - 1
527                    };
528                    let fp = typed_meta_value::<Link<M>>(
529                        fr.slot_owners[i].metadata_perm,
530                        new.repr_perms[np],
531                    );
532                    &&& fr.contains(i)
533                    &&& fr.slots[i].addr() == old.list[p].paddr
534                    &&& fr.slots[i].pptr() == r0.slots[i].pptr()
535                    &&& fr.slot_owners[i].ref_count() == REF_COUNT_UNIQUE
536                    &&& fr.slot_owners[i].usage is Frame
537                    &&& fr.slot_owners[i].in_list_perm.value() == new.list_id
538                    &&& typed_meta_wf::<Link<M>>(
539                        *fr.slots[i],
540                        fr.slot_owners[i].metadata_perm,
541                        new.repr_perms[np],
542                    )
543                    &&& fr.slots[i].addr() % META_SLOT_SIZE == 0
544                    &&& FRAME_METADATA_RANGE.start <= fr.slots[i].addr()
545                        < FRAME_METADATA_RANGE.start + MAX_NR_PAGES * META_SLOT_SIZE
546                    &&& (p == n - 1 ==> fp.next == old.meta_value_at(r0, n).next)
547                    &&& (p != n - 1 ==> fp.next == old.meta_value_at(r0, p).next)
548                    &&& (p == n + 1 ==> fp.prev == old.meta_value_at(r0, n).prev)
549                    &&& (p != n + 1 ==> fp.prev == old.meta_value_at(r0, p).prev)
550                }),
551        ensures
552            new.relate_region(fr),
553    {
554        let nlen = new.list.len() as int;
555
556        assert forall|k: int| #![trigger meta_to_index(new.list[k].paddr)] 0 <= k < nlen implies {
557            let p = if k < n {
558                k
559            } else {
560                k + 1
561            };
562            &&& new.list[k] == old.list[p]
563            &&& meta_to_index(new.list[k].paddr) == meta_to_index(old.list[p].paddr)
564        } by {}
565
566        assert forall|a: int, b: int|
567            #![trigger meta_to_index(new.list[a].paddr), meta_to_index(new.list[b].paddr)]
568            0 <= a < nlen && 0 <= b < nlen && a != b implies meta_to_index(new.list[a].paddr)
569            != meta_to_index(new.list[b].paddr) by {
570            let pa = if a < n {
571                a
572            } else {
573                a + 1
574            };
575            let pb = if b < n {
576                b
577            } else {
578                b + 1
579            };
580        }
581
582        assert forall|m: int| #![trigger new.meta_addr_at(fr, m)] 0 <= m < nlen implies {
583            let pm = if m < n {
584                m
585            } else {
586                m + 1
587            };
588            &&& new.meta_addr_at(fr, m) == old.meta_addr_at(r0, pm)
589            &&& new.meta_pptr_at(fr, m) == old.meta_pptr_at(r0, pm)
590        } by {
591            let pm = if m < n {
592                m
593            } else {
594                m + 1
595            };
596            let _ = old.list[pm];
597            old.relate_region_at_facts(r0, pm);
598        }
599
600        assert forall|k: int|
601            #![trigger new.relate_region_at(fr, k)]
602            0 <= k < nlen implies new.relate_region_at(fr, k) by {
603            let p = if k < n {
604                k
605            } else {
606                k + 1
607            };
608            let _ = old.list[p];
609            old.relate_region_at_facts(r0, p);
610            let _ = old.list[n];
611            old.relate_region_at_facts(r0, n);
612            if p - 1 >= 0 {
613                let _ = old.list[p - 1];
614                old.relate_region_at_facts(r0, p - 1);
615            }
616            if p + 1 < old.list.len() {
617                let _ = old.list[p + 1];
618                old.relate_region_at_facts(r0, p + 1);
619            }
620            if n - 1 >= 0 {
621                let _ = old.list[n - 1];
622                old.relate_region_at_facts(r0, n - 1);
623            }
624            if n + 1 < old.list.len() {
625                old.relate_region_at_facts(r0, n + 1);
626            }
627            new.relate_region_at_from_clauses(fr, k);
628        }
629
630    }
631
632    /// The list-rewiring "surgery" for inserting `link` before index `n`
633    /// (`0 <= n <= old.list.len()`): given the entry `relate_region` and a
634    /// per-slot characterization of the post-insert region `fr`, the longer list
635    /// `new = old.list.insert(n, link)` satisfies `relate_region(fr)`.
636    ///
637    /// New position `k` maps to old position `k` (k<n), is the inserted link
638    /// (k==n), or maps to old `k-1` (k>n). The inserted link sits at slot
639    /// `ins = meta_to_index(new.list[n].paddr)`; its `prev`/`next` point to old `n-1`/`n`
640    /// (or `None` at the ends), and old `n-1`'s `next` / old `n`'s `prev` are
641    /// rewired to point at the inserted link. Mirror of
642    /// [`pop_preserves_relate_region`].
643    pub open spec fn insert_old_slot_post_clauses(
644        self,
645        fr: MetaRegionOwners,
646        old: LinkedListOwner<M>,
647        r0: MetaRegionOwners,
648        n: int,
649        link: LinkOwner,
650        p: int,
651    ) -> bool {
652        let i = meta_to_index(old.list[p].paddr);
653        let ins = meta_to_index(self.list[n].paddr);
654        let np = if p < n {
655            p
656        } else {
657            p + 1
658        };
659        let fp = typed_meta_value::<Link<M>>(fr.slot_owners[i].metadata_perm, self.repr_perms[np]);
660        &&& fr.contains(i)
661        &&& fr.slots[i].addr() == old.list[p].paddr
662        &&& fr.slots[i].pptr() == r0.slots[i].pptr()
663        &&& fr.slot_owners[i].ref_count() == REF_COUNT_UNIQUE
664        &&& fr.slot_owners[i].usage is Frame
665        &&& fr.slot_owners[i].in_list_perm.value() == self.list_id
666        &&& self.meta_wf_at(fr, np)
667        &&& fr.slots[i].addr() % META_SLOT_SIZE == 0
668        &&& FRAME_METADATA_RANGE.start <= fr.slots[i].addr() < FRAME_METADATA_RANGE.start
669            + MAX_NR_PAGES * META_SLOT_SIZE
670        &&& (p == n - 1 ==> {
671            &&& fp.next is Some
672            &&& fp.next->0.addr() == link.paddr
673            &&& fp.next->0.ptr.addr() == fr.slots[ins].pptr().addr()
674        })
675        &&& (p != n - 1 ==> fp.next == old.meta_value_at(r0, p).next)
676        &&& (p == n ==> {
677            &&& fp.prev is Some
678            &&& fp.prev->0.addr() == link.paddr
679            &&& fp.prev->0.ptr.addr() == fr.slots[ins].pptr().addr()
680        })
681        &&& (p != n ==> fp.prev == old.meta_value_at(r0, p).prev)
682    }
683
684    #[verifier::opaque]
685    pub open spec fn insert_old_slot_post_at(
686        self,
687        fr: MetaRegionOwners,
688        old: LinkedListOwner<M>,
689        r0: MetaRegionOwners,
690        n: int,
691        link: LinkOwner,
692        p: int,
693    ) -> bool {
694        self.insert_old_slot_post_clauses(fr, old, r0, n, link, p)
695    }
696
697    pub proof fn insert_old_slot_post_at_facts(
698        self,
699        fr: MetaRegionOwners,
700        old: LinkedListOwner<M>,
701        r0: MetaRegionOwners,
702        n: int,
703        link: LinkOwner,
704        p: int,
705    )
706        requires
707            self.insert_old_slot_post_at(fr, old, r0, n, link, p),
708        ensures
709            self.insert_old_slot_post_clauses(fr, old, r0, n, link, p),
710    {
711        reveal(LinkedListOwner::insert_old_slot_post_at);
712    }
713
714    #[verifier::spinoff_prover]
715    #[verifier::rlimit(120)]
716    pub proof fn insert_preserves_relate_region(
717        old: LinkedListOwner<M>,
718        r0: MetaRegionOwners,
719        new: LinkedListOwner<M>,
720        fr: MetaRegionOwners,
721        n: int,
722        link: LinkOwner,
723    )
724        requires
725            0 <= n <= old.list.len(),
726            old.relate_region(r0),
727            new.list == old.list.insert(n, link),
728            new.repr_perms.len() == new.list.len(),
729            new.list_id != 0,
730            old.list.len() > 0 ==> new.list_id == old.list_id,
731            link.in_list == new.list_id,
732            forall|p: int|
733                #![trigger meta_to_index(old.list[p].paddr)]
734                (0 <= p < old.list.len()) ==> meta_to_index(old.list[p].paddr) != meta_to_index(
735                    new.list[n].paddr,
736                ),
737            ({
738                let ins = meta_to_index(new.list[n].paddr);
739                let fpn = new.meta_value_at(fr, n);
740                &&& fr.contains(ins)
741                &&& fr.slots[ins].addr() == link.paddr
742                &&& fr.slot_owners[ins].ref_count() == REF_COUNT_UNIQUE
743                &&& fr.slot_owners[ins].usage is Frame
744                &&& fr.slot_owners[ins].in_list_perm.value() == new.list_id
745                &&& new.meta_wf_at(fr, n)
746                &&& fr.slots[ins].addr() % META_SLOT_SIZE == 0
747                &&& FRAME_METADATA_RANGE.start <= fr.slots[ins].addr() < FRAME_METADATA_RANGE.start
748                    + MAX_NR_PAGES * META_SLOT_SIZE
749                &&& (n == 0 <==> fpn.prev is None)
750                &&& (n == old.list.len() <==> fpn.next is None)
751                &&& (n > 0 ==> {
752                    &&& fpn.prev is Some
753                    &&& fpn.prev->0.addr() == old.list[n - 1].paddr
754                    &&& fpn.prev->0.ptr.addr() == old.meta_pptr_at(r0, n - 1).addr()
755                })
756                &&& (n < old.list.len() ==> {
757                    &&& fpn.next is Some
758                    &&& fpn.next->0.addr() == old.list[n].paddr
759                    &&& fpn.next->0.ptr.addr() == old.meta_pptr_at(r0, n).addr()
760                })
761            }),
762            forall|p: int|
763                #![trigger new.insert_old_slot_post_at(fr, old, r0, n, link, p)]
764                (0 <= p < old.list.len()) ==> new.insert_old_slot_post_at(fr, old, r0, n, link, p),
765        ensures
766            new.relate_region(fr),
767    {
768        let nlen = new.list.len() as int;
769        let ins = meta_to_index(new.list[n].paddr);
770
771        assert forall|k: int| #![trigger meta_to_index(new.list[k].paddr)] 0 <= k < nlen implies ({
772            &&& (k < n ==> new.list[k] == old.list[k] && meta_to_index(new.list[k].paddr)
773                == meta_to_index(old.list[k].paddr))
774            &&& (k == n ==> new.list[k] == link && meta_to_index(new.list[k].paddr) == ins)
775            &&& (k > n ==> new.list[k] == old.list[k - 1] && meta_to_index(new.list[k].paddr)
776                == meta_to_index(old.list[k - 1].paddr))
777        }) by {}
778
779        assert forall|a: int, b: int|
780            #![trigger meta_to_index(new.list[a].paddr), meta_to_index(new.list[b].paddr)]
781            0 <= a < nlen && 0 <= b < nlen && a != b implies meta_to_index(new.list[a].paddr)
782            != meta_to_index(new.list[b].paddr) by {}
783
784        assert forall|m: int| #![trigger new.meta_addr_at(fr, m)] 0 <= m < nlen implies ({
785            &&& (m < n ==> new.meta_addr_at(fr, m) == old.meta_addr_at(r0, m) && new.meta_pptr_at(
786                fr,
787                m,
788            ) == old.meta_pptr_at(r0, m))
789            &&& (m > n ==> new.meta_addr_at(fr, m) == old.meta_addr_at(r0, m - 1)
790                && new.meta_pptr_at(fr, m) == old.meta_pptr_at(r0, m - 1))
791        }) by {
792            if m < n {
793                let _ = old.list[m];
794                old.relate_region_at_facts(r0, m);
795                new.insert_old_slot_post_at_facts(fr, old, r0, n, link, m);
796            }
797            if m > n {
798                let _ = old.list[m - 1];
799                old.relate_region_at_facts(r0, m - 1);
800                new.insert_old_slot_post_at_facts(fr, old, r0, n, link, m - 1);
801            }
802        }
803
804        assert forall|k: int|
805            #![trigger new.relate_region_at(fr, k)]
806            0 <= k < nlen implies new.relate_region_at(fr, k) by {
807            if k < n {
808                let _ = old.list[k];
809                old.relate_region_at_facts(r0, k);
810                new.insert_old_slot_post_at_facts(fr, old, r0, n, link, k);
811            }
812            if k > n {
813                let _ = old.list[k - 1];
814                old.relate_region_at_facts(r0, k - 1);
815                new.insert_old_slot_post_at_facts(fr, old, r0, n, link, k - 1);
816            }
817            if n - 1 >= 0 && n - 1 < old.list.len() {
818                let _ = old.list[n - 1];
819                old.relate_region_at_facts(r0, n - 1);
820            }
821            if n >= 0 && n < old.list.len() {
822                let _ = old.list[n];
823                old.relate_region_at_facts(r0, n);
824            }
825            new.relate_region_at_from_clauses(fr, k);
826        }
827
828        // `new` has `old.len + 1 ≥ 1 > 0` elements and a non-zero id by hypothesis.
829    }
830
831    pub open spec fn view_helper(owners: Seq<LinkOwner>) -> Seq<LinkModel>
832        decreases owners.len(),
833    {
834        if owners.len() == 0 {
835            Seq::<LinkModel>::empty()
836        } else {
837            seq![owners[0].view()].add(Self::view_helper(owners.remove(0)))
838        }
839    }
840
841    pub proof fn view_preserves_len(owners: Seq<LinkOwner>)
842        ensures
843            Self::view_helper(owners).len() == owners.len(),
844        decreases owners.len(),
845    {
846        if owners.len() > 0 {
847            Self::view_preserves_len(owners.remove(0))
848        }
849    }
850
851    /// Proves that view_helper preserves indexing: view_helper(s)[i] == s[i].view()
852    pub proof fn view_helper_index(owners: Seq<LinkOwner>, i: int)
853        requires
854            0 <= i < owners.len(),
855        ensures
856            Self::view_helper(owners)[i] == owners[i].view(),
857        decreases owners.len(),
858    {
859        Self::view_preserves_len(owners);
860        if i > 0 {
861            Self::view_helper_index(owners.remove(0), i - 1);
862        }
863    }
864
865    /// Proves that view_helper commutes with remove:
866    /// view_helper(s.remove(i)) == view_helper(s).remove(i)
867    pub proof fn view_helper_remove(owners: Seq<LinkOwner>, i: int)
868        requires
869            0 <= i < owners.len(),
870        ensures
871            Self::view_helper(owners.remove(i)) == Self::view_helper(owners).remove(i),
872    {
873        Self::view_preserves_len(owners);
874        Self::view_preserves_len(owners.remove(i));
875        assert forall|j: int|
876            0 <= j < Self::view_helper(owners.remove(i)).len() implies Self::view_helper(
877            owners.remove(i),
878        )[j] == Self::view_helper(owners).remove(i)[j] by {
879            Self::view_helper_index(owners.remove(i), j);
880            if j < i {
881                Self::view_helper_index(owners, j);
882            } else {
883                Self::view_helper_index(owners, j + 1);
884            }
885        };
886    }
887
888    /// Proves that view_helper commutes with insert:
889    /// view_helper(s.insert(i, v)) == view_helper(s).insert(i, v.view())
890    pub proof fn view_helper_insert(owners: Seq<LinkOwner>, i: int, v: LinkOwner)
891        requires
892            0 <= i <= owners.len(),
893        ensures
894            Self::view_helper(owners.insert(i, v)) == Self::view_helper(owners).insert(i, v.view()),
895    {
896        Self::view_preserves_len(owners);
897        Self::view_preserves_len(owners.insert(i, v));
898        assert forall|j: int|
899            0 <= j < Self::view_helper(
900                owners.insert(i, v),
901            ).len() implies #[trigger] Self::view_helper(owners.insert(i, v))[j]
902            == Self::view_helper(owners).insert(i, v.view())[j] by {
903            Self::view_helper_index(owners.insert(i, v), j);
904            if j < i {
905                Self::view_helper_index(owners, j);
906            } else if j == i {
907                // owners.insert(i, v)[i] == v, and view_helper(owners).insert(i, v@)[i] == v@
908            } else {
909                Self::view_helper_index(owners, j - 1);
910            }
911        };
912    }
913}
914
915impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> View for LinkedListOwner<M> {
916    type V = LinkedListModel;
917
918    open spec fn view(&self) -> Self::V {
919        LinkedListModel { list: Self::view_helper(self.list) }
920    }
921}
922
923impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> InvView for LinkedListOwner<M> {
924    proof fn view_preserves_inv(self) {
925    }
926}
927
928impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedListOwner<M> {
929    /// Take ownership of `*owner` by swapping it with a fresh empty
930    /// `LinkedListOwner`. The resulting "leftover" `*owner` has an empty
931    /// `list`, so its `inv()` holds vacuously. Used by drop-style call sites
932    /// that need to feed an owned `LinkedListOwner` to a downstream API while
933    /// themselves only having a `&mut` to it.
934    pub proof fn tracked_take(tracked owner: &mut Self) -> (tracked res: Self)
935        ensures
936            res == *old(owner),
937            final(owner).list == Seq::<LinkOwner>::empty(),
938            final(owner).repr_perms == Seq::<LinkInnerPerms<M>>::empty(),
939            final(owner).inv(),
940    {
941        let tracked mut tmp = crate::specs::mm::embedding::list_store::tracked_empty_list_owner::<
942            M,
943        >();
944        tracked_swap(owner, &mut tmp);
945        tmp
946    }
947
948    /// Discard a logically-empty `LinkedListOwner`. Sound because such an
949    /// owner has an empty `list` and claims no external permissions (the
950    /// borrow model parks all permissions in `MetaRegionOwners`).
951    #[verifier::external_body]
952    pub proof fn tracked_destroy_empty(tracked self)
953        requires
954            self.list =~= Seq::<LinkOwner>::empty(),
955            self.repr_perms =~= Seq::<LinkInnerPerms<M>>::empty(),
956    {
957        unimplemented!()
958    }
959}
960
961impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> OwnerOf for LinkedList<M> {
962    type Owner = LinkedListOwner<M>;
963
964    /// Structural well-formedness of the LinkedList against its owner: size,
965    /// list_id, and front/back addresses match. The per-link pointer-permission
966    /// facts (pptr/ptr equality against the front/back) live in `wf_region`,
967    /// which sources them from `MetaRegionOwners`.
968    open spec fn wf(self, owner: Self::Owner) -> bool {
969        &&& self.front is None <==> owner.list.len() == 0
970        &&& self.back is None <==> owner.list.len() == 0
971        &&& owner.list.len() > 0 ==> self.front is Some && self.front->0.addr()
972            == owner.list[0].paddr && self.back is Some && self.back->0.addr()
973            == owner.list[owner.list.len() - 1].paddr
974        &&& self.size == owner.list.len()
975        &&& self.list_id == owner.list_id
976    }
977}
978
979pub ghost struct CursorModel {
980    pub ghost fore: Seq<LinkModel>,
981    pub ghost rear: Seq<LinkModel>,
982    pub ghost list_model: LinkedListModel,
983}
984
985impl Inv for CursorModel {
986    open spec fn inv(self) -> bool {
987        self.list_model.inv()
988    }
989}
990
991pub tracked struct CursorOwner<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
992    pub list_own: LinkedListOwner<M>,
993    pub ghost index: int,
994}
995
996impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> Inv for CursorOwner<M> {
997    open spec fn inv(self) -> bool {
998        &&& 0 <= self.index <= self.length()
999        &&& self.list_own.inv()
1000    }
1001}
1002
1003impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> View for CursorOwner<M> {
1004    type V = CursorModel;
1005
1006    open spec fn view(&self) -> Self::V {
1007        let list = self.list_own.view();
1008        CursorModel {
1009            fore: list.list.take(self.index),
1010            rear: list.list.skip(self.index),
1011            list_model: list,
1012        }
1013    }
1014}
1015
1016impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> InvView for CursorOwner<M> {
1017    proof fn view_preserves_inv(self) {
1018    }
1019}
1020
1021impl<'a, M: AnyFrameMeta + Repr<MetaSlotSmall>> OwnerOf for CursorMut<'a, M> {
1022    type Owner = CursorOwner<M>;
1023
1024    /// Structural well-formedness: `current` matches the link at `index`'s
1025    /// address. Pointer-permission facts (pptr/ptr equality) are stated in
1026    /// `wf_region` over the region-owned slot pointers.
1027    open spec fn wf(self, owner: Self::Owner) -> bool {
1028        &&& 0 <= owner.index < owner.length() ==> self.current.is_some() && self.current->0.addr()
1029            == owner.list_own.list[owner.index].paddr
1030        &&& owner.index == owner.list_own.list.len() ==> self.current.is_none()
1031        &&& (*self.list).wf(owner.list_own)
1032    }
1033}
1034
1035impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> LinkedList<M> {
1036    /// Region-based analog of [`LinkedList::wf`]: the front/back pointer facts
1037    /// are stated directly over the region-owned slot pointers.
1038    pub open spec fn wf_region(self, owner: LinkedListOwner<M>, regions: MetaRegionOwners) -> bool {
1039        &&& self.front is None <==> owner.list.len() == 0
1040        &&& self.back is None <==> owner.list.len() == 0
1041        &&& owner.list.len() > 0 ==> self.front is Some && self.front->0.addr()
1042            == owner.list[0].paddr && owner.meta_pptr_at(regions, 0).addr() == self.front->0.addr()
1043            && self.back is Some && self.back->0.addr() == owner.list[owner.list.len() - 1].paddr
1044            && owner.meta_pptr_at(regions, owner.list.len() - 1).addr() == self.back->0.addr()
1045        &&& self.size == owner.list.len()
1046        &&& self.list_id == owner.list_id
1047    }
1048}
1049
1050impl<'a, M: AnyFrameMeta + Repr<MetaSlotSmall>> CursorMut<'a, M> {
1051    /// Region-based analog of [`CursorMut::wf`]: the current-link pointer facts
1052    /// are stated over the corresponding region-owned slot pointer.
1053    pub open spec fn wf_region(self, owner: CursorOwner<M>, regions: MetaRegionOwners) -> bool {
1054        &&& 0 <= owner.index < owner.length() ==> self.current.is_some() && self.current->0.addr()
1055            == owner.list_own.list[owner.index].paddr && owner.list_own.meta_pptr_at(
1056            regions,
1057            owner.index,
1058        ).addr() == self.current->0.addr()
1059        &&& owner.index == owner.list_own.list.len() ==> self.current.is_none()
1060        &&& (*self.list).wf_region(owner.list_own, regions)
1061    }
1062}
1063
1064impl CursorModel {
1065    pub open spec fn current(self) -> Option<LinkModel> {
1066        if self.rear.len() > 0 {
1067            Some(self.rear[0])
1068        } else {
1069            None
1070        }
1071    }
1072}
1073
1074impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> CursorOwner<M> {
1075    pub open spec fn length(self) -> int {
1076        self.list_own.list.len() as int
1077    }
1078
1079    /// Region-based analog of [`CursorOwner::inv`]: replaces `list_own.inv()`
1080    /// (over the owned `perms`) with `list_own.relate_region(regions)` (over
1081    /// the region permissions).
1082    pub open spec fn wf_with_region(self, regions: MetaRegionOwners) -> bool {
1083        &&& 0 <= self.index <= self.length()
1084        &&& self.list_own.relate_region(regions)
1085    }
1086
1087    pub open spec fn current(self) -> Option<LinkOwner> {
1088        if 0 <= self.index < self.length() {
1089            Some(self.list_own.list[self.index])
1090        } else {
1091            None
1092        }
1093    }
1094
1095    pub open spec fn list_insert(
1096        cursor: Self,
1097        link: LinkOwner,
1098        repr_perm: LinkInnerPerms<M>,
1099        list_id: u64,
1100    ) -> (Self, LinkOwner) {
1101        let link = LinkOwner { paddr: link.paddr, in_list: list_id };
1102        (
1103            Self {
1104                list_own: LinkedListOwner::<M> {
1105                    list: cursor.list_own.list.insert(cursor.index, link),
1106                    repr_perms: cursor.list_own.repr_perms.insert(cursor.index, repr_perm),
1107                    list_id,
1108                    _marker: PhantomData,
1109                },
1110                index: cursor.index + 1,
1111            },
1112            link,
1113        )
1114    }
1115
1116    /// Tracked update to the cursor's owner state when a new link is inserted
1117    /// before the current position. The inserted `link`'s `paddr` is unchanged
1118    /// and its `in_list` is stamped with `list_id` — the (non-zero) id the
1119    /// concrete `lazy_get_id` resolved. The cursor's list gains `link` at
1120    /// `old.index`, adopts `list_id` (which equals the old id when that was
1121    /// already non-zero), and `index` advances by one. In the borrow model, the
1122    /// link's tracked permission remains parked in `MetaRegionOwners.slots`;
1123    /// this axiom doesn't need to take or carry a perm.
1124    pub proof fn tracked_list_insert(
1125        tracked cursor: &mut Self,
1126        tracked link: &mut LinkOwner,
1127        tracked repr_perm: LinkInnerPerms<M>,
1128        list_id: u64,
1129    )
1130        requires
1131            list_id != 0,
1132            0 <= old(cursor).index <= old(cursor).list_own.list.len(),
1133            old(cursor).list_own.repr_perms.len() == old(cursor).list_own.list.len(),
1134            old(cursor).list_own.list.len() > 0 ==> list_id == old(cursor).list_own.list_id,
1135            old(cursor).list_own.list_id != 0 ==> list_id == old(cursor).list_own.list_id,
1136        ensures
1137            ({
1138                let res = Self::list_insert(*old(cursor), *old(link), repr_perm, list_id);
1139
1140                res.0 == *final(cursor) && res.1 == *final(link)
1141            }),
1142    {
1143        let ghost idx = cursor.index;
1144        let ghost link_paddr = link.paddr;
1145        let tracked list_entry = LinkOwner { paddr: link_paddr, in_list: list_id };
1146
1147        cursor.list_own.list.tracked_insert(idx, list_entry);
1148        cursor.list_own.repr_perms.tracked_insert(idx, repr_perm);
1149        cursor.list_own.list_id = list_id;
1150        cursor.index = idx + 1;
1151        *link = LinkOwner { paddr: link_paddr, in_list: list_id };
1152    }
1153
1154    pub open spec fn front_owner(list_own: LinkedListOwner<M>) -> Self {
1155        CursorOwner::<M> { list_own: list_own, index: 0 }
1156    }
1157
1158    pub open spec fn cursor_mut_at_owner(list_own: LinkedListOwner<M>, index: int) -> Self {
1159        CursorOwner::<M> { list_own: list_own, index: index }
1160    }
1161
1162    pub proof fn tracked_cursor_mut_at_owner(
1163        tracked list_own: LinkedListOwner<M>,
1164        index: int,
1165    ) -> tracked Self
1166        returns
1167            Self::cursor_mut_at_owner(list_own, index),
1168    {
1169        let tracked res = CursorOwner::<M> { list_own, index };
1170        res
1171    }
1172
1173    pub proof fn tracked_front_owner(tracked list_own: LinkedListOwner<M>) -> tracked Self
1174        returns
1175            Self::front_owner(list_own),
1176    {
1177        let tracked res = CursorOwner::<M> { list_own, index: 0 };
1178        res
1179    }
1180
1181    pub open spec fn back_owner(list_own: LinkedListOwner<M>) -> Self {
1182        CursorOwner::<M> {
1183            list_own: list_own,
1184            index: if list_own.list.len() > 0 {
1185                list_own.list.len() - 1
1186            } else {
1187                0
1188            },
1189        }
1190    }
1191
1192    pub proof fn tracked_back_owner(tracked list_own: LinkedListOwner<M>) -> (tracked res: Self)
1193        ensures
1194            res == Self::back_owner(list_own),
1195    {
1196        CursorOwner::<M> {
1197            list_own: list_own,
1198            index: if list_own.list.len() > 0 {
1199                list_own.list.len() - 1
1200            } else {
1201                0
1202            },
1203        }
1204    }
1205
1206    pub open spec fn ghost_owner(list_own: LinkedListOwner<M>) -> Self {
1207        CursorOwner::<M> { list_own: list_own, index: list_own.list.len() as int }
1208    }
1209
1210    pub proof fn tracked_ghost_owner(tracked list_own: LinkedListOwner<M>) -> (tracked res: Self)
1211        ensures
1212            res == Self::ghost_owner(list_own),
1213    {
1214        CursorOwner::<M> { list_own: list_own, index: list_own.list.len() as int }
1215    }
1216}
1217
1218impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> UniqueFrameOwner<Link<M>> {
1219    pub open spec fn frame_link_inv(&self, regions: MetaRegionOwners) -> bool {
1220        &&& self.meta_value(regions).prev is None
1221        &&& self.meta_value(regions).next is None
1222        &&& self.meta_own.paddr == regions.slots[self.slot_index].addr()
1223        &&& regions.slot_owners[self.slot_index].in_list_perm.value() == 0
1224    }
1225}
1226
1227} // verus!