2 use crate::hir::def_id::{DefId, DefIndex};
3 use crate::hir::map::DefPathHash;
4 use crate::hir::map::definitions::Definitions;
5 use crate::ich::{self, CachingSourceMapView, Fingerprint};
6 use crate::middle::cstore::CrateStore;
7 use crate::ty::{TyCtxt, fast_reject};
8 use crate::session::Session;
11 use std::hash as std_hash;
12 use std::cell::RefCell;
15 use syntax::source_map::SourceMap;
16 use syntax::symbol::Symbol;
17 use syntax::tokenstream::DelimSpan;
18 use syntax_pos::{Span, DUMMY_SP};
19 use syntax_pos::hygiene::{self, SyntaxContext};
21 use rustc_data_structures::stable_hasher::{
22 HashStable, StableHasher, ToStableHashKey,
24 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
25 use smallvec::SmallVec;
27 fn compute_ignored_attr_names() -> FxHashSet<Symbol> {
28 debug_assert!(ich::IGNORED_ATTRIBUTES.len() > 0);
29 ich::IGNORED_ATTRIBUTES.iter().map(|&s| s).collect()
32 /// This is the context state available during incr. comp. hashing. It contains
33 /// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e.,
34 /// a reference to the `TyCtxt`) and it holds a few caches for speeding up various
35 /// things (e.g., each `DefId`/`DefPath` is only hashed once).
37 pub struct StableHashingContext<'a> {
39 definitions: &'a Definitions,
40 cstore: &'a dyn CrateStore,
41 body_resolver: BodyResolver<'a>,
44 node_id_hashing_mode: NodeIdHashingMode,
46 // Very often, we are hashing something that does not need the
47 // `CachingSourceMapView`, so we initialize it lazily.
48 raw_source_map: &'a SourceMap,
49 caching_source_map: Option<CachingSourceMapView<'a>>,
52 #[derive(PartialEq, Eq, Clone, Copy)]
53 pub enum NodeIdHashingMode {
58 /// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`.
59 /// We could also just store a plain reference to the `hir::Crate` but we want
60 /// to avoid that the crate is used to get untracked access to all of the HIR.
61 #[derive(Clone, Copy)]
62 struct BodyResolver<'tcx>(&'tcx hir::Crate);
64 impl<'tcx> BodyResolver<'tcx> {
65 /// Returns a reference to the `hir::Body` with the given `BodyId`.
66 /// **Does not do any tracking**; use carefully.
67 fn body(self, id: hir::BodyId) -> &'tcx hir::Body {
72 impl<'a> StableHashingContext<'a> {
73 /// The `krate` here is only used for mapping `BodyId`s to `Body`s.
74 /// Don't use it for anything else or you'll run the risk of
75 /// leaking data out of the tracking system.
77 pub fn new(sess: &'a Session,
78 krate: &'a hir::Crate,
79 definitions: &'a Definitions,
80 cstore: &'a dyn CrateStore)
82 let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans;
84 StableHashingContext {
86 body_resolver: BodyResolver(krate),
89 caching_source_map: None,
90 raw_source_map: sess.source_map(),
91 hash_spans: hash_spans_initial,
93 node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
98 pub fn sess(&self) -> &'a Session {
103 pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
106 let prev_hash_bodies = self.hash_bodies;
107 self.hash_bodies = hash_bodies;
109 self.hash_bodies = prev_hash_bodies;
113 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
116 let prev_hash_spans = self.hash_spans;
117 self.hash_spans = hash_spans;
119 self.hash_spans = prev_hash_spans;
123 pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
124 mode: NodeIdHashingMode,
126 let prev = self.node_id_hashing_mode;
127 self.node_id_hashing_mode = mode;
129 self.node_id_hashing_mode = prev;
133 pub fn def_path_hash(&self, def_id: DefId) -> DefPathHash {
134 if def_id.is_local() {
135 self.definitions.def_path_hash(def_id.index)
137 self.cstore.def_path_hash(def_id)
142 pub fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash {
143 self.definitions.def_path_hash(def_index)
147 pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId {
148 self.definitions.node_to_hir_id(node_id)
152 pub fn hash_bodies(&self) -> bool {
157 pub fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
158 match self.caching_source_map {
159 Some(ref mut cm) => {
163 *none = Some(CachingSourceMapView::new(self.raw_source_map));
164 none.as_mut().unwrap()
170 pub fn is_ignored_attr(&self, name: Symbol) -> bool {
172 static IGNORED_ATTRIBUTES: FxHashSet<Symbol> = compute_ignored_attr_names();
174 IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name))
177 pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F) {
178 let prev_hash_node_ids = self.node_id_hashing_mode;
179 self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
183 self.node_id_hashing_mode = prev_hash_node_ids;
187 /// Something that can provide a stable hashing context.
188 pub trait StableHashingContextProvider<'a> {
189 fn get_stable_hashing_context(&self) -> StableHashingContext<'a>;
192 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
194 fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
195 (**self).get_stable_hashing_context()
199 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
201 fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
202 (**self).get_stable_hashing_context()
206 impl StableHashingContextProvider<'tcx> for TyCtxt<'tcx> {
207 fn get_stable_hashing_context(&self) -> StableHashingContext<'tcx> {
208 (*self).create_stable_hashing_context()
212 impl<'a> StableHashingContextProvider<'a> for StableHashingContext<'a> {
213 fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
218 impl<'a> crate::dep_graph::DepGraphSafe for StableHashingContext<'a> {}
220 impl<'a> HashStable<StableHashingContext<'a>> for hir::BodyId {
221 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
222 if hcx.hash_bodies() {
223 hcx.body_resolver.body(*self).hash_stable(hcx, hasher);
228 impl<'a> HashStable<StableHashingContext<'a>> for hir::HirId {
230 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
231 match hcx.node_id_hashing_mode {
232 NodeIdHashingMode::Ignore => {
233 // Don't do anything.
235 NodeIdHashingMode::HashDefPath => {
241 hcx.local_def_path_hash(owner).hash_stable(hcx, hasher);
242 local_id.hash_stable(hcx, hasher);
248 impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::HirId {
249 type KeyType = (DefPathHash, hir::ItemLocalId);
252 fn to_stable_hash_key(&self,
253 hcx: &StableHashingContext<'a>)
254 -> (DefPathHash, hir::ItemLocalId) {
255 let def_path_hash = hcx.local_def_path_hash(self.owner);
256 (def_path_hash, self.local_id)
260 impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId {
261 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
262 match hcx.node_id_hashing_mode {
263 NodeIdHashingMode::Ignore => {
264 // Don't do anything.
266 NodeIdHashingMode::HashDefPath => {
267 hcx.definitions.node_to_hir_id(*self).hash_stable(hcx, hasher);
273 impl<'a> ToStableHashKey<StableHashingContext<'a>> for ast::NodeId {
274 type KeyType = (DefPathHash, hir::ItemLocalId);
277 fn to_stable_hash_key(&self,
278 hcx: &StableHashingContext<'a>)
279 -> (DefPathHash, hir::ItemLocalId) {
280 hcx.definitions.node_to_hir_id(*self).to_stable_hash_key(hcx)
284 impl<'a> HashStable<StableHashingContext<'a>> for Span {
285 /// Hashes a span in a stable way. We can't directly hash the span's `BytePos`
286 /// fields (that would be similar to hashing pointers, since those are just
287 /// offsets into the `SourceMap`). Instead, we hash the (file name, line, column)
288 /// triple, which stays the same even if the containing `SourceFile` has moved
289 /// within the `SourceMap`.
290 /// Also note that we are hashing byte offsets for the column, not unicode
291 /// codepoint offsets. For the purpose of the hash that's sufficient.
292 /// Also, hashing filenames is expensive so we avoid doing it twice when the
293 /// span starts and ends in the same file, which is almost always the case.
294 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
295 const TAG_VALID_SPAN: u8 = 0;
296 const TAG_INVALID_SPAN: u8 = 1;
297 const TAG_EXPANSION: u8 = 0;
298 const TAG_NO_EXPANSION: u8 = 1;
304 if *self == DUMMY_SP {
305 return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
308 // If this is not an empty or invalid span, we want to hash the last
309 // position that belongs to it, as opposed to hashing the first
311 let span = self.data();
312 let (file_lo, line_lo, col_lo) = match hcx.source_map()
313 .byte_pos_to_line_and_col(span.lo) {
316 return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
320 if !file_lo.contains(span.hi) {
321 return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
324 std_hash::Hash::hash(&TAG_VALID_SPAN, hasher);
325 // We truncate the stable ID hash and line and column numbers. The chances
326 // of causing a collision this way should be minimal.
327 std_hash::Hash::hash(&(file_lo.name_hash as u64), hasher);
329 let col = (col_lo.0 as u64) & 0xFF;
330 let line = ((line_lo as u64) & 0xFF_FF_FF) << 8;
331 let len = ((span.hi - span.lo).0 as u64) << 32;
332 let line_col_len = col | line | len;
333 std_hash::Hash::hash(&line_col_len, hasher);
335 if span.ctxt == SyntaxContext::root() {
336 TAG_NO_EXPANSION.hash_stable(hcx, hasher);
338 TAG_EXPANSION.hash_stable(hcx, hasher);
340 // Since the same expansion context is usually referenced many
341 // times, we cache a stable hash of it and hash that instead of
342 // recursing every time.
344 static CACHE: RefCell<FxHashMap<hygiene::ExpnId, u64>> = Default::default();
347 let sub_hash: u64 = CACHE.with(|cache| {
348 let expn_id = span.ctxt.outer_expn();
350 if let Some(&sub_hash) = cache.borrow().get(&expn_id) {
354 let mut hasher = StableHasher::new();
355 expn_id.expn_data().hash_stable(hcx, &mut hasher);
356 let sub_hash: Fingerprint = hasher.finish();
357 let sub_hash = sub_hash.to_smaller_hash();
358 cache.borrow_mut().insert(expn_id, sub_hash);
362 sub_hash.hash_stable(hcx, hasher);
367 impl<'a> HashStable<StableHashingContext<'a>> for DelimSpan {
368 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
369 self.open.hash_stable(hcx, hasher);
370 self.close.hash_stable(hcx, hasher);
374 pub fn hash_stable_trait_impls<'a>(
375 hcx: &mut StableHashingContext<'a>,
376 hasher: &mut StableHasher,
377 blanket_impls: &[DefId],
378 non_blanket_impls: &FxHashMap<fast_reject::SimplifiedType, Vec<DefId>>,
381 let mut blanket_impls: SmallVec<[_; 8]> = blanket_impls
383 .map(|&def_id| hcx.def_path_hash(def_id))
386 if blanket_impls.len() > 1 {
387 blanket_impls.sort_unstable();
390 blanket_impls.hash_stable(hcx, hasher);
394 let mut keys: SmallVec<[_; 8]> =
395 non_blanket_impls.keys()
396 .map(|k| (k, k.map_def(|d| hcx.def_path_hash(d))))
398 keys.sort_unstable_by(|&(_, ref k1), &(_, ref k2)| k1.cmp(k2));
399 keys.len().hash_stable(hcx, hasher);
400 for (key, ref stable_key) in keys {
401 stable_key.hash_stable(hcx, hasher);
402 let mut impls : SmallVec<[_; 8]> = non_blanket_impls[key]
404 .map(|&impl_id| hcx.def_path_hash(impl_id))
408 impls.sort_unstable();
411 impls.hash_stable(hcx, hasher);