use std::{ cmp::Ordering, hash::{Hash, Hasher}, ops::Deref, }; use rustc_data_structures::{ fingerprint::Fingerprint, stable_hasher::{HashStable, StableHasher}, }; use crate::{DebruijnIndex, TypeFlags}; /// A helper type that you can wrap round your own type in order to automatically /// cache the stable hash, type flags and debruijn index on creation and /// not recompute it whenever the information is needed. /// This is only done in incremental mode. You can also opt out of caching by using /// StableHash::ZERO for the hash, in which case the hash gets computed each time. /// This is useful if you have values that you intern but never (can?) use for stable /// hashing. #[derive(Copy, Clone)] pub struct WithCachedTypeInfo { pub internee: T, pub stable_hash: Fingerprint, /// This field provides fast access to information that is also contained /// in `kind`. /// /// This field shouldn't be used directly and may be removed in the future. /// Use `Ty::flags()` instead. pub flags: TypeFlags, /// This field provides fast access to information that is also contained /// in `kind`. /// /// This is a kind of confusing thing: it stores the smallest /// binder such that /// /// (a) the binder itself captures nothing but /// (b) all the late-bound things within the type are captured /// by some sub-binder. /// /// So, for a type without any late-bound things, like `u32`, this /// will be *innermost*, because that is the innermost binder that /// captures nothing. But for a type `&'D u32`, where `'D` is a /// late-bound region with De Bruijn index `D`, this would be `D + 1` /// -- the binder itself does not capture `D`, but `D` is captured /// by an inner binder. /// /// We call this concept an "exclusive" binder `D` because all /// De Bruijn indices within the type are contained within `0..D` /// (exclusive). pub outer_exclusive_binder: DebruijnIndex, } impl PartialEq for WithCachedTypeInfo { #[inline] fn eq(&self, other: &Self) -> bool { self.internee.eq(&other.internee) } } impl Eq for WithCachedTypeInfo {} impl PartialOrd for WithCachedTypeInfo { fn partial_cmp(&self, other: &WithCachedTypeInfo) -> Option { Some(self.internee.cmp(&other.internee)) } } impl Ord for WithCachedTypeInfo { fn cmp(&self, other: &WithCachedTypeInfo) -> Ordering { self.internee.cmp(&other.internee) } } impl Deref for WithCachedTypeInfo { type Target = T; #[inline] fn deref(&self) -> &T { &self.internee } } impl Hash for WithCachedTypeInfo { #[inline] fn hash(&self, s: &mut H) { if self.stable_hash != Fingerprint::ZERO { self.stable_hash.hash(s) } else { self.internee.hash(s) } } } impl, CTX> HashStable for WithCachedTypeInfo { fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { if self.stable_hash == Fingerprint::ZERO || cfg!(debug_assertions) { // No cached hash available. This can only mean that incremental is disabled. // We don't cache stable hashes in non-incremental mode, because they are used // so rarely that the performance actually suffers. // We need to build the hash as if we cached it and then hash that hash, as // otherwise the hashes will differ between cached and non-cached mode. let stable_hash: Fingerprint = { let mut hasher = StableHasher::new(); self.internee.hash_stable(hcx, &mut hasher); hasher.finish() }; if cfg!(debug_assertions) && self.stable_hash != Fingerprint::ZERO { assert_eq!( stable_hash, self.stable_hash, "cached stable hash does not match freshly computed stable hash" ); } stable_hash.hash_stable(hcx, hasher); } else { self.stable_hash.hash_stable(hcx, hasher); } } }