@@ -3,11 +3,16 @@ use alloc::{
33 alloc:: Allocator ,
44 collections:: { BTreeMap , btree_map:: CursorMut } ,
55} ;
6- use core:: { borrow:: Borrow , ops:: Bound } ;
6+ use core:: { borrow:: Borrow , cmp :: Ordering , ops:: Bound } ;
77use vstd:: {
88 laws_cmp:: obeys_cmp,
99 prelude:: * ,
10- std_specs:: btree:: { borrowed_key_removed, contains_borrowed_key, maps_borrowed_key_to_value} ,
10+ std_specs:: {
11+ btree:: {
12+ borrowed_key_removed, contains_borrowed_key, increasing_seq, maps_borrowed_key_to_value,
13+ } ,
14+ cmp:: OrdSpec ,
15+ } ,
1116} ;
1217
1318verus ! {
@@ -20,13 +25,41 @@ verus! {
2025#[ verifier:: reject_recursive_types( A ) ]
2126pub struct ExCursorMut <' a, K : ' a, V : ' a, A >( CursorMut <' a, K , V , A >) ;
2227
23- /// Additional ghost state used to remember the keys matching an excluded lower bound.
24- pub trait CursorMutAdditionalSpecFns <Key > {
25- spec fn excluded_keys( & self ) -> ISet <Key >;
28+ /// The abstract state of a mutable B-tree cursor.
29+ ///
30+ /// A cursor points at the gap immediately before `keys[position]`. Therefore `peek_next`
31+ /// accesses `keys[position]`, while `peek_prev` accesses `keys[position - 1]`.
32+ pub ghost struct CursorMutModel <Key , Value > {
33+ /// All keys in the underlying map, in strictly increasing order.
34+ pub keys: Seq <Key >,
35+ /// The index of the element immediately after the cursor.
36+ pub position: int,
37+ /// The current contents of the complete map borrowed by the cursor.
38+ pub map: Map <Key , Value >,
2639}
2740
28- impl <' a, Key , Value , A > CursorMutAdditionalSpecFns <Key > for CursorMut <' a, Key , Value , A > {
29- uninterp spec fn excluded_keys( & self ) -> ISet <Key >;
41+ /// Additional abstract and prophetic state for mutable B-tree cursors.
42+ pub trait CursorMutAdditionalSpecFns <Key , Value >: Sized {
43+ spec fn model( self ) -> CursorMutModel <Key , Value >;
44+
45+ /// The contents of the borrowed map when this cursor's borrow is resolved.
46+ #[ verifier:: prophetic]
47+ spec fn final_map( self ) -> Map <Key , Value >;
48+ }
49+
50+ impl <' a, Key , Value , A > CursorMutAdditionalSpecFns <Key , Value > for CursorMut <' a, Key , Value , A > {
51+ uninterp spec fn model( self ) -> CursorMutModel <Key , Value >;
52+
53+ #[ verifier:: prophetic]
54+ uninterp spec fn final_map( self ) -> Map <Key , Value >;
55+ }
56+
57+ /// Whether a cursor model consistently represents an ordered map and a gap in that map.
58+ pub open spec fn cursor_model_wf<Key , Value >( model: CursorMutModel <Key , Value >) -> bool {
59+ &&& 0 <= model. position <= model. keys. len( )
60+ &&& model. keys. no_duplicates( )
61+ &&& model. keys. to_set( ) == model. map. dom( )
62+ &&& increasing_seq( model. keys)
3063}
3164
3265/// Whether a borrowed lookup key's ordering agrees with the ordering of stored keys.
@@ -35,12 +68,71 @@ impl<'a, Key, Value, A> CursorMutAdditionalSpecFns<Key> for CursorMut<'a, Key, V
3568/// borrowed-key `BTreeMap` operations.
3669pub uninterp spec fn borrowed_key_ordering_matches<Key , Q : ?Sized >( key: & Q ) -> bool ;
3770
71+ /// The ordering of a stored key relative to a borrowed lookup key.
72+ pub uninterp spec fn borrowed_key_cmp<Key , Q : ?Sized >( stored_key: Key , key: & Q ) -> Ordering ;
73+
3874/// A key type has the same ordering as itself.
3975pub broadcast axiom fn axiom_deref_key_ordering_matches<Key >( key: & Key )
4076 ensures
4177 #[ trigger] borrowed_key_ordering_matches:: <Key , Key >( key) ,
4278;
4379
80+ /// Comparing a stored key against a borrowed key of the same type agrees with `Ord`'s model.
81+ pub broadcast axiom fn axiom_deref_key_cmp<Key : Ord >( stored_key: Key , key: & Key )
82+ ensures
83+ #[ trigger] borrowed_key_cmp:: <Key , Key >( stored_key, key) == stored_key. cmp_spec( key) ,
84+ ;
85+
86+ /// Whether a key occurs before the gap returned by `lower_bound_mut`.
87+ pub open spec fn before_lower_bound<Key , Q : ?Sized >( key: Key , bound: Bound <& Q >) -> bool {
88+ match bound {
89+ Bound :: Included ( bound_key) => borrowed_key_cmp( key, bound_key) is Less ,
90+ Bound :: Excluded ( bound_key) => !( borrowed_key_cmp( key, bound_key) is Greater ) ,
91+ Bound :: Unbounded => false ,
92+ }
93+ }
94+
95+ /// Whether a key occurs before the gap returned by `upper_bound_mut`.
96+ pub open spec fn before_upper_bound<Key , Q : ?Sized >( key: Key , bound: Bound <& Q >) -> bool {
97+ match bound {
98+ Bound :: Included ( bound_key) => !( borrowed_key_cmp( key, bound_key) is Greater ) ,
99+ Bound :: Excluded ( bound_key) => borrowed_key_cmp( key, bound_key) is Less ,
100+ Bound :: Unbounded => true ,
101+ }
102+ }
103+
104+ /// Whether a cursor is at the gap selected by `lower_bound_mut`.
105+ pub open spec fn positioned_at_lower_bound<Key , Value , Q : ?Sized >(
106+ model: CursorMutModel <Key , Value >,
107+ bound: Bound <& Q >,
108+ ) -> bool {
109+ &&& forall|i: int|
110+ #![ trigger before_lower_bound( model. keys[ i] , bound) ]
111+ 0 <= i < model. position ==> before_lower_bound( model. keys[ i] , bound)
112+ &&& forall|i: int|
113+ #![ trigger before_lower_bound( model. keys[ i] , bound) ]
114+ model. position <= i < model. keys. len( ) ==> !before_lower_bound( model. keys[ i] , bound)
115+ }
116+
117+ /// Whether a cursor is at the gap selected by `upper_bound_mut`.
118+ pub open spec fn positioned_at_upper_bound<Key , Value , Q : ?Sized >(
119+ model: CursorMutModel <Key , Value >,
120+ bound: Bound <& Q >,
121+ ) -> bool {
122+ &&& forall|i: int|
123+ #![ trigger before_upper_bound( model. keys[ i] , bound) ]
124+ 0 <= i < model. position ==> before_upper_bound( model. keys[ i] , bound)
125+ &&& forall|i: int|
126+ #![ trigger before_upper_bound( model. keys[ i] , bound) ]
127+ model. position <= i < model. keys. len( ) ==> !before_upper_bound( model. keys[ i] , bound)
128+ }
129+
130+ /// Once the cursor has been dropped, its prophesied map is its current map.
131+ pub broadcast axiom fn axiom_has_resolved_cursor<Key , Value , A >( cursor: CursorMut <Key , Value , A >)
132+ ensures
133+ #[ trigger] has_resolved( cursor) ==> cursor. final_map( ) == cursor. model( ) . map,
134+ ;
135+
44136/// Relates a map before and after mutating the value selected by a borrowed key.
45137pub open spec fn borrowed_key_mutated<Key , Value , Q : ?Sized >(
46138 old_map: Map <Key , Value >,
@@ -63,6 +155,8 @@ pub open spec fn borrowed_key_mutated<Key, Value, Q: ?Sized>(
63155/// Additional axioms for mutable B-tree operations.
64156pub broadcast group group_btree_extra_axioms {
65157 axiom_deref_key_ordering_matches,
158+ axiom_deref_key_cmp,
159+ axiom_has_resolved_cursor,
66160}
67161
68162/// Specification for [`BTreeMap::get_mut`].
@@ -86,10 +180,6 @@ pub assume_specification<
86180;
87181
88182/// Specification for [`BTreeMap::lower_bound_mut`].
89- ///
90- /// This deliberately over-approximates the cursor position until vstd exposes a reusable ordered
91- /// cursor model. It still provides a sound verified boundary for callers that do not rely on the
92- /// selected key in their postconditions.
93183pub assume_specification<
94184 ' a,
95185 Key : Borrow <Q > + Ord ,
@@ -108,13 +198,11 @@ pub assume_specification<
108198 Bound :: Unbounded => true ,
109199 } ,
110200 ensures
111- final( map) @. dom( ) == old( map) @. dom( ) ,
112- cursor. excluded_keys( ) == match bound {
113- Bound :: Excluded ( key) => ISet :: new(
114- |stored_key: Key |
115- contains_borrowed_key( Map :: <Key , ( ) >:: empty( ) . insert( stored_key, ( ) ) , key) ,
116- ) ,
117- _ => ISet :: empty( ) ,
201+ obeys_cmp:: <Key >( ) ==> {
202+ &&& cursor_model_wf( cursor. model( ) )
203+ &&& cursor. model( ) . map == old( map) @
204+ &&& final( map) @ == cursor. final_map( )
205+ &&& positioned_at_lower_bound( cursor. model( ) , bound)
118206 } ,
119207;
120208
@@ -137,27 +225,65 @@ pub assume_specification<
137225 Bound :: Unbounded => true ,
138226 } ,
139227 ensures
140- final( map) @. dom( ) == old( map) @. dom( ) ,
141- cursor. excluded_keys( ) == ISet :: empty( ) ,
228+ obeys_cmp:: <Key >( ) ==> {
229+ &&& cursor_model_wf( cursor. model( ) )
230+ &&& cursor. model( ) . map == old( map) @
231+ &&& final( map) @ == cursor. final_map( )
232+ &&& positioned_at_upper_bound( cursor. model( ) , bound)
233+ } ,
142234;
143235
144236/// Specification for [`CursorMut::peek_prev`].
145237pub assume_specification<' a, ' b, Key , Value , A >[ CursorMut :: <' a, Key , Value , A >:: peek_prev ] (
146238 cursor: & ' b mut CursorMut <' a, Key , Value , A >,
147239) -> ( result: Option <( & ' b Key , & ' b mut Value ) >)
148240 ensures
149- final( cursor) . excluded_keys( ) == old( cursor) . excluded_keys( ) ,
241+ final( cursor) . final_map( ) == old( cursor) . final_map( ) ,
242+ cursor_model_wf( old( cursor) . model( ) ) ==> cursor_model_wf( final( cursor) . model( ) ) ,
243+ match result {
244+ Some ( ( key, value) ) => {
245+ let old_model = old( cursor) . model( ) ;
246+ let new_model = final( cursor) . model( ) ;
247+ &&& old_model. position > 0
248+ &&& * key == old_model. keys[ old_model. position - 1 ]
249+ &&& * value == old_model. map[ * key]
250+ &&& new_model. keys == old_model. keys
251+ &&& new_model. position == old_model. position
252+ &&& new_model. map == old_model. map. insert( * key, * final( value) )
253+ } ,
254+ None => {
255+ &&& old( cursor) . model( ) . position == 0
256+ &&& final( cursor) . model( ) . keys == old( cursor) . model( ) . keys
257+ &&& final( cursor) . model( ) . position == old( cursor) . model( ) . position
258+ &&& final( cursor) . model( ) . map == old( cursor) . model( ) . map
259+ } ,
260+ } ,
150261;
151262
152263/// Specification for [`CursorMut::peek_next`].
153264pub assume_specification<' a, ' b, Key , Value , A >[ CursorMut :: <' a, Key , Value , A >:: peek_next ] (
154265 cursor: & ' b mut CursorMut <' a, Key , Value , A >,
155266) -> ( result: Option <( & ' b Key , & ' b mut Value ) >)
156267 ensures
157- final( cursor) . excluded_keys( ) == old( cursor) . excluded_keys( ) ,
268+ final( cursor) . final_map( ) == old( cursor) . final_map( ) ,
269+ cursor_model_wf( old( cursor) . model( ) ) ==> cursor_model_wf( final( cursor) . model( ) ) ,
158270 match result {
159- Some ( ( key, _) ) => !old( cursor) . excluded_keys( ) . contains( * key) ,
160- None => true ,
271+ Some ( ( key, value) ) => {
272+ let old_model = old( cursor) . model( ) ;
273+ let new_model = final( cursor) . model( ) ;
274+ &&& old_model. position < old_model. keys. len( )
275+ &&& * key == old_model. keys[ old_model. position]
276+ &&& * value == old_model. map[ * key]
277+ &&& new_model. keys == old_model. keys
278+ &&& new_model. position == old_model. position
279+ &&& new_model. map == old_model. map. insert( * key, * final( value) )
280+ } ,
281+ None => {
282+ &&& old( cursor) . model( ) . position == old( cursor) . model( ) . keys. len( )
283+ &&& final( cursor) . model( ) . keys == old( cursor) . model( ) . keys
284+ &&& final( cursor) . model( ) . position == old( cursor) . model( ) . position
285+ &&& final( cursor) . model( ) . map == old( cursor) . model( ) . map
286+ } ,
161287 } ,
162288;
163289
0 commit comments