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