Skip to main content

ostd/specs/mm/embedding/
list_store.rs

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