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