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 ich::{self, CachingCodemapView};
14 use session::config::DebugInfoLevel::NoDebugInfo;
16 use util::nodemap::NodeMap;
18 use std::hash as std_hash;
19 use std::collections::{HashMap, HashSet};
23 use syntax::ext::hygiene::SyntaxContext;
24 use syntax::symbol::Symbol;
27 use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
29 use rustc_data_structures::accumulate_vec::AccumulateVec;
31 /// This is the context state available during incr. comp. hashing. It contains
32 /// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
33 /// a reference to the TyCtxt) and it holds a few caches for speeding up various
34 /// things (e.g. each DefId/DefPath is only hashed once).
35 pub struct StableHashingContext<'a, 'tcx: 'a> {
36 tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
37 codemap: CachingCodemapView<'tcx>,
40 overflow_checks_enabled: bool,
41 node_id_hashing_mode: NodeIdHashingMode,
42 // A sorted array of symbol keys for fast lookup.
43 ignored_attr_names: Vec<Symbol>,
46 #[derive(PartialEq, Eq, Clone, Copy)]
47 pub enum NodeIdHashingMode {
53 impl<'a, 'tcx: 'a> StableHashingContext<'a, 'tcx> {
55 pub fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Self {
56 let hash_spans_initial = tcx.sess.opts.debuginfo != NoDebugInfo;
57 let check_overflow_initial = tcx.sess.overflow_checks();
59 let mut ignored_attr_names: Vec<_> = ich::IGNORED_ATTRIBUTES
61 .map(|&s| Symbol::intern(s))
64 ignored_attr_names.sort();
66 StableHashingContext {
68 codemap: CachingCodemapView::new(tcx),
69 hash_spans: hash_spans_initial,
71 overflow_checks_enabled: check_overflow_initial,
72 node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
73 ignored_attr_names: ignored_attr_names,
78 pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
81 let prev_hash_bodies = self.hash_bodies;
82 self.hash_bodies = hash_bodies;
84 self.hash_bodies = prev_hash_bodies;
88 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
91 let prev_hash_spans = self.hash_spans;
92 self.hash_spans = hash_spans;
94 self.hash_spans = prev_hash_spans;
98 pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
99 mode: NodeIdHashingMode,
101 let prev = self.node_id_hashing_mode;
102 self.node_id_hashing_mode = mode;
104 self.node_id_hashing_mode = prev;
108 pub fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
113 pub fn def_path_hash(&mut self, def_id: DefId) -> u64 {
114 self.tcx.def_path_hash(def_id)
118 pub fn hash_spans(&self) -> bool {
123 pub fn hash_bodies(&self) -> bool {
128 pub fn codemap(&mut self) -> &mut CachingCodemapView<'tcx> {
133 pub fn is_ignored_attr(&self, name: Symbol) -> bool {
134 self.ignored_attr_names.binary_search(&name).is_ok()
137 pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
138 item_attrs: &[ast::Attribute],
140 let prev_overflow_checks = self.overflow_checks_enabled;
141 if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
142 self.overflow_checks_enabled = true;
144 let prev_hash_node_ids = self.node_id_hashing_mode;
145 self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
149 self.node_id_hashing_mode = prev_hash_node_ids;
150 self.overflow_checks_enabled = prev_overflow_checks;
154 pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
159 hir::BiMul => self.overflow_checks_enabled,
181 pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
186 hir::UnNeg => self.overflow_checks_enabled,
192 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ast::NodeId {
193 fn hash_stable<W: StableHasherResult>(&self,
194 hcx: &mut StableHashingContext<'a, 'tcx>,
195 hasher: &mut StableHasher<W>) {
196 match hcx.node_id_hashing_mode {
197 NodeIdHashingMode::Ignore => {
198 // Most NodeIds in the HIR can be ignored, but if there is a
199 // corresponding entry in the `trait_map` we need to hash that.
200 // Make sure we don't ignore too much by checking that there is
201 // no entry in a debug_assert!().
202 debug_assert!(hcx.tcx.trait_map.get(self).is_none());
204 NodeIdHashingMode::HashDefPath => {
205 hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
207 NodeIdHashingMode::HashTraitsInScope => {
208 if let Some(traits) = hcx.tcx.trait_map.get(self) {
209 // The ordering of the candidates is not fixed. So we hash
210 // the def-ids and then sort them and hash the collection.
211 let mut candidates: AccumulateVec<[_; 8]> =
213 .map(|&hir::TraitCandidate { def_id, import_id: _ }| {
214 hcx.def_path_hash(def_id)
217 if traits.len() > 1 {
220 candidates.hash_stable(hcx, hasher);
227 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for Span {
229 // Hash a span in a stable way. We can't directly hash the span's BytePos
230 // fields (that would be similar to hashing pointers, since those are just
231 // offsets into the CodeMap). Instead, we hash the (file name, line, column)
232 // triple, which stays the same even if the containing FileMap has moved
233 // within the CodeMap.
234 // Also note that we are hashing byte offsets for the column, not unicode
235 // codepoint offsets. For the purpose of the hash that's sufficient.
236 // Also, hashing filenames is expensive so we avoid doing it twice when the
237 // span starts and ends in the same file, which is almost always the case.
238 fn hash_stable<W: StableHasherResult>(&self,
239 hcx: &mut StableHashingContext<'a, 'tcx>,
240 hasher: &mut StableHasher<W>) {
247 // If this is not an empty or invalid span, we want to hash the last
248 // position that belongs to it, as opposed to hashing the first
250 let span_hi = if self.hi > self.lo {
251 // We might end up in the middle of a multibyte character here,
252 // but that's OK, since we are not trying to decode anything at
254 self.hi - ::syntax_pos::BytePos(1)
260 let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo);
261 let loc1 = loc1.as_ref()
262 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
263 .unwrap_or(("???", 0, 0));
265 let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi);
266 let loc2 = loc2.as_ref()
267 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
268 .unwrap_or(("???", 0, 0));
270 if loc1.0 == loc2.0 {
271 std_hash::Hash::hash(&0u8, hasher);
273 std_hash::Hash::hash(loc1.0, hasher);
274 std_hash::Hash::hash(&loc1.1, hasher);
275 std_hash::Hash::hash(&loc1.2, hasher);
277 // Do not hash the file name twice
278 std_hash::Hash::hash(&loc2.1, hasher);
279 std_hash::Hash::hash(&loc2.2, hasher);
281 std_hash::Hash::hash(&1u8, hasher);
283 std_hash::Hash::hash(loc1.0, hasher);
284 std_hash::Hash::hash(&loc1.1, hasher);
285 std_hash::Hash::hash(&loc1.2, hasher);
287 std_hash::Hash::hash(loc2.0, hasher);
288 std_hash::Hash::hash(&loc2.1, hasher);
289 std_hash::Hash::hash(&loc2.2, hasher);
293 if self.ctxt == SyntaxContext::empty() {
294 0u8.hash_stable(hcx, hasher);
296 1u8.hash_stable(hcx, hasher);
297 self.source_callsite().hash_stable(hcx, hasher);
302 pub fn hash_stable_hashmap<'a, 'tcx, K, V, R, SK, F, W>(hcx: &mut StableHashingContext<'a, 'tcx>,
303 hasher: &mut StableHasher<W>,
304 map: &HashMap<K, V, R>,
305 extract_stable_key: F)
306 where K: Eq + std_hash::Hash,
307 V: HashStable<StableHashingContext<'a, 'tcx>>,
308 R: std_hash::BuildHasher,
309 SK: HashStable<StableHashingContext<'a, 'tcx>> + Ord + Clone,
310 F: Fn(&mut StableHashingContext<'a, 'tcx>, &K) -> SK,
311 W: StableHasherResult,
313 let mut keys: Vec<_> = map.keys()
314 .map(|k| (extract_stable_key(hcx, k), k))
316 keys.sort_unstable_by_key(|&(ref stable_key, _)| stable_key.clone());
317 keys.len().hash_stable(hcx, hasher);
318 for (stable_key, key) in keys {
319 stable_key.hash_stable(hcx, hasher);
320 map[key].hash_stable(hcx, hasher);
324 pub fn hash_stable_hashset<'a, 'tcx, K, R, SK, F, W>(hcx: &mut StableHashingContext<'a, 'tcx>,
325 hasher: &mut StableHasher<W>,
327 extract_stable_key: F)
328 where K: Eq + std_hash::Hash,
329 R: std_hash::BuildHasher,
330 SK: HashStable<StableHashingContext<'a, 'tcx>> + Ord + Clone,
331 F: Fn(&mut StableHashingContext<'a, 'tcx>, &K) -> SK,
332 W: StableHasherResult,
334 let mut keys: Vec<_> = set.iter()
335 .map(|k| extract_stable_key(hcx, k))
337 keys.sort_unstable();
338 keys.hash_stable(hcx, hasher);
341 pub fn hash_stable_nodemap<'a, 'tcx, V, W>(hcx: &mut StableHashingContext<'a, 'tcx>,
342 hasher: &mut StableHasher<W>,
344 where V: HashStable<StableHashingContext<'a, 'tcx>>,
345 W: StableHasherResult,
347 hash_stable_hashmap(hcx, hasher, map, |hcx, node_id| {
348 hcx.tcx.hir.definitions().node_to_hir_id(*node_id).local_id