1use std::hash::Hasher;
2
3use rustc_crate_store::Untracked;
4use rustc_data_structures::fingerprint::Fingerprint;
5use rustc_data_structures::stable_hash::{
6 RawDefId, RawSpan, StableHash, StableHashControls, StableHashCtxt, StableHasher,
7};
8use rustc_data_structures::sync::AppendOnlyIndexVec;
9use rustc_hir::def_id::{DefId, LocalDefId};
10use rustc_session::Session;
11use rustc_span::source_map::SourceMap;
12use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, Pos, Span, SpanData};
13
14enum CachingSourceMap<'a> {
17 Unused(&'a SourceMap),
18 InUse(CachingSourceMapView<'a>),
19}
20
21pub struct StableHashState<'a> {
26 untracked: &'a Untracked,
27 incremental_ignore_spans: bool,
30 caching_source_map: CachingSourceMap<'a>,
31 stable_hash_controls: StableHashControls,
32 source_span_cache: SourceSpanCache,
33}
34
35impl<'a> StableHashState<'a> {
36 #[inline]
37 pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
38 let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
39
40 StableHashState {
41 untracked,
42 incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
43 caching_source_map: CachingSourceMap::Unused(sess.source_map()),
44 stable_hash_controls: StableHashControls { hash_spans: hash_spans_initial },
45 source_span_cache: SourceSpanCache::default(),
46 }
47 }
48
49 #[inline]
50 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) {
51 let prev_hash_spans = self.stable_hash_controls.hash_spans;
52 self.stable_hash_controls.hash_spans = hash_spans;
53 f(self);
54 self.stable_hash_controls.hash_spans = prev_hash_spans;
55 }
56
57 #[inline]
58 fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
59 match self.caching_source_map {
60 CachingSourceMap::InUse(ref mut sm) => sm,
61 CachingSourceMap::Unused(sm) => {
62 self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm));
63 self.source_map() }
65 }
66 }
67
68 #[inline]
69 pub fn stable_hash_controls(&self) -> StableHashControls {
70 self.stable_hash_controls
71 }
72}
73
74impl<'a> StableHashCtxt for StableHashState<'a> {
75 #[inline]
91 fn stable_hash_span(&mut self, raw_span: RawSpan, hasher: &mut StableHasher) {
92 const TAG_VALID_SPAN: u8 = 0;
93 const TAG_INVALID_SPAN: u8 = 1;
94 const TAG_RELATIVE_SPAN: u8 = 2;
95
96 #[inline]
97 fn pack_span_location(
98 line_lo: usize,
99 col_lo: BytePos,
100 line_hi: usize,
101 col_hi: BytePos,
102 ) -> u64 {
103 let col_lo_trunc = (col_lo.0 as u64) & 0xFF;
104 let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8;
105 let col_hi_trunc = ((col_hi.0 as u64) & 0xFF) << 32;
106 let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40;
107 col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc
108 }
109
110 if !self.stable_hash_controls().hash_spans {
111 return;
112 }
113
114 let span = Span::from_raw_span(raw_span);
115 let span = span.data_untracked();
116 span.ctxt.stable_hash(self, hasher);
117 span.parent.stable_hash(self, hasher);
118
119 if span.is_dummy() {
120 hasher.write_u8(TAG_INVALID_SPAN);
121 return;
122 }
123
124 let parent = span
125 .parent
126 .map(|parent| self.source_span_cache.lookup(parent, &self.untracked.source_span));
127 if let Some(parent) = parent
128 && parent.contains(span)
129 {
130 hasher.write_u8(TAG_RELATIVE_SPAN);
134 hasher.write_u32((span.lo - parent.lo).to_u32());
135 hasher.write_u32((span.hi - parent.lo).to_u32());
136 return;
137 }
138
139 let Some((file, line_lo, col_lo, line_hi, col_hi)) =
142 self.source_map().span_data_to_lines_and_cols(&span)
143 else {
144 hasher.write_u8(TAG_INVALID_SPAN);
145 return;
146 };
147 let file_stable_id = file.stable_id;
148
149 if let Some(parent) = parent
150 && file.contains(parent.lo)
151 {
152 hasher.write_u8(TAG_RELATIVE_SPAN);
155 hasher.write_u32(span.lo.0.wrapping_sub(parent.lo.0));
156 hasher.write_u32(span.hi.0.wrapping_sub(parent.lo.0));
157 return;
158 }
159
160 hasher.write_u8(TAG_VALID_SPAN);
161 file_stable_id.stable_hash(self, hasher);
162
163 let col_line = pack_span_location(line_lo, col_lo, line_hi, col_hi);
172 let len = (span.hi - span.lo).0;
173 hasher.write_u64(col_line);
174 hasher.write_u32(len);
175 }
176
177 #[inline]
178 fn def_path_hash(&self, raw_def_id: RawDefId) -> Fingerprint {
179 let def_id = DefId::from_raw_def_id(raw_def_id);
180 if let Some(def_id) = def_id.as_local() {
181 self.untracked.definitions.read().def_path_hash(def_id)
182 } else {
183 self.untracked.cstore.read().def_path_hash(def_id)
184 }
185 .0
186 }
187
188 #[inline]
193 fn assert_default_stable_hash_controls(&self, msg: &str) {
194 let stable_hash_controls = self.stable_hash_controls;
195 let StableHashControls { hash_spans } = stable_hash_controls;
196
197 {
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 StableHashControls: {1:?}",
msg, stable_hash_controls)));
}
}
}
};assert_eq!(
205 hash_spans, !self.incremental_ignore_spans,
206 "Attempted hashing of {msg} with non-default StableHashControls: {stable_hash_controls:?}"
207 );
208 }
209
210 #[inline]
211 fn stable_hash_controls(&self) -> StableHashControls {
212 self.stable_hash_controls
213 }
214}
215
216#[derive(#[automatically_derived]impl ::core::default::Default for SourceSpanCache {
#[inline]
fn default() -> Self {
SourceSpanCache(::core::default::Default::default())
}
}Default)]
223pub(crate) struct SourceSpanCache(Option<(LocalDefId, SpanData)>);
224
225impl SourceSpanCache {
226 #[inline]
227 pub(crate) fn lookup(
228 &mut self,
229 def_id: LocalDefId,
230 source_span: &AppendOnlyIndexVec<LocalDefId, Span>,
231 ) -> SpanData {
232 if let Some(cache) = self.0
233 && cache.0 == def_id
234 {
235 cache.1
236 } else {
237 let span_data = source_span.get(def_id).unwrap_or(DUMMY_SP).data_untracked();
238 self.0 = Some((def_id, span_data));
239 span_data
240 }
241 }
242}