#![allow(unused_imports)] use super::pervasive::*; use super::prelude::*; use super::set::*; use verus as verus_skip_verusfmt; // skip verusfmt due to unhandled return-value-pattern verus_skip_verusfmt! { broadcast use { super::set::group_set_lemmas, super::set_lib::group_set_lib_default, }; /// `Map` is an abstract map type for specifications. /// To use a "map" in compiled code, use an `exec` type like HashMap /// that has a `Map` as its specification type. /// /// An object `map: Map` has a _domain_, a set of keys given by [`map.dom()`](Map::dom), /// and a mapping for keys in the domain to values, given by [`map[key]`](Map::index). /// Alternatively, a map can be thought of as a set of `(K, V)` pairs where each key /// appears in at most entry. /// /// A Map must will always be finite. /// To work with infinite maps, see `IMap`. /// /// Maps can be constructed in a few different ways: /// * [`Map::empty()`] constructs an empty map. /// * [`Map::new`] constructs a map given a `Set` that specifies its domain and the mapping /// from keys to values (a _map comprehension_). /// * The [`map!`] macro, to construct small maps of a fixed size. /// * By manipulating an existing map with [`Map::insert`] or [`Map::remove`]. /// /// To prove that two maps are equal, it is usually easiest to use the extensionality operator `=~=`. /// /// `Map` always has a finite domain, so it can be used in recursive types. For instance, /// a type `T` can contain a `Map`. #[verifier::ext_equal] #[verifier::external_body] #[verifier::accept_recursive_types(K)] #[verifier::accept_recursive_types(V)] pub tracked struct Map { // To prevent Verus's internal checks from rejecting recursive types // using `Map`, we use an artificial definition of `Map` that hides its // inclusion of a function from keys to values. // // To make sure that proofs in this file don't take advantage of // this artificial structure (e.g., to prove that any two `Map`s are // equal), we mark this definition as `external_body`. dummy_key: core::marker::PhantomData, dummy_value: core::marker::PhantomData, } impl Map { /// The domain of the map as a set. pub uninterp spec fn dom(self) -> Set; /// Gets the value that the given key `key` maps to. /// For keys not in the domain, the result is meaningless and arbitrary. pub uninterp spec fn index(self, key: K) -> V recommends self.dom().contains(key), ; /// Gives a `Map` whose domain is given, mapping each key to /// the value given by `fv`. pub uninterp spec fn new(s: Set, fv: spec_fn(K) -> V) -> Map; /// This axiom says that `Map::new` produces a `Map` whose domain /// is given, mapping each key `k` to the value given by `fv(k)`. broadcast axiom fn axiom_new(s: Set, fv: spec_fn(K) -> V) ensures #![trigger Self::new(s, fv)] Self::new(s, fv).dom() == s, forall|k| s.contains(k) ==> #[trigger] Self::new(s, fv)[k] == fv(k), ; /// An empty map. pub closed spec fn empty() -> Map { Self::new(Set::::empty(), |k| arbitrary()) } /// `[]` operator, synonymous with `index` #[verifier::inline] pub open spec fn spec_index(self, key: K) -> V recommends self.dom().contains(key), { self.index(key) } /// Inserts the given (key, value) pair into the map. /// /// If the key is already present from the map, then its existing value is overwritten /// by the new value. pub closed spec fn insert(self, key: K, value: V) -> Map { Map::new(self.dom().insert(key), |k| if k == key { value } else { self[k] }) } /// Removes the given key and its associated value from the map. /// /// If the key is already absent from the map, then the map is left unchanged. pub closed spec fn remove(self, key: K) -> Map { Map::new(self.dom().remove(key), |k| self[k]) } /// Returns the number of key-value pairs in the map pub open spec fn len(self) -> nat { self.dom().len() } /// Converts this `Map` into an equivalent `IMap` pub open spec fn to_imap(self) -> IMap { IMap::new(|k| self.dom().contains(k), |k| self[k]) } /// Indicates if this `Map` is equivalent to the given `IMap`. pub open spec fn congruent(self, m2: IMap) -> bool { &&& self.dom().congruent(m2.dom()) &&& forall|k| #[trigger] self.dom().contains(k) ==> self[k] == m2[k] } /// Create an empty tracked map. /// /// This allows us to create a map, which we know is empty, that is _tracked_. pub axiom fn tracked_empty() -> (tracked out_v: Self) ensures out_v == Map::::empty(), ; /// Inserts the given `(key, tracked value)` pair into the map. /// /// If the key is already present from the map, then its existing value is overwritten /// by the new value. pub axiom fn tracked_insert(tracked &mut self, key: K, tracked value: V) ensures *final(self) == Map::insert(*old(self), key, value), ; /// Removes the given key and its associated _tracked_ value from the map. /// /// The key must exist in the map pub axiom fn tracked_remove(tracked &mut self, key: K) -> (tracked v: V) requires old(self).dom().contains(key), ensures *final(self) == Map::remove(*old(self), key), v == old(self)[key], ; /// Index into a tracked map, getting a tracked borrow of the value pub axiom fn tracked_borrow(tracked &self, key: K) -> (tracked v: &V) requires self.dom().contains(key), ensures *v == self.index(key), ; /// Index into a tracked map, getting a tracked mutable borrow of the value pub axiom fn tracked_borrow_mut(tracked &mut self, key: K) -> (tracked v: &mut V) requires self.dom().contains(key), ensures *v == old(self).index(key), *final(self) == old(self).insert(key, *final(v)) ; /// Split a mutable borrow of a map into two. pub axiom fn tracked_borrow_mut_split(tracked &mut self, keys: Set) -> (tracked (m1, m2): (&mut Self, &mut Self)) requires keys <= self.dom(), ensures *m1 == old(self).restrict(keys), *m2 == old(self).remove_keys(keys), *final(self) == final(m1).union_prefer_right(*final(m2)), ; /// Change the keys of a map, by reverse lookup in a different map. /// /// For each `(old_key, new_key)` pair in `key_map`, the new map will have `(new_key, old_map[old_key])`. /// Note the new map may be smaller than the old map if the `key_map` omits mappings for some of the old keys. pub axiom fn tracked_map_keys( tracked old_map: Map, key_map: Map, ) -> (tracked new_map: Map) requires forall|j| #![auto] key_map.contains_key(j) ==> old_map.contains_key(key_map[j]), forall|j1, j2| #![auto] j1 != j2 && key_map.contains_key(j1) && key_map.contains_key(j2) ==> key_map[j1] != key_map[j2], ensures new_map.dom() == key_map.dom(), forall|j| key_map.contains_key(j) ==> new_map.contains_key(j) && #[trigger] new_map[j] == old_map[key_map[j]], ; /// Extract a set of keys (and their corresponding values) out of the map. /// /// This allows us to split a map based on a subset of the domain. pub axiom fn tracked_remove_keys(tracked &mut self, keys: Set) -> (tracked out_map: Map< K, V, >) requires keys.subset_of(old(self).dom()), ensures *final(self) == old(self).remove_keys(keys), out_map == old(self).restrict(keys), ; /// Consume a tracked map and merge its entries into this tracked map. /// /// The new (key, value) pairs take precedence. pub axiom fn tracked_union_prefer_right(tracked &mut self, tracked right: Self) ensures *final(self) == old(self).union_prefer_right(right), ; } // Trusted axioms pub broadcast axiom fn axiom_map_index_decreases(m: Map, key: K) requires m.dom().contains(key), ensures #[trigger](decreases_to!(m => m[key])); pub broadcast axiom fn axiom_map_decreases_to_entry(m: Map, key: K) requires m.dom().contains(key), ensures #[trigger](decreases_to!(m => (key, m[key]))); /// Since `Map::new` is uninterpret, this broadcast lemma is needed to establish /// that it produces a map with the given set as its domain. pub broadcast proof fn lemma_map_new_domain(s: Set, fv: spec_fn(K) -> V) ensures #![trigger Map::new(s, fv)] Map::new(s, fv).dom() == s, { broadcast use Map::axiom_new; } /// Since `Map::new` is closed, this broadcast lemma is needed to establish /// that it produces a map that maps elements using the given function. pub broadcast proof fn lemma_map_new_index(s: Set, fv: spec_fn(K) -> V, k: K) requires s.contains(k), ensures #![trigger Map::new(s, fv)[k]] Map::new(s, fv)[k] == fv(k) { broadcast use Map::axiom_new; } /// The domain of the empty map is the empty set pub broadcast proof fn lemma_map_empty() ensures #[trigger] Map::::empty().dom() == Set::::empty(), { broadcast use Map::axiom_new; } /// The domain of a map after inserting a key-value pair is equivalent to inserting the key into /// the original map's domain set. pub broadcast proof fn lemma_map_insert_domain(m: Map, key: K, value: V) ensures #[trigger] m.insert(key, value).dom() == m.dom().insert(key), { broadcast use Map::axiom_new; } /// Inserting `value` at `key` in `m` results in a map that maps `key` to `value` pub broadcast proof fn lemma_map_insert_same(m: Map, key: K, value: V) ensures #[trigger] m.insert(key, value)[key] == value, { broadcast use Map::axiom_new; } /// Inserting `value` at `key2` does not change the value mapped to by any other keys in `m`. /// This has to be an axiom because normally constructing a new map sets values outside /// the domain to arbitrary values. pub broadcast axiom fn axiom_map_insert_different(m: Map, key1: K, key2: K, value: V) requires key1 != key2, ensures #[trigger] m.insert(key2, value)[key1] == m[key1], ; /// The domain of a map after removing a key-value pair is equivalent to removing the key from /// the original map's domain set. pub broadcast proof fn lemma_map_remove_domain(m: Map, key: K) ensures #[trigger] m.remove(key).dom() == m.dom().remove(key), { broadcast use Map::axiom_new; } /// Removing a key-value pair from a map does not change the value mapped to by /// any other keys in the map. /// This has to be an axiom because normally constructing a new map sets values outside /// the domain to arbitrary values. pub broadcast axiom fn axiom_map_remove_different(m: Map, key1: K, key2: K) requires key1 != key2, ensures #[trigger] m.remove(key2)[key1] == m[key1], ; /// Two maps are equivalent if their domains are equivalent and every key in their domains map to the same value. pub broadcast axiom fn axiom_map_ext_equal(m1: Map, m2: Map) ensures #[trigger] (m1 =~= m2) <==> { &&& m1.dom() =~= m2.dom() &&& forall|k: K| #![auto] m1.dom().contains(k) ==> m1[k] == m2[k] }, ; /// Two maps are deeply equivalent if their domains are equivalent and /// every key in their domains map to values that are deeply /// equivalent. pub broadcast proof fn axiom_map_ext_equal_deep(m1: Map, m2: Map) ensures #[trigger] (m1 =~~= m2) <==> { &&& m1.dom() =~~= m2.dom() &&& forall|k: K| #![auto] m1.dom().contains(k) ==> m1[k] =~~= m2[k] }, { axiom_map_ext_equal(m1, m2); } pub broadcast group group_map_lemmas { axiom_map_index_decreases, axiom_map_decreases_to_entry, lemma_map_new_domain, lemma_map_new_index, lemma_map_empty, lemma_map_insert_domain, lemma_map_insert_same, axiom_map_insert_different, lemma_map_remove_domain, axiom_map_remove_different, axiom_map_ext_equal, axiom_map_ext_equal_deep, } // Macros #[doc(hidden)] #[macro_export] macro_rules! map_internal { [$($key:expr => $value:expr),* $(,)?] => { $crate::vstd::map::Map::empty() $(.insert($key, $value))* } } /// Create a map using syntax like `map![key1 => val1, key2 => val, ...]`. /// /// This is equivalent to `Map::empty().insert(key1, val1).insert(key2, val2)...`. /// /// Note that this does _not_ require all keys to be distinct. In the case that two /// or more keys are equal, the resulting map uses the value of the rightmost entry. #[macro_export] macro_rules! map { [$($tail:tt)*] => { $crate::vstd::prelude::verus_proof_macro_exprs!($crate::vstd::map::map_internal!($($tail)*)) }; } #[doc(hidden)] #[verifier::inline] pub open spec fn check_argument_is_map(m: Map) -> Map { m } #[doc(hidden)] pub use map_internal; pub use map; /// Prove two maps `map1` and `map2` are equal by proving that their values are equal at each key. /// /// More precisely, `assert_maps_equal!` requires that for each key `k`: /// * `map1` contains `k` in its domain if and only if `map2` does (`map1.dom().contains(k) <==> map2.dom().contains(k)`) /// * If they contain `k` in their domains, then their values are equal (`map1.dom().contains(k) && map2.dom().contains(k) ==> map1[k] == map2[k]`) /// /// The property that equality follows from these facts is often called _extensionality_. /// /// `assert_maps_equal!` can handle many trivial-looking /// identities without any additional help: /// /// ```rust /// proof fn insert_remove(m: Map, k: int, v: int) /// requires !m.dom().contains(k) /// ensures m.insert(k, v).remove(k) == m /// { /// let m2 = m.insert(k, v).remove(k); /// assert_maps_equal!(m == m2); /// assert(m == m2); /// } /// ``` /// /// For more complex cases, a proof may be required for each key: /// /// ```rust /// proof fn bitvector_maps() { /// let m1 = Map::::new( /// |key: u64| key & 31 == key, /// |key: u64| key | 5); /// /// let m2 = Map::::new( /// |key: u64| key < 32, /// |key: u64| 5 | key); /// /// assert_maps_equal!(m1 == m2, key => { /// // Show that the domains of m1 and m2 are the same by showing their predicates /// // are equivalent. /// assert_bit_vector((key & 31 == key) <==> (key < 32)); /// /// // Show that the values are the same by showing that these expressions /// // are equivalent. /// assert_bit_vector(key | 5 == 5 | key); /// }); /// } /// ``` #[macro_export] macro_rules! assert_maps_equal { [$($tail:tt)*] => { $crate::vstd::prelude::verus_proof_macro_exprs!($crate::vstd::map::assert_maps_equal_internal!($($tail)*)) }; } #[macro_export] #[doc(hidden)] macro_rules! assert_maps_equal_internal { (::verus_builtin::spec_eq($m1:expr, $m2:expr)) => { assert_maps_equal_internal!($m1, $m2) }; (::verus_builtin::spec_eq($m1:expr, $m2:expr), $k:ident $( : $t:ty )? => $bblock:block) => { assert_maps_equal_internal!($m1, $m2, $k $( : $t )? => $bblock) }; ($m1:expr, $m2:expr $(,)?) => { assert_maps_equal_internal!($m1, $m2, key => { }) }; ($m1:expr, $m2:expr, $k:ident $( : $t:ty )? => $bblock:block) => { #[verifier::spec] let m1 = $crate::vstd::map::check_argument_is_map($m1); #[verifier::spec] let m2 = $crate::vstd::map::check_argument_is_map($m2); $crate::vstd::prelude::assert_by($crate::vstd::prelude::equal(m1, m2), { $crate::vstd::prelude::assert_forall_by(|$k $( : $t )?| { // TODO better error message here: show the individual conjunct that fails, // and maybe give an error message in english as well $crate::vstd::prelude::ensures([ $crate::vstd::prelude::imply(#[verifier::trigger] m1.dom().contains($k), m2.dom().contains($k)) && $crate::vstd::prelude::imply(m2.dom().contains($k), m1.dom().contains($k)) && $crate::vstd::prelude::imply(m1.dom().contains($k) && m2.dom().contains($k), $crate::vstd::prelude::equal(m1.index($k), m2.index($k))) ]); { $bblock } }); $crate::vstd::prelude::assert_($crate::vstd::prelude::ext_equal(m1, m2)); }); } } #[doc(hidden)] pub use assert_maps_equal_internal; pub use assert_maps_equal; } // verus! verus_skip_verusfmt! { // skip verusfmt, issue with 'final' impl Map { pub proof fn tracked_map_keys_in_place(tracked &mut self, key_map: Map) requires forall|j| #![auto] key_map.dom().contains(j) ==> old(self).dom().contains(key_map.index(j)), forall|j1, j2| #![auto] j1 != j2 && key_map.dom().contains(j1) && key_map.dom().contains(j2) ==> key_map.index(j1) != key_map.index(j2), ensures forall|j| #[trigger] final(self).dom().contains(j) == key_map.dom().contains(j), forall|j| key_map.dom().contains(j) ==> final(self).dom().contains(j) && #[trigger] final(self).index(j) == old(self).index(key_map.index(j)), { let tracked mut tmp = Self::tracked_empty(); super::modes::tracked_swap(&mut tmp, self); let tracked mut tmp = Self::tracked_map_keys(tmp, key_map); super::modes::tracked_swap(&mut tmp, self); } } } // verus!