]> git.lizzy.rs Git - rust.git/blob - src/librustc/ich/hcx.rs
Merge hir::ImplPolarity into ast::ImplPolarity.
[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 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};
20
21 use rustc_data_structures::stable_hasher::{
22     HashStable, StableHasher, ToStableHashKey,
23 };
24 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
25 use smallvec::SmallVec;
26
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()
30 }
31
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).
36 #[derive(Clone)]
37 pub struct StableHashingContext<'a> {
38     sess: &'a Session,
39     definitions: &'a Definitions,
40     cstore: &'a dyn CrateStore,
41     body_resolver: BodyResolver<'a>,
42     hash_spans: bool,
43     hash_bodies: bool,
44     node_id_hashing_mode: NodeIdHashingMode,
45
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>>,
50 }
51
52 #[derive(PartialEq, Eq, Clone, Copy)]
53 pub enum NodeIdHashingMode {
54     Ignore,
55     HashDefPath,
56 }
57
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);
63
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 {
68         self.0.body(id)
69     }
70 }
71
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.
76     #[inline]
77     pub fn new(sess: &'a Session,
78                krate: &'a hir::Crate,
79                definitions: &'a Definitions,
80                cstore: &'a dyn CrateStore)
81                -> Self {
82         let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans;
83
84         StableHashingContext {
85             sess,
86             body_resolver: BodyResolver(krate),
87             definitions,
88             cstore,
89             caching_source_map: None,
90             raw_source_map: sess.source_map(),
91             hash_spans: hash_spans_initial,
92             hash_bodies: true,
93             node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
94         }
95     }
96
97     #[inline]
98     pub fn sess(&self) -> &'a Session {
99         self.sess
100     }
101
102     #[inline]
103     pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
104                                                           hash_bodies: bool,
105                                                           f: F) {
106         let prev_hash_bodies = self.hash_bodies;
107         self.hash_bodies = hash_bodies;
108         f(self);
109         self.hash_bodies = prev_hash_bodies;
110     }
111
112     #[inline]
113     pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
114                                                      hash_spans: bool,
115                                                      f: F) {
116         let prev_hash_spans = self.hash_spans;
117         self.hash_spans = hash_spans;
118         f(self);
119         self.hash_spans = prev_hash_spans;
120     }
121
122     #[inline]
123     pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
124                                                            mode: NodeIdHashingMode,
125                                                            f: F) {
126         let prev = self.node_id_hashing_mode;
127         self.node_id_hashing_mode = mode;
128         f(self);
129         self.node_id_hashing_mode = prev;
130     }
131
132     #[inline]
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)
136         } else {
137             self.cstore.def_path_hash(def_id)
138         }
139     }
140
141     #[inline]
142     pub fn local_def_path_hash(&self, def_index: DefIndex) -> DefPathHash {
143         self.definitions.def_path_hash(def_index)
144     }
145
146     #[inline]
147     pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId {
148         self.definitions.node_to_hir_id(node_id)
149     }
150
151     #[inline]
152     pub fn hash_bodies(&self) -> bool {
153         self.hash_bodies
154     }
155
156     #[inline]
157     pub fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
158         match self.caching_source_map {
159             Some(ref mut cm) => {
160                 cm
161             }
162             ref mut none => {
163                 *none = Some(CachingSourceMapView::new(self.raw_source_map));
164                 none.as_mut().unwrap()
165             }
166         }
167     }
168
169     #[inline]
170     pub fn is_ignored_attr(&self, name: Symbol) -> bool {
171         thread_local! {
172             static IGNORED_ATTRIBUTES: FxHashSet<Symbol> = compute_ignored_attr_names();
173         }
174         IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name))
175     }
176
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;
180
181         f(self);
182
183         self.node_id_hashing_mode = prev_hash_node_ids;
184     }
185 }
186
187 /// Something that can provide a stable hashing context.
188 pub trait StableHashingContextProvider<'a> {
189     fn get_stable_hashing_context(&self) -> StableHashingContext<'a>;
190 }
191
192 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
193 for &'b T {
194     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
195         (**self).get_stable_hashing_context()
196     }
197 }
198
199 impl<'a, 'b, T: StableHashingContextProvider<'a>> StableHashingContextProvider<'a>
200 for &'b mut T {
201     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
202         (**self).get_stable_hashing_context()
203     }
204 }
205
206 impl StableHashingContextProvider<'tcx> for TyCtxt<'tcx> {
207     fn get_stable_hashing_context(&self) -> StableHashingContext<'tcx> {
208         (*self).create_stable_hashing_context()
209     }
210 }
211
212 impl<'a> StableHashingContextProvider<'a> for StableHashingContext<'a> {
213     fn get_stable_hashing_context(&self) -> StableHashingContext<'a> {
214         self.clone()
215     }
216 }
217
218 impl<'a> crate::dep_graph::DepGraphSafe for StableHashingContext<'a> {}
219
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);
224         }
225     }
226 }
227
228 impl<'a> HashStable<StableHashingContext<'a>> for hir::HirId {
229     #[inline]
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.
234             }
235             NodeIdHashingMode::HashDefPath => {
236                 let hir::HirId {
237                     owner,
238                     local_id,
239                 } = *self;
240
241                 hcx.local_def_path_hash(owner).hash_stable(hcx, hasher);
242                 local_id.hash_stable(hcx, hasher);
243             }
244         }
245     }
246 }
247
248 impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::HirId {
249     type KeyType = (DefPathHash, hir::ItemLocalId);
250
251     #[inline]
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)
257     }
258 }
259
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.
265             }
266             NodeIdHashingMode::HashDefPath => {
267                 hcx.definitions.node_to_hir_id(*self).hash_stable(hcx, hasher);
268             }
269         }
270     }
271 }
272
273 impl<'a> ToStableHashKey<StableHashingContext<'a>> for ast::NodeId {
274     type KeyType = (DefPathHash, hir::ItemLocalId);
275
276     #[inline]
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)
281     }
282 }
283
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;
299
300         if !hcx.hash_spans {
301             return
302         }
303
304         if *self == DUMMY_SP {
305             return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
306         }
307
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
310         // position past it.
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) {
314             Some(pos) => pos,
315             None => {
316                 return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
317             }
318         };
319
320         if !file_lo.contains(span.hi) {
321             return std_hash::Hash::hash(&TAG_INVALID_SPAN, hasher);
322         }
323
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);
328
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);
334
335         if span.ctxt == SyntaxContext::root() {
336             TAG_NO_EXPANSION.hash_stable(hcx, hasher);
337         } else {
338             TAG_EXPANSION.hash_stable(hcx, hasher);
339
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.
343             thread_local! {
344                 static CACHE: RefCell<FxHashMap<hygiene::ExpnId, u64>> = Default::default();
345             }
346
347             let sub_hash: u64 = CACHE.with(|cache| {
348                 let expn_id = span.ctxt.outer_expn();
349
350                 if let Some(&sub_hash) = cache.borrow().get(&expn_id) {
351                     return sub_hash;
352                 }
353
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);
359                 sub_hash
360             });
361
362             sub_hash.hash_stable(hcx, hasher);
363         }
364     }
365 }
366
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);
371     }
372 }
373
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>>,
379 ) {
380     {
381         let mut blanket_impls: SmallVec<[_; 8]> = blanket_impls
382             .iter()
383             .map(|&def_id| hcx.def_path_hash(def_id))
384             .collect();
385
386         if blanket_impls.len() > 1 {
387             blanket_impls.sort_unstable();
388         }
389
390         blanket_impls.hash_stable(hcx, hasher);
391     }
392
393     {
394         let mut keys: SmallVec<[_; 8]> =
395             non_blanket_impls.keys()
396                              .map(|k| (k, k.map_def(|d| hcx.def_path_hash(d))))
397                              .collect();
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]
403                 .iter()
404                 .map(|&impl_id| hcx.def_path_hash(impl_id))
405                 .collect();
406
407             if impls.len() > 1 {
408                 impls.sort_unstable();
409             }
410
411             impls.hash_stable(hcx, hasher);
412         }
413     }
414 }