Skip to content

Commit c6f2717

Browse files
authored
Add specs for HashMap entry API (verus-lang#2420)
1 parent b85a7c2 commit c6f2717

5 files changed

Lines changed: 495 additions & 175 deletions

File tree

examples/entry_api.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use vstd::prelude::*;
2+
use vstd::std_specs::hash::*;
3+
use std::hash::*;
4+
use std::collections::hash_map::*;
5+
6+
verus!{
7+
8+
fn main() {
9+
let mut m = HashMap::<u64, u64>::new();
10+
11+
// Use the entry to insert 0 => 20
12+
let entry0 = m.entry(0);
13+
entry0.insert_entry(20);
14+
15+
// Use the entry to insert 1 => 30, then change it to 40
16+
let entry1 = m.entry(1);
17+
let mut occupied_entry = entry1.insert_entry(30);
18+
let value_ref = occupied_entry.get_mut();
19+
*value_ref = 40;
20+
21+
assert(m@ =~= map![0 => 20, 1 => 40]);
22+
}
23+
24+
/// Inserts the given key, value pair into the map if absent,
25+
/// does nothing otherwise
26+
fn insert_if_absent(m: &mut HashMap<u64, u64>, key: u64, value: u64)
27+
ensures
28+
!old(m)@.dom().contains(key) ==> final(m)@ =~= old(m)@.insert(key, value),
29+
old(m)@.dom().contains(key) ==> final(m)@ =~= old(m)@,
30+
{
31+
m.entry(key).or_insert(value);
32+
}
33+
34+
/// If the key is absent, insert 1; else, double it
35+
fn try_double(m: &mut HashMap<u64, u64>, key: u64)
36+
requires
37+
m@.dom().contains(key) ==> m[key] * 2 <= u64::MAX,
38+
ensures
39+
!old(m)@.dom().contains(key) ==> final(m)@ =~= old(m)@.insert(key, 1),
40+
old(m)@.dom().contains(key) ==> final(m)@ =~= old(m)@.insert(key, (old(m)@[key] * 2) as u64)
41+
{
42+
match m.entry(key) {
43+
Entry::Occupied(mut occ_entry) => {
44+
*occ_entry.get_mut() *= 2;
45+
}
46+
Entry::Vacant(vac_entry) => {
47+
vac_entry.insert(1);
48+
}
49+
}
50+
}
51+
52+
}

examples/experimental_new_mut_ref/README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/experimental_new_mut_ref/hash_table_entry.rs

Lines changed: 0 additions & 173 deletions
This file was deleted.

source/rust_verify_test/tests/std.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,164 @@ test_verify_one_file! {
765765
}
766766
} => Err(err) => assert_fails(err, 3)
767767
}
768+
769+
test_verify_one_file! {
770+
#[test] hash_map_entry_api verus_code! {
771+
use vstd::prelude::*;
772+
use vstd::std_specs::hash::*;
773+
use std::collections::hash_map::*;
774+
use std::hash::*;
775+
776+
fn test1() {
777+
let mut m = HashMap::<u64, u64>::new();
778+
779+
// Use entry API to insert to the map
780+
781+
let entry = m.entry(5);
782+
assert(entry.key() == 5 && entry.value() === None);
783+
784+
let value_ref = entry.or_insert(20);
785+
assert(*value_ref == 20);
786+
787+
*value_ref = 40;
788+
789+
assert(m@.dom().contains(5) && m@[5] == 40);
790+
791+
// Use entry API to remove from the map
792+
793+
let entry = m.entry(5);
794+
match entry {
795+
Entry::Occupied(occupied_entry) => {
796+
let (k, v) = occupied_entry.remove_entry();
797+
assert(k == 5);
798+
assert(v == 40);
799+
}
800+
Entry::Vacant(_) => {
801+
assert(false);
802+
}
803+
}
804+
805+
assert(!m@.dom().contains(5));
806+
807+
assert(false); // FAILS
808+
}
809+
810+
fn test_occupied_entry() {
811+
let mut m = HashMap::<u64, u64>::new();
812+
let entry = m.entry(5);
813+
let mut occ_entry = entry.insert_entry(20);
814+
815+
assert(occ_entry.key() == 5);
816+
assert(occ_entry.value() == 20);
817+
818+
let x = occ_entry.get();
819+
assert(*x == 20);
820+
821+
let x = occ_entry.get_mut();
822+
assert(*x == 20);
823+
*x = 30;
824+
825+
assert(occ_entry.key() == 5);
826+
assert(occ_entry.value() == 30);
827+
828+
let x = occ_entry.into_mut();
829+
assert(*x == 30);
830+
*x = 40;
831+
832+
assert(m@.dom().contains(5));
833+
assert(m@[5] == 40);
834+
835+
// Now let's remove it
836+
837+
let entry = m.entry(5);
838+
let mut occ_entry = entry.insert_entry(60);
839+
840+
let (removed_key, removed_value) = occ_entry.remove_entry();
841+
assert(removed_key == 5);
842+
assert(removed_value == 60);
843+
844+
assert(m@ =~= Map::empty());
845+
846+
assert(false); // FAILS
847+
}
848+
849+
fn test_occupied_entry2() {
850+
let mut m = HashMap::<u64, u64>::new();
851+
let entry = m.entry(5);
852+
let mut occ_entry = entry.insert_entry(20);
853+
854+
let old_value = occ_entry.insert(17);
855+
assert(old_value == 20);
856+
857+
assert(m@.dom().contains(5));
858+
assert(m@[5] == 17);
859+
860+
let entry = m.entry(5);
861+
let mut occ_entry = entry.insert_entry(20);
862+
let mut old_value = occ_entry.remove();
863+
assert(old_value == 20);
864+
865+
assert(m@ =~= Map::empty());
866+
867+
assert(false); // FAILS
868+
}
869+
870+
fn test_vacant_entry() {
871+
let mut m = HashMap::<u64, u64>::new();
872+
let entry = m.entry(5);
873+
874+
let Entry::Vacant(vac_entry) = entry else { assert(false); return; };
875+
876+
let k = vac_entry.into_key();
877+
assert(k == 5);
878+
879+
assert(m@ =~= Map::empty());
880+
881+
assert(false); // FAILS
882+
}
883+
884+
fn test_vacant_entry2() {
885+
let mut m = HashMap::<u64, u64>::new();
886+
let entry = m.entry(5);
887+
888+
let Entry::Vacant(vac_entry) = entry else { assert(false); return; };
889+
890+
// do nothing
891+
892+
assert(m@ =~= Map::empty());
893+
894+
assert(false); // FAILS
895+
}
896+
897+
fn test_vacant_entry3() {
898+
let mut m = HashMap::<u64, u64>::new();
899+
let entry = m.entry(5);
900+
901+
let Entry::Vacant(vac_entry) = entry else { assert(false); return; };
902+
903+
let r = vac_entry.insert(20);
904+
assert(*r == 20);
905+
*r = 30;
906+
907+
assert(m@.dom().contains(5) && m[5] == 30);
908+
909+
assert(false); // FAILS
910+
}
911+
912+
fn test_vacant_entry4() {
913+
let mut m = HashMap::<u64, u64>::new();
914+
let entry = m.entry(5);
915+
916+
let Entry::Vacant(vac_entry) = entry else { assert(false); return; };
917+
918+
let mut occ_entry = vac_entry.insert_entry(20);
919+
let r = occ_entry.get_mut();
920+
assert(*r == 20);
921+
*r = 30;
922+
923+
assert(m@.dom().contains(5) && m[5] == 30);
924+
925+
assert(false); // FAILS
926+
}
927+
} => Err(err) => assert_fails(err, 7)
928+
}

0 commit comments

Comments
 (0)