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;
17 use std::hash as std_hash;
21 use syntax::ext::hygiene::SyntaxContext;
22 use syntax::symbol::Symbol;
25 use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
27 use rustc_data_structures::accumulate_vec::AccumulateVec;
29 /// This is the context state available during incr. comp. hashing. It contains
30 /// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
31 /// a reference to the TyCtxt) and it holds a few caches for speeding up various
32 /// things (e.g. each DefId/DefPath is only hashed once).
33 pub struct StableHashingContext<'a, 'tcx: 'a> {
34 tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
35 codemap: CachingCodemapView<'tcx>,
38 overflow_checks_enabled: bool,
39 node_id_hashing_mode: NodeIdHashingMode,
40 // A sorted array of symbol keys for fast lookup.
41 ignored_attr_names: Vec<Symbol>,
44 #[derive(PartialEq, Eq, Clone, Copy)]
45 pub enum NodeIdHashingMode {
51 impl<'a, 'tcx: 'a> StableHashingContext<'a, 'tcx> {
53 pub fn new(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>) -> Self {
54 let hash_spans_initial = tcx.sess.opts.debuginfo != NoDebugInfo;
55 let check_overflow_initial = tcx.sess.overflow_checks();
57 let mut ignored_attr_names: Vec<_> = ich::IGNORED_ATTRIBUTES
59 .map(|&s| Symbol::intern(s))
62 ignored_attr_names.sort();
64 StableHashingContext {
66 codemap: CachingCodemapView::new(tcx),
67 hash_spans: hash_spans_initial,
69 overflow_checks_enabled: check_overflow_initial,
70 node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
71 ignored_attr_names: ignored_attr_names,
76 pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
79 let prev_hash_bodies = self.hash_bodies;
80 self.hash_bodies = hash_bodies;
82 self.hash_bodies = prev_hash_bodies;
86 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self,
89 let prev_hash_spans = self.hash_spans;
90 self.hash_spans = hash_spans;
92 self.hash_spans = prev_hash_spans;
96 pub fn with_node_id_hashing_mode<F: FnOnce(&mut Self)>(&mut self,
97 mode: NodeIdHashingMode,
99 let prev = self.node_id_hashing_mode;
100 self.node_id_hashing_mode = mode;
102 self.node_id_hashing_mode = prev;
106 pub fn tcx(&self) -> ty::TyCtxt<'a, 'tcx, 'tcx> {
111 pub fn def_path_hash(&mut self, def_id: DefId) -> u64 {
112 self.tcx.def_path_hash(def_id)
116 pub fn hash_spans(&self) -> bool {
121 pub fn hash_bodies(&self) -> bool {
126 pub fn codemap(&mut self) -> &mut CachingCodemapView<'tcx> {
131 pub fn is_ignored_attr(&self, name: Symbol) -> bool {
132 self.ignored_attr_names.binary_search(&name).is_ok()
135 pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
136 item_attrs: &[ast::Attribute],
138 let prev_overflow_checks = self.overflow_checks_enabled;
139 if attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
140 self.overflow_checks_enabled = true;
142 let prev_hash_node_ids = self.node_id_hashing_mode;
143 self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
147 self.node_id_hashing_mode = prev_hash_node_ids;
148 self.overflow_checks_enabled = prev_overflow_checks;
152 pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
157 hir::BiMul => self.overflow_checks_enabled,
179 pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
184 hir::UnNeg => self.overflow_checks_enabled,
190 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for ast::NodeId {
191 fn hash_stable<W: StableHasherResult>(&self,
192 hcx: &mut StableHashingContext<'a, 'tcx>,
193 hasher: &mut StableHasher<W>) {
194 match hcx.node_id_hashing_mode {
195 NodeIdHashingMode::Ignore => {
196 // Most NodeIds in the HIR can be ignored, but if there is a
197 // corresponding entry in the `trait_map` we need to hash that.
198 // Make sure we don't ignore too much by checking that there is
199 // no entry in a debug_assert!().
200 debug_assert!(hcx.tcx.trait_map.get(self).is_none());
202 NodeIdHashingMode::HashDefPath => {
203 hcx.tcx.hir.definitions().node_to_hir_id(*self).hash_stable(hcx, hasher);
205 NodeIdHashingMode::HashTraitsInScope => {
206 if let Some(traits) = hcx.tcx.trait_map.get(self) {
207 // The ordering of the candidates is not fixed. So we hash
208 // the def-ids and then sort them and hash the collection.
209 let mut candidates: AccumulateVec<[_; 8]> =
211 .map(|&hir::TraitCandidate { def_id, import_id: _ }| {
212 hcx.def_path_hash(def_id)
215 if traits.len() > 1 {
218 candidates.hash_stable(hcx, hasher);
225 impl<'a, 'tcx> HashStable<StableHashingContext<'a, 'tcx>> for Span {
227 // Hash a span in a stable way. We can't directly hash the span's BytePos
228 // fields (that would be similar to hashing pointers, since those are just
229 // offsets into the CodeMap). Instead, we hash the (file name, line, column)
230 // triple, which stays the same even if the containing FileMap has moved
231 // within the CodeMap.
232 // Also note that we are hashing byte offsets for the column, not unicode
233 // codepoint offsets. For the purpose of the hash that's sufficient.
234 // Also, hashing filenames is expensive so we avoid doing it twice when the
235 // span starts and ends in the same file, which is almost always the case.
236 fn hash_stable<W: StableHasherResult>(&self,
237 hcx: &mut StableHashingContext<'a, 'tcx>,
238 hasher: &mut StableHasher<W>) {
245 // If this is not an empty or invalid span, we want to hash the last
246 // position that belongs to it, as opposed to hashing the first
248 let span_hi = if self.hi > self.lo {
249 // We might end up in the middle of a multibyte character here,
250 // but that's OK, since we are not trying to decode anything at
252 self.hi - ::syntax_pos::BytePos(1)
258 let loc1 = hcx.codemap().byte_pos_to_line_and_col(self.lo);
259 let loc1 = loc1.as_ref()
260 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
261 .unwrap_or(("???", 0, 0));
263 let loc2 = hcx.codemap().byte_pos_to_line_and_col(span_hi);
264 let loc2 = loc2.as_ref()
265 .map(|&(ref fm, line, col)| (&fm.name[..], line, col.to_usize()))
266 .unwrap_or(("???", 0, 0));
268 if loc1.0 == loc2.0 {
269 std_hash::Hash::hash(&0u8, hasher);
271 std_hash::Hash::hash(loc1.0, hasher);
272 std_hash::Hash::hash(&loc1.1, hasher);
273 std_hash::Hash::hash(&loc1.2, hasher);
275 // Do not hash the file name twice
276 std_hash::Hash::hash(&loc2.1, hasher);
277 std_hash::Hash::hash(&loc2.2, hasher);
279 std_hash::Hash::hash(&1u8, 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 std_hash::Hash::hash(loc2.0, hasher);
286 std_hash::Hash::hash(&loc2.1, hasher);
287 std_hash::Hash::hash(&loc2.2, hasher);
291 if self.ctxt == SyntaxContext::empty() {
292 0u8.hash_stable(hcx, hasher);
294 1u8.hash_stable(hcx, hasher);
295 self.source_callsite().hash_stable(hcx, hasher);