diff options
author | Son Ho | 2022-02-13 11:32:12 +0100 |
---|---|---|
committer | Son Ho | 2022-02-13 11:32:12 +0100 |
commit | 9fc3185c8acf96993c3224f196069cb8f7e2158e (patch) | |
tree | 6f32f27c300cab58999479f2f4683747851b4c87 | |
parent | d152fec84d54aa5fb919e8c54e1351020b13ed97 (diff) |
Add [contains_key], make progress on the proofs for [contains_key],
[get] and [get_mut] and stabilize the proof of
[hash_map_move_elements_fwd_back_lem_refin]
-rw-r--r-- | tests/hashmap/Hashmap.Clauses.fst | 6 | ||||
-rw-r--r-- | tests/hashmap/Hashmap.Funs.fst | 41 | ||||
-rw-r--r-- | tests/hashmap/Hashmap.Properties.fst | 372 |
3 files changed, 376 insertions, 43 deletions
diff --git a/tests/hashmap/Hashmap.Clauses.fst b/tests/hashmap/Hashmap.Clauses.fst index 81727b5a..4f3e37e9 100644 --- a/tests/hashmap/Hashmap.Clauses.fst +++ b/tests/hashmap/Hashmap.Clauses.fst @@ -35,6 +35,12 @@ let hash_map_move_elements_decreases (t : Type0) (ntable : hash_map_t t) (slots : vec (list_t t)) (i : usize) : nat = if i < length slots then length slots - i else 0 +(** [hashmap::HashMap::contains_key_in_list]: decreases clause *) +unfold +let hash_map_contains_key_in_list_decreases (t : Type0) (key : usize) + (ls : list_t t) : list_t t = + ls + (** [hashmap::HashMap::get_in_list]: decreases clause *) unfold let hash_map_get_in_list_decreases (t : Type0) (key : usize) (ls : list_t t) : diff --git a/tests/hashmap/Hashmap.Funs.fst b/tests/hashmap/Hashmap.Funs.fst index 3828ae98..d3d09765 100644 --- a/tests/hashmap/Hashmap.Funs.fst +++ b/tests/hashmap/Hashmap.Funs.fst @@ -310,6 +310,47 @@ let hash_map_insert_fwd_back end end +(** [hashmap::HashMap::contains_key_in_list] *) +let rec hash_map_contains_key_in_list_fwd + (t : Type0) (key : usize) (ls : list_t t) : + Tot (result bool) + (decreases (hash_map_contains_key_in_list_decreases t key ls)) + = + begin match ls with + | ListCons ckey x ls0 -> + let b = ckey = key in + if b + then Return true + else + begin match hash_map_contains_key_in_list_fwd t key ls0 with + | Fail -> Fail + | Return b0 -> Return b0 + end + | ListNil -> Return false + end + +(** [hashmap::HashMap::contains_key] *) +let hash_map_contains_key_fwd + (t : Type0) (self : hash_map_t t) (key : usize) : result bool = + begin match hash_key_fwd key with + | Fail -> Fail + | Return i -> + let v = self.hash_map_slots in + let i0 = vec_len (list_t t) v in + begin match usize_rem i i0 with + | Fail -> Fail + | Return hash_mod -> + begin match vec_index_fwd (list_t t) v hash_mod with + | Fail -> Fail + | Return l -> + begin match hash_map_contains_key_in_list_fwd t key l with + | Fail -> Fail + | Return b -> Return b + end + end + end + end + (** [hashmap::HashMap::get_in_list] *) let rec hash_map_get_in_list_fwd (t : Type0) (key : usize) (ls : list_t t) : diff --git a/tests/hashmap/Hashmap.Properties.fst b/tests/hashmap/Hashmap.Properties.fst index 822b630b..8053c911 100644 --- a/tests/hashmap/Hashmap.Properties.fst +++ b/tests/hashmap/Hashmap.Properties.fst @@ -1401,6 +1401,18 @@ let hash_map_insert_in_list_back_lem t len key value ls = /// We work on [hash_map_slots_s] (we use a higher-level view of the hash-map, but /// not too high). +/// A high-level version of insert, which doesn't check if the table is saturated +let hash_map_insert_no_fail_s + (#t : Type0) (hm : hash_map_slots_s t{length hm <= usize_max /\ length hm > 0}) + (key : usize) (value : t) : + hash_map_slots_s t = + let len = length hm in + let i = hash_mod_key key len in + let slot = index hm i in + let slot' = hash_map_insert_in_list_s key value slot in + let hm' = list_update hm i slot' in + hm' + // TODO: at some point I used hash_map_slots_s_nes and it broke proofs...x let hash_map_insert_no_resize_s (#t : Type0) (hm : hash_map_slots_s t{length hm <= usize_max /\ length hm > 0}) @@ -1409,15 +1421,7 @@ let hash_map_insert_no_resize_s // Check if the table is saturated (too many entries, and we need to insert one) let num_entries = length (flatten hm) in if None? (hash_map_slots_s_find hm key) && num_entries = usize_max then Fail - else - begin - let len = length hm in - let i = hash_mod_key key len in - let slot = index hm i in - let slot' = hash_map_insert_in_list_s key value slot in - let hm' = list_update hm i slot' in - Return hm' - end + else Return (hash_map_insert_no_fail_s hm key value) /// Prove that [hash_map_insert_no_resize_s] is a refinement of /// [hash_map_insert_no_resize'fwd_back] @@ -1676,8 +1680,54 @@ let rec hash_map_move_elements_from_list_fwd_back_lem t ntable ls = (*** move_elements *) +(**** move_elements: refinement 0 *) +/// The proof for [hash_map_move_elements_fwd_back_lem_refin] broke so many times +/// (while it is supposed to be super simple!) that we decided to add one refinement +/// level, to really do things step by step... +/// Doing this refinement layer made me notice that maybe the problem came from +/// the fact that at some point we have to prove `list_t_v ListNil == []`: I +/// added the corresponding assert to help Z3 and everything became stable. +/// I finally didn't use this "simple" refinement lemma, but I still keep it here +/// because it allows for easy comparisons with [hash_map_move_elements_s]. + +/// [hash_map_move_elements_fwd] refines this function, which is actually almost +/// the same (just a little bit shorter and cleaner, and has a pre). +/// +/// The way I wrote the refinement is the following: +/// - I copy-pasted the definition of [hash_map_move_elements_fwd], wrote the +/// signature which links this new definition to [hash_map_move_elements_fwd] and +/// checked that the proof passed +/// - I gradually simplified it, while making sure the proof still passes +#push-options "--fuel 1" +let rec hash_map_move_elements_s_simpl + (t : Type0) (ntable : hash_map_t t) + (slots : vec (list_t t)) + (i : usize{i <= length slots /\ length slots <= usize_max}) : + Pure (result ((hash_map_t t) & (vec (list_t t)))) + (requires (True)) + (ensures (fun res -> + match res, hash_map_move_elements_fwd_back t ntable slots i with + | Fail, Fail -> True + | Return (ntable1, slots1), Return (ntable2, slots2) -> + ntable1 == ntable2 /\ + slots1 == slots2 + | _ -> False)) + (decreases (hash_map_move_elements_decreases t ntable slots i)) + = + if i < length slots + then + let slot = index slots i in + begin match hash_map_move_elements_from_list_fwd_back t ntable slot with + | Fail -> Fail + | Return hm' -> + let slots' = list_update slots i ListNil in + hash_map_move_elements_s_simpl t hm' slots' (i+1) + end + else Return (ntable, slots) +#pop-options + (**** move_elements: refinement 1 *) -/// We prove a first refinement lemma: calling [move_elements] refines a function +/// We prove a second refinement lemma: calling [move_elements] refines a function /// which, for every slot, moves the element out of the slot. This first version is /// almost exactly the translated function, it just uses `list` instead of `list_t`. @@ -1743,13 +1793,22 @@ val hash_map_move_elements_fwd_back_lem_refin // the ifuel to unreasonable amounts). // // Finally, as I had succeeded in fixing the proof, I thought that maybe the -// initial problem with the type abbreviations fixed: I thus tried to use +// initial problem with the type abbreviations was fixed: I thus tried to use // them. Of course, it made the proof fail again, and this time no ifuel setting -// seemed to work (maybe my initial guess about the type inferencer was right?...). +// seemed to work. // // At this point I was just fed up and leave things as they were, without trying // to cleanup the previous definitions. // +// Finally, even later it broke, again, at which point I had no choice but to +// introduce an even simpler refinement proof (with [hash_map_move_elements_s_simpl]). +// Doing this allowed me to see that maybe the problem came from the fact that +// Z3 had to prove that `list_t_v ListNil == []` at some point, so I added the +// corresponding assertion and miraculously everything becamse stable... I then +// removed all the postconditions I had manually instanciated and inserted in +// the proof, and which took a lot of place. +// I still have no clue why `ifuel 4` made it work earlier. +// // The terrible thing is that this refinement proof is conceptually super simple: // - there are maybe two arithmetic proofs, which are directly solved by the // precondition @@ -1757,7 +1816,7 @@ val hash_map_move_elements_fwd_back_lem_refin // this is proven by another refinement lemma we proved above // - there is the recursive call (trivial) #restart-solver -#push-options "--z3rlimit 300 --fuel 1 --ifuel 4" +#push-options "--fuel 1" let rec hash_map_move_elements_fwd_back_lem_refin t ntable slots i = assert(hash_map_t_base_inv ntable); let i0 = vec_len (list_t t) slots in @@ -1770,22 +1829,12 @@ let rec hash_map_move_elements_fwd_back_lem_refin t ntable slots i = let l0 = mem_replace_fwd (list_t t) l ListNil in assert(l0 == l); hash_map_move_elements_from_list_fwd_back_lem t ntable l0; - begin - match hash_map_move_elements_from_list_fwd_back t ntable l0, - hash_map_move_elements_from_list_s (hash_map_t_slots_v ntable) (slot_t_v l0) - with - | Fail, Fail -> () - | Return hm', Return hm_v -> - assert(hash_map_t_base_inv hm'); - assert(hash_map_t_slots_v hm' == hm_v); - assert(hash_map_same_params hm' ntable) - | _ -> assert(False) - end; begin match hash_map_move_elements_from_list_fwd_back t ntable l0 with | Fail -> () | Return h -> let l1 = mem_replace_back (list_t t) l ListNil in assert(l1 == ListNil); + assert(slot_t_v #t ListNil == []); // THIS IS IMPORTANT begin match vec_index_mut_back (list_t t) slots i l1 with | Fail -> () | Return v -> @@ -1793,28 +1842,11 @@ let rec hash_map_move_elements_fwd_back_lem_refin t ntable slots i = | Fail -> () | Return i1 -> hash_map_move_elements_fwd_back_lem_refin t h v i1; - begin - match hash_map_move_elements_fwd_back t h v i1, - hash_map_move_elements_s (hash_map_t_slots_v h) (slots_t_v v) i1 - with - | Fail, Fail -> () - | Return (ntable', _), Return ntable'_v -> - assert(hash_map_t_base_inv ntable'); - assert(hash_map_t_slots_v ntable' == ntable'_v); - assert(hash_map_same_params ntable' ntable) - | _ -> assert(False) - end; begin match hash_map_move_elements_fwd_back t h v i1 with | Fail -> assert(hash_map_move_elements_fwd_back t ntable slots i == Fail); () - | Return (ntable', v0) -> - begin - // Trying to prove the postcondition - match hash_map_move_elements_fwd_back t ntable slots i with - | Fail -> assert(False) - | Return (ntable'', _) -> assert(ntable'' == ntable') - end + | Return (ntable', v0) -> () end end end @@ -2656,10 +2688,264 @@ let hash_map_insert_fwd_back_lem t self key value = end else () -(*** contains *) +(*** contains_key *) + +(**** contains_key_in_list *) + +val hash_map_contains_key_in_list_fwd_lem + (#t : Type0) (key : usize) (ls : list_t t) : + Lemma + (ensures ( + match hash_map_contains_key_in_list_fwd t key ls with + | Fail -> False + | Return b -> + b = Some? (slot_t_find_s key ls))) + + +#push-options "--fuel 1" +let rec hash_map_contains_key_in_list_fwd_lem #t key ls = + match ls with + | ListCons ckey x ls0 -> + let b = ckey = key in + if b + then () + else + begin + hash_map_contains_key_in_list_fwd_lem key ls0; + match hash_map_contains_key_in_list_fwd t key ls0 with + | Fail -> () + | Return b0 -> () + end + | ListNil -> () +#pop-options + +(**** contains_key *) + +val hash_map_contains_key_fwd_lem + (#t : Type0) (self : hash_map_t_nes t) (key : usize) : + Lemma + (ensures ( + match hash_map_contains_key_fwd t self key with + | Fail -> False + | Return b -> b = Some? (hash_map_t_find_s self key))) + +let hash_map_contains_key_fwd_lem #t self key = + begin match hash_key_fwd key with + | Fail -> () + | Return i -> + let v = self.hash_map_slots in + let i0 = vec_len (list_t t) v in + begin match usize_rem i i0 with + | Fail -> () + | Return hash_mod -> + begin match vec_index_fwd (list_t t) v hash_mod with + | Fail -> () + | Return l -> + hash_map_contains_key_in_list_fwd_lem key l; + begin match hash_map_contains_key_in_list_fwd t key l with + | Fail -> () + | Return b -> () + end + end + end + end (*** get *) +(**** get_in_list *) + +val hash_map_get_in_list_fwd_lem + (#t : Type0) (key : usize) (ls : list_t t) : + Lemma + (ensures ( + match hash_map_get_in_list_fwd t key ls, slot_t_find_s key ls with + | Fail, None -> True + | Return x, Some x' -> x == x' + | _ -> False)) + +#push-options "--fuel 1" +let rec hash_map_get_in_list_fwd_lem #t key ls = + begin match ls with + | ListCons ckey cvalue ls0 -> + let b = ckey = key in + if b + then () + else + begin + hash_map_get_in_list_fwd_lem key ls0; + match hash_map_get_in_list_fwd t key ls0 with + | Fail -> () + | Return x -> () + end + | ListNil -> () + end +#pop-options + +(**** get *) + +val hash_map_get_fwd_lem + (#t : Type0) (self : hash_map_t_nes t) (key : usize) : + Lemma + (ensures ( + match hash_map_get_fwd t self key, hash_map_t_find_s self key with + | Fail, None -> True + | Return x, Some x' -> x == x' + | _ -> False)) + +let hash_map_get_fwd_lem #t self key = + begin match hash_key_fwd key with + | Fail -> () + | Return i -> + let v = self.hash_map_slots in + let i0 = vec_len (list_t t) v in + begin match usize_rem i i0 with + | Fail -> () + | Return hash_mod -> + begin match vec_index_fwd (list_t t) v hash_mod with + | Fail -> () + | Return l -> + begin + hash_map_get_in_list_fwd_lem key l; + match hash_map_get_in_list_fwd t key l with + | Fail -> () + | Return x -> () + end + end + end + end + + (*** get_mut'fwd *) + +(**** get_mut_in_list *) + +val hash_map_get_mut_in_list_fwd_lem + (#t : Type0) (key : usize) (ls : list_t t) : + Lemma + (ensures ( + match hash_map_get_mut_in_list_fwd t key ls, slot_t_find_s key ls with + | Fail, None -> True + | Return x, Some x' -> x == x' + | _ -> False)) + +#push-options "--fuel 1" +let rec hash_map_get_mut_in_list_fwd_lem #t key ls = + begin match ls with + | ListCons ckey cvalue ls0 -> + let b = ckey = key in + if b + then () + else + begin + hash_map_get_mut_in_list_fwd_lem key ls0; + match hash_map_get_mut_in_list_fwd t key ls0 with + | Fail -> () + | Return x -> () + end + | ListNil -> () + end +#pop-options + +(**** get_mut *) + +val hash_map_get_mut_fwd_lem + (#t : Type0) (self : hash_map_t_nes t) (key : usize) : + Lemma + (ensures ( + match hash_map_get_mut_fwd t self key, hash_map_t_find_s self key with + | Fail, None -> True + | Return x, Some x' -> x == x' + | _ -> False)) + +let hash_map_get_mut_fwd_lem #t self key = + begin match hash_key_fwd key with + | Fail -> () + | Return i -> + let v = self.hash_map_slots in + let i0 = vec_len (list_t t) v in + begin match usize_rem i i0 with + | Fail -> () + | Return hash_mod -> + begin match vec_index_fwd (list_t t) v hash_mod with + | Fail -> () + | Return l -> + begin + hash_map_get_mut_in_list_fwd_lem key l; + match hash_map_get_mut_in_list_fwd t key l with + | Fail -> () + | Return x -> () + end + end + end + end + + (*** get_mut'back *) + +(**** get_mut_in_list *) + +val hash_map_get_mut_in_list_back_lem + (#t : Type0) (key : usize) (ls : list_t t) (ret : t) : + Lemma + (requires (Some? (slot_t_find_s key ls))) + (ensures ( + match hash_map_get_mut_in_list_back t key ls ret with + | Fail -> False + | Return ls' -> list_t_v ls' == find_update (same_key key) (list_t_v ls) (key,ret) + | _ -> False)) + +#push-options "--fuel 1" +let rec hash_map_get_mut_in_list_back_lem #t key ls ret = + begin match ls with + | ListCons ckey cvalue ls0 -> + let b = ckey = key in + if b + then let ls1 = ListCons ckey ret ls0 in () + else + begin + hash_map_get_mut_in_list_back_lem key ls0 ret; + match hash_map_get_mut_in_list_back t key ls0 ret with + | Fail -> () + | Return l -> let ls1 = ListCons ckey cvalue l in () + end + | ListNil -> () + end +#pop-options + +(**** get_mut *) + +val hash_map_get_mut_back_lem + (#t : Type0) (self : hash_map_t t) (key : usize) (ret : t) : + Lemma + (requires (Some? (hash_map_t_find_s self key)) + (ensures ( + match hash_map_get_mut_back t self key ret with + | Fail -> False + | Return hm' -> hash_map_t_slots_v hm' == hash_map_insert_no_fail hm key ret)) + +let hash_map_get_mut_back_lem #t self key = + begin match hash_key_back key with + | Fail -> () + | Return i -> + let v = self.hash_map_slots in + let i0 = vec_len (list_t t) v in + begin match usize_rem i i0 with + | Fail -> () + | Return hash_mod -> + begin match vec_index_back (list_t t) v hash_mod with + | Fail -> () + | Return l -> + begin + hash_map_get_mut_in_list_back_lem key l; + match hash_map_get_mut_in_list_back t key l with + | Fail -> () + | Return x -> () + end + end + end + end + + + +//val hash_map_insert_no_resize_s_lem |