pub struct ListStore<M: AnyFrameMeta + Repr<MetaSlotSmall>> {
pub regions: MetaRegionOwners,
pub lists: Map<ListId, LinkedListOwner<M>>,
pub loose: Map<LooseId, UniqueFrameOwner<Link<M>>>,
pub cursors: Map<CursorId, CursorOwner<M>>,
}Expand description
One-step-soundness store for the frame LinkedList. Holds the shared
regions, the set of held lists, the pool of loose
(push-eligible) UniqueFrame<Link<M>> handles, and the live cursors.
A cursor checks out its list: a live CursorMut borrows the
LinkedList exclusively, so while a cursor exists its
LinkedListOwner lives inside the CursorOwner (cursors) rather
than in lists. Dropping the cursor returns the list to lists.
Fields§
§regions: MetaRegionOwners§lists: Map<ListId, LinkedListOwner<M>>§loose: Map<LooseId, UniqueFrameOwner<Link<M>>>§cursors: Map<CursorId, CursorOwner<M>>Implementations§
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M>
Sourcepub open spec fn inv(self) -> bool
pub open spec fn inv(self) -> bool
{
&&& self.regions.inv()
&&& forall |id: ListId| {
#[trigger] self.lists.dom().contains(id)
==> {
&&& self.lists[id].inv()
&&& self.lists[id].relate_region(self.regions)
}
}
&&& forall |lid: LooseId| {
#[trigger] self.loose.dom().contains(lid)
==> {
&&& self.loose[lid].inv()
&&& self.loose[lid].global_inv(self.regions)
&&& self.loose[lid].frame_link_inv(self.regions)
&&& self
.regions
.slot_owners[self.loose[lid].slot_index]
.inner_perms
.in_list
.value() == 0
}
}
&&& forall |id1: ListId, id2: ListId| {
self.lists.dom().contains(id1) && self.lists.dom().contains(id2)
&& self.lists[id1].list_id == self.lists[id2].list_id
&& self.lists[id1].list_id != 0 ==> id1 == id2
}
&&& forall |lid1: LooseId, lid2: LooseId| {
self.loose.dom().contains(lid1) && self.loose.dom().contains(lid2)
&& self.loose[lid1].slot_index == self.loose[lid2].slot_index
==> lid1 == lid2
}
&&& self.lists.dom().disjoint(self.cursors.dom())
&&& forall |cid: CursorId| {
#[trigger] self.cursors.dom().contains(cid)
==> {
&&& self.cursors[cid].list_own.inv()
&&& self.cursors[cid].wf_with_region(self.regions)
}
}
&&& forall |id: ListId, cid: CursorId| {
self.lists.dom().contains(id) && self.cursors.dom().contains(cid)
&& self.lists[id].list_id == self.cursors[cid].list_own.list_id
&& self.lists[id].list_id != 0 ==> false
}
&&& forall |cid1: CursorId, cid2: CursorId| {
self.cursors.dom().contains(cid1) && self.cursors.dom().contains(cid2)
&& self.cursors[cid1].list_own.list_id == self.cursors[cid2].list_own.list_id
&& self.cursors[cid1].list_own.list_id != 0 ==> cid1 == cid2
}
&&& forall |id: ListId| {
#[trigger] self.lists.dom().contains(id)
==> list_registry_ok(self.regions, self.lists[id])
}
&&& forall |cid: CursorId| {
#[trigger] self.cursors.dom().contains(cid)
==> list_registry_ok(self.regions, self.cursors[cid].list_own)
}
}The store’s top-level invariant.
Source§impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M>
impl<M: AnyFrameMeta + Repr<MetaSlotSmall>> ListStore<M>
Sourcepub proof fn step_size(tracked &self, id: ListId) -> res : nat
pub proof fn step_size(tracked &self, id: ListId) -> res : nat
self.inv(),self.lists.dom().contains(id),ensuresres == self.lists[id].list.len(),LinkedList::size: the number of links in list id. A read-only
query — the store is unchanged.
Sourcepub proof fn step_is_empty(tracked &self, id: ListId) -> res : bool
pub proof fn step_is_empty(tracked &self, id: ListId) -> res : bool
self.inv(),self.lists.dom().contains(id),ensuresres <==> self.lists[id].list.len() == 0,LinkedList::is_empty: whether list id has no links. Read-only.
Sourcepub proof fn step_contains(tracked &self, id: ListId, frame: Paddr) -> res : bool
pub proof fn step_contains(tracked &self, id: ListId, frame: Paddr) -> res : bool
self.inv(),self.lists.dom().contains(id),ensuresres
<==> (has_safe_slot(frame)
&& exists |i: int| {
0 <= i < self.lists[id].list.len()
&& self.lists[id].slot_index_at(i) == frame_to_index(frame)
}),LinkedList::contains: whether frame is a link of list id. A
read-only query mirroring exec contains(frame) -> bool. res
holds iff frame is a safe managed slot AND one of the list’s
links: for a real (non-zero) id the membership registry
(list_registry_ok, an inv clause) makes the
in_list[frame] == list_id comparison an exact membership test;
an empty/never-pushed list (list_id == 0, hence empty) or a
frame that is not a safe slot (which exec’s get_slot rejects)
contains nothing.
Sourcepub proof fn step_list_new(tracked &mut self) -> res : ListId
pub proof fn step_list_new(tracked &mut self) -> res : ListId
old(self).inv(),ensuresfinal(self).inv(),final(self).regions == old(self).regions,final(self).loose == old(self).loose,!old(self).lists.dom().contains(res),final(self).lists == old(self).lists.insert(res, final(self).lists[res]),final(self).lists[res].list.len() == 0,LinkedList::new: register a fresh empty list. No region
change; the new list is empty with list_id == 0 (minted on
first push). Returns the fresh list id.
Sourcepub proof fn step_list_drop(tracked &mut self, id: ListId)
pub proof fn step_list_drop(tracked &mut self, id: ListId)
old(self).inv(),old(self).lists.dom().contains(id),forall |i: int| {
0 <= i < old(self).lists[id].list.len()
==> old(self)
.regions
.frame_obligations
.count(#[trigger] old(self).lists[id].slot_index_at(i)) == 0
},ensuresfinal(self).inv(),!final(self).lists.dom().contains(id),final(self).loose == old(self).loose,final(self).cursors == old(self).cursors,Drop of LinkedList id: tear the whole list down, freeing
every link’s frame (slot → UNUSED, in_list → 0) and removing the
list from the store. Faithful to the verified destructor (each
link is popped and UniqueFrame::dropped — no orphaning).
The per-link frame_obligations.count == 0 precondition mirrors
the exec drop_requires (listed frames are forgotten); the
accounting-free ListStore cannot itself supply it, so it is left
to the caller. The freed frames leave the store entirely (they
return to the allocator’s UNUSED pool, tracked by nobody here).
Sourcepub proof fn step_push_front(tracked &mut self, id: ListId, lid: LooseId)
pub proof fn step_push_front(tracked &mut self, id: ListId, lid: LooseId)
old(self).inv(),old(self).lists.dom().contains(id),old(self).loose.dom().contains(lid),ensuresfinal(self).inv(),LinkedList::push_front: move the loose handle lid to the front
of list id. The frame is forgotten into the list (its
drop-obligation consumed); the loose entry is removed.
Sourcepub proof fn step_pop_front(tracked &mut self, id: ListId) -> res : Option<LooseId>
pub proof fn step_pop_front(tracked &mut self, id: ListId) -> res : Option<LooseId>
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),old(self).lists[id].list.len() == 0 ==> res is None && *final(self) == *old(self),old(self).lists[id].list.len() > 0 ==> res is Some,LinkedList::pop_front: pop the front link of list id back into
the loose pool as a fresh UniqueFrame<Link<M>>. Requires the
list be non-empty. Returns the fresh loose id.
Sourcepub proof fn step_push_back(tracked &mut self, id: ListId, lid: LooseId)
pub proof fn step_push_back(tracked &mut self, id: ListId, lid: LooseId)
old(self).inv(),old(self).lists.dom().contains(id),old(self).loose.dom().contains(lid),ensuresfinal(self).inv(),LinkedList::push_back: move the loose handle lid to the back
of list id. Same global effect as Self::step_push_front —
only the link’s position within the list differs.
Sourcepub proof fn step_pop_back(tracked &mut self, id: ListId) -> res : Option<LooseId>
pub proof fn step_pop_back(tracked &mut self, id: ListId) -> res : Option<LooseId>
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),old(self).lists[id].list.len() == 0 ==> res is None && *final(self) == *old(self),old(self).lists[id].list.len() > 0 ==> res is Some,LinkedList::pop_back: pop the back link of list id back into
the loose pool. Same global effect as Self::step_pop_front —
only which link is removed differs.
Sourcepub proof fn step_insert_before_at(tracked &mut self, id: ListId, n: int, lid: LooseId)
pub proof fn step_insert_before_at(tracked &mut self, id: ListId, n: int, lid: LooseId)
old(self).inv(),old(self).lists.dom().contains(id),old(self).loose.dom().contains(lid),0 <= n <= old(self).lists[id].list.len(),ensuresfinal(self).inv(),Cursor insert_before at an arbitrary position n: move the
loose handle lid into list id at index n (0 <= n <= len).
The general form of Self::step_push_front /
Self::step_push_back; same global effect.
Sourcepub proof fn step_take_at(tracked &mut self, id: ListId, n: int) -> res : Option<LooseId>
pub proof fn step_take_at(tracked &mut self, id: ListId, n: int) -> res : Option<LooseId>
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),!(0 <= n < old(self).lists[id].list.len()) ==> res is None && *final(self) == *old(self),0 <= n < old(self).lists[id].list.len() ==> res is Some,Cursor take_current at an arbitrary position n: pop the link
at index n (0 <= n < len) of list id back into the loose
pool. The general form of Self::step_pop_front /
Self::step_pop_back; same global effect.
Sourcepub proof fn step_cursor_front_mut(tracked &mut self, id: ListId)
pub proof fn step_cursor_front_mut(tracked &mut self, id: ListId)
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),!final(self).lists.dom().contains(id),final(self).cursors.dom().contains(id),final(self).cursors[id] == CursorOwner::front_owner(old(self).lists[id]),LinkedList::cursor_front_mut: check list id out into a cursor
positioned at the front (index 0). The list leaves lists and
enters cursors under the same id (its borrow).
Sourcepub proof fn step_cursor_back_mut(tracked &mut self, id: ListId)
pub proof fn step_cursor_back_mut(tracked &mut self, id: ListId)
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),!final(self).lists.dom().contains(id),final(self).cursors.dom().contains(id),final(self).cursors[id] == CursorOwner::back_owner(old(self).lists[id]),LinkedList::cursor_back_mut: check list id out into a cursor
at the back (the last element, or the ghost slot when empty).
Sourcepub proof fn step_cursor_mut_at(tracked &mut self, id: ListId, frame: Paddr) -> res : bool
pub proof fn step_cursor_mut_at(tracked &mut self, id: ListId, frame: Paddr) -> res : bool
old(self).inv(),old(self).lists.dom().contains(id),ensuresfinal(self).inv(),res
== (exists |i: int| {
0 <= i < old(self).lists[id].list.len()
&& old(self).lists[id].slot_index_at(i) == frame_to_index(frame)
}),res
==> !final(self).lists.dom().contains(id) && final(self).cursors.dom().contains(id)
&& exists |i: int| {
0 <= i < old(self).lists[id].list.len()
&& old(self).lists[id].slot_index_at(i) == frame_to_index(frame)
&& final(self).cursors[id]
== CursorOwner::cursor_mut_at_owner(old(self).lists[id], i)
},!res ==> *final(self) == *old(self),LinkedList::cursor_mut_at: search list id for frame and, if
it is one of the list’s links, check the list out into a cursor
positioned at that link; otherwise (the frame is absent — or not a
safe managed slot, which can never be a link) leave the store
unchanged. Mirrors exec cursor_mut_at(frame) -> Option<CursorMut>
(the Some/None outcome is returned as res).
Sourcepub proof fn step_move_next(tracked &mut self, id: CursorId)
pub proof fn step_move_next(tracked &mut self, id: CursorId)
old(self).inv(),old(self).cursors.dom().contains(id),ensuresfinal(self).inv(),final(self).regions == old(self).regions,final(self).cursors[id] == old(self).cursors[id].move_next_owner_spec(),CursorMut::move_next: advance cursor id one step toward the
back (wrapping through the ghost slot). Pure position change.
Sourcepub proof fn step_move_prev(tracked &mut self, id: CursorId)
pub proof fn step_move_prev(tracked &mut self, id: CursorId)
old(self).inv(),old(self).cursors.dom().contains(id),ensuresfinal(self).inv(),final(self).regions == old(self).regions,final(self).cursors[id] == old(self).cursors[id].move_prev_owner_spec(),CursorMut::move_prev: retreat cursor id one step toward the
front (wrapping through the ghost slot). Pure position change.
Sourcepub proof fn step_current_meta(tracked &self, id: CursorId) -> res : Option<LinkOwner>
pub proof fn step_current_meta(tracked &self, id: CursorId) -> res : Option<LinkOwner>
self.inv(),self.cursors.dom().contains(id),ensuresres == self.cursors[id].current(),res.is_some() <==> 0 <= self.cursors[id].index < self.cursors[id].length(),CursorMut::current_meta: read the link the cursor id is on.
A read-only query — returns the current LinkOwner (None at
the ghost slot); the store is unchanged.
Sourcepub proof fn step_as_list(tracked &self, id: CursorId) -> res : Seq<LinkOwner>
pub proof fn step_as_list(tracked &self, id: CursorId) -> res : Seq<LinkOwner>
self.inv(),self.cursors.dom().contains(id),ensuresres == self.cursors[id].list_own.list,res.len() == self.cursors[id].length(),CursorMut::as_list: borrow the cursor’s checked-out list for
reading. A pure no-op — &self on the store, so regions /
lists / loose / cursors are all untouched; it merely exposes
the list’s contents (the model Seq of links). Modeled for
completeness, to demonstrate the read-only view changes nothing.
Sourcepub proof fn step_cursor_drop(tracked &mut self, id: CursorId)
pub proof fn step_cursor_drop(tracked &mut self, id: CursorId)
old(self).inv(),old(self).cursors.dom().contains(id),ensuresfinal(self).inv(),!final(self).cursors.dom().contains(id),final(self).lists.dom().contains(id),final(self).lists[id] == old(self).cursors[id].list_own,Drop of a CursorMut: check the cursor’s list back into lists
under its home id, ending the borrow. Inverse of
Self::step_cursor_front_mut et al.
Sourcepub proof fn step_cursor_insert_before(tracked &mut self, id: CursorId, lid: LooseId)
pub proof fn step_cursor_insert_before(tracked &mut self, id: CursorId, lid: LooseId)
old(self).inv(),old(self).cursors.dom().contains(id),old(self).loose.dom().contains(lid),ensuresfinal(self).inv(),CursorMut::insert_before: through the checked-out cursor id,
move the loose handle lid into the cursor’s list at the current
position (index n), advancing the cursor to n + 1. The general
Self::step_insert_before_at, but on the list parked in
cursors rather than lists — so every held list is an “other
list” preserved by the axiom’s frame.
Sourcepub proof fn step_cursor_take_current(tracked &mut self, id: CursorId) -> res : Option<LooseId>
pub proof fn step_cursor_take_current(tracked &mut self, id: CursorId) -> res : Option<LooseId>
old(self).inv(),old(self).cursors.dom().contains(id),ensuresfinal(self).inv(),!(0 <= old(self).cursors[id].index < old(self).cursors[id].length())
==> res is None && *final(self) == *old(self),0 <= old(self).cursors[id].index < old(self).cursors[id].length() ==> res is Some,CursorMut::take_current: through the checked-out cursor id,
pop the link the cursor is on (index n, requires the cursor be
on an element) back into the loose pool, leaving the cursor at the
same index (now on the following link). The general
Self::step_take_at on a cursored list. Returns the fresh loose
id.