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