1use std::hash::Hash;
2
3use rustc_data_structures::stable_hasher::{
4 HashStable, HashStableContext, HashingControls, RawDefId, RawDefPathHash, RawSpan, StableHasher,
5};
6use rustc_hir::def_id::{DefId, LocalDefId};
7use rustc_session::Session;
8use rustc_session::cstore::Untracked;
9use rustc_span::source_map::SourceMap;
10use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span};
11
12enum CachingSourceMap<'a> {
15 Unused(&'a SourceMap),
16 InUse(CachingSourceMapView<'a>),
17}
18
19pub struct StableHashingContext<'a> {
24 untracked: &'a Untracked,
25 incremental_ignore_spans: bool,
28 caching_source_map: CachingSourceMap<'a>,
29 hashing_controls: HashingControls,
30}
31
32impl<'a> StableHashingContext<'a> {
33 #[inline]
34 pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
35 let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
36
37 StableHashingContext {
38 untracked,
39 incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
40 caching_source_map: CachingSourceMap::Unused(sess.source_map()),
41 hashing_controls: HashingControls { hash_spans: hash_spans_initial },
42 }
43 }
44
45 #[inline]
46 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) {
47 let prev_hash_spans = self.hashing_controls.hash_spans;
48 self.hashing_controls.hash_spans = hash_spans;
49 f(self);
50 self.hashing_controls.hash_spans = prev_hash_spans;
51 }
52
53 #[inline]
54 fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
55 match self.caching_source_map {
56 CachingSourceMap::InUse(ref mut sm) => sm,
57 CachingSourceMap::Unused(sm) => {
58 self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm));
59 self.source_map() }
61 }
62 }
63
64 #[inline]
65 fn def_span(&self, def_id: LocalDefId) -> Span {
66 self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP)
67 }
68
69 #[inline]
70 pub fn hashing_controls(&self) -> HashingControls {
71 self.hashing_controls
72 }
73}
74
75impl<'a> HashStableContext for StableHashingContext<'a> {
76 #[inline]
88 fn span_hash_stable(&mut self, raw_span: RawSpan, hasher: &mut StableHasher) {
89 const TAG_VALID_SPAN: u8 = 0;
90 const TAG_INVALID_SPAN: u8 = 1;
91 const TAG_RELATIVE_SPAN: u8 = 2;
92
93 if !self.hashing_controls().hash_spans {
94 return;
95 }
96
97 let span = Span::from_raw_span(raw_span);
98 let span = span.data_untracked();
99 span.ctxt.hash_stable(self, hasher);
100 span.parent.hash_stable(self, hasher);
101
102 if span.is_dummy() {
103 Hash::hash(&TAG_INVALID_SPAN, hasher);
104 return;
105 }
106
107 let parent = span.parent.map(|parent| self.def_span(parent).data_untracked());
108 if let Some(parent) = parent
109 && parent.contains(span)
110 {
111 Hash::hash(&TAG_RELATIVE_SPAN, hasher);
115 (span.lo - parent.lo).to_u32().hash_stable(self, hasher);
116 (span.hi - parent.lo).to_u32().hash_stable(self, hasher);
117 return;
118 }
119
120 let Some((file, line_lo, col_lo, line_hi, col_hi)) =
123 self.source_map().span_data_to_lines_and_cols(&span)
124 else {
125 Hash::hash(&TAG_INVALID_SPAN, hasher);
126 return;
127 };
128
129 if let Some(parent) = parent
130 && file.contains(parent.lo)
131 {
132 Hash::hash(&TAG_RELATIVE_SPAN, hasher);
135 Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher);
136 Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher);
137 return;
138 }
139
140 Hash::hash(&TAG_VALID_SPAN, hasher);
141 Hash::hash(&file.stable_id, hasher);
142
143 let col_lo_trunc = (col_lo.0 as u64) & 0xFF;
152 let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8;
153 let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32;
154 let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40;
155 let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc;
156 let len = (span.hi - span.lo).0;
157 Hash::hash(&col_line, hasher);
158 Hash::hash(&len, hasher);
159 }
160
161 #[inline]
162 fn def_path_hash(&self, raw_def_id: RawDefId) -> RawDefPathHash {
163 let def_id = DefId::from_raw_def_id(raw_def_id);
164 if let Some(def_id) = def_id.as_local() {
165 self.untracked.definitions.read().def_path_hash(def_id)
166 } else {
167 self.untracked.cstore.read().def_path_hash(def_id)
168 }
169 .to_raw_def_path_hash()
170 }
171
172 #[inline]
177 fn assert_default_hashing_controls(&self, msg: &str) {
178 let hashing_controls = self.hashing_controls;
179 let HashingControls { hash_spans } = hashing_controls;
180
181 match (&hash_spans, &!self.incremental_ignore_spans) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("Attempted hashing of {0} with non-default HashingControls: {1:?}",
msg, hashing_controls)));
}
}
};assert_eq!(
189 hash_spans, !self.incremental_ignore_spans,
190 "Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}"
191 );
192 }
193
194 #[inline]
195 fn hashing_controls(&self) -> HashingControls {
196 self.hashing_controls
197 }
198}