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.
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.
12 use hir::def_id::DefId;
13 use hir::map::DefPathHash;
14 use ich::{self, CachingCodemapView};
15 use session::config::DebugInfoLevel::NoDebugInfo;
17 use util::nodemap::{NodeMap, ItemLocalMap};
19 use std::hash as std_hash;
20 use std::collections::{HashMap, HashSet, BTreeMap};
24 use syntax::ext::hygiene::SyntaxContext;
25 use syntax::symbol::Symbol;
28 use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
30 use rustc_data_structures::accumulate_vec::AccumulateVec;
32 /// This is the context state available during incr. comp. hashing. It contains
33 /// enough information to transform DefIds and HirIds into stable DefPaths (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 pub struct StableHashingContext<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
37 tcx: ty::TyCtxt<'a, 'gcx, 'tcx>,
38 codemap: CachingCodemapView<'gcx>,
41 overflow_checks_enabled: bool,
42 node_id_hashing_mode: NodeIdHashingMode,
43 // A sorted array of symbol keys for fast lookup.
44 ignored_attr_names: Vec<Symbol>,
47 #[derive(PartialEq, Eq, Clone, Copy)]
48 pub enum NodeIdHashingMode {
54 impl<'a, 'gcx, 'tcx> StableHashingContext<'a, 'gcx, 'tcx> {
56 pub fn new(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>) -> Self {
57 let hash_spans_initial = tcx.sess.opts.debuginfo != NoDebugInfo;
58 let check_overflow_initial = tcx.sess.overflow_checks();
60 let mut ignored_attr_names: Vec<_> = ich::IGNORED_ATTRIBUTES
62 .map(|&s| Symbol::intern(s))
65 ignored_attr_names.sort();
67 StableHashingContext {
69 codemap: CachingCodemapView::new(tcx),
70 hash_spans: hash_spans_initial,
72 overflow_checks_enabled: check_overflow_initial,
73 node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
78 pub fn force_span_hashing(mut self) -> Self {
79 self.hash_spans = true;
84 pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
87 let prev_hash_bodies = self.hash_bodies;
88 self.hash_bodies = hash_bodies;
90 self.hash_bodies = prev_hash_bodies;
94 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
97 let prev_hash_spans = self.hash_spans;
98 self.hash_spans = hash_spans;
100 self.hash_spans = prev_hash_spans;
104 pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
105 mode: NodeIdHashingMode,
107 let prev = self.node_id_hashing_mode;
108 self.node_id_hashing_mode = mode;
110 self.node_id_hashing_mode = prev;
114 pub fn tcx(&self) -> ty::TyCtxt<'a, 'gcx, 'tcx> {
119 pub fn def_path_hash(&mut self, def_id: DefId) -> DefPathHash {
120 self.tcx.def_path_hash(def_id)
124 pub fn hash_spans(&self) -> bool {
129 pub fn hash_bodies(&self) -> bool {
134 pub fn codemap(&mut self) -> &mut CachingCodemapView<'gcx> {
139 pub fn is_ignored_attr(&self, name: Symbol) -> bool {
140 self.ignored_attr_names.binary_search(&name).is_ok()
143 pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
144 item_attrs: &[ast::Attribute],
146 let prev_overflow_checks = self.overflow_checks_enabled;
147 if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
148 self.overflow_checks_enabled = true;
150 let prev_hash_node_ids = self.node_id_hashing_mode;
151 self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
155 self.node_id_hashing_mode = prev_hash_node_ids;
156 self.overflow_checks_enabled = prev_overflow_checks;
160 pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
165 hir::BiMul => self.overflow_checks_enabled,
187 pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
192 hir::UnNeg => self.overflow_checks_enabled,
198 impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for ast::NodeId {
199 fn hash_stable<W: StableHasherResult>(&self,
200 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
201 hasher: &mut StableHasher<W>) {
202 match hcx.node_id_hashing_mode {
203 NodeIdHashingMode::Ignore => {
204 // Most NodeIds in the HIR can be ignored, but if there is a
205 // corresponding entry in the `trait_map` we need to hash that.
206 // Make sure we don't ignore too much by checking that there is
207 // no entry in a debug_assert!().
208 let hir_id = hcx.tcx.hir.node_to_hir_id(*self);
209 debug_assert!(hcx.tcx.in_scope_traits(hir_id).is_none());
211 NodeIdHashingMode::HashDefPath => {
212 hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
214 NodeIdHashingMode::HashTraitsInScope => {
215 let hir_id = hcx.tcx.hir.node_to_hir_id(*self);
216 if let Some(traits) = hcx.tcx.in_scope_traits(hir_id) {
217 // The ordering of the candidates is not fixed. So we hash
218 // the def-ids and then sort them and hash the collection.
219 let mut candidates: AccumulateVec<[_; 8]> =
221 .map(|&hir::TraitCandidate { def_id, import_id: _ }| {
222 hcx.def_path_hash(def_id)
225 if traits.len() > 1 {
228 candidates.hash_stable(hcx, hasher);
235 impl<'a, 'gcx, 'tcx> HashStable<StableHashingContext<'a, 'gcx, 'tcx>> for Span {
237 // Hash a span in a stable way. We can't directly hash the span's BytePos
238 // fields (that would be similar to hashing pointers, since those are just
239 // offsets into the CodeMap). Instead, we hash the (file name, line, column)
240 // triple, which stays the same even if the containing FileMap has moved
241 // within the CodeMap.
242 // Also note that we are hashing byte offsets for the column, not unicode
243 // codepoint offsets. For the purpose of the hash that's sufficient.
244 // Also, hashing filenames is expensive so we avoid doing it twice when the
245 // span starts and ends in the same file, which is almost always the case.
246 fn hash_stable<W: StableHasherResult>(&self,
247 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
248 hasher: &mut StableHasher<W>) {
255 // If this is not an empty or invalid span, we want to hash the last
256 // position that belongs to it, as opposed to hashing the first
258 let span_hi = if self.hi() > self.lo() {
259 // We might end up in the middle of a multibyte character here,
260 // but that's OK, since we are not trying to decode anything at
262 self.hi() - ::syntax_pos::BytePos(1)
268 let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo());
269 let loc1 = loc1.as_ref()
270 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
271 .unwrap_or(("???", 0, 0));
273 let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi);
274 let loc2 = loc2.as_ref()
275 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
276 .unwrap_or(("???", 0, 0));
278 if loc1.0 == loc2.0 {
279 std_hash::Hash::hash(&0u8, hasher);
281 std_hash::Hash::hash(loc1.0, hasher);
282 std_hash::Hash::hash(&loc1.1, hasher);
283 std_hash::Hash::hash(&loc1.2, hasher);
285 // Do not hash the file name twice
286 std_hash::Hash::hash(&loc2.1, hasher);
287 std_hash::Hash::hash(&loc2.2, hasher);
289 std_hash::Hash::hash(&1u8, hasher);
291 std_hash::Hash::hash(loc1.0, hasher);
292 std_hash::Hash::hash(&loc1.1, hasher);
293 std_hash::Hash::hash(&loc1.2, hasher);
295 std_hash::Hash::hash(loc2.0, hasher);
296 std_hash::Hash::hash(&loc2.1, hasher);
297 std_hash::Hash::hash(&loc2.2, hasher);
301 if self.ctxt() == SyntaxContext::empty() {
302 0u8.hash_stable(hcx, hasher);
304 1u8.hash_stable(hcx, hasher);
305 self.source_callsite().hash_stable(hcx, hasher);
310 pub fn hash_stable_hashmap<'a, 'gcx, 'tcx, K, V, R, SK, F, W>(
311 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
312 hasher: &mut StableHasher<W>,
313 map: &HashMap<K, V, R>,
314 extract_stable_key: F)
315 where K: Eq + std_hash::Hash,
316 V: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>,
317 R: std_hash::BuildHasher,
318 SK: HashStable<StableHashingContext<'a, 'gcx, 'tcx>> + Ord + Clone,
319 F: Fn(&mut StableHashingContext<'a, 'gcx, 'tcx>, &K) -> SK,
320 W: StableHasherResult,
322 let mut keys: Vec<_> = map.keys()
323 .map(|k| (extract_stable_key(hcx, k), k))
325 keys.sort_unstable_by_key(|&(ref stable_key, _)| stable_key.clone());
326 keys.len().hash_stable(hcx, hasher);
327 for (stable_key, key) in keys {
328 stable_key.hash_stable(hcx, hasher);
329 map[key].hash_stable(hcx, hasher);
333 pub fn hash_stable_hashset<'a, 'tcx, 'gcx, K, R, SK, F, W>(
334 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
335 hasher: &mut StableHasher<W>,
337 extract_stable_key: F)
338 where K: Eq + std_hash::Hash,
339 R: std_hash::BuildHasher,
340 SK: HashStable<StableHashingContext<'a, 'gcx, 'tcx>> + Ord + Clone,
341 F: Fn(&mut StableHashingContext<'a, 'gcx, 'tcx>, &K) -> SK,
342 W: StableHasherResult,
344 let mut keys: Vec<_> = set.iter()
345 .map(|k| extract_stable_key(hcx, k))
347 keys.sort_unstable();
348 keys.hash_stable(hcx, hasher);
351 pub fn hash_stable_nodemap<'a, 'tcx, 'gcx, V, W>(
352 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
353 hasher: &mut StableHasher<W>,
355 where V: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>,
356 W: StableHasherResult,
358 hash_stable_hashmap(hcx, hasher, map, |hcx, node_id| {
359 hcx.tcx.hir.definitions().node_to_hir_id(*node_id).local_id
363 pub fn hash_stable_itemlocalmap<'a, 'tcx, 'gcx, V, W>(
364 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
365 hasher: &mut StableHasher<W>,
366 map: &ItemLocalMap<V>)
367 where V: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>,
368 W: StableHasherResult,
370 hash_stable_hashmap(hcx, hasher, map, |_, local_id| {
376 pub fn hash_stable_btreemap<'a, 'tcx, 'gcx, K, V, SK, F, W>(
377 hcx: &mut StableHashingContext<'a, 'gcx, 'tcx>,
378 hasher: &mut StableHasher<W>,
379 map: &BTreeMap<K, V>,
380 extract_stable_key: F)
382 V: HashStable<StableHashingContext<'a, 'gcx, 'tcx>>,
383 SK: HashStable<StableHashingContext<'a, 'gcx, 'tcx>> + Ord + Clone,
384 F: Fn(&mut StableHashingContext<'a, 'gcx, 'tcx>, &K) -> SK,
385 W: StableHasherResult,
387 let mut keys: Vec<_> = map.keys()
388 .map(|k| (extract_stable_key(hcx, k), k))
390 keys.sort_unstable_by_key(|&(ref stable_key, _)| stable_key.clone());
391 keys.len().hash_stable(hcx, hasher);
392 for (stable_key, key) in keys {
393 stable_key.hash_stable(hcx, hasher);
394 map[key].hash_stable(hcx, hasher);