rustc_data_structures/
stable_hasher.rs

1use std::hash::{BuildHasher, Hash, Hasher};
2use std::marker::PhantomData;
3use std::mem;
4use std::num::NonZero;
5
6use rustc_index::bit_set::{self, DenseBitSet};
7use rustc_index::{Idx, IndexSlice, IndexVec};
8use smallvec::SmallVec;
9
10#[cfg(test)]
11mod tests;
12
13pub use rustc_stable_hash::{
14    FromStableHash, SipHasher128Hash as StableHasherHash, StableSipHasher128 as StableHasher,
15};
16
17pub use crate::hashes::{Hash64, Hash128};
18
19/// Something that implements `HashStable<CTX>` can be hashed in a way that is
20/// stable across multiple compilation sessions.
21///
22/// Note that `HashStable` imposes rather more strict requirements than usual
23/// hash functions:
24///
25/// - Stable hashes are sometimes used as identifiers. Therefore they must
26///   conform to the corresponding `PartialEq` implementations:
27///
28///     - `x == y` implies `hash_stable(x) == hash_stable(y)`, and
29///     - `x != y` implies `hash_stable(x) != hash_stable(y)`.
30///
31///   That second condition is usually not required for hash functions
32///   (e.g. `Hash`). In practice this means that `hash_stable` must feed any
33///   information into the hasher that a `PartialEq` comparison takes into
34///   account. See [#49300](https://github.com/rust-lang/rust/issues/49300)
35///   for an example where violating this invariant has caused trouble in the
36///   past.
37///
38/// - `hash_stable()` must be independent of the current
39///    compilation session. E.g. they must not hash memory addresses or other
40///    things that are "randomly" assigned per compilation session.
41///
42/// - `hash_stable()` must be independent of the host architecture. The
43///   `StableHasher` takes care of endianness and `isize`/`usize` platform
44///   differences.
45pub trait HashStable<CTX> {
46    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher);
47}
48
49/// Implement this for types that can be turned into stable keys like, for
50/// example, for DefId that can be converted to a DefPathHash. This is used for
51/// bringing maps into a predictable order before hashing them.
52pub trait ToStableHashKey<HCX> {
53    type KeyType: Ord + Sized + HashStable<HCX>;
54    fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType;
55}
56
57/// Trait for marking a type as having a sort order that is
58/// stable across compilation session boundaries. More formally:
59///
60/// ```txt
61/// Ord::cmp(a1, b1) == Ord::cmp(a2, b2)
62///    where a2 = decode(encode(a1, context1), context2)
63///          b2 = decode(encode(b1, context1), context2)
64/// ```
65///
66/// i.e. the result of `Ord::cmp` is not influenced by encoding
67/// the values in one session and then decoding them in another
68/// session.
69///
70/// This is trivially true for types where encoding and decoding
71/// don't change the bytes of the values that are used during
72/// comparison and comparison only depends on these bytes (as
73/// opposed to some non-local state). Examples are u32, String,
74/// Path, etc.
75///
76/// But it is not true for:
77///  - `*const T` and `*mut T` because the values of these pointers
78///    will change between sessions.
79///  - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete
80///    values depend on state that might be different between
81///    compilation sessions.
82///
83/// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether
84/// unstable sorting can be used for this type. Set to true if and
85/// only if `a == b` implies `a` and `b` are fully indistinguishable.
86pub trait StableOrd: Ord {
87    const CAN_USE_UNSTABLE_SORT: bool;
88
89    /// Marker to ensure that implementors have carefully considered
90    /// whether their `Ord` implementation obeys this trait's contract.
91    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: ();
92}
93
94impl<T: StableOrd> StableOrd for &T {
95    const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
96
97    // Ordering of a reference is exactly that of the referent, and since
98    // the ordering of the referet is stable so must be the ordering of the
99    // reference.
100    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
101}
102
103/// This is a companion trait to `StableOrd`. Some types like `Symbol` can be
104/// compared in a cross-session stable way, but their `Ord` implementation is
105/// not stable. In such cases, a `StableOrd` implementation can be provided
106/// to offer a lightweight way for stable sorting. (The more heavyweight option
107/// is to sort via `ToStableHashKey`, but then sorting needs to have access to
108/// a stable hashing context and `ToStableHashKey` can also be expensive as in
109/// the case of `Symbol` where it has to allocate a `String`.)
110///
111/// See the documentation of [StableOrd] for how stable sort order is defined.
112/// The same definition applies here. Be careful when implementing this trait.
113pub trait StableCompare {
114    const CAN_USE_UNSTABLE_SORT: bool;
115
116    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering;
117}
118
119/// `StableOrd` denotes that the type's `Ord` implementation is stable, so
120/// we can implement `StableCompare` by just delegating to `Ord`.
121impl<T: StableOrd> StableCompare for T {
122    const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
123
124    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
125        self.cmp(other)
126    }
127}
128
129/// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
130/// that has the same requirements.
131///
132/// **WARNING** This is only valid for types that *really* don't need any context for fingerprinting.
133/// But it is easy to misuse this macro (see [#96013](https://github.com/rust-lang/rust/issues/96013)
134/// for examples). Therefore this macro is not exported and should only be used in the limited cases
135/// here in this module.
136///
137/// Use `#[derive(HashStable_Generic)]` instead.
138macro_rules! impl_stable_traits_for_trivial_type {
139    ($t:ty) => {
140        impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t {
141            #[inline]
142            fn hash_stable(&self, _: &mut CTX, hasher: &mut $crate::stable_hasher::StableHasher) {
143                ::std::hash::Hash::hash(self, hasher);
144            }
145        }
146
147        impl $crate::stable_hasher::StableOrd for $t {
148            const CAN_USE_UNSTABLE_SORT: bool = true;
149
150            // Encoding and decoding doesn't change the bytes of trivial types
151            // and `Ord::cmp` depends only on those bytes.
152            const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
153        }
154    };
155}
156
157pub(crate) use impl_stable_traits_for_trivial_type;
158
159impl_stable_traits_for_trivial_type!(i8);
160impl_stable_traits_for_trivial_type!(i16);
161impl_stable_traits_for_trivial_type!(i32);
162impl_stable_traits_for_trivial_type!(i64);
163impl_stable_traits_for_trivial_type!(isize);
164
165impl_stable_traits_for_trivial_type!(u8);
166impl_stable_traits_for_trivial_type!(u16);
167impl_stable_traits_for_trivial_type!(u32);
168impl_stable_traits_for_trivial_type!(u64);
169impl_stable_traits_for_trivial_type!(usize);
170
171impl_stable_traits_for_trivial_type!(u128);
172impl_stable_traits_for_trivial_type!(i128);
173
174impl_stable_traits_for_trivial_type!(char);
175impl_stable_traits_for_trivial_type!(());
176
177impl_stable_traits_for_trivial_type!(Hash64);
178
179// We need a custom impl as the default hash function will only hash half the bits. For stable
180// hashing we want to hash the full 128-bit hash.
181impl<CTX> HashStable<CTX> for Hash128 {
182    #[inline]
183    fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
184        self.as_u128().hash(hasher);
185    }
186}
187
188impl StableOrd for Hash128 {
189    const CAN_USE_UNSTABLE_SORT: bool = true;
190
191    // Encoding and decoding doesn't change the bytes of `Hash128`
192    // and `Ord::cmp` depends only on those bytes.
193    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
194}
195
196impl<CTX> HashStable<CTX> for ! {
197    fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) {
198        unreachable!()
199    }
200}
201
202impl<CTX, T> HashStable<CTX> for PhantomData<T> {
203    fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) {}
204}
205
206impl<CTX> HashStable<CTX> for NonZero<u32> {
207    #[inline]
208    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
209        self.get().hash_stable(ctx, hasher)
210    }
211}
212
213impl<CTX> HashStable<CTX> for NonZero<usize> {
214    #[inline]
215    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
216        self.get().hash_stable(ctx, hasher)
217    }
218}
219
220impl<CTX> HashStable<CTX> for f32 {
221    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
222        let val: u32 = self.to_bits();
223        val.hash_stable(ctx, hasher);
224    }
225}
226
227impl<CTX> HashStable<CTX> for f64 {
228    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
229        let val: u64 = self.to_bits();
230        val.hash_stable(ctx, hasher);
231    }
232}
233
234impl<CTX> HashStable<CTX> for ::std::cmp::Ordering {
235    #[inline]
236    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
237        (*self as i8).hash_stable(ctx, hasher);
238    }
239}
240
241impl<T1: HashStable<CTX>, CTX> HashStable<CTX> for (T1,) {
242    #[inline]
243    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
244        let (ref _0,) = *self;
245        _0.hash_stable(ctx, hasher);
246    }
247}
248
249impl<T1: HashStable<CTX>, T2: HashStable<CTX>, CTX> HashStable<CTX> for (T1, T2) {
250    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
251        let (ref _0, ref _1) = *self;
252        _0.hash_stable(ctx, hasher);
253        _1.hash_stable(ctx, hasher);
254    }
255}
256
257impl<T1: StableOrd, T2: StableOrd> StableOrd for (T1, T2) {
258    const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT;
259
260    // Ordering of tuples is a pure function of their elements' ordering, and since
261    // the ordering of each element is stable so must be the ordering of the tuple.
262    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
263}
264
265impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
266where
267    T1: HashStable<CTX>,
268    T2: HashStable<CTX>,
269    T3: HashStable<CTX>,
270{
271    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
272        let (ref _0, ref _1, ref _2) = *self;
273        _0.hash_stable(ctx, hasher);
274        _1.hash_stable(ctx, hasher);
275        _2.hash_stable(ctx, hasher);
276    }
277}
278
279impl<T1: StableOrd, T2: StableOrd, T3: StableOrd> StableOrd for (T1, T2, T3) {
280    const CAN_USE_UNSTABLE_SORT: bool =
281        T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT;
282
283    // Ordering of tuples is a pure function of their elements' ordering, and since
284    // the ordering of each element is stable so must be the ordering of the tuple.
285    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
286}
287
288impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
289where
290    T1: HashStable<CTX>,
291    T2: HashStable<CTX>,
292    T3: HashStable<CTX>,
293    T4: HashStable<CTX>,
294{
295    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
296        let (ref _0, ref _1, ref _2, ref _3) = *self;
297        _0.hash_stable(ctx, hasher);
298        _1.hash_stable(ctx, hasher);
299        _2.hash_stable(ctx, hasher);
300        _3.hash_stable(ctx, hasher);
301    }
302}
303
304impl<T1: StableOrd, T2: StableOrd, T3: StableOrd, T4: StableOrd> StableOrd for (T1, T2, T3, T4) {
305    const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT
306        && T2::CAN_USE_UNSTABLE_SORT
307        && T3::CAN_USE_UNSTABLE_SORT
308        && T4::CAN_USE_UNSTABLE_SORT;
309
310    // Ordering of tuples is a pure function of their elements' ordering, and since
311    // the ordering of each element is stable so must be the ordering of the tuple.
312    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
313}
314
315impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
316    default fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
317        self.len().hash_stable(ctx, hasher);
318        for item in self {
319            item.hash_stable(ctx, hasher);
320        }
321    }
322}
323
324impl<CTX> HashStable<CTX> for [u8] {
325    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
326        self.len().hash_stable(ctx, hasher);
327        hasher.write(self);
328    }
329}
330
331impl<T: HashStable<CTX>, CTX> HashStable<CTX> for Vec<T> {
332    #[inline]
333    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
334        self[..].hash_stable(ctx, hasher);
335    }
336}
337
338impl<K, V, R, CTX> HashStable<CTX> for indexmap::IndexMap<K, V, R>
339where
340    K: HashStable<CTX> + Eq + Hash,
341    V: HashStable<CTX>,
342    R: BuildHasher,
343{
344    #[inline]
345    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
346        self.len().hash_stable(ctx, hasher);
347        for kv in self {
348            kv.hash_stable(ctx, hasher);
349        }
350    }
351}
352
353impl<K, R, CTX> HashStable<CTX> for indexmap::IndexSet<K, R>
354where
355    K: HashStable<CTX> + Eq + Hash,
356    R: BuildHasher,
357{
358    #[inline]
359    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
360        self.len().hash_stable(ctx, hasher);
361        for key in self {
362            key.hash_stable(ctx, hasher);
363        }
364    }
365}
366
367impl<A, const N: usize, CTX> HashStable<CTX> for SmallVec<[A; N]>
368where
369    A: HashStable<CTX>,
370{
371    #[inline]
372    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
373        self[..].hash_stable(ctx, hasher);
374    }
375}
376
377impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for Box<T> {
378    #[inline]
379    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
380        (**self).hash_stable(ctx, hasher);
381    }
382}
383
384impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::rc::Rc<T> {
385    #[inline]
386    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
387        (**self).hash_stable(ctx, hasher);
388    }
389}
390
391impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for ::std::sync::Arc<T> {
392    #[inline]
393    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
394        (**self).hash_stable(ctx, hasher);
395    }
396}
397
398impl<CTX> HashStable<CTX> for str {
399    #[inline]
400    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
401        self.as_bytes().hash_stable(ctx, hasher);
402    }
403}
404
405impl StableOrd for &str {
406    const CAN_USE_UNSTABLE_SORT: bool = true;
407
408    // Encoding and decoding doesn't change the bytes of string slices
409    // and `Ord::cmp` depends only on those bytes.
410    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
411}
412
413impl<CTX> HashStable<CTX> for String {
414    #[inline]
415    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
416        self[..].hash_stable(hcx, hasher);
417    }
418}
419
420impl StableOrd for String {
421    const CAN_USE_UNSTABLE_SORT: bool = true;
422
423    // String comparison only depends on their contents and the
424    // contents are not changed by (de-)serialization.
425    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
426}
427
428impl<HCX> ToStableHashKey<HCX> for String {
429    type KeyType = String;
430    #[inline]
431    fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
432        self.clone()
433    }
434}
435
436impl<HCX, T1: ToStableHashKey<HCX>, T2: ToStableHashKey<HCX>> ToStableHashKey<HCX> for (T1, T2) {
437    type KeyType = (T1::KeyType, T2::KeyType);
438    #[inline]
439    fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
440        (self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx))
441    }
442}
443
444impl<CTX> HashStable<CTX> for bool {
445    #[inline]
446    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
447        (if *self { 1u8 } else { 0u8 }).hash_stable(ctx, hasher);
448    }
449}
450
451impl StableOrd for bool {
452    const CAN_USE_UNSTABLE_SORT: bool = true;
453
454    // sort order of bools is not changed by (de-)serialization.
455    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
456}
457
458impl<T, CTX> HashStable<CTX> for Option<T>
459where
460    T: HashStable<CTX>,
461{
462    #[inline]
463    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
464        if let Some(ref value) = *self {
465            1u8.hash_stable(ctx, hasher);
466            value.hash_stable(ctx, hasher);
467        } else {
468            0u8.hash_stable(ctx, hasher);
469        }
470    }
471}
472
473impl<T: StableOrd> StableOrd for Option<T> {
474    const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
475
476    // the Option wrapper does not add instability to comparison.
477    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
478}
479
480impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
481where
482    T1: HashStable<CTX>,
483    T2: HashStable<CTX>,
484{
485    #[inline]
486    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
487        mem::discriminant(self).hash_stable(ctx, hasher);
488        match *self {
489            Ok(ref x) => x.hash_stable(ctx, hasher),
490            Err(ref x) => x.hash_stable(ctx, hasher),
491        }
492    }
493}
494
495impl<'a, T, CTX> HashStable<CTX> for &'a T
496where
497    T: HashStable<CTX> + ?Sized,
498{
499    #[inline]
500    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
501        (**self).hash_stable(ctx, hasher);
502    }
503}
504
505impl<T, CTX> HashStable<CTX> for ::std::mem::Discriminant<T> {
506    #[inline]
507    fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
508        ::std::hash::Hash::hash(self, hasher);
509    }
510}
511
512impl<T, CTX> HashStable<CTX> for ::std::ops::RangeInclusive<T>
513where
514    T: HashStable<CTX>,
515{
516    #[inline]
517    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
518        self.start().hash_stable(ctx, hasher);
519        self.end().hash_stable(ctx, hasher);
520    }
521}
522
523impl<I: Idx, T, CTX> HashStable<CTX> for IndexSlice<I, T>
524where
525    T: HashStable<CTX>,
526{
527    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
528        self.len().hash_stable(ctx, hasher);
529        for v in &self.raw {
530            v.hash_stable(ctx, hasher);
531        }
532    }
533}
534
535impl<I: Idx, T, CTX> HashStable<CTX> for IndexVec<I, T>
536where
537    T: HashStable<CTX>,
538{
539    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
540        self.len().hash_stable(ctx, hasher);
541        for v in &self.raw {
542            v.hash_stable(ctx, hasher);
543        }
544    }
545}
546
547impl<I: Idx, CTX> HashStable<CTX> for DenseBitSet<I> {
548    fn hash_stable(&self, _ctx: &mut CTX, hasher: &mut StableHasher) {
549        ::std::hash::Hash::hash(self, hasher);
550    }
551}
552
553impl<R: Idx, C: Idx, CTX> HashStable<CTX> for bit_set::BitMatrix<R, C> {
554    fn hash_stable(&self, _ctx: &mut CTX, hasher: &mut StableHasher) {
555        ::std::hash::Hash::hash(self, hasher);
556    }
557}
558
559impl<T, CTX> HashStable<CTX> for bit_set::FiniteBitSet<T>
560where
561    T: HashStable<CTX> + bit_set::FiniteBitSetTy,
562{
563    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
564        self.0.hash_stable(hcx, hasher);
565    }
566}
567
568impl_stable_traits_for_trivial_type!(::std::path::Path);
569impl_stable_traits_for_trivial_type!(::std::path::PathBuf);
570
571// It is not safe to implement HashStable for HashSet, HashMap or any other collection type
572// with unstable but observable iteration order.
573// See https://github.com/rust-lang/compiler-team/issues/533 for further information.
574impl<V, HCX> !HashStable<HCX> for std::collections::HashSet<V> {}
575impl<K, V, HCX> !HashStable<HCX> for std::collections::HashMap<K, V> {}
576
577impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V>
578where
579    K: HashStable<HCX> + StableOrd,
580    V: HashStable<HCX>,
581{
582    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
583        self.len().hash_stable(hcx, hasher);
584        for entry in self.iter() {
585            entry.hash_stable(hcx, hasher);
586        }
587    }
588}
589
590impl<K, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K>
591where
592    K: HashStable<HCX> + StableOrd,
593{
594    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
595        self.len().hash_stable(hcx, hasher);
596        for entry in self.iter() {
597            entry.hash_stable(hcx, hasher);
598        }
599    }
600}
601
602/// Controls what data we do or do not hash.
603/// Whenever a `HashStable` implementation caches its
604/// result, it needs to include `HashingControls` as part
605/// of the key, to ensure that it does not produce an incorrect
606/// result (for example, using a `Fingerprint` produced while
607/// hashing `Span`s when a `Fingerprint` without `Span`s is
608/// being requested)
609#[derive(Clone, Hash, Eq, PartialEq, Debug)]
610pub struct HashingControls {
611    pub hash_spans: bool,
612}