Coverage Report

Created: 2026-08-28 08:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/cranelift/codegen/src/scoped_hash_map.rs
Line
Count
Source
1
//! `ScopedHashMap`
2
//!
3
//! This module defines a struct `ScopedHashMap<C, K, V>` which
4
//! defines a `FxHashMap`-like container that has a concept of scopes
5
//! that can be entered and exited, such that values inserted while
6
//! inside a scope aren't visible outside the scope.
7
//!
8
//! The context type `C` is given to `CtxEq` and `CtxHash` methods on
9
//! the key values so that keys do not need to be fully
10
//! self-contained.
11
12
use crate::ctxhash::{CtxEq, CtxHash, CtxHashMap};
13
use smallvec::{SmallVec, smallvec};
14
15
struct Val<V> {
16
    value: V,
17
    level: u32,
18
    generation: u32,
19
}
20
21
/// A view into an occupied entry in a `ScopedHashMap`. It is part of the `Entry` enum.
22
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
23
    entry: crate::ctxhash::OccupiedEntry<'a, K, Val<V>>,
24
}
25
26
impl<'a, K, V> OccupiedEntry<'a, K, V> {
27
    /// Gets a reference to the value in the entry.
28
56.2k
    pub fn get(&self) -> &V {
29
56.2k
        &self.entry.get().value
30
56.2k
    }
31
}
32
33
/// A view into a vacant entry in a `ScopedHashMap`. It is part of the `Entry` enum.
34
pub struct VacantEntry<'a, K: 'a, V: 'a> {
35
    entry: InsertLoc<'a, K, V>,
36
    depth: u32,
37
    generation: u32,
38
}
39
40
/// Where to insert from a `VacantEntry`. May be vacant or occupied in
41
/// the underlying map because of lazy (generation-based) deletion.
42
enum InsertLoc<'a, K: 'a, V: 'a> {
43
    Vacant(crate::ctxhash::VacantEntry<'a, K, Val<V>>),
44
    Occupied(crate::ctxhash::OccupiedEntry<'a, K, Val<V>>),
45
}
46
47
impl<'a, K, V> VacantEntry<'a, K, V> {
48
    /// Sets the value of the entry with the `VacantEntry`'s key.
49
30.6M
    pub fn insert(self, value: V) {
50
30.6M
        let val = Val {
51
30.6M
            value,
52
30.6M
            level: self.depth,
53
30.6M
            generation: self.generation,
54
30.6M
        };
55
30.6M
        match self.entry {
56
28.4M
            InsertLoc::Vacant(v) => {
57
28.4M
                v.insert(val);
58
28.4M
            }
59
2.19M
            InsertLoc::Occupied(mut o) => {
60
2.19M
                *o.get_mut() = val;
61
2.19M
            }
62
        }
63
30.6M
    }
<cranelift_codegen::scoped_hash_map::VacantEntry<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::insert
Line
Count
Source
49
30.0M
    pub fn insert(self, value: V) {
50
30.0M
        let val = Val {
51
30.0M
            value,
52
30.0M
            level: self.depth,
53
30.0M
            generation: self.generation,
54
30.0M
        };
55
30.0M
        match self.entry {
56
27.8M
            InsertLoc::Vacant(v) => {
57
27.8M
                v.insert(val);
58
27.8M
            }
59
2.19M
            InsertLoc::Occupied(mut o) => {
60
2.19M
                *o.get_mut() = val;
61
2.19M
            }
62
        }
63
30.0M
    }
<cranelift_codegen::scoped_hash_map::VacantEntry<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::insert
Line
Count
Source
49
639k
    pub fn insert(self, value: V) {
50
639k
        let val = Val {
51
639k
            value,
52
639k
            level: self.depth,
53
639k
            generation: self.generation,
54
639k
        };
55
639k
        match self.entry {
56
636k
            InsertLoc::Vacant(v) => {
57
636k
                v.insert(val);
58
636k
            }
59
3.35k
            InsertLoc::Occupied(mut o) => {
60
3.35k
                *o.get_mut() = val;
61
3.35k
            }
62
        }
63
639k
    }
64
}
65
66
/// A view into a single entry in a map, which may either be vacant or occupied.
67
///
68
/// This enum is constructed from the `entry` method on `ScopedHashMap`.
69
pub enum Entry<'a, K: 'a, V: 'a> {
70
    Occupied(OccupiedEntry<'a, K, V>),
71
    Vacant(VacantEntry<'a, K, V>),
72
}
73
74
/// A wrapper around a `FxHashMap` which adds the concept of scopes. Items inserted
75
/// within a scope are removed when the scope is exited.
76
///
77
/// Shadowing, where one scope has entries with the same keys as a containing scope,
78
/// is not supported in this implementation.
79
pub struct ScopedHashMap<K, V> {
80
    map: CtxHashMap<K, Val<V>>,
81
    generation_by_depth: SmallVec<[u32; 8]>,
82
    generation: u32,
83
}
84
85
impl<K, V> ScopedHashMap<K, V>
86
where
87
    K: Clone,
88
{
89
    /// Creates an empty `ScopedHashMap`.
90
    #[cfg(test)]
91
    pub fn new() -> Self {
92
        Self::with_capacity(16)
93
    }
94
95
    /// Creates an empty `ScopedHashMap` with some pre-allocated capacity.
96
2.29M
    pub fn with_capacity(cap: usize) -> Self {
97
        Self {
98
2.29M
            map: CtxHashMap::with_capacity(cap),
99
            generation: 0,
100
2.29M
            generation_by_depth: smallvec![0],
101
        }
102
2.29M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::with_capacity
Line
Count
Source
96
1.14M
    pub fn with_capacity(cap: usize) -> Self {
97
        Self {
98
1.14M
            map: CtxHashMap::with_capacity(cap),
99
            generation: 0,
100
1.14M
            generation_by_depth: smallvec![0],
101
        }
102
1.14M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::with_capacity
Line
Count
Source
96
1.14M
    pub fn with_capacity(cap: usize) -> Self {
97
        Self {
98
1.14M
            map: CtxHashMap::with_capacity(cap),
99
            generation: 0,
100
1.14M
            generation_by_depth: smallvec![0],
101
        }
102
1.14M
    }
103
104
    /// Similar to `FxHashMap::entry`, gets the given key's corresponding entry in the map for
105
    /// in-place manipulation.
106
695k
    pub fn entry<'a, C>(&'a mut self, ctx: &C, key: K) -> Entry<'a, K, V>
107
695k
    where
108
695k
        C: CtxEq<K, K> + CtxHash<K>,
109
    {
110
695k
        self.entry_with_depth(ctx, key, self.depth())
111
695k
    }
112
113
    /// Get the entry, setting the scope depth at which to insert.
114
33.1M
    pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V>
115
33.1M
    where
116
33.1M
        C: CtxEq<K, K> + CtxHash<K>,
117
    {
118
33.1M
        debug_assert!(depth <= self.generation_by_depth.len());
119
33.1M
        let generation = self.generation_by_depth[depth];
120
33.1M
        let depth = depth as u32;
121
33.1M
        match self.map.entry(key, ctx) {
122
4.70M
            crate::ctxhash::Entry::Occupied(entry) => {
123
4.70M
                let entry_generation = entry.get().generation;
124
4.70M
                let entry_depth = entry.get().level as usize;
125
4.70M
                if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) {
126
2.50M
                    Entry::Occupied(OccupiedEntry { entry })
127
                } else {
128
2.19M
                    Entry::Vacant(VacantEntry {
129
2.19M
                        entry: InsertLoc::Occupied(entry),
130
2.19M
                        depth,
131
2.19M
                        generation,
132
2.19M
                    })
133
                }
134
            }
135
28.4M
            crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
136
28.4M
                entry: InsertLoc::Vacant(entry),
137
28.4M
                depth,
138
28.4M
                generation,
139
28.4M
            }),
140
        }
141
33.1M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::entry_with_depth::<cranelift_codegen::ctxhash::NullCtx>
Line
Count
Source
114
32.4M
    pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V>
115
32.4M
    where
116
32.4M
        C: CtxEq<K, K> + CtxHash<K>,
117
    {
118
32.4M
        debug_assert!(depth <= self.generation_by_depth.len());
119
32.4M
        let generation = self.generation_by_depth[depth];
120
32.4M
        let depth = depth as u32;
121
32.4M
        match self.map.entry(key, ctx) {
122
4.64M
            crate::ctxhash::Entry::Occupied(entry) => {
123
4.64M
                let entry_generation = entry.get().generation;
124
4.64M
                let entry_depth = entry.get().level as usize;
125
4.64M
                if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) {
126
2.45M
                    Entry::Occupied(OccupiedEntry { entry })
127
                } else {
128
2.19M
                    Entry::Vacant(VacantEntry {
129
2.19M
                        entry: InsertLoc::Occupied(entry),
130
2.19M
                        depth,
131
2.19M
                        generation,
132
2.19M
                    })
133
                }
134
            }
135
27.8M
            crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
136
27.8M
                entry: InsertLoc::Vacant(entry),
137
27.8M
                depth,
138
27.8M
                generation,
139
27.8M
            }),
140
        }
141
32.4M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::entry_with_depth::<cranelift_codegen::ctxhash::NullCtx>
Line
Count
Source
114
695k
    pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V>
115
695k
    where
116
695k
        C: CtxEq<K, K> + CtxHash<K>,
117
    {
118
695k
        debug_assert!(depth <= self.generation_by_depth.len());
119
695k
        let generation = self.generation_by_depth[depth];
120
695k
        let depth = depth as u32;
121
695k
        match self.map.entry(key, ctx) {
122
59.6k
            crate::ctxhash::Entry::Occupied(entry) => {
123
59.6k
                let entry_generation = entry.get().generation;
124
59.6k
                let entry_depth = entry.get().level as usize;
125
59.6k
                if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) {
126
56.2k
                    Entry::Occupied(OccupiedEntry { entry })
127
                } else {
128
3.35k
                    Entry::Vacant(VacantEntry {
129
3.35k
                        entry: InsertLoc::Occupied(entry),
130
3.35k
                        depth,
131
3.35k
                        generation,
132
3.35k
                    })
133
                }
134
            }
135
636k
            crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
136
636k
                entry: InsertLoc::Vacant(entry),
137
636k
                depth,
138
636k
                generation,
139
636k
            }),
140
        }
141
695k
    }
142
143
    /// Get a value from a key, if present.
144
133M
    pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V>
145
133M
    where
146
133M
        C: CtxEq<K, K> + CtxHash<K>,
147
    {
148
133M
        self.map
149
133M
            .get(key, ctx)
150
133M
            .filter(|entry| {
151
58.7M
                let level = entry.level as usize;
152
58.7M
                self.generation_by_depth.get(level).cloned() == Some(entry.generation)
153
58.7M
            })
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::get::<cranelift_codegen::ctxhash::NullCtx>::{closure#0}
Line
Count
Source
150
29.2M
            .filter(|entry| {
151
29.2M
                let level = entry.level as usize;
152
29.2M
                self.generation_by_depth.get(level).cloned() == Some(entry.generation)
153
29.2M
            })
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::get::<cranelift_codegen::egraph::GVNContext>::{closure#0}
Line
Count
Source
150
29.4M
            .filter(|entry| {
151
29.4M
                let level = entry.level as usize;
152
29.4M
                self.generation_by_depth.get(level).cloned() == Some(entry.generation)
153
29.4M
            })
154
133M
            .map(|entry| &entry.value)
155
133M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::get::<cranelift_codegen::ctxhash::NullCtx>
Line
Count
Source
144
66.1M
    pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V>
145
66.1M
    where
146
66.1M
        C: CtxEq<K, K> + CtxHash<K>,
147
    {
148
66.1M
        self.map
149
66.1M
            .get(key, ctx)
150
66.1M
            .filter(|entry| {
151
                let level = entry.level as usize;
152
                self.generation_by_depth.get(level).cloned() == Some(entry.generation)
153
            })
154
66.1M
            .map(|entry| &entry.value)
155
66.1M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::get::<cranelift_codegen::egraph::GVNContext>
Line
Count
Source
144
67.0M
    pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V>
145
67.0M
    where
146
67.0M
        C: CtxEq<K, K> + CtxHash<K>,
147
    {
148
67.0M
        self.map
149
67.0M
            .get(key, ctx)
150
67.0M
            .filter(|entry| {
151
                let level = entry.level as usize;
152
                self.generation_by_depth.get(level).cloned() == Some(entry.generation)
153
            })
154
67.0M
            .map(|entry| &entry.value)
155
67.0M
    }
156
157
    /// Insert a key-value pair if absent. No-op if already exists.
158
11.4M
    pub fn insert_if_absent<C>(&mut self, ctx: &C, key: K, value: V)
159
11.4M
    where
160
11.4M
        C: CtxEq<K, K> + CtxHash<K>,
161
    {
162
11.4M
        self.insert_if_absent_with_depth(ctx, key, value, self.depth());
163
11.4M
    }
164
165
    /// Insert a key-value pair if absent, using the given depth for
166
    /// the insertion. No-op if already exists.
167
32.4M
    pub fn insert_if_absent_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize)
168
32.4M
    where
169
32.4M
        C: CtxEq<K, K> + CtxHash<K>,
170
    {
171
32.4M
        match self.entry_with_depth(ctx, key, depth) {
172
30.0M
            Entry::Vacant(v) => {
173
30.0M
                v.insert(value);
174
30.0M
            }
175
2.45M
            Entry::Occupied(_) => {
176
2.45M
                // Nothing.
177
2.45M
            }
178
        }
179
32.4M
    }
180
181
    /// Insert a key-value pair, using the given depth for the
182
    /// insertion. Removes existing entry and overwrites if already
183
    /// existed.
184
37.5M
    pub fn insert_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize)
185
37.5M
    where
186
37.5M
        C: CtxEq<K, K> + CtxHash<K>,
187
    {
188
37.5M
        let val = Val {
189
37.5M
            value,
190
37.5M
            level: depth as u32,
191
37.5M
            generation: self.generation_by_depth[depth],
192
37.5M
        };
193
37.5M
        self.map.insert(key, val, ctx);
194
37.5M
    }
195
196
    /// Enter a new scope.
197
20.1M
    pub fn increment_depth(&mut self) {
198
20.1M
        self.generation_by_depth.push(self.generation);
199
20.1M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::increment_depth
Line
Count
Source
197
10.0M
    pub fn increment_depth(&mut self) {
198
10.0M
        self.generation_by_depth.push(self.generation);
199
10.0M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::increment_depth
Line
Count
Source
197
10.0M
    pub fn increment_depth(&mut self) {
198
10.0M
        self.generation_by_depth.push(self.generation);
199
10.0M
    }
200
201
    /// Exit the current scope.
202
16.6M
    pub fn decrement_depth(&mut self) {
203
16.6M
        self.generation += 1;
204
16.6M
        self.generation_by_depth.pop();
205
16.6M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::decrement_depth
Line
Count
Source
202
10.0M
    pub fn decrement_depth(&mut self) {
203
10.0M
        self.generation += 1;
204
10.0M
        self.generation_by_depth.pop();
205
10.0M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::decrement_depth
Line
Count
Source
202
6.58M
    pub fn decrement_depth(&mut self) {
203
6.58M
        self.generation += 1;
204
6.58M
        self.generation_by_depth.pop();
205
6.58M
    }
206
207
    /// Return the current scope depth.
208
32.8M
    pub fn depth(&self) -> usize {
209
32.8M
        self.generation_by_depth
210
32.8M
            .len()
211
32.8M
            .checked_sub(1)
212
32.8M
            .expect("generation_by_depth cannot be empty")
213
32.8M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::depth
Line
Count
Source
208
32.1M
    pub fn depth(&self) -> usize {
209
32.1M
        self.generation_by_depth
210
32.1M
            .len()
211
32.1M
            .checked_sub(1)
212
32.1M
            .expect("generation_by_depth cannot be empty")
213
32.1M
    }
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::depth
Line
Count
Source
208
695k
    pub fn depth(&self) -> usize {
209
695k
        self.generation_by_depth
210
695k
            .len()
211
695k
            .checked_sub(1)
212
695k
            .expect("generation_by_depth cannot be empty")
213
695k
    }
214
}
215
216
#[cfg(test)]
217
mod tests {
218
    use super::*;
219
    use crate::ctxhash::NullCtx;
220
221
    #[test]
222
    fn basic() {
223
        let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new();
224
225
        match map.entry(&NullCtx, 0) {
226
            Entry::Occupied(_entry) => panic!(),
227
            Entry::Vacant(entry) => entry.insert(1),
228
        }
229
        match map.entry(&NullCtx, 2) {
230
            Entry::Occupied(_entry) => panic!(),
231
            Entry::Vacant(entry) => entry.insert(8),
232
        }
233
        match map.entry(&NullCtx, 2) {
234
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
235
            Entry::Vacant(_entry) => panic!(),
236
        }
237
        map.increment_depth();
238
        match map.entry(&NullCtx, 2) {
239
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
240
            Entry::Vacant(_entry) => panic!(),
241
        }
242
        match map.entry(&NullCtx, 1) {
243
            Entry::Occupied(_entry) => panic!(),
244
            Entry::Vacant(entry) => entry.insert(3),
245
        }
246
        match map.entry(&NullCtx, 1) {
247
            Entry::Occupied(entry) => assert!(*entry.get() == 3),
248
            Entry::Vacant(_entry) => panic!(),
249
        }
250
        match map.entry(&NullCtx, 0) {
251
            Entry::Occupied(entry) => assert!(*entry.get() == 1),
252
            Entry::Vacant(_entry) => panic!(),
253
        }
254
        match map.entry(&NullCtx, 2) {
255
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
256
            Entry::Vacant(_entry) => panic!(),
257
        }
258
        map.decrement_depth();
259
        match map.entry(&NullCtx, 0) {
260
            Entry::Occupied(entry) => assert!(*entry.get() == 1),
261
            Entry::Vacant(_entry) => panic!(),
262
        }
263
        match map.entry(&NullCtx, 2) {
264
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
265
            Entry::Vacant(_entry) => panic!(),
266
        }
267
        map.increment_depth();
268
        match map.entry(&NullCtx, 2) {
269
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
270
            Entry::Vacant(_entry) => panic!(),
271
        }
272
        match map.entry(&NullCtx, 1) {
273
            Entry::Occupied(_entry) => panic!(),
274
            Entry::Vacant(entry) => entry.insert(4),
275
        }
276
        match map.entry(&NullCtx, 1) {
277
            Entry::Occupied(entry) => assert!(*entry.get() == 4),
278
            Entry::Vacant(_entry) => panic!(),
279
        }
280
        match map.entry(&NullCtx, 2) {
281
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
282
            Entry::Vacant(_entry) => panic!(),
283
        }
284
        map.decrement_depth();
285
        map.increment_depth();
286
        map.increment_depth();
287
        map.increment_depth();
288
        match map.entry(&NullCtx, 2) {
289
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
290
            Entry::Vacant(_entry) => panic!(),
291
        }
292
        match map.entry(&NullCtx, 1) {
293
            Entry::Occupied(_entry) => panic!(),
294
            Entry::Vacant(entry) => entry.insert(5),
295
        }
296
        match map.entry(&NullCtx, 1) {
297
            Entry::Occupied(entry) => assert!(*entry.get() == 5),
298
            Entry::Vacant(_entry) => panic!(),
299
        }
300
        match map.entry(&NullCtx, 2) {
301
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
302
            Entry::Vacant(_entry) => panic!(),
303
        }
304
        map.decrement_depth();
305
        map.decrement_depth();
306
        map.decrement_depth();
307
        match map.entry(&NullCtx, 2) {
308
            Entry::Occupied(entry) => assert!(*entry.get() == 8),
309
            Entry::Vacant(_entry) => panic!(),
310
        }
311
        match map.entry(&NullCtx, 1) {
312
            Entry::Occupied(_entry) => panic!(),
313
            Entry::Vacant(entry) => entry.insert(3),
314
        }
315
    }
316
317
    #[test]
318
    fn insert_arbitrary_depth() {
319
        let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new();
320
        map.insert_if_absent(&NullCtx, 1, 2);
321
        assert_eq!(map.get(&NullCtx, &1), Some(&2));
322
        map.increment_depth();
323
        assert_eq!(map.get(&NullCtx, &1), Some(&2));
324
        map.insert_if_absent(&NullCtx, 3, 4);
325
        assert_eq!(map.get(&NullCtx, &3), Some(&4));
326
        map.decrement_depth();
327
        assert_eq!(map.get(&NullCtx, &3), None);
328
        map.increment_depth();
329
        map.insert_if_absent_with_depth(&NullCtx, 3, 4, 0);
330
        assert_eq!(map.get(&NullCtx, &3), Some(&4));
331
        map.decrement_depth();
332
        assert_eq!(map.get(&NullCtx, &3), Some(&4));
333
    }
334
}