]> git.lizzy.rs Git - rust.git/blob - src/librustc/ich/hcx.rs
39f6b0d434440abfa7429406bd8f9bad07108979
[rust.git] / src / librustc / ich / hcx.rs
1 use crate::hir;
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;
9
10 use std::cmp::Ord;
11 use std::hash as std_hash;
12 use std::cell::RefCell;
13
14 use syntax::ast;
15
16 use syntax::source_map::SourceMap;
17 use syntax::ext::hygiene::SyntaxContext;
18 use syntax::symbol::Symbol;
19 use syntax::tokenstream::DelimSpan;
20 use syntax_pos::{Span, DUMMY_SP};
21 use syntax_pos::hygiene;
22
23 use rustc_data_structures::stable_hasher::{HashStable,
24                                            StableHasher, StableHasherResult,
25                                            ToStableHashKey};
26 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
27 use smallvec::SmallVec;
28
29 fn compute_ignored_attr_names() -> FxHashSet<Symbol> {
30     debug_assert!(ich::IGNORED_ATTRIBUTES.len() > 0);
31     ich::IGNORED_ATTRIBUTES.iter().map(|&s| s).collect()
32 }
33
34 /// This is the context state available during incr. comp. hashing. It contains
35 /// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
36 /// a reference to the TyCtxt) and it holds a few caches for speeding up various
37 /// things (e.g., each DefId/DefPath is only hashed once).
38 #[derive(Clone)]
39 pub struct StableHashingContext<'a> {
40     sess: &'a Session,
41     definitions: &'a Definitions,
42     cstore: &'a dyn CrateStore,
43     body_resolver: BodyResolver<'a>,
44     hash_spans: bool,
45     hash_bodies: bool,
46     node_id_hashing_mode: NodeIdHashingMode,
47
48     // Very often, we are hashing something that does not need the
49     // CachingSourceMapView, so we initialize it lazily.
50     raw_source_map: &'a SourceMap,
51     caching_source_map: Option<CachingSourceMapView<'a>>,
52 }
53
54 #[derive(PartialEq, Eq, Clone, Copy)]
55 pub enum NodeIdHashingMode {
56     Ignore,
57     HashDefPath,
58 }
59
60 /// The BodyResolver allows to map a BodyId to the corresponding hir::Body.
61 /// We could also just store a plain reference to the hir::Crate but we want
62 /// to avoid that the crate is used to get untracked access to all of the HIR.
63 #[derive(Clone, Copy)]
64 struct BodyResolver<'tcx>(&'tcx hir::Crate);
65
66 impl<'tcx> BodyResolver<'tcx> {
67     // Return a reference to the hir::Body with the given BodyId.
68     // DOES NOT DO ANY TRACKING, use carefully.
69     fn body(self, id: hir::BodyId) -> &'tcx hir::Body {
70         self.0.body(id)
71     }
72 }
73
74 impl<'a> StableHashingContext<'a> {
75     // The `krate` here is only used for mapping BodyIds to Bodies.
76     // Don't use it for anything else or you'll run the risk of
77     // leaking data out of the tracking system.
78     #[inline]
79     pub fn new(sess: &'a Session,
80                krate: &'a hir::Crate,
81                definitions: &'a Definitions,
82                cstore: &'a dyn CrateStore)
83                -> Self {
84         let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans;
85
86         StableHashingContext {
87             sess,
88             body_resolver: BodyResolver(krate),
89             definitions,
90             cstore,
91             caching_source_map: None,
92             raw_source_map: sess.source_map(),
93             hash_spans: hash_spans_initial,
94             hash_bodies: true,
95             node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
96         }
97     }
98
99     #[inline]
100     pub fn sess(&self) -> &'a Session {
101         self.sess
102     }
103
104     #[inline]
105     pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
106                                                           hash_bodies: bool,
107                                                           f: F) {
108         let prev_hash_bodies = self.hash_bodies;
109         self.hash_bodies = hash_bodies;
110         f(self);
111         self.hash_bodies = prev_hash_bodies;
112     }
113
114     #[inline]
115     pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
116                                                      hash_spans: bool,
117                                                      f: F) {
118         let prev_hash_spans = self.hash_spans;
119         self.hash_spans = hash_spans;
120         f(self);
121         self.hash_spans = prev_hash_spans;
122     }
123
124     #[inline]
125     pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
126                                                            mode: NodeIdHashingMode,
127                                                            f: F) {
128         let prev = self.node_id_hashing_mode;
129         self.node_id_hashing_mode = mode;
130         f(self);
131         self.node_id_hashing_mode = prev;
132     }
133
134     #[inline]
135     pub fn def_path_hash(&self, def_id: DefId) -> DefPathHash {
136         if def_id.is_local() {
137             self.definitions.def_path_hash(def_id.index)
138         } else {
139             self.cstore.def_path_hash(def_id)
140         }
141     }
142
143     #[inline]
144     pub fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash {
145         self.definitions.def_path_hash(def_index)
146     }
147
148     #[inline]
149     pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId {
150         self.definitions.node_to_hir_id(node_id)
151     }
152
153     #[inline]
154     pub fn hash_bodies(&self) -> bool {
155         self.hash_bodies
156     }
157
158     #[inline]
159     pub fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
160         match self.caching_source_map {
161             Some(ref mut cm) => {
162                 cm
163             }
164             ref mut none => {
165                 *none = Some(CachingSourceMapView::new(self.raw_source_map));
166                 none.as_mut().unwrap()
167             }
168         }
169     }
170
171     #[inline]
172     pub fn is_ignored_attr(&self, name: Symbol) -> bool {
173         thread_local! {
174             static IGNORED_ATTRIBUTES: FxHashSet<Symbol> = compute_ignored_attr_names();
175         }
176         IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name))
177     }
178
179     pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F) {
180         let prev_hash_node_ids = self.node_id_hashing_mode;
181         self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
182
183         f(self);
184
185         self.node_id_hashing_mode = prev_hash_node_ids;
186     }
187 }
188
189 /// Something that can provide a stable hashing context.
190 pub trait StableHashingContextProvider<'a> {
191     fn get_stable_hashing_context(&self) -> StableHashingContext<'a>;
192 }
193
194 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
195 for &'b T {
196     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
197         (**self).get_stable_hashing_context()
198     }
199 }
200
201 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
202 for &'b mut T {
203     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
204         (**self).get_stable_hashing_context()
205     }
206 }
207
208 impl StableHashingContextProvider<'tcx> for TyCtxt<'tcx> {
209     fn get_stable_hashing_context(&self) -> StableHashingContext<'tcx> {
210         (*self).create_stable_hashing_context()
211     }
212 }
213
214 impl<'a> StableHashingContextProvider<'a> for StableHashingContext<'a> {
215     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
216         self.clone()
217     }
218 }
219
220 impl<'a> crate::dep_graph::DepGraphSafe for StableHashingContext<'a> {
221 }
222
223
224 impl<'a> HashStable<StableHashingContext<'a>> for hir::BodyId {
225     fn hash_stable<W: StableHasherResult>(&self,
226                                           hcx: &mut StableHashingContext<'a>,
227                                           hasher: &mut StableHasher<W>) {
228         if hcx.hash_bodies() {
229             hcx.body_resolver.body(*self).hash_stable(hcx, hasher);
230         }
231     }
232 }
233
234 impl<'a> HashStable<StableHashingContext<'a>> for hir::HirId {
235     #[inline]
236     fn hash_stable<W: StableHasherResult>(&self,
237                                           hcx: &mut StableHashingContext<'a>,
238                                           hasher: &mut StableHasher<W>) {
239         match hcx.node_id_hashing_mode {
240             NodeIdHashingMode::Ignore => {
241                 // Don't do anything.
242             }
243             NodeIdHashingMode::HashDefPath => {
244                 let hir::HirId {
245                     owner,
246                     local_id,
247                 } = *self;
248
249                 hcx.local_def_path_hash(owner).hash_stable(hcx, hasher);
250                 local_id.hash_stable(hcx, hasher);
251             }
252         }
253     }
254 }
255
256 impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::HirId {
257     type KeyType = (DefPathHash, hir::ItemLocalId);
258
259     #[inline]
260     fn to_stable_hash_key(&self,
261                           hcx: &StableHashingContext<'a>)
262                           -> (DefPathHash, hir::ItemLocalId) {
263         let def_path_hash = hcx.local_def_path_hash(self.owner);
264         (def_path_hash, self.local_id)
265     }
266 }
267
268 impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId {
269     fn hash_stable<W: StableHasherResult>(&self,
270                                           hcx: &mut StableHashingContext<'a>,
271                                           hasher: &mut StableHasher<W>) {
272         match hcx.node_id_hashing_mode {
273             NodeIdHashingMode::Ignore => {
274                 // Don't do anything.
275             }
276             NodeIdHashingMode::HashDefPath => {
277                 hcx.definitions.node_to_hir_id(*self).hash_stable(hcx, hasher);
278             }
279         }
280     }
281 }
282
283 impl<'a> ToStableHashKey<StableHashingContext<'a>> for ast::NodeId {
284     type KeyType = (DefPathHash, hir::ItemLocalId);
285
286     #[inline]
287     fn to_stable_hash_key(&self,
288                           hcx: &StableHashingContext<'a>)
289                           -> (DefPathHash, hir::ItemLocalId) {
290         hcx.definitions.node_to_hir_id(*self).to_stable_hash_key(hcx)
291     }
292 }
293
294 impl<'a> HashStable<StableHashingContext<'a>> for Span {
295
296     // Hash a span in a stable way. We can't directly hash the span's BytePos
297     // fields (that would be similar to hashing pointers, since those are just
298     // offsets into the SourceMap). Instead, we hash the (file name, line, column)
299     // triple, which stays the same even if the containing SourceFile has moved
300     // within the SourceMap.
301     // Also note that we are hashing byte offsets for the column, not unicode
302     // codepoint offsets. For the purpose of the hash that's sufficient.
303     // Also, hashing filenames is expensive so we avoid doing it twice when the
304     // span starts and ends in the same file, which is almost always the case.
305     fn hash_stable<W: StableHasherResult>(&self,
306                                           hcx: &mut StableHashingContext<'a>,
307                                           hasher: &mut StableHasher<W>) {
308         const TAG_VALID_SPAN: u8 = 0;
309         const TAG_INVALID_SPAN: u8 = 1;
310         const TAG_EXPANSION: u8 = 0;
311         const TAG_NO_EXPANSION: u8 = 1;
312
313         if !hcx.hash_spans {
314             return
315         }
316
317         if *self == DUMMY_SP {
318             return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
319         }
320
321         // If this is not an empty or invalid span, we want to hash the last
322         // position that belongs to it, as opposed to hashing the first
323         // position past it.
324         let span = self.data();
325
326         if span.hi < span.lo {
327             return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
328         }
329
330         let (file_lo, line_lo, col_lo) = match hcx.source_map()
331                                                   .byte_pos_to_line_and_col(span.lo) {
332             Some(pos) => pos,
333             None => {
334                 return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
335             }
336         };
337
338         if !file_lo.contains(span.hi) {
339             return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
340         }
341
342         std_hash::Hash::hash(&TAG_VALID_SPAN, hasher);
343         // We truncate the stable_id hash and line and col numbers. The chances
344         // of causing a collision this way should be minimal.
345         std_hash::Hash::hash(&(file_lo.name_hash as u64), hasher);
346
347         let col = (col_lo.0 as u64) & 0xFF;
348         let line = ((line_lo as u64) & 0xFF_FF_FF) << 8;
349         let len = ((span.hi - span.lo).0 as u64) << 32;
350         let line_col_len = col | line | len;
351         std_hash::Hash::hash(&line_col_len, hasher);
352
353         if span.ctxt == SyntaxContext::root() {
354             TAG_NO_EXPANSION.hash_stable(hcx, hasher);
355         } else {
356             TAG_EXPANSION.hash_stable(hcx, hasher);
357
358             // Since the same expansion context is usually referenced many
359             // times, we cache a stable hash of it and hash that instead of
360             // recursing every time.
361             thread_local! {
362                 static CACHE: RefCell<FxHashMap<hygiene::ExpnId, u64>> = Default::default();
363             }
364
365             let sub_hash: u64 = CACHE.with(|cache| {
366                 let expn_id = span.ctxt.outer_expn();
367
368                 if let Some(&sub_hash) = cache.borrow().get(&expn_id) {
369                     return sub_hash;
370                 }
371
372                 let mut hasher = StableHasher::new();
373                 expn_id.expn_info().hash_stable(hcx, &mut hasher);
374                 let sub_hash: Fingerprint = hasher.finish();
375                 let sub_hash = sub_hash.to_smaller_hash();
376                 cache.borrow_mut().insert(expn_id, sub_hash);
377                 sub_hash
378             });
379
380             sub_hash.hash_stable(hcx, hasher);
381         }
382     }
383 }
384
385 impl<'a> HashStable<StableHashingContext<'a>> for DelimSpan {
386     fn hash_stable<W: StableHasherResult>(
387         &self,
388         hcx: &mut StableHashingContext<'a>,
389         hasher: &mut StableHasher<W>,
390     ) {
391         self.open.hash_stable(hcx, hasher);
392         self.close.hash_stable(hcx, hasher);
393     }
394 }
395
396 pub fn hash_stable_trait_impls<'a, W>(
397     hcx: &mut StableHashingContext<'a>,
398     hasher: &mut StableHasher<W>,
399     blanket_impls: &[DefId],
400     non_blanket_impls: &FxHashMap<fast_reject::SimplifiedType, Vec<DefId>>,
401 ) where
402     W: StableHasherResult,
403 {
404     {
405         let mut blanket_impls: SmallVec<[_; 8]> = blanket_impls
406             .iter()
407             .map(|&def_id| hcx.def_path_hash(def_id))
408             .collect();
409
410         if blanket_impls.len() > 1 {
411             blanket_impls.sort_unstable();
412         }
413
414         blanket_impls.hash_stable(hcx, hasher);
415     }
416
417     {
418         let mut keys: SmallVec<[_; 8]> =
419             non_blanket_impls.keys()
420                              .map(|k| (k, k.map_def(|d| hcx.def_path_hash(d))))
421                              .collect();
422         keys.sort_unstable_by(|&(_, ref k1), &(_, ref k2)| k1.cmp(k2));
423         keys.len().hash_stable(hcx, hasher);
424         for (key, ref stable_key) in keys {
425             stable_key.hash_stable(hcx, hasher);
426             let mut impls : SmallVec<[_; 8]> = non_blanket_impls[key]
427                 .iter()
428                 .map(|&impl_id| hcx.def_path_hash(impl_id))
429                 .collect();
430
431             if impls.len() > 1 {
432                 impls.sort_unstable();
433             }
434
435             impls.hash_stable(hcx, hasher);
436         }
437     }
438 }