X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_data_structures%2Fsrc%2Fintern.rs;h=009b5d5340afe3c724950d881014d6e716b75c62;hb=0b3e75e1cdc8d56fc5540e17450d96228b26e268;hp=7a320b10b60300460fc3efe19811798742efb599;hpb=b41374598f3274e28273a447d2d7d82a6f26c1f3;p=rust.git diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs index 7a320b10b60..009b5d5340a 100644 --- a/compiler/rustc_data_structures/src/intern.rs +++ b/compiler/rustc_data_structures/src/intern.rs @@ -4,6 +4,8 @@ use std::ops::Deref; use std::ptr; +use crate::fingerprint::Fingerprint; + mod private { #[derive(Clone, Copy, Debug)] pub struct PrivateZst; @@ -108,5 +110,87 @@ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { } } +/// A helper trait so that `Interned` things can cache stable hashes reproducibly. +pub trait InternedHashingContext { + fn with_def_path_and_no_spans(&mut self, f: impl FnOnce(&mut Self)); +} + +/// A helper type that you can wrap round your own type in order to automatically +/// cache the stable hash on creation and not recompute it whenever the stable hash +/// of the type is computed. +/// 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 WithStableHash { + pub internee: T, + pub stable_hash: Fingerprint, +} + +impl PartialEq for WithStableHash { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.internee.eq(&other.internee) + } +} + +impl Eq for WithStableHash {} + +impl PartialOrd for WithStableHash { + fn partial_cmp(&self, other: &WithStableHash) -> Option { + Some(self.internee.cmp(&other.internee)) + } +} + +impl Ord for WithStableHash { + fn cmp(&self, other: &WithStableHash) -> Ordering { + self.internee.cmp(&other.internee) + } +} + +impl Deref for WithStableHash { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.internee + } +} + +impl Hash for WithStableHash { + #[inline] + fn hash(&self, s: &mut H) { + self.internee.hash(s) + } +} + +impl, CTX: InternedHashingContext> HashStable for WithStableHash { + 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(); + hcx.with_def_path_and_no_spans(|hcx| 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); + } + } +} + #[cfg(test)] mod tests;