Skip to main content

ostd/specs/mm/embedding/
list_store.rs

1//! Self-contained one-step-soundness harness for the frame `LinkedList`
2//! ([`crate::mm::frame::LinkedList`]) — the embedding's companion to
3//! [`super::VmStore`], specialised to linked-list operations.
4//!
5//! # Why a separate, generic store
6//!
7//! Unlike `VmStore` (which fixes concrete configs such as
8//! `UserPtConfig`), `ListStore<M>` is *generic* over the link metadata
9//! `M`. The kernel's `LinkedList<M>` is a generic library with no
10//! canonical concrete instantiation in ostd, and `LinkedListOwner<M>`
11//! cannot be type-erased to `dyn`: its per-link permission is the
12//! associated type `<M as Repr<MetaSlotSmall>>::Perm`, embedded in
13//! `LinkInnerPerms<M>`, so the trait is not object-safe (cf.
14//! `Frame<dyn AnyFrameMeta>`, which works only because it exposes no
15//! associated type post-erasure).
16//!
17//! # Why `in_list` is a non-issue here
18//!
19//! `ListStore<M>` requires only `regions.inv()`
20//! ([`MetaRegionOwners::inv`]), which — unlike
21//! `VmStore::structural_inv` — does **not** constrain `in_list`. A
22//! listed frame sits at `rc == REF_COUNT_UNIQUE` with
23//! `in_list == list_id != 0`; the UNIQUE branch of `MetaSlotOwner::inv`
24//! pins only `storage`/`vtable_ptr` init, leaving `in_list` free. So
25//! listed frames are admitted with *no* invariant weakening — the
26//! `in_list == 0` constraint is purely a `VmStore` concern and does not
27//! arise in this harness.
28//!
29//! # State
30//!
31//! - `regions`: the shared metadata-region ownership.
32//! - `lists`: held [`LinkedListOwner`]s. Each link is a forgotten
33//!   `UniqueFrame<Link<M>>` (its drop-obligation was consumed by
34//!   `into_raw` on push); the owner's [`LinkedListOwner::relate_region`]
35//!   ties every link to its UNIQUE region slot and pins the
36//!   `next`/`prev` pointer wiring.
37//! - `loose`: held-but-unlisted [`UniqueFrameOwner`]s — live
38//!   `UniqueFrame<Link<M>>` handles (drop-obligation present) eligible
39//!   to be pushed. `push` moves one from `loose` into a list; `pop`
40//!   moves a list's end link out into `loose`.
41//!
42//! # Roadmap
43//!
44//! Landed: the store + invariant, the front/back
45//! allocate-build-teardown suite (`new`, `push_front` / `pop_front`,
46//! `push_back` / `pop_back`), the general cursor surgery —
47//! `insert_before` / `take_current` at an *arbitrary* index
48//! ([`ListStore::step_insert_before_at`] / [`ListStore::step_take_at`]),
49//! which subsume the front/back ops — the read-only accessors
50//! ([`ListStore::step_size`] / [`ListStore::step_is_empty`]), and the
51//! full **persistent cursor** lifecycle: a cursor checks its list out of
52//! `lists` into `cursors` ([`ListStore::step_cursor_front_mut`] /
53//! `step_cursor_back_mut` / `step_cursor_mut_at`), walks it
54//! (`step_move_next` / `step_move_prev` / `step_current_meta`), mutates
55//! through it (`step_cursor_insert_before` / `step_cursor_take_current`),
56//! and checks it back in on drop ([`ListStore::step_cursor_drop`]).
57use vstd::prelude::*;
58use vstd_extra::{cast_ptr::Repr, ownership::*, set_extra::lemma_finite_int_set_has_unused};
59
60use crate::specs::{
61    arch::valid_frame_paddr,
62    mm::frame::{
63        linked_list::linked_list_owners::{
64            CursorOwner, LinkInnerPerms, LinkOwner, LinkedListOwner, MetaSlotSmall,
65        },
66        mapping::{frame_to_index, meta_to_index},
67        meta_owners::PageUsage,
68        meta_region_owners::MetaRegionOwners,
69        unique::UniqueFrameOwner,
70    },
71};
72
73use crate::mm::{
74    Paddr,
75    frame::{
76        AnyFrameMeta, Link,
77        meta::{REF_COUNT_UNIQUE, REF_COUNT_UNUSED},
78    },
79};
80
81verus! {
82
83/// Logical identifier for a held [`LinkedListOwner`] in the store.
84pub type ListId = int;
85
86/// Logical identifier for a loose (held-but-unlisted)
87/// `UniqueFrame<Link<M>>` in the store.
88pub type LooseId = int;
89
90/// Logical identifier for a live [`CursorOwner`] in the store. A cursor
91/// is keyed by the *home* [`ListId`] whose list it checked out, so a
92/// list is cursored iff its id is in `cursors` (and then absent from
93/// `lists`).
94pub type CursorId = ListId;
95
96/// The membership registry relating one (held or checked-out) list `lo`
97/// to the physical `in_list` tags in `regions`:
98///   - **forward**: every link's region slot carries `lo.list_id` (the
99///     exec `insert_before` stamps it via `store(lazy_get_id())`);
100///   - **reverse** (only for a real, non-zero id): every region slot
101///     carrying that id is one of `lo`'s links — the global
102///     `in_list`-uniqueness the id allocator guarantees (a freshly
103///     minted id is system-wide unused; ids are never reused).
104/// Together they make `in_list == list_id` an *exact* membership test,
105/// which is exactly what [`crate::mm::frame::LinkedList::contains`]
106/// computes.
107pub open spec fn list_registry_ok<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
108    regions: MetaRegionOwners,
109    lo: LinkedListOwner<M>,
110) -> bool {
111    &&& forall|i: int|
112        #![trigger meta_to_index(lo.list[i].paddr)]
113        0 <= i < lo.list.len() ==> regions.slot_owners[meta_to_index(
114            lo.list[i].paddr,
115        )].in_list_perm.value() == lo.list_id
116    &&& lo.list_id != 0 ==> forall|idx: int|
117        #![trigger regions.slot_owners[idx]]
118        regions.contains(idx) && regions.slot_owners[idx].in_list_perm.value() == lo.list_id
119            ==> exists|i: int|
120            0 <= i < lo.list.len() && #[trigger] meta_to_index(lo.list[i].paddr) == idx
121}
122
123/// One-step-soundness store for the frame `LinkedList`. Holds the shared
124/// `regions`, the set of held lists, the pool of loose
125/// (push-eligible) `UniqueFrame<Link<M>>` handles, and the live cursors.
126///
127/// A cursor *checks out* its list: a live `CursorMut` borrows the
128/// `LinkedList` exclusively, so while a cursor exists its
129/// `LinkedListOwner` lives inside the [`CursorOwner`] (`cursors`) rather
130/// than in `lists`. Dropping the cursor returns the list to `lists`.
131pub tracked struct ListStore<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
132    pub regions: MetaRegionOwners,
133    pub lists: Map<ListId, LinkedListOwner<M>>,
134    pub loose: Map<LooseId, UniqueFrameOwner<Link<M>>>,
135    pub cursors: Map<CursorId, CursorOwner<M>>,
136}
137
138impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M> {
139    /// The store's top-level invariant.
140    pub open spec fn inv(self) -> bool {
141        &&& self.regions.inv()
142        // Each held list is well-formed and every link relates to its
143        // UNIQUE region slot (incl. the `next`/`prev` pointer wiring).
144        &&& forall|id: ListId| #[trigger]
145            self.lists.dom().contains(id) ==> {
146                &&& self.lists[id].inv()
147                &&& self.lists[id].relate_region(self.regions)
148            }
149            // Each loose handle is a valid live `UniqueFrame<Link<M>>`:
150            // a UNIQUE slot with a pending drop-obligation, sitting
151            // *outside* every list — `in_list == 0` and unlinked
152            // (`frame_link_inv`: no `prev`/`next`). The `in_list == 0`
153            // fact makes list-vs-loose slot disjointness derivable (a
154            // listed slot has `in_list == list_id != 0`).
155        &&& forall|lid: LooseId| #[trigger]
156            self.loose.dom().contains(lid) ==> {
157                &&& self.loose[lid].inv()
158                &&& self.loose[lid].global_inv(self.regions)
159                &&& self.loose[lid].frame_link_inv(self.regions)
160                &&& self.regions.slot_owners[self.loose[lid].slot_index].in_list_perm.value() == 0
161            }
162            // Distinct lists carry distinct *nonzero* ids (`lazy_get_id`
163            // mints a globally fresh id per list — even a list emptied by
164            // pops keeps its unique id; only never-pushed lists share the
165            // placeholder `list_id == 0`). With each link's
166            // `in_list == list_id`, this makes cross-list slot
167            // disjointness derivable.
168        &&& forall|id1: ListId, id2: ListId|
169            #![trigger self.lists.dom().contains(id1), self.lists.dom().contains(id2)]
170            self.lists.dom().contains(id1) && self.lists.dom().contains(id2)
171                && self.lists[id1].list_id == self.lists[id2].list_id && self.lists[id1].list_id
172                != 0 ==> id1
173                == id2
174            // Distinct loose handles occupy distinct slots (a UNIQUE frame
175            // is held in at most one place).
176        &&& forall|lid1: LooseId, lid2: LooseId|
177            #![trigger self.loose.dom().contains(lid1), self.loose.dom().contains(lid2)]
178            self.loose.dom().contains(lid1) && self.loose.dom().contains(lid2)
179                && self.loose[lid1].slot_index == self.loose[lid2].slot_index ==> lid1
180                == lid2
181            // A cursored list is *checked out*: it lives in `cursors`
182            // (keyed by its home id), never simultaneously in `lists`. This
183            // is the borrow — a live `CursorMut` holds the list exclusively.
184        &&& self.lists.dom().disjoint(
185            self.cursors.dom(),
186        )
187        // Each live cursor's checked-out list is well-formed and every
188        // link relates to its UNIQUE region slot, exactly as for a held
189        // list; additionally the cursor index is in range
190        // (`wf_with_region`). `list_own.inv()` is carried so the trusted
191        // per-op other-lists frame (stated over `inv() && relate_region`)
192        // applies to a cursor's list under region-changing ops.
193        &&& forall|cid: CursorId| #[trigger]
194            self.cursors.dom().contains(cid) ==> {
195                &&& self.cursors[cid].list_own.inv()
196                &&& self.cursors[cid].wf_with_region(self.regions)
197            }
198            // A cursor's list shares no nonzero id with any held list —
199            // cross list/cursor slot disjointness, mirroring lists×lists.
200        &&& forall|id: ListId, cid: CursorId|
201            #![trigger self.lists.dom().contains(id), self.cursors.dom().contains(cid)]
202            self.lists.dom().contains(id) && self.cursors.dom().contains(cid)
203                && self.lists[id].list_id == self.cursors[cid].list_own.list_id
204                && self.lists[id].list_id != 0
205                ==> false
206            // Distinct cursors carry distinct *nonzero* list ids.
207        &&& forall|cid1: CursorId, cid2: CursorId|
208            #![trigger self.cursors.dom().contains(cid1), self.cursors.dom().contains(cid2)]
209            self.cursors.dom().contains(cid1) && self.cursors.dom().contains(cid2)
210                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
211                && self.cursors[cid1].list_own.list_id != 0 ==> cid1
212                == cid2
213            // Membership registry: each held list's id tags exactly its own
214            // links in the region (forward + reverse — see [`list_registry_ok`]).
215            // This is what makes `contains` an exact membership test.
216        &&& forall|id: ListId| #[trigger]
217            self.lists.dom().contains(id) ==> list_registry_ok(
218                self.regions,
219                self.lists[id],
220            )
221        // Same registry for each checked-out cursor's list.
222        &&& forall|cid: CursorId| #[trigger]
223            self.cursors.dom().contains(cid) ==> list_registry_ok(
224                self.regions,
225                self.cursors[cid].list_own,
226            )
227    }
228}
229
230// =============================================================================
231// Fresh-id helpers + tracked constructors
232// =============================================================================
233/// Tracked constructor for a fresh *empty* list owner. Sound: an empty
234/// `LinkedListOwner` claims no permissions (cf.
235/// [`LinkedListOwner::tracked_destroy_empty`]), and carries
236/// `list_id == 0` — the real id is minted lazily on first push.
237pub proof fn tracked_empty_list_owner<M: AnyFrameMeta + Repr<MetaSlotSmall>>() -> (tracked res:
238    LinkedListOwner<M>)
239    ensures
240        res.list =~= Seq::<LinkOwner>::empty(),
241        res.repr_perms =~= Seq::<LinkInnerPerms<M>>::empty(),
242        res.list_id == 0,
243{
244    let tracked list = Seq::<LinkOwner>::tracked_empty();
245    let tracked repr_perms = Seq::<LinkInnerPerms<M>>::tracked_empty();
246    let tracked res = LinkedListOwner::<M> {
247        list,
248        repr_perms,
249        list_id: 0,
250        _marker: core::marker::PhantomData,
251    };
252    res
253}
254
255/// Fresh-id helper for the list id space. The id must avoid both held
256/// lists *and* checked-out cursors (a cursored list's home id is absent
257/// from `lists` but reserved in `cursors`).
258pub open spec fn fresh_list_id<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
259    lists: Map<ListId, LinkedListOwner<M>>,
260    cursors: Map<CursorId, CursorOwner<M>>,
261) -> ListId {
262    choose|id: ListId| !lists.dom().contains(id) && !cursors.dom().contains(id)
263}
264
265pub proof fn lemma_fresh_list_id_not_in_dom<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
266    lists: Map<ListId, LinkedListOwner<M>>,
267    cursors: Map<CursorId, CursorOwner<M>>,
268)
269    ensures
270        !lists.dom().contains(fresh_list_id(lists, cursors)) && !cursors.dom().contains(
271            fresh_list_id(lists, cursors),
272        ),
273{
274    lemma_finite_int_set_has_unused(lists.dom() + cursors.dom());
275}
276
277/// Trusted reflection of [`crate::mm::frame::LinkedList::push_front`]'s
278/// effect on `(regions, owner, frame_own)`. The first block of `ensures`
279/// mirrors the now-verified exec `push_front` ensures verbatim
280/// (`relate_region` of the pushed owner, the list / id / `in_list`
281/// effects, `s` consumption, and the outside-the-list
282/// slot-preservation frame). The last two add facts that *follow* from
283/// them — sound, hence safe to assert here:
284///   - **fresh minted id** (`old.list_id == 0 ==> final.list_id ∉
285///     used_ids`): the exec mints the id from a global counter, so it is
286///     fresh w.r.t. any finite in-use set; the caller passes the other
287///     lists' ids, keeping cross-list id uniqueness.
288///   - **other lists preserved**: any well-formed list `l` with a
289///     *different* id keeps its `relate_region`. The only slots the
290///     surgery touches are the loose frame's (`in_list == 0`, required
291///     below) and the old front's (`in_list == new id`); both are
292///     disjoint from `l`'s slots (which carry `in_list == l.list_id`),
293///     so by the slot-preservation frame `l` is untouched.
294pub proof fn push_front_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
295    tracked regions: &mut MetaRegionOwners,
296    tracked owner: &mut LinkedListOwner<M>,
297    tracked frame_own: &mut UniqueFrameOwner<Link<M>>,
298    used_ids: Set<u64>,
299)
300    requires
301        old(regions).inv(),
302        old(owner).inv(),
303        old(owner).relate_region(*old(regions)),
304        old(frame_own).inv(),
305        old(frame_own).global_inv(*old(regions)),
306        old(frame_own).frame_link_inv(*old(regions)),
307        old(regions).slot_owners[old(frame_own).slot_index].in_list_perm.value() == 0,
308    ensures
309        final(regions).inv(),
310        final(owner).inv(),
311        final(owner).relate_region(*final(regions)),
312        final(owner).list == old(owner).list.insert(0, final(frame_own).meta_own),
313        old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
314        final(owner).list_id != 0,
315        old(owner).list_id == 0 ==> !used_ids.contains(final(owner).list_id),
316        final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
317        final(frame_own).meta_own.in_list == final(owner).list_id,
318        final(regions).frame_obligations =~= old(regions).frame_obligations.remove(
319            old(frame_own).slot_index,
320        ),
321        forall|k: int|
322            #![trigger final(regions).slots[k]]
323            #![trigger final(regions).slot_owners[k]]
324            k != old(frame_own).slot_index && (old(owner).list.len() > 0 ==> k != meta_to_index(
325                old(owner).list[0].paddr,
326            )) ==> final(regions).slots[k] == old(regions).slots[k] && final(regions).slot_owners[k]
327                == old(regions).slot_owners[k],
328        forall|l: LinkedListOwner<M>|
329            #![trigger l.relate_region(*old(regions))]
330            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
331                ==> l.relate_region(*final(regions)),
332        // Membership registry for the operated list: forward stamp of
333        // the (minted or preserved) id + reverse global uniqueness (see
334        // [`list_registry_ok`]).
335        list_registry_ok(*final(regions), *final(owner)),
336        // Every other list/cursor list keeps its registry: the only slot
337        // the surgery retags now carries `final(owner).list_id` (or 0),
338        // never another list's id — so no foreign list gains or loses a
339        // tagged slot.
340        forall|l: LinkedListOwner<M>|
341            #![trigger l.relate_region(*old(regions))]
342            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
343                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
344        // **other loose handles preserved**: a loose frame `fo` sitting
345        // at a different `in_list == 0` slot is untouched. Sound by the
346        // same disjointness — a list slot carries `in_list == list_id
347        // != 0`, so `fo`'s slot is neither the pushed frame's nor the
348        // old front's.
349        forall|fo: UniqueFrameOwner<Link<M>>|
350            #![trigger fo.global_inv(*old(regions))]
351            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
352                regions,
353            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 && fo.slot_index != old(
354                frame_own,
355            ).slot_index ==> fo.global_inv(*final(regions)) && fo.frame_link_inv(*final(regions))
356                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0,
357{
358    insert_before_at_embedded(regions, owner, frame_own, 0, used_ids);
359}
360
361/// Fresh-id helper for the loose-frame id space.
362pub open spec fn fresh_loose_id<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
363    m: Map<LooseId, UniqueFrameOwner<Link<M>>>,
364) -> LooseId {
365    choose|id: LooseId| !m.dom().contains(id)
366}
367
368pub proof fn lemma_fresh_loose_id_not_in_dom<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
369    m: Map<LooseId, UniqueFrameOwner<Link<M>>>,
370)
371    ensures
372        !m.dom().contains(fresh_loose_id(m)),
373{
374    lemma_finite_int_set_has_unused(m.dom());
375}
376
377/// Checked front specialization of [`take_at_embedded`], reflecting the
378/// (now properly `&mut owner`-threaded and fully verified)
379/// [`crate::mm::frame::LinkedList::pop_front`]. Pops the
380/// front link off `owner`, restoring it to a loose
381/// `UniqueFrame<Link<M>>` (its drop-obligation re-minted by `from_raw`,
382/// `in_list` reset to 0, `prev`/`next` cleared). The list shrinks by one
383/// from the front with `list_id` preserved.
384///
385/// The first block of `ensures` mirrors the verified exec `pop_front`
386/// verbatim. The last two are the sound companion facts (cf.
387/// [`push_front_embedded`]): other lists and other loose frames are
388/// untouched, and — additionally — the popped slot is *distinct* from
389/// every loose slot (it was a list link, `in_list == list_id != 0`),
390/// which keeps loose-slot disjointness when the popped frame joins
391/// `loose`.
392pub proof fn tracked_pop_front_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
393    tracked regions: &mut MetaRegionOwners,
394    tracked owner: &mut LinkedListOwner<M>,
395) -> (tracked frame_own: UniqueFrameOwner<Link<M>>)
396    requires
397        old(regions).inv(),
398        old(owner).inv(),
399        old(owner).relate_region(*old(regions)),
400        old(owner).list.len() > 0,
401    ensures
402        final(regions).inv(),
403        final(owner).inv(),
404        final(owner).relate_region(*final(regions)),
405        final(owner).list == old(owner).list.remove(0),
406        final(owner).list_id == old(owner).list_id,
407        // The popped frame is a valid loose handle at the old front slot.
408        frame_own.inv(),
409        frame_own.global_inv(*final(regions)),
410        frame_own.frame_link_inv(*final(regions)),
411        frame_own.slot_index == meta_to_index(old(owner).list[0].paddr),
412        final(regions).slot_owners[frame_own.slot_index].in_list_perm.value() == 0,
413        // `from_raw` re-mints the drop-obligation.
414        final(regions).frame_obligations =~= old(regions).frame_obligations.insert(
415            meta_to_index(old(owner).list[0].paddr),
416        ),
417        // Outside-the-list slot preservation (front specialisation:
418        // popped slot + the new front's metadata index).
419        forall|j: int|
420            #![trigger final(regions).slots[j]]
421            #![trigger final(regions).slot_owners[j]]
422            j != meta_to_index(old(owner).list[0].paddr) && (old(owner).list.len() > 1 ==> j
423                != meta_to_index(old(owner).list[1].paddr)) ==> final(regions).slots[j] == old(
424                regions,
425            ).slots[j] && final(regions).slot_owners[j] == old(regions).slot_owners[j],
426        // Other lists preserved.
427        forall|l: LinkedListOwner<M>|
428            #![trigger l.relate_region(*old(regions))]
429            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
430                ==> l.relate_region(*final(regions)),
431        // Membership registry for the operated list: forward stamp of
432        // the (minted or preserved) id + reverse global uniqueness (see
433        // [`list_registry_ok`]).
434        list_registry_ok(*final(regions), *final(owner)),
435        // Every other list/cursor list keeps its registry: the only slot
436        // the surgery retags now carries `final(owner).list_id` (or 0),
437        // never another list's id — so no foreign list gains or loses a
438        // tagged slot.
439        forall|l: LinkedListOwner<M>|
440            #![trigger l.relate_region(*old(regions))]
441            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
442                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
443        // Other loose frames preserved, and the popped slot is disjoint
444        // from every loose slot.
445        forall|fo: UniqueFrameOwner<Link<M>>|
446            #![trigger fo.global_inv(*old(regions))]
447            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
448                regions,
449            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 ==> fo.global_inv(
450                *final(regions),
451            ) && fo.frame_link_inv(*final(regions))
452                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0
453                && fo.slot_index != meta_to_index(old(owner).list[0].paddr),
454{
455    let tracked frame_own = take_at_embedded(regions, owner, 0);
456    frame_own
457}
458
459/// Checked back specialization of [`insert_before_at_embedded`], reflecting the
460/// (verified) [`crate::mm::frame::LinkedList::push_back`]. Identical to
461/// [`push_front_embedded`] except the frame is spliced in at the *tail*
462/// (touching the back neighbours instead of the front).
463pub proof fn lemma_push_back_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
464    tracked regions: &mut MetaRegionOwners,
465    tracked owner: &mut LinkedListOwner<M>,
466    tracked frame_own: &mut UniqueFrameOwner<Link<M>>,
467    used_ids: Set<u64>,
468)
469    requires
470        old(regions).inv(),
471        old(owner).inv(),
472        old(owner).relate_region(*old(regions)),
473        old(frame_own).inv(),
474        old(frame_own).global_inv(*old(regions)),
475        old(frame_own).frame_link_inv(*old(regions)),
476        old(regions).slot_owners[old(frame_own).slot_index].in_list_perm.value() == 0,
477    ensures
478        final(regions).inv(),
479        final(owner).inv(),
480        final(owner).relate_region(*final(regions)),
481        old(owner).list.len() > 0 ==> final(owner).list == old(owner).list.insert(
482            old(owner).list.len() - 1,
483            final(frame_own).meta_own,
484        ),
485        old(owner).list.len() == 0 ==> final(owner).list == old(owner).list.insert(
486            0,
487            final(frame_own).meta_own,
488        ),
489        old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
490        final(owner).list_id != 0,
491        old(owner).list_id == 0 ==> !used_ids.contains(final(owner).list_id),
492        final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
493        final(frame_own).meta_own.in_list == final(owner).list_id,
494        final(regions).frame_obligations =~= old(regions).frame_obligations.remove(
495            old(frame_own).slot_index,
496        ),
497        forall|k: int|
498            #![trigger final(regions).slots[k]]
499            #![trigger final(regions).slot_owners[k]]
500            k != old(frame_own).slot_index && (old(owner).list.len() > 1 ==> k != meta_to_index(
501                old(owner).list[old(owner).list.len() - 2].paddr,
502            )) && (old(owner).list.len() > 0 ==> k != meta_to_index(
503                old(owner).list[old(owner).list.len() - 1].paddr,
504            )) ==> final(regions).slots[k] == old(regions).slots[k] && final(regions).slot_owners[k]
505                == old(regions).slot_owners[k],
506        forall|l: LinkedListOwner<M>|
507            #![trigger l.relate_region(*old(regions))]
508            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
509                ==> l.relate_region(*final(regions)),
510        // Membership registry for the operated list: forward stamp of
511        // the (minted or preserved) id + reverse global uniqueness (see
512        // [`list_registry_ok`]).
513        list_registry_ok(*final(regions), *final(owner)),
514        // Every other list/cursor list keeps its registry: the only slot
515        // the surgery retags now carries `final(owner).list_id` (or 0),
516        // never another list's id — so no foreign list gains or loses a
517        // tagged slot.
518        forall|l: LinkedListOwner<M>|
519            #![trigger l.relate_region(*old(regions))]
520            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
521                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
522        forall|fo: UniqueFrameOwner<Link<M>>|
523            #![trigger fo.global_inv(*old(regions))]
524            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
525                regions,
526            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 && fo.slot_index != old(
527                frame_own,
528            ).slot_index ==> fo.global_inv(*final(regions)) && fo.frame_link_inv(*final(regions))
529                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0,
530{
531    let ghost n = if owner.list.len() > 0 {
532        owner.list.len() - 1
533    } else {
534        0
535    };
536    insert_before_at_embedded(regions, owner, frame_own, n, used_ids);
537}
538
539/// Checked back specialization of [`take_at_embedded`], reflecting the
540/// (verified) [`crate::mm::frame::LinkedList::pop_back`]. Identical to
541/// [`tracked_pop_front_embedded`] except the *last* link is popped (touching the
542/// back neighbour's metadata index).
543pub proof fn tracked_pop_back_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
544    tracked regions: &mut MetaRegionOwners,
545    tracked owner: &mut LinkedListOwner<M>,
546) -> (tracked frame_own: UniqueFrameOwner<Link<M>>)
547    requires
548        old(regions).inv(),
549        old(owner).inv(),
550        old(owner).relate_region(*old(regions)),
551        old(owner).list.len() > 0,
552    ensures
553        final(regions).inv(),
554        final(owner).inv(),
555        final(owner).relate_region(*final(regions)),
556        final(owner).list == old(owner).list.remove(old(owner).list.len() - 1),
557        final(owner).list_id == old(owner).list_id,
558        frame_own.inv(),
559        frame_own.global_inv(*final(regions)),
560        frame_own.frame_link_inv(*final(regions)),
561        frame_own.slot_index == meta_to_index(old(owner).list[old(owner).list.len() - 1].paddr),
562        final(regions).slot_owners[frame_own.slot_index].in_list_perm.value() == 0,
563        final(regions).frame_obligations =~= old(regions).frame_obligations.insert(
564            meta_to_index(old(owner).list[old(owner).list.len() - 1].paddr),
565        ),
566        forall|j: int|
567            #![trigger final(regions).slots[j]]
568            #![trigger final(regions).slot_owners[j]]
569            j != meta_to_index(old(owner).list[old(owner).list.len() - 1].paddr) && (old(
570                owner,
571            ).list.len() > 1 ==> j != meta_to_index(
572                old(owner).list[old(owner).list.len() - 2].paddr,
573            )) ==> final(regions).slots[j] == old(regions).slots[j] && final(regions).slot_owners[j]
574                == old(regions).slot_owners[j],
575        forall|l: LinkedListOwner<M>|
576            #![trigger l.relate_region(*old(regions))]
577            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
578                ==> l.relate_region(*final(regions)),
579        // Membership registry for the operated list: forward stamp of
580        // the (minted or preserved) id + reverse global uniqueness (see
581        // [`list_registry_ok`]).
582        list_registry_ok(*final(regions), *final(owner)),
583        // Every other list/cursor list keeps its registry: the only slot
584        // the surgery retags now carries `final(owner).list_id` (or 0),
585        // never another list's id — so no foreign list gains or loses a
586        // tagged slot.
587        forall|l: LinkedListOwner<M>|
588            #![trigger l.relate_region(*old(regions))]
589            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
590                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
591        forall|fo: UniqueFrameOwner<Link<M>>|
592            #![trigger fo.global_inv(*old(regions))]
593            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
594                regions,
595            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 ==> fo.global_inv(
596                *final(regions),
597            ) && fo.frame_link_inv(*final(regions))
598                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0
599                && fo.slot_index != meta_to_index(old(owner).list[old(owner).list.len() - 1].paddr),
600{
601    let ghost n = owner.list.len() - 1;
602    let tracked frame_own = take_at_embedded(regions, owner, n);
603    frame_own
604}
605
606/// Trusted reflection of [`crate::mm::frame::CursorMut::insert_before`]
607/// applied to a cursor at an arbitrary index `n` over `owner`. The
608/// general form of [`push_front_embedded`] (`n == 0`) /
609/// [`lemma_push_back_embedded`]: splices the loose frame in at position `n`
610/// (`0 <= n <= len`), touching `n`'s ≤2 link neighbours.
611pub axiom fn insert_before_at_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
612    tracked regions: &mut MetaRegionOwners,
613    tracked owner: &mut LinkedListOwner<M>,
614    tracked frame_own: &mut UniqueFrameOwner<Link<M>>,
615    n: int,
616    used_ids: Set<u64>,
617)
618    requires
619        old(regions).inv(),
620        old(owner).inv(),
621        old(owner).relate_region(*old(regions)),
622        old(frame_own).inv(),
623        old(frame_own).global_inv(*old(regions)),
624        old(frame_own).frame_link_inv(*old(regions)),
625        old(regions).slot_owners[old(frame_own).slot_index].in_list_perm.value() == 0,
626        0 <= n <= old(owner).list.len(),
627    ensures
628        final(regions).inv(),
629        final(owner).inv(),
630        final(owner).relate_region(*final(regions)),
631        final(owner).list == old(owner).list.insert(n, final(frame_own).meta_own),
632        old(owner).list_id != 0 ==> final(owner).list_id == old(owner).list_id,
633        final(owner).list_id != 0,
634        old(owner).list_id == 0 ==> !used_ids.contains(final(owner).list_id),
635        final(frame_own).meta_own.paddr == old(frame_own).meta_own.paddr,
636        final(frame_own).meta_own.in_list == final(owner).list_id,
637        final(regions).frame_obligations =~= old(regions).frame_obligations.remove(
638            old(frame_own).slot_index,
639        ),
640        forall|k: int|
641            #![trigger final(regions).slots[k]]
642            #![trigger final(regions).slot_owners[k]]
643            k != old(frame_own).slot_index && (n > 0 ==> k != meta_to_index(
644                old(owner).list[n - 1].paddr,
645            )) && (n < old(owner).list.len() ==> k != meta_to_index(old(owner).list[n].paddr))
646                ==> final(regions).slots[k] == old(regions).slots[k]
647                && final(regions).slot_owners[k] == old(regions).slot_owners[k],
648        forall|l: LinkedListOwner<M>|
649            #![trigger l.relate_region(*old(regions))]
650            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
651                ==> l.relate_region(*final(regions)),
652        // Membership registry for the operated list: forward stamp of
653        // the (minted or preserved) id + reverse global uniqueness (see
654        // [`list_registry_ok`]).
655        list_registry_ok(*final(regions), *final(owner)),
656        // Every other list/cursor list keeps its registry: the only slot
657        // the surgery retags now carries `final(owner).list_id` (or 0),
658        // never another list's id — so no foreign list gains or loses a
659        // tagged slot.
660        forall|l: LinkedListOwner<M>|
661            #![trigger l.relate_region(*old(regions))]
662            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
663                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
664        forall|fo: UniqueFrameOwner<Link<M>>|
665            #![trigger fo.global_inv(*old(regions))]
666            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
667                regions,
668            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 && fo.slot_index != old(
669                frame_own,
670            ).slot_index ==> fo.global_inv(*final(regions)) && fo.frame_link_inv(*final(regions))
671                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0,
672;
673
674/// Trusted reflection of [`crate::mm::frame::CursorMut::take_current`]
675/// at an arbitrary index `n` over `owner`. The general form of
676/// [`tracked_pop_front_embedded`] (`n == 0`) / [`tracked_pop_back_embedded`]: removes
677/// the link at position `n` (`0 <= n < len`) back into a loose handle,
678/// touching `n`'s ≤2 bridged neighbours.
679pub axiom fn take_at_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
680    tracked regions: &mut MetaRegionOwners,
681    tracked owner: &mut LinkedListOwner<M>,
682    n: int,
683) -> (tracked frame_own: UniqueFrameOwner<Link<M>>)
684    requires
685        old(regions).inv(),
686        old(owner).inv(),
687        old(owner).relate_region(*old(regions)),
688        0 <= n < old(owner).list.len(),
689    ensures
690        final(regions).inv(),
691        final(owner).inv(),
692        final(owner).relate_region(*final(regions)),
693        final(owner).list == old(owner).list.remove(n),
694        final(owner).list_id == old(owner).list_id,
695        frame_own.inv(),
696        frame_own.global_inv(*final(regions)),
697        frame_own.frame_link_inv(*final(regions)),
698        frame_own.slot_index == meta_to_index(old(owner).list[n].paddr),
699        final(regions).slot_owners[frame_own.slot_index].in_list_perm.value() == 0,
700        final(regions).frame_obligations =~= old(regions).frame_obligations.insert(
701            meta_to_index(old(owner).list[n].paddr),
702        ),
703        forall|j: int|
704            #![trigger final(regions).slots[j]]
705            #![trigger final(regions).slot_owners[j]]
706            j != meta_to_index(old(owner).list[n].paddr) && (n > 0 ==> j != meta_to_index(
707                old(owner).list[n - 1].paddr,
708            )) && (n < old(owner).list.len() - 1 ==> j != meta_to_index(
709                old(owner).list[n + 1].paddr,
710            )) ==> final(regions).slots[j] == old(regions).slots[j] && final(regions).slot_owners[j]
711                == old(regions).slot_owners[j],
712        forall|l: LinkedListOwner<M>|
713            #![trigger l.relate_region(*old(regions))]
714            l.inv() && l.relate_region(*old(regions)) && l.list_id != final(owner).list_id
715                ==> l.relate_region(*final(regions)),
716        // Membership registry for the operated list: forward stamp of
717        // the (minted or preserved) id + reverse global uniqueness (see
718        // [`list_registry_ok`]).
719        list_registry_ok(*final(regions), *final(owner)),
720        // Every other list/cursor list keeps its registry: the only slot
721        // the surgery retags now carries `final(owner).list_id` (or 0),
722        // never another list's id — so no foreign list gains or loses a
723        // tagged slot.
724        forall|l: LinkedListOwner<M>|
725            #![trigger l.relate_region(*old(regions))]
726            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
727                && l.list_id != final(owner).list_id ==> list_registry_ok(*final(regions), l),
728        forall|fo: UniqueFrameOwner<Link<M>>|
729            #![trigger fo.global_inv(*old(regions))]
730            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
731                regions,
732            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 ==> fo.global_inv(
733                *final(regions),
734            ) && fo.frame_link_inv(*final(regions))
735                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0
736                && fo.slot_index != meta_to_index(old(owner).list[n].paddr),
737;
738
739/// Trusted reflection of the (now-strengthened, verified) whole-list
740/// teardown [`crate::mm::frame::LinkedList`]'s `Drop`/`TrackDrop`. The
741/// destructor pops every link via `take_current` and `UniqueFrame::drop`s
742/// the recovered frame, so each former link's slot is **freed** —
743/// `rc → REF_COUNT_UNUSED`, `in_list → 0` — not orphaned. `owner` is
744/// consumed (emptied). The per-link `frame_obligations.count == 0`
745/// precondition mirrors the exec `drop_requires` (a listed frame was
746/// forgotten via `into_raw`); `ListStore` doesn't track that accounting
747/// fact, so it is surfaced here for an accounting-aware caller to supply.
748///
749/// `ensures` mirror the verified `drop_ensures` (freed slots + full
750/// preservation of every out-of-list slot, `slots.dom()`, `inv()`) plus
751/// the sound companion frames (cf. the push/pop axioms): other lists /
752/// cursors keep `relate_region` + [`list_registry_ok`], other loose
753/// frames are untouched, and — when the list was empty — the region is
754/// unchanged outright.
755pub axiom fn list_drop_embedded<M: AnyFrameMeta + Repr<MetaSlotSmall>>(
756    tracked regions: &mut MetaRegionOwners,
757    tracked owner: LinkedListOwner<M>,
758)
759    requires
760        old(regions).inv(),
761        owner.inv(),
762        owner.relate_region(*old(regions)),
763        forall|i: int|
764            #![trigger meta_to_index(owner.list[i].paddr)]
765            0 <= i < owner.list.len() ==> old(regions).frame_obligations.count(
766                meta_to_index(owner.list[i].paddr),
767            ) == 0,
768        // Mirrors the exec `TrackDrop for LinkedList::drop_requires`
769        // conjunct (`linked_list.rs`): each link's slot has no live PTE
770        // mapping. The destructor `UniqueFrame::drop`s each link to
771        // `REF_COUNT_UNUSED`, which is only valid for an unmapped frame
772        // (a mapping is itself a reference). Discharged in `step_list_drop`
773        // from `MetaSlotOwner::inv`'s UNIQUE branch (a UNIQUE slot — which
774        // every link is, via `relate_region`) has empty `paths_in_pt`).
775        forall|i: int|
776            #![trigger meta_to_index(owner.list[i].paddr)]
777            0 <= i < owner.list.len() ==> old(regions).slot_owners[meta_to_index(
778                owner.list[i].paddr,
779            )].paths_in_pt.is_empty(),
780    ensures
781        final(regions).inv(),
782        final(regions).slots.dom() =~= old(regions).slots.dom(),
783        // An empty list frees nothing — the region is untouched.
784        owner.list.len() == 0 ==> *final(regions) == *old(regions),
785        // Each former link is freed: its slot is UNUSED with `in_list` 0.
786        forall|i: int|
787            #![trigger meta_to_index(owner.list[i].paddr)]
788            0 <= i < owner.list.len() ==> {
789                let idx = meta_to_index(owner.list[i].paddr);
790                &&& final(regions).slot_owners[idx].ref_count() == REF_COUNT_UNUSED
791                &&& final(regions).slot_owners[idx].in_list_perm.value() == 0
792            },
793        // Every slot outside the dropped list is fully preserved.
794        forall|idx: int|
795            #![trigger final(regions).slot_owners[idx]]
796            (forall|i: int|
797                0 <= i < owner.list.len() ==> idx != #[trigger] meta_to_index(owner.list[i].paddr))
798                ==> final(regions).slot_owners[idx] == old(regions).slot_owners[idx]
799                && final(regions).slots[idx] == old(regions).slots[idx]
800                && final(regions).frame_obligations.count(idx) == old(
801                regions,
802            ).frame_obligations.count(idx),
803        // Other lists / cursors keep their `relate_region` and registry.
804        forall|l: LinkedListOwner<M>|
805            #![trigger l.relate_region(*old(regions))]
806            l.inv() && l.relate_region(*old(regions)) && l.list_id != owner.list_id
807                ==> l.relate_region(*final(regions)),
808        forall|l: LinkedListOwner<M>|
809            #![trigger l.relate_region(*old(regions))]
810            l.inv() && l.relate_region(*old(regions)) && list_registry_ok(*old(regions), l)
811                && l.list_id != owner.list_id ==> list_registry_ok(*final(regions), l),
812        // Other loose frames (at `in_list == 0` slots disjoint from the
813        // dropped list's) are untouched.
814        forall|fo: UniqueFrameOwner<Link<M>>|
815            #![trigger fo.global_inv(*old(regions))]
816            fo.global_inv(*old(regions)) && fo.frame_link_inv(*old(regions)) && old(
817                regions,
818            ).slot_owners[fo.slot_index].in_list_perm.value() == 0 ==> fo.global_inv(
819                *final(regions),
820            ) && fo.frame_link_inv(*final(regions))
821                && final(regions).slot_owners[fo.slot_index].in_list_perm.value() == 0,
822;
823
824// =============================================================================
825// Operations
826// =============================================================================
827impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M> {
828    /// `LinkedList::size`: the number of links in list `id`. A read-only
829    /// query — the store is unchanged.
830    pub proof fn step_size(tracked &self, id: ListId) -> (res: nat)
831        requires
832            self.inv(),
833            self.lists.dom().contains(id),
834        ensures
835            res == self.lists[id].list.len(),
836    {
837        self.lists[id].list.len()
838    }
839
840    /// `LinkedList::is_empty`: whether list `id` has no links. Read-only.
841    pub proof fn step_is_empty(tracked &self, id: ListId) -> (res: bool)
842        requires
843            self.inv(),
844            self.lists.dom().contains(id),
845        ensures
846            res <==> self.lists[id].list.len() == 0,
847    {
848        self.lists[id].list.len() == 0
849    }
850
851    /// `LinkedList::contains`: whether `frame` is a link of list `id`. A
852    /// read-only query mirroring exec `contains(frame) -> bool`. `res`
853    /// holds iff `frame` is a safe managed slot AND one of the list's
854    /// links: for a real (non-zero) id the membership registry
855    /// ([`list_registry_ok`], an `inv` clause) makes the
856    /// `in_list[frame] == list_id` comparison an exact membership test;
857    /// an empty/never-pushed list (`list_id == 0`, hence empty) or a
858    /// `frame` that is not a safe slot (which exec's `get_slot` rejects)
859    /// contains nothing.
860    pub proof fn step_contains(tracked &self, id: ListId, frame: Paddr) -> (res: bool)
861        requires
862            self.inv(),
863            self.lists.dom().contains(id),
864        ensures
865            res <==> (valid_frame_paddr(frame) && exists|i: int|
866                0 <= i < self.lists[id].list.len() && #[trigger] meta_to_index(
867                    self.lists[id].list[i].paddr,
868                ) == frame_to_index(frame)),
869    {
870        let idx = frame_to_index(frame);
871        if valid_frame_paddr(frame) {
872            // A safe slot is a managed region key.
873            self.regions.lemma_contains_valid_frame_paddr(frame);
874            assert(self.regions.contains(idx));
875            if self.lists[id].list_id != 0 {
876                // The registry for list `id` (forward + reverse) from `inv`.
877                assert(list_registry_ok(self.regions, self.lists[id]));
878                let res = self.regions.slot_owners[idx].in_list_perm.value()
879                    == self.lists[id].list_id;
880                if res {
881                    // reverse: a slot tagged with the id is one of the links.
882                    assert(exists|i: int|
883                        0 <= i < self.lists[id].list.len() && #[trigger] meta_to_index(
884                            self.lists[id].list[i].paddr,
885                        ) == idx);
886                } else {
887                    // forward: every link's slot is tagged, so an untagged
888                    // slot is no link.
889                    assert forall|i: int|
890                        0 <= i < self.lists[id].list.len() implies #[trigger] meta_to_index(
891                        self.lists[id].list[i].paddr,
892                    ) != idx by {
893                        assert(self.regions.slot_owners[meta_to_index(
894                            self.lists[id].list[i].paddr,
895                        )].in_list_perm.value() == self.lists[id].list_id);
896                    };
897                }
898                res
899            } else {
900                // `list_id == 0` ⟹ the list is empty (`LinkedListOwner::inv`:
901                // `len > 0 ==> list_id != 0`), so it has no links.
902                assert(self.lists[id].list.len() == 0);
903                false
904            }
905        } else {
906            // `!valid_frame_paddr(frame)`: exec `get_slot` rejects it, so the
907            // guarded membership is vacuously false.
908            false
909        }
910    }
911
912    /// `LinkedList::new`: register a fresh *empty* list. No region
913    /// change; the new list is empty with `list_id == 0` (minted on
914    /// first push). Returns the fresh list id.
915    pub proof fn step_list_new(tracked &mut self) -> (res: ListId)
916        requires
917            old(self).inv(),
918        ensures
919            final(self).inv(),
920            final(self).regions == old(self).regions,
921            final(self).loose == old(self).loose,
922            !old(self).lists.dom().contains(res),
923            final(self).lists == old(self).lists.insert(res, final(self).lists[res]),
924            final(self).lists[res].list.len() == 0,
925    {
926        let ghost old_self = *self;
927        let ghost id = fresh_list_id(self.lists, self.cursors);
928        lemma_fresh_list_id_not_in_dom(self.lists, self.cursors);
929        let tracked empty = tracked_empty_list_owner::<M>();
930        self.lists.tracked_insert(id, empty);
931        assert(self.lists[id].list.len() == 0);
932        // The new list is empty: `inv()` (`len > 0 ==> ...` vacuous,
933        // per-link forall vacuous) and `relate_region` (both foralls
934        // vacuous over an empty `list`) hold. Every other list / loose
935        // entry is unchanged, and `regions` is untouched.
936        assert(self.lists[id].relate_region(self.regions));
937        // Cursors untouched; `id` is fresh w.r.t. `cursors` (so
938        // disjointness holds), and the new list's `list_id == 0` makes
939        // the cross list/cursor id clause vacuous for it.
940        assert(self.cursors == old_self.cursors);
941        assert(self.lists.dom().disjoint(self.cursors.dom()));
942        assert(self.lists[id].list_id == 0);
943        id
944    }
945
946    /// Drop of `LinkedList` `id`: tear the whole list down, *freeing*
947    /// every link's frame (slot → UNUSED, `in_list` → 0) and removing the
948    /// list from the store. Faithful to the verified destructor (each
949    /// link is popped and `UniqueFrame::drop`ped — no orphaning).
950    ///
951    /// The per-link `frame_obligations.count == 0` precondition mirrors
952    /// the exec `drop_requires` (listed frames are forgotten); the
953    /// accounting-free `ListStore` cannot itself supply it, so it is left
954    /// to the caller. The freed frames leave the store entirely (they
955    /// return to the allocator's UNUSED pool, tracked by nobody here).
956    pub proof fn step_list_drop(tracked &mut self, id: ListId)
957        requires
958            old(self).inv(),
959            old(self).lists.dom().contains(id),
960            forall|i: int|
961                0 <= i < old(self).lists[id].list.len() ==> old(
962                    self,
963                ).regions.frame_obligations.count(
964                    #[trigger] meta_to_index(old(self).lists[id].list[i].paddr),
965                ) == 0,
966        ensures
967            final(self).inv(),
968            !final(self).lists.dom().contains(id),
969            final(self).loose == old(self).loose,
970            final(self).cursors == old(self).cursors,
971    {
972        let ghost old_self = *self;
973        let ghost old_regions = self.regions;
974        let ghost dropped_id = self.lists[id].list_id;
975        let ghost is_empty = self.lists[id].list.len() == 0;
976        assert(self.lists[id].relate_region(self.regions));
977
978        // Discharge the axiom's unmapped-link precondition: every link's
979        // slot is a non-MMIO UNIQUE frame (via `relate_region_at`:
980        // `ref_count == REF_COUNT_UNIQUE` + `usage == Frame`), and
981        // `regions.inv()`'s UNIQUE branch (`usage != MMIO ==> empty`) then
982        // gives it an empty `paths_in_pt`.
983        assert forall|i: int|
984            #![trigger meta_to_index(self.lists[id].list[i].paddr)]
985            0 <= i < self.lists[id].list.len() implies self.regions.slot_owners[meta_to_index(
986            self.lists[id].list[i].paddr,
987        )].paths_in_pt.is_empty() by {
988            let idx = meta_to_index(self.lists[id].list[i].paddr);
989            // Instantiate `relate_region`'s per-link forall (trigger
990            // `self.list[i]`) to get `relate_region_at(regions, i)`.
991            let _ = self.lists[id].list[i];
992            self.lists[id].relate_region_at_facts(self.regions, i);
993            assert(self.regions.contains(idx));
994            assert(self.regions.slot_owners[idx].ref_count() == REF_COUNT_UNIQUE);
995            assert(self.regions.slot_owners[idx].usage is Frame);
996        };
997
998        let tracked owner = self.lists.tracked_remove(id);
999        list_drop_embedded(&mut self.regions, owner);
1000        assert(self.lists =~= old_self.lists.remove(id));
1001        if is_empty {
1002            assert(self.regions == old_regions);
1003        }
1004        // A non-empty dropped list has a real (non-zero) id, so every
1005        // other list/cursor is separated from it by the id uniqueness;
1006        // an empty drop left `regions` untouched outright.
1007
1008        if !is_empty {
1009            assert(dropped_id != 0);
1010        }
1011        // --- per-list: remaining lists preserved ---
1012
1013        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1014            &&& self.lists[i].inv()
1015            &&& self.lists[i].relate_region(self.regions)
1016        } by {
1017            assert(i != id);
1018            assert(old_self.lists.dom().contains(i));
1019            assert(old_self.lists[i] == self.lists[i]);
1020            assert(old_self.lists[i].relate_region(old_regions));
1021            if !is_empty {
1022                assert(self.lists[i].list_id != dropped_id);
1023            }
1024        };
1025
1026        // --- per-loose preserved ---
1027        assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1028            &&& self.loose[lid2].inv()
1029            &&& self.loose[lid2].global_inv(self.regions)
1030            &&& self.loose[lid2].frame_link_inv(self.regions)
1031            &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1032        } by {
1033            assert(old_self.loose.dom().contains(lid2));
1034            assert(old_self.loose[lid2].global_inv(old_regions));
1035            assert(old_self.loose[lid2].frame_link_inv(old_regions));
1036            assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0);
1037        };
1038
1039        // --- per-cursor preserved (cursor lists are "other lists") ---
1040        assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1041            &&& self.cursors[cid].list_own.inv()
1042            &&& self.cursors[cid].wf_with_region(self.regions)
1043        } by {
1044            assert(old_self.cursors.dom().contains(cid));
1045            assert(old_self.cursors[cid].wf_with_region(old_regions));
1046            assert(self.cursors[cid].list_own.relate_region(old_regions));
1047            if !is_empty {
1048                assert(self.cursors[cid].list_own.list_id != dropped_id);
1049            }
1050        };
1051
1052        // --- lists×lists uniqueness (subset of old) ---
1053        assert forall|i1: ListId, i2: ListId| #[trigger]
1054            self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1055                && self.lists[i1].list_id == self.lists[i2].list_id && self.lists[i1].list_id
1056                != 0 implies i1 == i2 by {
1057            assert(old_self.lists.dom().contains(i1));
1058            assert(old_self.lists.dom().contains(i2));
1059        };
1060
1061        // --- loose-internal disjointness (loose unchanged) ---
1062        assert forall|l1: LooseId, l2: LooseId| #[trigger]
1063            self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1064                && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1065            assert(old_self.loose.dom().contains(l1));
1066            assert(old_self.loose.dom().contains(l2));
1067        };
1068
1069        // --- disjointness + cross/cursor uniqueness (lists lost `id`) ---
1070        assert(self.lists.dom().disjoint(self.cursors.dom()));
1071        assert forall|id2: ListId, cid: CursorId| #[trigger]
1072            self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1073                && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1074                && self.lists[id2].list_id != 0 implies false by {
1075            assert(old_self.lists.dom().contains(id2));
1076            assert(old_self.cursors.dom().contains(cid));
1077        };
1078        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1079            self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1080                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1081                && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1082            assert(old_self.cursors.dom().contains(cid1));
1083            assert(old_self.cursors.dom().contains(cid2));
1084        };
1085
1086        // --- membership registry (remaining lists & cursors) ---
1087        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies list_registry_ok(
1088            self.regions,
1089            self.lists[i],
1090        ) by {
1091            assert(old_self.lists.dom().contains(i));
1092            assert(old_self.lists[i] == self.lists[i]);
1093            assert(old_self.lists[i].relate_region(old_regions));
1094            if !is_empty {
1095                assert(self.lists[i].list_id != dropped_id);
1096            }
1097        };
1098        assert forall|cid: CursorId| #[trigger]
1099            self.cursors.dom().contains(cid) implies list_registry_ok(
1100            self.regions,
1101            self.cursors[cid].list_own,
1102        ) by {
1103            assert(old_self.cursors.dom().contains(cid));
1104            assert(old_self.cursors[cid].list_own.relate_region(old_regions));
1105            if !is_empty {
1106                assert(self.cursors[cid].list_own.list_id != dropped_id);
1107            }
1108        };
1109    }
1110
1111    /// `LinkedList::push_front`: move the loose handle `lid` to the front
1112    /// of list `id`. The frame is forgotten into the list (its
1113    /// drop-obligation consumed); the `loose` entry is removed.
1114    pub proof fn step_push_front(tracked &mut self, id: ListId, lid: LooseId)
1115        requires
1116            old(self).inv(),
1117            old(self).lists.dom().contains(id),
1118            old(self).loose.dom().contains(lid),
1119        ensures
1120            final(self).inv(),
1121    {
1122        let ghost old_self = *self;
1123        let ghost old_regions = self.regions;
1124        let ghost fidx = self.loose[lid].slot_index;
1125        // The other lists' ids — the lazily-minted id must avoid these.
1126        let ghost used = Set::<u64>::full().unwrap().filter(
1127            |x: u64|
1128                (exists|i: ListId| #[trigger]
1129                    old_self.lists.dom().contains(i) && i != id && old_self.lists[i].list_id == x)
1130                    || (exists|cid: CursorId| #[trigger]
1131                    old_self.cursors.dom().contains(cid) && old_self.cursors[cid].list_own.list_id
1132                        == x),
1133        );
1134        // Push preconditions, sourced from `inv`.
1135        assert(self.lists[id].relate_region(self.regions));
1136        assert(self.loose[lid].global_inv(self.regions));
1137        assert(self.loose[lid].frame_link_inv(self.regions));
1138        assert(self.regions.slot_owners[fidx].in_list_perm.value() == 0);
1139
1140        let tracked mut owner = self.lists.tracked_remove(id);
1141        let tracked mut frame_own = self.loose.tracked_remove(lid);
1142        push_front_embedded(&mut self.regions, &mut owner, &mut frame_own, used);
1143        self.lists.tracked_insert(id, owner);
1144        assert(self.loose =~= old_self.loose.remove(lid));
1145        assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1146        let ghost new_id = self.lists[id].list_id;
1147
1148        // Other lists with a nonzero id keep it distinct from `new_id`:
1149        // the pushed list either kept its (uniquely-minted) id or minted
1150        // one outside `used` (which holds every other list's id).
1151        assert forall|i: ListId| #[trigger]
1152            self.lists.dom().contains(i) && i != id && self.lists[i].list_id
1153                != 0 implies self.lists[i].list_id != new_id by {
1154            assert(old_self.lists.dom().contains(i));
1155            assert(old_self.lists[i] == self.lists[i]);
1156            if old_self.lists[id].list_id != 0 {
1157                // `new_id == old id`; old nonzero-id uniqueness separates `i`.
1158                assert(new_id == old_self.lists[id].list_id);
1159            } else {
1160                assert(used.contains(self.lists[i].list_id));
1161            }
1162        };
1163
1164        // --- per-list: inv + relate_region ---
1165        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1166            &&& self.lists[i].inv()
1167            &&& self.lists[i].relate_region(self.regions)
1168        } by {
1169            if i != id {
1170                assert(old_self.lists.dom().contains(i));
1171                assert(old_self.lists[i] == self.lists[i]);
1172                assert(old_self.lists[i].relate_region(old_regions));
1173                if self.lists[i].list.len() > 0 {
1174                    assert(self.lists[i].list_id != new_id);
1175                }
1176            }
1177        };
1178
1179        // --- per-loose: a different loose frame is at a `!= fidx` slot,
1180        // so the axiom's other-loose clause carries it. ---
1181        assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1182            &&& self.loose[lid2].inv()
1183            &&& self.loose[lid2].global_inv(self.regions)
1184            &&& self.loose[lid2].frame_link_inv(self.regions)
1185            &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1186        } by {
1187            assert(lid2 != lid);
1188            assert(old_self.loose.dom().contains(lid2));
1189            assert(old_self.loose[lid2] == self.loose[lid2]);
1190            assert(old_self.loose[lid2].global_inv(old_regions));
1191            assert(old_self.loose[lid2].frame_link_inv(old_regions));
1192            assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0);
1193            assert(self.loose[lid2].slot_index != fidx);
1194        };
1195
1196        // --- list_id uniqueness (non-empty lists) ---
1197        assert forall|i1: ListId, i2: ListId| #[trigger]
1198            self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1199                && self.lists[i1].list.len() > 0 && self.lists[i2].list.len() > 0
1200                && self.lists[i1].list_id == self.lists[i2].list_id implies i1 == i2 by {
1201            if i1 != id && i2 != id {
1202                assert(old_self.lists[i1] == self.lists[i1]);
1203                assert(old_self.lists[i2] == self.lists[i2]);
1204            } else if i1 == id && i2 != id {
1205                assert(self.lists[i2].list_id != new_id);
1206            } else if i2 == id && i1 != id {
1207                assert(self.lists[i1].list_id != new_id);
1208            }
1209        };
1210
1211        // --- loose-internal slot disjointness (subset of old) ---
1212        assert forall|l1: LooseId, l2: LooseId| #[trigger]
1213            self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1214                && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1215            assert(old_self.loose.dom().contains(l1));
1216            assert(old_self.loose.dom().contains(l2));
1217        };
1218
1219        // --- cursors: checked-out lists are untouched ---
1220        // `cursors` is not read or written by a list op, and every
1221        // cursor's list carries an id distinct from the just-minted
1222        // `new_id` (other cursors' ids are in `used`, or — when the id
1223        // was preserved — separated by the old list/cursor uniqueness),
1224        // so the axiom's other-lists frame preserves each cursor's
1225        // `relate_region`. Index bounds are unchanged.
1226        assert(self.cursors == old_self.cursors);
1227        assert(self.lists.dom() =~= old_self.lists.dom());
1228        assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1229            &&& self.cursors[cid].list_own.inv()
1230            &&& self.cursors[cid].wf_with_region(self.regions)
1231        } by {
1232            assert(old_self.cursors.dom().contains(cid));
1233            assert(old_self.cursors[cid].wf_with_region(old_regions));
1234            assert(self.cursors[cid].list_own.relate_region(old_regions));
1235            if old_self.lists[id].list_id != 0 {
1236                assert(new_id == old_self.lists[id].list_id);
1237            } else {
1238                assert(used.contains(self.cursors[cid].list_own.list_id));
1239            }
1240            assert(self.cursors[cid].list_own.list_id != new_id);
1241            assert(self.cursors[cid].list_own.relate_region(self.regions));
1242        };
1243        assert forall|id2: ListId, cid: CursorId| #[trigger]
1244            self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1245                && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1246                && self.lists[id2].list_id != 0 implies false by {
1247            assert(old_self.cursors.dom().contains(cid));
1248            if id2 == id {
1249                assert(self.lists[id].list_id == new_id);
1250                if old_self.lists[id].list_id != 0 {
1251                    assert(new_id == old_self.lists[id].list_id);
1252                } else {
1253                    assert(used.contains(self.cursors[cid].list_own.list_id));
1254                }
1255            } else {
1256                assert(old_self.lists.dom().contains(id2));
1257                assert(old_self.lists[id2] == self.lists[id2]);
1258            }
1259        };
1260        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1261            self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1262                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1263                && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1264            assert(old_self.cursors.dom().contains(cid1));
1265            assert(old_self.cursors.dom().contains(cid2));
1266        };
1267    }
1268
1269    /// `LinkedList::pop_front`: pop the front link of list `id` back into
1270    /// the loose pool as a fresh `UniqueFrame<Link<M>>`. Requires the
1271    /// list be non-empty. Returns the fresh loose id.
1272    pub proof fn step_pop_front(tracked &mut self, id: ListId) -> (res: Option<LooseId>)
1273        requires
1274            old(self).inv(),
1275            old(self).lists.dom().contains(id),
1276        ensures
1277            final(self).inv(),
1278            old(self).lists[id].list.len() == 0 ==> res is None && *final(self) == *old(self),
1279            old(self).lists[id].list.len() > 0 ==> res is Some,
1280    {
1281        if self.lists[id].list.len() == 0 {
1282            // Exec `LinkedList::pop_front` returns `None` on an empty
1283            // list; the store is unchanged.
1284            Option::None
1285        } else {
1286            let ghost old_self = *self;
1287            let ghost old_regions = self.regions;
1288            let ghost popped_idx = meta_to_index(self.lists[id].list[0].paddr);
1289            let ghost old_list_id = self.lists[id].list_id;
1290            // Pop preconditions from `inv`.
1291            assert(self.lists[id].relate_region(self.regions));
1292
1293            let tracked mut owner = self.lists.tracked_remove(id);
1294            let tracked frame_own = tracked_pop_front_embedded(&mut self.regions, &mut owner);
1295            self.lists.tracked_insert(id, owner);
1296            let ghost new_loose = fresh_loose_id(self.loose);
1297            lemma_fresh_loose_id_not_in_dom(self.loose);
1298            self.loose.tracked_insert(new_loose, frame_own);
1299
1300            assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1301            assert(self.loose =~= old_self.loose.insert(new_loose, frame_own));
1302            assert(self.lists[id].list_id == old_list_id);
1303            assert(frame_own.slot_index == popped_idx);
1304
1305            // --- per-list: inv + relate_region ---
1306            assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1307                &&& self.lists[i].inv()
1308                &&& self.lists[i].relate_region(self.regions)
1309            } by {
1310                if i != id {
1311                    assert(old_self.lists.dom().contains(i));
1312                    assert(old_self.lists[i] == self.lists[i]);
1313                    assert(old_self.lists[i].relate_region(old_regions));
1314                    if self.lists[i].list.len() > 0 {
1315                        // non-empty ⟹ nonzero id, distinct from `id`'s
1316                        // (preserved) id by old uniqueness.
1317                        assert(self.lists[i].list_id != old_list_id);
1318                    }
1319                }
1320            };
1321
1322            // --- per-loose: new entry from the axiom; others preserved ---
1323            assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1324                &&& self.loose[lid2].inv()
1325                &&& self.loose[lid2].global_inv(self.regions)
1326                &&& self.loose[lid2].frame_link_inv(self.regions)
1327                &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1328            } by {
1329                if lid2 != new_loose {
1330                    assert(old_self.loose.dom().contains(lid2));
1331                    assert(old_self.loose[lid2] == self.loose[lid2]);
1332                    assert(old_self.loose[lid2].global_inv(old_regions));
1333                    assert(old_self.loose[lid2].frame_link_inv(old_regions));
1334                    assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value()
1335                        == 0);
1336                }
1337            };
1338
1339            // --- list_id uniqueness (all ids unchanged) ---
1340            assert forall|i1: ListId, i2: ListId| #[trigger]
1341                self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1342                    && self.lists[i1].list_id == self.lists[i2].list_id && self.lists[i1].list_id
1343                    != 0 implies i1 == i2 by {
1344                assert(old_self.lists.dom().contains(i1));
1345                assert(old_self.lists.dom().contains(i2));
1346                assert(self.lists[i1].list_id == old_self.lists[i1].list_id);
1347                assert(self.lists[i2].list_id == old_self.lists[i2].list_id);
1348            };
1349
1350            // --- loose-internal disjointness ---
1351            assert forall|l1: LooseId, l2: LooseId| #[trigger]
1352                self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1353                    && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1354                if l1 == new_loose && l2 != new_loose {
1355                    assert(old_self.loose.dom().contains(l2));
1356                    assert(self.loose[l2].slot_index != popped_idx);
1357                } else if l2 == new_loose && l1 != new_loose {
1358                    assert(old_self.loose.dom().contains(l1));
1359                    assert(self.loose[l1].slot_index != popped_idx);
1360                } else if l1 != new_loose && l2 != new_loose {
1361                    assert(old_self.loose.dom().contains(l1));
1362                    assert(old_self.loose.dom().contains(l2));
1363                }
1364            };
1365
1366            // --- cursors: checked-out lists are untouched ---
1367            // `id`'s (preserved, nonzero) `old_list_id` is separated from
1368            // every cursor's list id by the old list/cursor uniqueness, so
1369            // the axiom's other-lists frame preserves each cursor.
1370            assert(self.cursors == old_self.cursors);
1371            assert(self.lists.dom() =~= old_self.lists.dom());
1372            assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1373                &&& self.cursors[cid].list_own.inv()
1374                &&& self.cursors[cid].wf_with_region(self.regions)
1375            } by {
1376                assert(old_self.cursors.dom().contains(cid));
1377                assert(old_self.cursors[cid].wf_with_region(old_regions));
1378                assert(self.cursors[cid].list_own.relate_region(old_regions));
1379                assert(old_self.lists.dom().contains(id));
1380                assert(old_self.lists[id].list_id == old_list_id);
1381                assert(self.cursors[cid].list_own.list_id != old_list_id);
1382                assert(self.cursors[cid].list_own.relate_region(self.regions));
1383            };
1384            assert forall|id2: ListId, cid: CursorId| #[trigger]
1385                self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1386                    && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1387                    && self.lists[id2].list_id != 0 implies false by {
1388                assert(old_self.cursors.dom().contains(cid));
1389                assert(old_self.lists.dom().contains(id2));
1390                assert(old_self.lists[id2].list_id == self.lists[id2].list_id);
1391            };
1392            assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1393                self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1394                    && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1395                    && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1396                assert(old_self.cursors.dom().contains(cid1));
1397                assert(old_self.cursors.dom().contains(cid2));
1398            };
1399            Option::Some(new_loose)
1400        }
1401    }
1402
1403    /// `LinkedList::push_back`: move the loose handle `lid` to the back
1404    /// of list `id`. Same global effect as [`Self::step_push_front`] —
1405    /// only the link's position within the list differs.
1406    pub proof fn step_push_back(tracked &mut self, id: ListId, lid: LooseId)
1407        requires
1408            old(self).inv(),
1409            old(self).lists.dom().contains(id),
1410            old(self).loose.dom().contains(lid),
1411        ensures
1412            final(self).inv(),
1413    {
1414        let ghost old_self = *self;
1415        let ghost old_regions = self.regions;
1416        let ghost fidx = self.loose[lid].slot_index;
1417        let ghost used = Set::<u64>::full().unwrap().filter(
1418            |x: u64|
1419                (exists|i: ListId| #[trigger]
1420                    old_self.lists.dom().contains(i) && i != id && old_self.lists[i].list_id == x)
1421                    || (exists|cid: CursorId| #[trigger]
1422                    old_self.cursors.dom().contains(cid) && old_self.cursors[cid].list_own.list_id
1423                        == x),
1424        );
1425        assert(self.lists[id].relate_region(self.regions));
1426        assert(self.loose[lid].global_inv(self.regions));
1427        assert(self.loose[lid].frame_link_inv(self.regions));
1428        assert(self.regions.slot_owners[fidx].in_list_perm.value() == 0);
1429
1430        let tracked mut owner = self.lists.tracked_remove(id);
1431        let tracked mut frame_own = self.loose.tracked_remove(lid);
1432        lemma_push_back_embedded(&mut self.regions, &mut owner, &mut frame_own, used);
1433        self.lists.tracked_insert(id, owner);
1434        assert(self.loose =~= old_self.loose.remove(lid));
1435        assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1436        let ghost new_id = self.lists[id].list_id;
1437
1438        assert forall|i: ListId| #[trigger]
1439            self.lists.dom().contains(i) && i != id && self.lists[i].list_id
1440                != 0 implies self.lists[i].list_id != new_id by {
1441            assert(old_self.lists.dom().contains(i));
1442            assert(old_self.lists[i] == self.lists[i]);
1443            if old_self.lists[id].list_id != 0 {
1444                assert(new_id == old_self.lists[id].list_id);
1445            } else {
1446                assert(used.contains(self.lists[i].list_id));
1447            }
1448        };
1449
1450        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1451            &&& self.lists[i].inv()
1452            &&& self.lists[i].relate_region(self.regions)
1453        } by {
1454            if i != id {
1455                assert(old_self.lists.dom().contains(i));
1456                assert(old_self.lists[i] == self.lists[i]);
1457                assert(old_self.lists[i].relate_region(old_regions));
1458                if self.lists[i].list.len() > 0 {
1459                    assert(self.lists[i].list_id != new_id);
1460                }
1461            }
1462        };
1463
1464        assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1465            &&& self.loose[lid2].inv()
1466            &&& self.loose[lid2].global_inv(self.regions)
1467            &&& self.loose[lid2].frame_link_inv(self.regions)
1468            &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1469        } by {
1470            assert(lid2 != lid);
1471            assert(old_self.loose.dom().contains(lid2));
1472            assert(old_self.loose[lid2] == self.loose[lid2]);
1473            assert(old_self.loose[lid2].global_inv(old_regions));
1474            assert(old_self.loose[lid2].frame_link_inv(old_regions));
1475            assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0);
1476            assert(self.loose[lid2].slot_index != fidx);
1477        };
1478
1479        assert forall|i1: ListId, i2: ListId| #[trigger]
1480            self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1481                && self.lists[i1].list.len() > 0 && self.lists[i2].list.len() > 0
1482                && self.lists[i1].list_id == self.lists[i2].list_id implies i1 == i2 by {
1483            if i1 != id && i2 != id {
1484                assert(old_self.lists[i1] == self.lists[i1]);
1485                assert(old_self.lists[i2] == self.lists[i2]);
1486            } else if i1 == id && i2 != id {
1487                assert(self.lists[i2].list_id != new_id);
1488            } else if i2 == id && i1 != id {
1489                assert(self.lists[i1].list_id != new_id);
1490            }
1491        };
1492
1493        assert forall|l1: LooseId, l2: LooseId| #[trigger]
1494            self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1495                && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1496            assert(old_self.loose.dom().contains(l1));
1497            assert(old_self.loose.dom().contains(l2));
1498        };
1499
1500        // --- cursors: checked-out lists are untouched ---
1501        // `cursors` is not read or written by a list op, and every
1502        // cursor's list carries an id distinct from the just-minted
1503        // `new_id` (other cursors' ids are in `used`, or — when the id
1504        // was preserved — separated by the old list/cursor uniqueness),
1505        // so the axiom's other-lists frame preserves each cursor's
1506        // `relate_region`. Index bounds are unchanged.
1507        assert(self.cursors == old_self.cursors);
1508        assert(self.lists.dom() =~= old_self.lists.dom());
1509        assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1510            &&& self.cursors[cid].list_own.inv()
1511            &&& self.cursors[cid].wf_with_region(self.regions)
1512        } by {
1513            assert(old_self.cursors.dom().contains(cid));
1514            assert(old_self.cursors[cid].wf_with_region(old_regions));
1515            assert(self.cursors[cid].list_own.relate_region(old_regions));
1516            if old_self.lists[id].list_id != 0 {
1517                assert(new_id == old_self.lists[id].list_id);
1518            } else {
1519                assert(used.contains(self.cursors[cid].list_own.list_id));
1520            }
1521            assert(self.cursors[cid].list_own.list_id != new_id);
1522            assert(self.cursors[cid].list_own.relate_region(self.regions));
1523        };
1524        assert forall|id2: ListId, cid: CursorId| #[trigger]
1525            self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1526                && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1527                && self.lists[id2].list_id != 0 implies false by {
1528            assert(old_self.cursors.dom().contains(cid));
1529            if id2 == id {
1530                assert(self.lists[id].list_id == new_id);
1531                if old_self.lists[id].list_id != 0 {
1532                    assert(new_id == old_self.lists[id].list_id);
1533                } else {
1534                    assert(used.contains(self.cursors[cid].list_own.list_id));
1535                }
1536            } else {
1537                assert(old_self.lists.dom().contains(id2));
1538                assert(old_self.lists[id2] == self.lists[id2]);
1539            }
1540        };
1541        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1542            self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1543                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1544                && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1545            assert(old_self.cursors.dom().contains(cid1));
1546            assert(old_self.cursors.dom().contains(cid2));
1547        };
1548    }
1549
1550    /// `LinkedList::pop_back`: pop the back link of list `id` back into
1551    /// the loose pool. Same global effect as [`Self::step_pop_front`] —
1552    /// only which link is removed differs.
1553    pub proof fn step_pop_back(tracked &mut self, id: ListId) -> (res: Option<LooseId>)
1554        requires
1555            old(self).inv(),
1556            old(self).lists.dom().contains(id),
1557        ensures
1558            final(self).inv(),
1559            old(self).lists[id].list.len() == 0 ==> res is None && *final(self) == *old(self),
1560            old(self).lists[id].list.len() > 0 ==> res is Some,
1561    {
1562        if self.lists[id].list.len() == 0 {
1563            // Exec `LinkedList::pop_back` returns `None` on an empty list;
1564            // the store is unchanged.
1565            Option::None
1566        } else {
1567            let ghost old_self = *self;
1568            let ghost old_regions = self.regions;
1569            let ghost popped_idx = meta_to_index(
1570                self.lists[id].list[self.lists[id].list.len() - 1].paddr,
1571            );
1572            let ghost old_list_id = self.lists[id].list_id;
1573            assert(self.lists[id].relate_region(self.regions));
1574
1575            let tracked mut owner = self.lists.tracked_remove(id);
1576            let tracked frame_own = tracked_pop_back_embedded(&mut self.regions, &mut owner);
1577            self.lists.tracked_insert(id, owner);
1578            let ghost new_loose = fresh_loose_id(self.loose);
1579            lemma_fresh_loose_id_not_in_dom(self.loose);
1580            self.loose.tracked_insert(new_loose, frame_own);
1581
1582            assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1583            assert(self.loose =~= old_self.loose.insert(new_loose, frame_own));
1584            assert(self.lists[id].list_id == old_list_id);
1585            assert(frame_own.slot_index == popped_idx);
1586
1587            assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1588                &&& self.lists[i].inv()
1589                &&& self.lists[i].relate_region(self.regions)
1590            } by {
1591                if i != id {
1592                    assert(old_self.lists.dom().contains(i));
1593                    assert(old_self.lists[i] == self.lists[i]);
1594                    assert(old_self.lists[i].relate_region(old_regions));
1595                    if self.lists[i].list.len() > 0 {
1596                        assert(self.lists[i].list_id != old_list_id);
1597                    }
1598                }
1599            };
1600
1601            assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1602                &&& self.loose[lid2].inv()
1603                &&& self.loose[lid2].global_inv(self.regions)
1604                &&& self.loose[lid2].frame_link_inv(self.regions)
1605                &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1606            } by {
1607                if lid2 != new_loose {
1608                    assert(old_self.loose.dom().contains(lid2));
1609                    assert(old_self.loose[lid2] == self.loose[lid2]);
1610                    assert(old_self.loose[lid2].global_inv(old_regions));
1611                    assert(old_self.loose[lid2].frame_link_inv(old_regions));
1612                    assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value()
1613                        == 0);
1614                }
1615            };
1616
1617            assert forall|i1: ListId, i2: ListId| #[trigger]
1618                self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1619                    && self.lists[i1].list_id == self.lists[i2].list_id && self.lists[i1].list_id
1620                    != 0 implies i1 == i2 by {
1621                assert(old_self.lists.dom().contains(i1));
1622                assert(old_self.lists.dom().contains(i2));
1623                assert(self.lists[i1].list_id == old_self.lists[i1].list_id);
1624                assert(self.lists[i2].list_id == old_self.lists[i2].list_id);
1625            };
1626
1627            assert forall|l1: LooseId, l2: LooseId| #[trigger]
1628                self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1629                    && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1630                if l1 == new_loose && l2 != new_loose {
1631                    assert(old_self.loose.dom().contains(l2));
1632                    assert(self.loose[l2].slot_index != popped_idx);
1633                } else if l2 == new_loose && l1 != new_loose {
1634                    assert(old_self.loose.dom().contains(l1));
1635                    assert(self.loose[l1].slot_index != popped_idx);
1636                } else if l1 != new_loose && l2 != new_loose {
1637                    assert(old_self.loose.dom().contains(l1));
1638                    assert(old_self.loose.dom().contains(l2));
1639                }
1640            };
1641
1642            // --- cursors: checked-out lists are untouched ---
1643            // `id`'s (preserved, nonzero) `old_list_id` is separated from
1644            // every cursor's list id by the old list/cursor uniqueness, so
1645            // the axiom's other-lists frame preserves each cursor.
1646            assert(self.cursors == old_self.cursors);
1647            assert(self.lists.dom() =~= old_self.lists.dom());
1648            assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1649                &&& self.cursors[cid].list_own.inv()
1650                &&& self.cursors[cid].wf_with_region(self.regions)
1651            } by {
1652                assert(old_self.cursors.dom().contains(cid));
1653                assert(old_self.cursors[cid].wf_with_region(old_regions));
1654                assert(self.cursors[cid].list_own.relate_region(old_regions));
1655                assert(old_self.lists.dom().contains(id));
1656                assert(old_self.lists[id].list_id == old_list_id);
1657                assert(self.cursors[cid].list_own.list_id != old_list_id);
1658                assert(self.cursors[cid].list_own.relate_region(self.regions));
1659            };
1660            assert forall|id2: ListId, cid: CursorId| #[trigger]
1661                self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1662                    && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1663                    && self.lists[id2].list_id != 0 implies false by {
1664                assert(old_self.cursors.dom().contains(cid));
1665                assert(old_self.lists.dom().contains(id2));
1666                assert(old_self.lists[id2].list_id == self.lists[id2].list_id);
1667            };
1668            assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1669                self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1670                    && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1671                    && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1672                assert(old_self.cursors.dom().contains(cid1));
1673                assert(old_self.cursors.dom().contains(cid2));
1674            };
1675            Option::Some(new_loose)
1676        }
1677    }
1678
1679    /// Cursor `insert_before` at an arbitrary position `n`: move the
1680    /// loose handle `lid` into list `id` at index `n` (`0 <= n <= len`).
1681    /// The general form of [`Self::step_push_front`] /
1682    /// [`Self::step_push_back`]; same global effect.
1683    pub proof fn step_insert_before_at(tracked &mut self, id: ListId, n: int, lid: LooseId)
1684        requires
1685            old(self).inv(),
1686            old(self).lists.dom().contains(id),
1687            old(self).loose.dom().contains(lid),
1688            0 <= n <= old(self).lists[id].list.len(),
1689        ensures
1690            final(self).inv(),
1691    {
1692        let ghost old_self = *self;
1693        let ghost old_regions = self.regions;
1694        let ghost fidx = self.loose[lid].slot_index;
1695        let ghost used = Set::<u64>::full().unwrap().filter(
1696            |x: u64|
1697                (exists|i: ListId| #[trigger]
1698                    old_self.lists.dom().contains(i) && i != id && old_self.lists[i].list_id == x)
1699                    || (exists|cid: CursorId| #[trigger]
1700                    old_self.cursors.dom().contains(cid) && old_self.cursors[cid].list_own.list_id
1701                        == x),
1702        );
1703        assert(self.lists[id].relate_region(self.regions));
1704        assert(self.loose[lid].global_inv(self.regions));
1705        assert(self.loose[lid].frame_link_inv(self.regions));
1706        assert(self.regions.slot_owners[fidx].in_list_perm.value() == 0);
1707
1708        let tracked mut owner = self.lists.tracked_remove(id);
1709        let tracked mut frame_own = self.loose.tracked_remove(lid);
1710        insert_before_at_embedded(&mut self.regions, &mut owner, &mut frame_own, n, used);
1711        self.lists.tracked_insert(id, owner);
1712        assert(self.loose =~= old_self.loose.remove(lid));
1713        assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1714        let ghost new_id = self.lists[id].list_id;
1715
1716        assert forall|i: ListId| #[trigger]
1717            self.lists.dom().contains(i) && i != id && self.lists[i].list_id
1718                != 0 implies self.lists[i].list_id != new_id by {
1719            assert(old_self.lists.dom().contains(i));
1720            assert(old_self.lists[i] == self.lists[i]);
1721            if old_self.lists[id].list_id != 0 {
1722                assert(new_id == old_self.lists[id].list_id);
1723            } else {
1724                assert(used.contains(self.lists[i].list_id));
1725            }
1726        };
1727
1728        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1729            &&& self.lists[i].inv()
1730            &&& self.lists[i].relate_region(self.regions)
1731        } by {
1732            if i != id {
1733                assert(old_self.lists.dom().contains(i));
1734                assert(old_self.lists[i] == self.lists[i]);
1735                assert(old_self.lists[i].relate_region(old_regions));
1736                if self.lists[i].list.len() > 0 {
1737                    assert(self.lists[i].list_id != new_id);
1738                }
1739            }
1740        };
1741
1742        assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1743            &&& self.loose[lid2].inv()
1744            &&& self.loose[lid2].global_inv(self.regions)
1745            &&& self.loose[lid2].frame_link_inv(self.regions)
1746            &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1747        } by {
1748            assert(lid2 != lid);
1749            assert(old_self.loose.dom().contains(lid2));
1750            assert(old_self.loose[lid2] == self.loose[lid2]);
1751            assert(old_self.loose[lid2].global_inv(old_regions));
1752            assert(old_self.loose[lid2].frame_link_inv(old_regions));
1753            assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0);
1754            assert(self.loose[lid2].slot_index != fidx);
1755        };
1756
1757        assert forall|i1: ListId, i2: ListId| #[trigger]
1758            self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1759                && self.lists[i1].list.len() > 0 && self.lists[i2].list.len() > 0
1760                && self.lists[i1].list_id == self.lists[i2].list_id implies i1 == i2 by {
1761            if i1 != id && i2 != id {
1762                assert(old_self.lists[i1] == self.lists[i1]);
1763                assert(old_self.lists[i2] == self.lists[i2]);
1764            } else if i1 == id && i2 != id {
1765                assert(self.lists[i2].list_id != new_id);
1766            } else if i2 == id && i1 != id {
1767                assert(self.lists[i1].list_id != new_id);
1768            }
1769        };
1770
1771        assert forall|l1: LooseId, l2: LooseId| #[trigger]
1772            self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1773                && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1774            assert(old_self.loose.dom().contains(l1));
1775            assert(old_self.loose.dom().contains(l2));
1776        };
1777
1778        // --- cursors: checked-out lists are untouched ---
1779        // `cursors` is not read or written by a list op, and every
1780        // cursor's list carries an id distinct from the just-minted
1781        // `new_id` (other cursors' ids are in `used`, or — when the id
1782        // was preserved — separated by the old list/cursor uniqueness),
1783        // so the axiom's other-lists frame preserves each cursor's
1784        // `relate_region`. Index bounds are unchanged.
1785        assert(self.cursors == old_self.cursors);
1786        assert(self.lists.dom() =~= old_self.lists.dom());
1787        assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1788            &&& self.cursors[cid].list_own.inv()
1789            &&& self.cursors[cid].wf_with_region(self.regions)
1790        } by {
1791            assert(old_self.cursors.dom().contains(cid));
1792            assert(old_self.cursors[cid].wf_with_region(old_regions));
1793            assert(self.cursors[cid].list_own.relate_region(old_regions));
1794            if old_self.lists[id].list_id != 0 {
1795                assert(new_id == old_self.lists[id].list_id);
1796            } else {
1797                assert(used.contains(self.cursors[cid].list_own.list_id));
1798            }
1799            assert(self.cursors[cid].list_own.list_id != new_id);
1800            assert(self.cursors[cid].list_own.relate_region(self.regions));
1801        };
1802        assert forall|id2: ListId, cid: CursorId| #[trigger]
1803            self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1804                && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1805                && self.lists[id2].list_id != 0 implies false by {
1806            assert(old_self.cursors.dom().contains(cid));
1807            if id2 == id {
1808                assert(self.lists[id].list_id == new_id);
1809                if old_self.lists[id].list_id != 0 {
1810                    assert(new_id == old_self.lists[id].list_id);
1811                } else {
1812                    assert(used.contains(self.cursors[cid].list_own.list_id));
1813                }
1814            } else {
1815                assert(old_self.lists.dom().contains(id2));
1816                assert(old_self.lists[id2] == self.lists[id2]);
1817            }
1818        };
1819        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1820            self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1821                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1822                && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1823            assert(old_self.cursors.dom().contains(cid1));
1824            assert(old_self.cursors.dom().contains(cid2));
1825        };
1826    }
1827
1828    /// Cursor `take_current` at an arbitrary position `n`: pop the link
1829    /// at index `n` (`0 <= n < len`) of list `id` back into the loose
1830    /// pool. The general form of [`Self::step_pop_front`] /
1831    /// [`Self::step_pop_back`]; same global effect.
1832    pub proof fn step_take_at(tracked &mut self, id: ListId, n: int) -> (res: Option<LooseId>)
1833        requires
1834            old(self).inv(),
1835            old(self).lists.dom().contains(id),
1836        ensures
1837            final(self).inv(),
1838            !(0 <= n < old(self).lists[id].list.len()) ==> res is None && *final(self) == *old(
1839                self,
1840            ),
1841            0 <= n < old(self).lists[id].list.len() ==> res is Some,
1842    {
1843        if !(0 <= n < self.lists[id].list.len()) {
1844            // Exec take-at-position returns `None` when `n` is out of
1845            // range; the store is unchanged.
1846            Option::None
1847        } else {
1848            let ghost old_self = *self;
1849            let ghost old_regions = self.regions;
1850            let ghost popped_idx = meta_to_index(self.lists[id].list[n].paddr);
1851            let ghost old_list_id = self.lists[id].list_id;
1852            assert(self.lists[id].relate_region(self.regions));
1853
1854            let tracked mut owner = self.lists.tracked_remove(id);
1855            let tracked frame_own = take_at_embedded(&mut self.regions, &mut owner, n);
1856            self.lists.tracked_insert(id, owner);
1857            let ghost new_loose = fresh_loose_id(self.loose);
1858            lemma_fresh_loose_id_not_in_dom(self.loose);
1859            self.loose.tracked_insert(new_loose, frame_own);
1860
1861            assert(self.lists =~= old_self.lists.remove(id).insert(id, owner));
1862            assert(self.loose =~= old_self.loose.insert(new_loose, frame_own));
1863            assert(self.lists[id].list_id == old_list_id);
1864            assert(frame_own.slot_index == popped_idx);
1865
1866            assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
1867                &&& self.lists[i].inv()
1868                &&& self.lists[i].relate_region(self.regions)
1869            } by {
1870                if i != id {
1871                    assert(old_self.lists.dom().contains(i));
1872                    assert(old_self.lists[i] == self.lists[i]);
1873                    assert(old_self.lists[i].relate_region(old_regions));
1874                    if self.lists[i].list.len() > 0 {
1875                        assert(self.lists[i].list_id != old_list_id);
1876                    }
1877                }
1878            };
1879
1880            assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
1881                &&& self.loose[lid2].inv()
1882                &&& self.loose[lid2].global_inv(self.regions)
1883                &&& self.loose[lid2].frame_link_inv(self.regions)
1884                &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
1885            } by {
1886                if lid2 != new_loose {
1887                    assert(old_self.loose.dom().contains(lid2));
1888                    assert(old_self.loose[lid2] == self.loose[lid2]);
1889                    assert(old_self.loose[lid2].global_inv(old_regions));
1890                    assert(old_self.loose[lid2].frame_link_inv(old_regions));
1891                    assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value()
1892                        == 0);
1893                }
1894            };
1895
1896            assert forall|i1: ListId, i2: ListId| #[trigger]
1897                self.lists.dom().contains(i1) && #[trigger] self.lists.dom().contains(i2)
1898                    && self.lists[i1].list_id == self.lists[i2].list_id && self.lists[i1].list_id
1899                    != 0 implies i1 == i2 by {
1900                assert(old_self.lists.dom().contains(i1));
1901                assert(old_self.lists.dom().contains(i2));
1902                assert(self.lists[i1].list_id == old_self.lists[i1].list_id);
1903                assert(self.lists[i2].list_id == old_self.lists[i2].list_id);
1904            };
1905
1906            assert forall|l1: LooseId, l2: LooseId| #[trigger]
1907                self.loose.dom().contains(l1) && #[trigger] self.loose.dom().contains(l2)
1908                    && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
1909                if l1 == new_loose && l2 != new_loose {
1910                    assert(old_self.loose.dom().contains(l2));
1911                    assert(self.loose[l2].slot_index != popped_idx);
1912                } else if l2 == new_loose && l1 != new_loose {
1913                    assert(old_self.loose.dom().contains(l1));
1914                    assert(self.loose[l1].slot_index != popped_idx);
1915                } else if l1 != new_loose && l2 != new_loose {
1916                    assert(old_self.loose.dom().contains(l1));
1917                    assert(old_self.loose.dom().contains(l2));
1918                }
1919            };
1920
1921            // --- cursors: checked-out lists are untouched ---
1922            // `id`'s (preserved, nonzero) `old_list_id` is separated from
1923            // every cursor's list id by the old list/cursor uniqueness, so
1924            // the axiom's other-lists frame preserves each cursor.
1925            assert(self.cursors == old_self.cursors);
1926            assert(self.lists.dom() =~= old_self.lists.dom());
1927            assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
1928                &&& self.cursors[cid].list_own.inv()
1929                &&& self.cursors[cid].wf_with_region(self.regions)
1930            } by {
1931                assert(old_self.cursors.dom().contains(cid));
1932                assert(old_self.cursors[cid].wf_with_region(old_regions));
1933                assert(self.cursors[cid].list_own.relate_region(old_regions));
1934                assert(old_self.lists.dom().contains(id));
1935                assert(old_self.lists[id].list_id == old_list_id);
1936                assert(self.cursors[cid].list_own.list_id != old_list_id);
1937                assert(self.cursors[cid].list_own.relate_region(self.regions));
1938            };
1939            assert forall|id2: ListId, cid: CursorId| #[trigger]
1940                self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
1941                    && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
1942                    && self.lists[id2].list_id != 0 implies false by {
1943                assert(old_self.cursors.dom().contains(cid));
1944                assert(old_self.lists.dom().contains(id2));
1945                assert(old_self.lists[id2].list_id == self.lists[id2].list_id);
1946            };
1947            assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
1948                self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
1949                    && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
1950                    && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
1951                assert(old_self.cursors.dom().contains(cid1));
1952                assert(old_self.cursors.dom().contains(cid2));
1953            };
1954            Option::Some(new_loose)
1955        }
1956    }
1957
1958    // -------------------------------------------------------------------
1959    // Persistent cursor lifecycle
1960    // -------------------------------------------------------------------
1961    /// Invariant-preservation lemma for *checking a list out* into a
1962    /// cursor: `lists[id]` (a held list) moves to `cursors[id]` (the
1963    /// same list, now position-tracked at `index`). Region-free — only
1964    /// the `lists`/`cursors` bookkeeping moves. All disjointness /
1965    /// uniqueness facts transfer from the old store (a checked-out list
1966    /// keeps its id, distinct by the same arguments as a held list).
1967    proof fn lemma_checkout_inv(old_self: Self, new_self: Self, id: ListId, index: int)
1968        requires
1969            old_self.inv(),
1970            old_self.lists.dom().contains(id),
1971            0 <= index <= old_self.lists[id].list.len(),
1972            new_self.regions == old_self.regions,
1973            new_self.loose == old_self.loose,
1974            new_self.lists == old_self.lists.remove(id),
1975            new_self.cursors == old_self.cursors.insert(
1976                id,
1977                CursorOwner::cursor_mut_at_owner(old_self.lists[id], index),
1978            ),
1979        ensures
1980            new_self.inv(),
1981    {
1982        assert forall|i: ListId| #[trigger] new_self.lists.dom().contains(i) implies {
1983            &&& new_self.lists[i].inv()
1984            &&& new_self.lists[i].relate_region(new_self.regions)
1985        } by {
1986            assert(i != id);
1987            assert(old_self.lists.dom().contains(i));
1988            assert(old_self.lists[i] == new_self.lists[i]);
1989        };
1990        assert forall|lid: LooseId| #[trigger] new_self.loose.dom().contains(lid) implies {
1991            &&& new_self.loose[lid].inv()
1992            &&& new_self.loose[lid].global_inv(new_self.regions)
1993            &&& new_self.loose[lid].frame_link_inv(new_self.regions)
1994            &&& new_self.regions.slot_owners[new_self.loose[lid].slot_index].in_list_perm.value()
1995                == 0
1996        } by {
1997            assert(old_self.loose.dom().contains(lid));
1998        };
1999        assert forall|i1: ListId, i2: ListId| #[trigger]
2000            new_self.lists.dom().contains(i1) && #[trigger] new_self.lists.dom().contains(i2)
2001                && new_self.lists[i1].list_id == new_self.lists[i2].list_id
2002                && new_self.lists[i1].list_id != 0 implies i1 == i2 by {
2003            assert(old_self.lists.dom().contains(i1));
2004            assert(old_self.lists.dom().contains(i2));
2005        };
2006        assert forall|l1: LooseId, l2: LooseId| #[trigger]
2007            new_self.loose.dom().contains(l1) && #[trigger] new_self.loose.dom().contains(l2)
2008                && new_self.loose[l1].slot_index == new_self.loose[l2].slot_index implies l1
2009            == l2 by {
2010            assert(old_self.loose.dom().contains(l1));
2011            assert(old_self.loose.dom().contains(l2));
2012        };
2013        assert(new_self.lists.dom().disjoint(new_self.cursors.dom()));
2014        assert forall|cid: CursorId| #[trigger] new_self.cursors.dom().contains(cid) implies {
2015            &&& new_self.cursors[cid].list_own.inv()
2016            &&& new_self.cursors[cid].wf_with_region(new_self.regions)
2017        } by {
2018            if cid != id {
2019                assert(old_self.cursors.dom().contains(cid));
2020            } else {
2021                assert(new_self.cursors[id].list_own == old_self.lists[id]);
2022                assert(old_self.lists[id].relate_region(old_self.regions));
2023            }
2024        };
2025        assert forall|id2: ListId, cid: CursorId| #[trigger]
2026            new_self.lists.dom().contains(id2) && #[trigger] new_self.cursors.dom().contains(cid)
2027                && new_self.lists[id2].list_id == new_self.cursors[cid].list_own.list_id
2028                && new_self.lists[id2].list_id != 0 implies false by {
2029            assert(id2 != id);
2030            assert(old_self.lists.dom().contains(id2));
2031            assert(old_self.lists[id2] == new_self.lists[id2]);
2032            if cid == id {
2033                assert(new_self.cursors[id].list_own == old_self.lists[id]);
2034                assert(old_self.lists.dom().contains(id));
2035            } else {
2036                assert(old_self.cursors.dom().contains(cid));
2037                assert(old_self.cursors[cid] == new_self.cursors[cid]);
2038            }
2039        };
2040        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
2041            new_self.cursors.dom().contains(cid1) && #[trigger] new_self.cursors.dom().contains(
2042                cid2,
2043            ) && new_self.cursors[cid1].list_own.list_id == new_self.cursors[cid2].list_own.list_id
2044                && new_self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
2045            if cid1 == id && cid2 != id {
2046                assert(old_self.cursors.dom().contains(cid2));
2047                assert(new_self.cursors[id].list_own == old_self.lists[id]);
2048                assert(old_self.lists.dom().contains(id));
2049            } else if cid2 == id && cid1 != id {
2050                assert(old_self.cursors.dom().contains(cid1));
2051                assert(new_self.cursors[id].list_own == old_self.lists[id]);
2052                assert(old_self.lists.dom().contains(id));
2053            } else if cid1 != id && cid2 != id {
2054                assert(old_self.cursors.dom().contains(cid1));
2055                assert(old_self.cursors.dom().contains(cid2));
2056            }
2057        };
2058    }
2059
2060    /// Invariant-preservation lemma for *checking a list back in* on
2061    /// cursor drop: `cursors[id]`'s list moves back to `lists[id]`. The
2062    /// exact inverse of [`Self::lemma_checkout_inv`].
2063    proof fn lemma_checkin_inv(old_self: Self, new_self: Self, id: CursorId)
2064        requires
2065            old_self.inv(),
2066            old_self.cursors.dom().contains(id),
2067            new_self.regions == old_self.regions,
2068            new_self.loose == old_self.loose,
2069            new_self.cursors == old_self.cursors.remove(id),
2070            new_self.lists == old_self.lists.insert(id, old_self.cursors[id].list_own),
2071        ensures
2072            new_self.inv(),
2073    {
2074        assert(!old_self.lists.dom().contains(id));
2075        assert forall|i: ListId| #[trigger] new_self.lists.dom().contains(i) implies {
2076            &&& new_self.lists[i].inv()
2077            &&& new_self.lists[i].relate_region(new_self.regions)
2078        } by {
2079            if i == id {
2080                assert(new_self.lists[id] == old_self.cursors[id].list_own);
2081                assert(old_self.cursors[id].wf_with_region(old_self.regions));
2082            } else {
2083                assert(old_self.lists.dom().contains(i));
2084                assert(old_self.lists[i] == new_self.lists[i]);
2085            }
2086        };
2087        assert forall|lid: LooseId| #[trigger] new_self.loose.dom().contains(lid) implies {
2088            &&& new_self.loose[lid].inv()
2089            &&& new_self.loose[lid].global_inv(new_self.regions)
2090            &&& new_self.loose[lid].frame_link_inv(new_self.regions)
2091            &&& new_self.regions.slot_owners[new_self.loose[lid].slot_index].in_list_perm.value()
2092                == 0
2093        } by {
2094            assert(old_self.loose.dom().contains(lid));
2095        };
2096        assert forall|i1: ListId, i2: ListId| #[trigger]
2097            new_self.lists.dom().contains(i1) && #[trigger] new_self.lists.dom().contains(i2)
2098                && new_self.lists[i1].list_id == new_self.lists[i2].list_id
2099                && new_self.lists[i1].list_id != 0 implies i1 == i2 by {
2100            // The reinstated list at `id` carries the cursor's id; any
2101            // other list with the same nonzero id is separated by the old
2102            // cross list/cursor uniqueness.
2103            if i1 == id && i2 != id {
2104                assert(old_self.lists.dom().contains(i2));
2105                assert(new_self.lists[id] == old_self.cursors[id].list_own);
2106                assert(old_self.cursors.dom().contains(id));
2107            } else if i2 == id && i1 != id {
2108                assert(old_self.lists.dom().contains(i1));
2109                assert(new_self.lists[id] == old_self.cursors[id].list_own);
2110                assert(old_self.cursors.dom().contains(id));
2111            } else if i1 != id && i2 != id {
2112                assert(old_self.lists.dom().contains(i1));
2113                assert(old_self.lists.dom().contains(i2));
2114            }
2115        };
2116        assert forall|l1: LooseId, l2: LooseId| #[trigger]
2117            new_self.loose.dom().contains(l1) && #[trigger] new_self.loose.dom().contains(l2)
2118                && new_self.loose[l1].slot_index == new_self.loose[l2].slot_index implies l1
2119            == l2 by {
2120            assert(old_self.loose.dom().contains(l1));
2121            assert(old_self.loose.dom().contains(l2));
2122        };
2123        assert(new_self.lists.dom().disjoint(new_self.cursors.dom()));
2124        assert forall|cid: CursorId| #[trigger] new_self.cursors.dom().contains(cid) implies {
2125            &&& new_self.cursors[cid].list_own.inv()
2126            &&& new_self.cursors[cid].wf_with_region(new_self.regions)
2127        } by {
2128            assert(cid != id);
2129            assert(old_self.cursors.dom().contains(cid));
2130            assert(old_self.cursors[cid] == new_self.cursors[cid]);
2131        };
2132        assert forall|id2: ListId, cid: CursorId| #[trigger]
2133            new_self.lists.dom().contains(id2) && #[trigger] new_self.cursors.dom().contains(cid)
2134                && new_self.lists[id2].list_id == new_self.cursors[cid].list_own.list_id
2135                && new_self.lists[id2].list_id != 0 implies false by {
2136            assert(cid != id);
2137            assert(old_self.cursors.dom().contains(cid));
2138            assert(old_self.cursors[cid] == new_self.cursors[cid]);
2139            if id2 == id {
2140                assert(new_self.lists[id] == old_self.cursors[id].list_own);
2141                assert(old_self.cursors.dom().contains(id));
2142            } else {
2143                assert(old_self.lists.dom().contains(id2));
2144                assert(old_self.lists[id2] == new_self.lists[id2]);
2145            }
2146        };
2147        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
2148            new_self.cursors.dom().contains(cid1) && #[trigger] new_self.cursors.dom().contains(
2149                cid2,
2150            ) && new_self.cursors[cid1].list_own.list_id == new_self.cursors[cid2].list_own.list_id
2151                && new_self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
2152            assert(old_self.cursors.dom().contains(cid1));
2153            assert(old_self.cursors.dom().contains(cid2));
2154        };
2155    }
2156
2157    /// Invariant-preservation lemma for *revising a cursor's position in
2158    /// place*: `cursors[id]` keeps its checked-out list (same `list_own`)
2159    /// but adopts a new in-range `index`. Region-free; everything else is
2160    /// untouched, so every fact transfers from the old store.
2161    proof fn lemma_revise_cursor_inv(old_self: Self, new_self: Self, id: CursorId)
2162        requires
2163            old_self.inv(),
2164            old_self.cursors.dom().contains(id),
2165            new_self.regions == old_self.regions,
2166            new_self.lists == old_self.lists,
2167            new_self.loose == old_self.loose,
2168            new_self.cursors.dom() == old_self.cursors.dom(),
2169            new_self.cursors[id].list_own == old_self.cursors[id].list_own,
2170            0 <= new_self.cursors[id].index <= new_self.cursors[id].list_own.list.len(),
2171            forall|c: CursorId| #[trigger]
2172                new_self.cursors.dom().contains(c) && c != id ==> new_self.cursors[c]
2173                    == old_self.cursors[c],
2174        ensures
2175            new_self.inv(),
2176    {
2177        assert forall|i: ListId| #[trigger] new_self.lists.dom().contains(i) implies {
2178            &&& new_self.lists[i].inv()
2179            &&& new_self.lists[i].relate_region(new_self.regions)
2180        } by {
2181            assert(old_self.lists.dom().contains(i));
2182        };
2183        assert forall|lid: LooseId| #[trigger] new_self.loose.dom().contains(lid) implies {
2184            &&& new_self.loose[lid].inv()
2185            &&& new_self.loose[lid].global_inv(new_self.regions)
2186            &&& new_self.loose[lid].frame_link_inv(new_self.regions)
2187            &&& new_self.regions.slot_owners[new_self.loose[lid].slot_index].in_list_perm.value()
2188                == 0
2189        } by {
2190            assert(old_self.loose.dom().contains(lid));
2191        };
2192        assert forall|i1: ListId, i2: ListId| #[trigger]
2193            new_self.lists.dom().contains(i1) && #[trigger] new_self.lists.dom().contains(i2)
2194                && new_self.lists[i1].list_id == new_self.lists[i2].list_id
2195                && new_self.lists[i1].list_id != 0 implies i1 == i2 by {
2196            assert(old_self.lists.dom().contains(i1));
2197            assert(old_self.lists.dom().contains(i2));
2198        };
2199        assert forall|l1: LooseId, l2: LooseId| #[trigger]
2200            new_self.loose.dom().contains(l1) && #[trigger] new_self.loose.dom().contains(l2)
2201                && new_self.loose[l1].slot_index == new_self.loose[l2].slot_index implies l1
2202            == l2 by {
2203            assert(old_self.loose.dom().contains(l1));
2204            assert(old_self.loose.dom().contains(l2));
2205        };
2206        assert(new_self.lists.dom().disjoint(new_self.cursors.dom()));
2207        assert forall|cid: CursorId| #[trigger] new_self.cursors.dom().contains(cid) implies {
2208            &&& new_self.cursors[cid].list_own.inv()
2209            &&& new_self.cursors[cid].wf_with_region(new_self.regions)
2210        } by {
2211            assert(old_self.cursors.dom().contains(cid));
2212            if cid != id {
2213                assert(new_self.cursors[cid] == old_self.cursors[cid]);
2214            } else {
2215                assert(new_self.cursors[id].list_own == old_self.cursors[id].list_own);
2216                assert(old_self.cursors[id].wf_with_region(old_self.regions));
2217            }
2218        };
2219        assert forall|id2: ListId, cid: CursorId| #[trigger]
2220            new_self.lists.dom().contains(id2) && #[trigger] new_self.cursors.dom().contains(cid)
2221                && new_self.lists[id2].list_id == new_self.cursors[cid].list_own.list_id
2222                && new_self.lists[id2].list_id != 0 implies false by {
2223            assert(old_self.lists.dom().contains(id2));
2224            assert(old_self.cursors.dom().contains(cid));
2225            assert(new_self.cursors[cid].list_own.list_id
2226                == old_self.cursors[cid].list_own.list_id);
2227        };
2228        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
2229            new_self.cursors.dom().contains(cid1) && #[trigger] new_self.cursors.dom().contains(
2230                cid2,
2231            ) && new_self.cursors[cid1].list_own.list_id == new_self.cursors[cid2].list_own.list_id
2232                && new_self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
2233            assert(old_self.cursors.dom().contains(cid1));
2234            assert(old_self.cursors.dom().contains(cid2));
2235            assert(new_self.cursors[cid1].list_own.list_id
2236                == old_self.cursors[cid1].list_own.list_id);
2237            assert(new_self.cursors[cid2].list_own.list_id
2238                == old_self.cursors[cid2].list_own.list_id);
2239        };
2240    }
2241
2242    /// `LinkedList::cursor_front_mut`: check list `id` out into a cursor
2243    /// positioned at the front (index 0). The list leaves `lists` and
2244    /// enters `cursors` under the same id (its borrow).
2245    pub proof fn step_cursor_front_mut(tracked &mut self, id: ListId)
2246        requires
2247            old(self).inv(),
2248            old(self).lists.dom().contains(id),
2249        ensures
2250            final(self).inv(),
2251            !final(self).lists.dom().contains(id),
2252            final(self).cursors.dom().contains(id),
2253            final(self).cursors[id] == CursorOwner::front_owner(old(self).lists[id]),
2254    {
2255        let ghost old_self = *self;
2256        let tracked owner = self.lists.tracked_remove(id);
2257        let tracked cur = CursorOwner::tracked_front_owner(owner);
2258        self.cursors.tracked_insert(id, cur);
2259        assert(self.lists =~= old_self.lists.remove(id));
2260        assert(self.cursors =~= old_self.cursors.insert(
2261            id,
2262            CursorOwner::cursor_mut_at_owner(old_self.lists[id], 0),
2263        ));
2264        Self::lemma_checkout_inv(old_self, *self, id, 0);
2265    }
2266
2267    /// `LinkedList::cursor_back_mut`: check list `id` out into a cursor
2268    /// at the back (the last element, or the ghost slot when empty).
2269    pub proof fn step_cursor_back_mut(tracked &mut self, id: ListId)
2270        requires
2271            old(self).inv(),
2272            old(self).lists.dom().contains(id),
2273        ensures
2274            final(self).inv(),
2275            !final(self).lists.dom().contains(id),
2276            final(self).cursors.dom().contains(id),
2277            final(self).cursors[id] == CursorOwner::back_owner(old(self).lists[id]),
2278    {
2279        let ghost old_self = *self;
2280        let tracked owner = self.lists.tracked_remove(id);
2281        let ghost bidx = CursorOwner::back_owner(owner).index;
2282        let tracked cur = CursorOwner::tracked_back_owner(owner);
2283        self.cursors.tracked_insert(id, cur);
2284        assert(self.lists =~= old_self.lists.remove(id));
2285        assert(self.cursors =~= old_self.cursors.insert(
2286            id,
2287            CursorOwner::cursor_mut_at_owner(old_self.lists[id], bidx),
2288        ));
2289        Self::lemma_checkout_inv(old_self, *self, id, bidx);
2290    }
2291
2292    /// `LinkedList::cursor_mut_at`: search list `id` for `frame` and, if
2293    /// it is one of the list's links, check the list out into a cursor
2294    /// positioned at that link; otherwise (the frame is absent — or not a
2295    /// safe managed slot, which can never be a link) leave the store
2296    /// unchanged. Mirrors exec `cursor_mut_at(frame) -> Option<CursorMut>`
2297    /// (the `Some`/`None` outcome is returned as `res`).
2298    pub proof fn step_cursor_mut_at(tracked &mut self, id: ListId, frame: Paddr) -> (res: bool)
2299        requires
2300            old(self).inv(),
2301            old(self).lists.dom().contains(id),
2302        ensures
2303            final(self).inv(),
2304            // `res` is exactly list membership of `frame`.
2305            res == (exists|i: int|
2306                0 <= i < old(self).lists[id].list.len() && #[trigger] meta_to_index(
2307                    old(self).lists[id].list[i].paddr,
2308                ) == frame_to_index(frame)),
2309            // On a hit: the list is checked out into a cursor positioned
2310            // at the matching link.
2311            res ==> !final(self).lists.dom().contains(id) && final(self).cursors.dom().contains(id)
2312                && exists|i: int|
2313                0 <= i < old(self).lists[id].list.len() && #[trigger] meta_to_index(
2314                    old(self).lists[id].list[i].paddr,
2315                ) == #[trigger] frame_to_index(frame) && final(self).cursors[id]
2316                    == CursorOwner::cursor_mut_at_owner(old(self).lists[id], i),
2317            // On a miss: no checkout, the store is unchanged.
2318            !res ==> *final(self) == *old(self),
2319    {
2320        if exists|i: int|
2321            0 <= i < self.lists[id].list.len() && #[trigger] meta_to_index(
2322                self.lists[id].list[i].paddr,
2323            ) == frame_to_index(frame) {
2324            let ghost index = choose|i: int|
2325                0 <= i < self.lists[id].list.len() && #[trigger] meta_to_index(
2326                    self.lists[id].list[i].paddr,
2327                ) == frame_to_index(frame);
2328            let ghost old_self = *self;
2329            let tracked owner = self.lists.tracked_remove(id);
2330            let tracked cur = CursorOwner::tracked_cursor_mut_at_owner(owner, index);
2331            self.cursors.tracked_insert(id, cur);
2332            assert(self.lists =~= old_self.lists.remove(id));
2333            assert(self.cursors =~= old_self.cursors.insert(
2334                id,
2335                CursorOwner::cursor_mut_at_owner(old_self.lists[id], index),
2336            ));
2337            Self::lemma_checkout_inv(old_self, *self, id, index);
2338            true
2339        } else {
2340            false
2341        }
2342    }
2343
2344    /// `CursorMut::move_next`: advance cursor `id` one step toward the
2345    /// back (wrapping through the ghost slot). Pure position change.
2346    pub proof fn step_move_next(tracked &mut self, id: CursorId)
2347        requires
2348            old(self).inv(),
2349            old(self).cursors.dom().contains(id),
2350        ensures
2351            final(self).inv(),
2352            final(self).regions == old(self).regions,
2353            final(self).cursors[id] == old(self).cursors[id].move_next_owner_spec(),
2354    {
2355        let ghost old_self = *self;
2356        let tracked cur = self.cursors.tracked_remove(id);
2357        let ghost ni = cur.move_next_owner_spec().index;
2358        let tracked CursorOwner { list_own, index: _ } = cur;
2359        let tracked cur2 = CursorOwner::tracked_cursor_mut_at_owner(list_own, ni);
2360        self.cursors.tracked_insert(id, cur2);
2361        assert(self.cursors[id] == old_self.cursors[id].move_next_owner_spec());
2362        assert(self.cursors.dom() =~= old_self.cursors.dom());
2363        Self::lemma_revise_cursor_inv(old_self, *self, id);
2364    }
2365
2366    /// `CursorMut::move_prev`: retreat cursor `id` one step toward the
2367    /// front (wrapping through the ghost slot). Pure position change.
2368    pub proof fn step_move_prev(tracked &mut self, id: CursorId)
2369        requires
2370            old(self).inv(),
2371            old(self).cursors.dom().contains(id),
2372        ensures
2373            final(self).inv(),
2374            final(self).regions == old(self).regions,
2375            final(self).cursors[id] == old(self).cursors[id].move_prev_owner_spec(),
2376    {
2377        let ghost old_self = *self;
2378        let tracked cur = self.cursors.tracked_remove(id);
2379        let ghost ni = cur.move_prev_owner_spec().index;
2380        let tracked CursorOwner { list_own, index: _ } = cur;
2381        let tracked cur2 = CursorOwner::tracked_cursor_mut_at_owner(list_own, ni);
2382        self.cursors.tracked_insert(id, cur2);
2383        assert(self.cursors[id] == old_self.cursors[id].move_prev_owner_spec());
2384        assert(self.cursors.dom() =~= old_self.cursors.dom());
2385        Self::lemma_revise_cursor_inv(old_self, *self, id);
2386    }
2387
2388    /// `CursorMut::current_meta`: read the link the cursor `id` is on.
2389    /// A read-only query — returns the current [`LinkOwner`] (`None` at
2390    /// the ghost slot); the store is unchanged.
2391    pub proof fn step_current_meta(tracked &self, id: CursorId) -> (res: Option<LinkOwner>)
2392        requires
2393            self.inv(),
2394            self.cursors.dom().contains(id),
2395        ensures
2396            res == self.cursors[id].current(),
2397            res.is_some() <==> 0 <= self.cursors[id].index < self.cursors[id].length(),
2398    {
2399        self.cursors[id].current()
2400    }
2401
2402    /// `CursorMut::as_list`: borrow the cursor's checked-out list for
2403    /// reading. A pure no-op — `&self` on the store, so `regions` /
2404    /// `lists` / `loose` / `cursors` are all untouched; it merely exposes
2405    /// the list's contents (the model `Seq` of links). Modeled for
2406    /// completeness, to demonstrate the read-only view changes nothing.
2407    pub proof fn step_as_list(tracked &self, id: CursorId) -> (res: Seq<LinkOwner>)
2408        requires
2409            self.inv(),
2410            self.cursors.dom().contains(id),
2411        ensures
2412            res == self.cursors[id].list_own.list,
2413            res.len() == self.cursors[id].length(),
2414    {
2415        self.cursors[id].list_own.list
2416    }
2417
2418    /// Drop of a `CursorMut`: check the cursor's list back into `lists`
2419    /// under its home id, ending the borrow. Inverse of
2420    /// [`Self::step_cursor_front_mut`] et al.
2421    pub proof fn step_cursor_drop(tracked &mut self, id: CursorId)
2422        requires
2423            old(self).inv(),
2424            old(self).cursors.dom().contains(id),
2425        ensures
2426            final(self).inv(),
2427            !final(self).cursors.dom().contains(id),
2428            final(self).lists.dom().contains(id),
2429            final(self).lists[id] == old(self).cursors[id].list_own,
2430    {
2431        let ghost old_self = *self;
2432        let tracked cur = self.cursors.tracked_remove(id);
2433        let tracked CursorOwner { list_own, index: _ } = cur;
2434        self.lists.tracked_insert(id, list_own);
2435        assert(self.cursors =~= old_self.cursors.remove(id));
2436        assert(self.lists =~= old_self.lists.insert(id, old_self.cursors[id].list_own));
2437        Self::lemma_checkin_inv(old_self, *self, id);
2438    }
2439
2440    /// `CursorMut::insert_before`: through the checked-out cursor `id`,
2441    /// move the loose handle `lid` into the cursor's list at the current
2442    /// position (index `n`), advancing the cursor to `n + 1`. The general
2443    /// [`Self::step_insert_before_at`], but on the list parked in
2444    /// `cursors` rather than `lists` — so *every* held list is an "other
2445    /// list" preserved by the axiom's frame.
2446    pub proof fn step_cursor_insert_before(tracked &mut self, id: CursorId, lid: LooseId)
2447        requires
2448            old(self).inv(),
2449            old(self).cursors.dom().contains(id),
2450            old(self).loose.dom().contains(lid),
2451        ensures
2452            final(self).inv(),
2453    {
2454        let ghost old_self = *self;
2455        let ghost old_regions = self.regions;
2456        let ghost fidx = self.loose[lid].slot_index;
2457        let ghost n = self.cursors[id].index;
2458        // Avoid every other list's *and* every other cursor's id.
2459        let ghost used = Set::<u64>::full().unwrap().filter(
2460            |x: u64|
2461                (exists|i: ListId| #[trigger]
2462                    old_self.lists.dom().contains(i) && old_self.lists[i].list_id == x) || (exists|
2463                    cid: CursorId,
2464                | #[trigger]
2465                    old_self.cursors.dom().contains(cid) && cid != id
2466                        && old_self.cursors[cid].list_own.list_id == x),
2467        );
2468        assert(self.cursors[id].wf_with_region(self.regions));
2469        assert(self.cursors[id].list_own.relate_region(self.regions));
2470        assert(self.loose[lid].global_inv(self.regions));
2471        assert(self.loose[lid].frame_link_inv(self.regions));
2472        assert(self.regions.slot_owners[fidx].in_list_perm.value() == 0);
2473
2474        let tracked cur = self.cursors.tracked_remove(id);
2475        let tracked CursorOwner { list_own: mut owner, index: _ } = cur;
2476        let tracked mut frame_own = self.loose.tracked_remove(lid);
2477        insert_before_at_embedded(&mut self.regions, &mut owner, &mut frame_own, n, used);
2478        let tracked cur2 = CursorOwner::tracked_cursor_mut_at_owner(owner, n + 1);
2479        self.cursors.tracked_insert(id, cur2);
2480        assert(self.loose =~= old_self.loose.remove(lid));
2481        assert(self.cursors =~= old_self.cursors.remove(id).insert(id, cur2));
2482        let ghost new_id = self.cursors[id].list_own.list_id;
2483        assert(self.cursors[id].list_own == owner);
2484
2485        // `new_id` is distinct from every list id and every *other*
2486        // cursor id (minted outside `used`, or — when the cursor's list
2487        // already had an id — separated by the old uniqueness).
2488        assert forall|i: ListId| #[trigger]
2489            old_self.lists.dom().contains(i) && self.lists[i].list_id
2490                != 0 implies self.lists[i].list_id != new_id by {
2491            if old_self.cursors[id].list_own.list_id != 0 {
2492                assert(new_id == old_self.cursors[id].list_own.list_id);
2493                assert(old_self.cursors.dom().contains(id));
2494            } else {
2495                assert(used.contains(self.lists[i].list_id));
2496            }
2497        };
2498        assert forall|cid: CursorId| #[trigger]
2499            old_self.cursors.dom().contains(cid) && cid != id
2500                && old_self.cursors[cid].list_own.list_id
2501                != 0 implies old_self.cursors[cid].list_own.list_id != new_id by {
2502            if old_self.cursors[id].list_own.list_id != 0 {
2503                assert(new_id == old_self.cursors[id].list_own.list_id);
2504            } else {
2505                assert(used.contains(old_self.cursors[cid].list_own.list_id));
2506            }
2507        };
2508
2509        // --- per-list: every list is preserved (none is operating) ---
2510        assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
2511            &&& self.lists[i].inv()
2512            &&& self.lists[i].relate_region(self.regions)
2513        } by {
2514            assert(old_self.lists.dom().contains(i));
2515            assert(old_self.lists[i] == self.lists[i]);
2516            assert(old_self.lists[i].relate_region(old_regions));
2517            assert(self.lists[i].list_id != new_id);
2518            assert(self.lists[i].relate_region(self.regions));
2519        };
2520
2521        // --- per-loose: `lid` removed; others at `!= fidx` preserved ---
2522        assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
2523            &&& self.loose[lid2].inv()
2524            &&& self.loose[lid2].global_inv(self.regions)
2525            &&& self.loose[lid2].frame_link_inv(self.regions)
2526            &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
2527        } by {
2528            assert(lid2 != lid);
2529            assert(old_self.loose.dom().contains(lid2));
2530            assert(old_self.loose[lid2] == self.loose[lid2]);
2531            assert(old_self.loose[lid2].global_inv(old_regions));
2532            assert(old_self.loose[lid2].frame_link_inv(old_regions));
2533            assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0);
2534            assert(self.loose[lid2].slot_index != fidx);
2535        };
2536
2537        // --- disjointness (list/cursor domains unchanged) ---
2538        assert(self.lists.dom() =~= old_self.lists.dom());
2539        assert(self.cursors.dom() =~= old_self.cursors.dom());
2540        assert(self.lists.dom().disjoint(self.cursors.dom()));
2541
2542        // --- per-cursor: operating cursor rebuilt; others preserved ---
2543        assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
2544            &&& self.cursors[cid].list_own.inv()
2545            &&& self.cursors[cid].wf_with_region(self.regions)
2546        } by {
2547            if cid == id {
2548                assert(self.cursors[id].list_own == owner);
2549                assert(self.cursors[id].index == n + 1);
2550                assert(owner.list.len() == old_self.cursors[id].list_own.list.len() + 1);
2551            } else {
2552                assert(old_self.cursors.dom().contains(cid));
2553                assert(old_self.cursors[cid] == self.cursors[cid]);
2554                assert(old_self.cursors[cid].wf_with_region(old_regions));
2555                assert(self.cursors[cid].list_own.relate_region(old_regions));
2556                assert(self.cursors[cid].list_own.list_id != new_id);
2557                assert(self.cursors[cid].list_own.relate_region(self.regions));
2558            }
2559        };
2560
2561        // --- cross list/cursor uniqueness ---
2562        assert forall|id2: ListId, cid: CursorId| #[trigger]
2563            self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
2564                && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
2565                && self.lists[id2].list_id != 0 implies false by {
2566            assert(old_self.lists.dom().contains(id2));
2567            assert(old_self.lists[id2] == self.lists[id2]);
2568            if cid == id {
2569                assert(self.cursors[id].list_own.list_id == new_id);
2570                assert(self.lists[id2].list_id != new_id);
2571            } else {
2572                assert(old_self.cursors.dom().contains(cid));
2573                assert(old_self.cursors[cid] == self.cursors[cid]);
2574            }
2575        };
2576
2577        // --- cursor×cursor uniqueness ---
2578        assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
2579            self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
2580                && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
2581                && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
2582            if cid1 == id && cid2 != id {
2583                assert(self.cursors[id].list_own.list_id == new_id);
2584                assert(old_self.cursors.dom().contains(cid2));
2585                assert(old_self.cursors[cid2] == self.cursors[cid2]);
2586                assert(self.cursors[cid2].list_own.list_id != new_id);
2587            } else if cid2 == id && cid1 != id {
2588                assert(self.cursors[id].list_own.list_id == new_id);
2589                assert(old_self.cursors.dom().contains(cid1));
2590                assert(old_self.cursors[cid1] == self.cursors[cid1]);
2591                assert(self.cursors[cid1].list_own.list_id != new_id);
2592            } else if cid1 != id && cid2 != id {
2593                assert(old_self.cursors.dom().contains(cid1));
2594                assert(old_self.cursors.dom().contains(cid2));
2595            }
2596        };
2597    }
2598
2599    /// `CursorMut::take_current`: through the checked-out cursor `id`,
2600    /// pop the link the cursor is on (index `n`, requires the cursor be
2601    /// on an element) back into the loose pool, leaving the cursor at the
2602    /// same index (now on the following link). The general
2603    /// [`Self::step_take_at`] on a cursored list. Returns the fresh loose
2604    /// id.
2605    pub proof fn step_cursor_take_current(tracked &mut self, id: CursorId) -> (res: Option<LooseId>)
2606        requires
2607            old(self).inv(),
2608            old(self).cursors.dom().contains(id),
2609        ensures
2610            final(self).inv(),
2611            !(0 <= old(self).cursors[id].index < old(self).cursors[id].length()) ==> res is None
2612                && *final(self) == *old(self),
2613            0 <= old(self).cursors[id].index < old(self).cursors[id].length() ==> res is Some,
2614    {
2615        if !(0 <= self.cursors[id].index < self.cursors[id].length()) {
2616            // Exec `CursorMut::take_current` returns `None` when the
2617            // cursor is not on an element; the store is unchanged.
2618            Option::None
2619        } else {
2620            let ghost old_self = *self;
2621            let ghost old_regions = self.regions;
2622            let ghost n = self.cursors[id].index;
2623            let ghost old_list_id = self.cursors[id].list_own.list_id;
2624            let ghost popped_idx = meta_to_index(self.cursors[id].list_own.list[n].paddr);
2625            assert(self.cursors[id].list_own.relate_region(self.regions));
2626            // A non-empty list carries a nonzero id (`LinkedListOwner::inv`).
2627            assert(old_list_id != 0);
2628
2629            let tracked cur = self.cursors.tracked_remove(id);
2630            let tracked CursorOwner { list_own: mut owner, index: _ } = cur;
2631            let tracked frame_own = take_at_embedded(&mut self.regions, &mut owner, n);
2632            let tracked cur2 = CursorOwner::tracked_cursor_mut_at_owner(owner, n);
2633            self.cursors.tracked_insert(id, cur2);
2634            let ghost new_loose = fresh_loose_id(self.loose);
2635            lemma_fresh_loose_id_not_in_dom(self.loose);
2636            self.loose.tracked_insert(new_loose, frame_own);
2637
2638            assert(self.cursors =~= old_self.cursors.remove(id).insert(id, cur2));
2639            assert(self.loose =~= old_self.loose.insert(new_loose, frame_own));
2640            assert(self.cursors[id].list_own.list_id == old_list_id);
2641            assert(self.cursors[id].list_own == owner);
2642            assert(frame_own.slot_index == popped_idx);
2643
2644            // --- per-list: every list preserved (operating is a cursor) ---
2645            assert forall|i: ListId| #[trigger] self.lists.dom().contains(i) implies {
2646                &&& self.lists[i].inv()
2647                &&& self.lists[i].relate_region(self.regions)
2648            } by {
2649                assert(old_self.lists.dom().contains(i));
2650                assert(old_self.lists[i] == self.lists[i]);
2651                assert(old_self.lists[i].relate_region(old_regions));
2652                assert(old_self.cursors.dom().contains(id));
2653                assert(self.lists[i].list_id != old_list_id);
2654                assert(self.lists[i].relate_region(self.regions));
2655            };
2656
2657            // --- per-loose: new entry from the axiom; others preserved;
2658            // the popped slot is disjoint from every loose slot ---
2659            assert forall|lid2: LooseId| #[trigger] self.loose.dom().contains(lid2) implies {
2660                &&& self.loose[lid2].inv()
2661                &&& self.loose[lid2].global_inv(self.regions)
2662                &&& self.loose[lid2].frame_link_inv(self.regions)
2663                &&& self.regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value() == 0
2664            } by {
2665                if lid2 != new_loose {
2666                    assert(old_self.loose.dom().contains(lid2));
2667                    assert(old_self.loose[lid2] == self.loose[lid2]);
2668                    assert(old_self.loose[lid2].global_inv(old_regions));
2669                    assert(old_self.loose[lid2].frame_link_inv(old_regions));
2670                    assert(old_regions.slot_owners[self.loose[lid2].slot_index].in_list_perm.value()
2671                        == 0);
2672                }
2673            };
2674
2675            // --- disjointness ---
2676            assert(self.lists.dom() =~= old_self.lists.dom());
2677            assert(self.cursors.dom() =~= old_self.cursors.dom());
2678            assert(self.lists.dom().disjoint(self.cursors.dom()));
2679
2680            // --- per-cursor: operating cursor rebuilt; others preserved ---
2681            assert forall|cid: CursorId| #[trigger] self.cursors.dom().contains(cid) implies {
2682                &&& self.cursors[cid].list_own.inv()
2683                &&& self.cursors[cid].wf_with_region(self.regions)
2684            } by {
2685                if cid == id {
2686                    assert(self.cursors[id].list_own == owner);
2687                    assert(self.cursors[id].index == n);
2688                    assert(owner.list.len() == old_self.cursors[id].list_own.list.len() - 1);
2689                } else {
2690                    assert(old_self.cursors.dom().contains(cid));
2691                    assert(old_self.cursors[cid] == self.cursors[cid]);
2692                    assert(old_self.cursors[cid].wf_with_region(old_regions));
2693                    assert(self.cursors[cid].list_own.relate_region(old_regions));
2694                    assert(self.cursors[cid].list_own.list_id != old_list_id);
2695                    assert(self.cursors[cid].list_own.relate_region(self.regions));
2696                }
2697            };
2698
2699            // --- cross list/cursor uniqueness ---
2700            assert forall|id2: ListId, cid: CursorId| #[trigger]
2701                self.lists.dom().contains(id2) && #[trigger] self.cursors.dom().contains(cid)
2702                    && self.lists[id2].list_id == self.cursors[cid].list_own.list_id
2703                    && self.lists[id2].list_id != 0 implies false by {
2704                assert(old_self.lists.dom().contains(id2));
2705                assert(old_self.lists[id2] == self.lists[id2]);
2706                if cid == id {
2707                    assert(self.cursors[id].list_own.list_id == old_list_id);
2708                    assert(self.lists[id2].list_id != old_list_id);
2709                } else {
2710                    assert(old_self.cursors.dom().contains(cid));
2711                    assert(old_self.cursors[cid] == self.cursors[cid]);
2712                }
2713            };
2714
2715            // --- cursor×cursor uniqueness ---
2716            assert forall|cid1: CursorId, cid2: CursorId| #[trigger]
2717                self.cursors.dom().contains(cid1) && #[trigger] self.cursors.dom().contains(cid2)
2718                    && self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
2719                    && self.cursors[cid1].list_own.list_id != 0 implies cid1 == cid2 by {
2720                assert(old_self.cursors.dom().contains(cid1));
2721                assert(old_self.cursors.dom().contains(cid2));
2722                assert(self.cursors[cid1].list_own.list_id
2723                    == old_self.cursors[cid1].list_own.list_id);
2724                assert(self.cursors[cid2].list_own.list_id
2725                    == old_self.cursors[cid2].list_own.list_id);
2726            };
2727
2728            // --- loose-internal slot disjointness ---
2729            assert forall|l1: LooseId, l2: LooseId|
2730                #![trigger self.loose.dom().contains(l1), self.loose.dom().contains(l2)]
2731                self.loose.dom().contains(l1) && self.loose.dom().contains(l2)
2732                    && self.loose[l1].slot_index == self.loose[l2].slot_index implies l1 == l2 by {
2733                if l1 == new_loose && l2 != new_loose {
2734                    assert(old_self.loose.dom().contains(l2));
2735                    assert(self.loose[l2].slot_index != popped_idx);
2736                } else if l2 == new_loose && l1 != new_loose {
2737                    assert(old_self.loose.dom().contains(l1));
2738                    assert(self.loose[l1].slot_index != popped_idx);
2739                } else if l1 != new_loose && l2 != new_loose {
2740                    assert(old_self.loose.dom().contains(l1));
2741                    assert(old_self.loose.dom().contains(l2));
2742                }
2743            };
2744            Option::Some(new_loose)
2745        }
2746    }
2747}
2748
2749} // verus!