1use std::hash::Hash;
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_hir::def_id::{DefId, LocalDefId};
9use rustc_session::Session;
10use rustc_span::source_map::SourceMap;
11use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, Pos, Span};
12
13enum CachingSourceMap<'a> {
16 Unused(&'a SourceMap),
17 InUse(CachingSourceMapView<'a>),
18}
19
20pub struct StableHashState<'a> {
25 untracked: &'a Untracked,
26 incremental_ignore_spans: bool,
29 caching_source_map: CachingSourceMap<'a>,
30 stable_hash_controls: StableHashControls,
31}
32
33impl<'a> StableHashState<'a> {
34 #[inline]
35 pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
36 let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
37
38 StableHashState {
39 untracked,
40 incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
41 caching_source_map: CachingSourceMap::Unused(sess.source_map()),
42 stable_hash_controls: StableHashControls { hash_spans: hash_spans_initial },
43 }
44 }
45
46 #[inline]
47 pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) {
48 let prev_hash_spans = self.stable_hash_controls.hash_spans;
49 self.stable_hash_controls.hash_spans = hash_spans;
50 f(self);
51 self.stable_hash_controls.hash_spans = prev_hash_spans;
52 }
53
54 #[inline]
55 fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
56 match self.caching_source_map {
57 CachingSourceMap::InUse(ref mut sm) => sm,
58 CachingSourceMap::Unused(sm) => {
59 self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm));
60 self.source_map() }
62 }
63 }
64
65 #[inline]
66 fn def_span(&self, def_id: LocalDefId) -> Span {
67 self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP)
68 }
69
70 #[inline]
71 pub fn stable_hash_controls(&self) -> StableHashControls {
72 self.stable_hash_controls
73 }
74}
75
76impl<'a> StableHashCtxt for StableHashState<'a> {
77 #[inline]
89 fn stable_hash_span(&mut self, raw_span: RawSpan, hasher: &mut StableHasher) {
90 const TAG_VALID_SPAN: u8 = 0;
91 const TAG_INVALID_SPAN: u8 = 1;
92 const TAG_RELATIVE_SPAN: u8 = 2;
93
94 #[inline]
95 fn pack_span_location(
96 line_lo: usize,
97 col_lo: BytePos,
98 line_hi: usize,
99 col_hi: BytePos,
100 ) -> u64 {
101 let col_lo_trunc = (col_lo.0 as u64) & 0xFF;
102 let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8;
103 let col_hi_trunc = ((col_hi.0 as u64) & 0xFF) << 32;
104 let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40;
105 col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc
106 }
107
108 if !self.stable_hash_controls().hash_spans {
109 return;
110 }
111
112 let span = Span::from_raw_span(raw_span);
113 let span = span.data_untracked();
114 span.ctxt.stable_hash(self, hasher);
115 span.parent.stable_hash(self, hasher);
116
117 if span.is_dummy() {
118 Hash::hash(&TAG_INVALID_SPAN, hasher);
119 return;
120 }
121
122 let parent = span.parent.map(|parent| self.def_span(parent).data_untracked());
123 if let Some(parent) = parent
124 && parent.contains(span)
125 {
126 Hash::hash(&TAG_RELATIVE_SPAN, hasher);
130 (span.lo - parent.lo).to_u32().stable_hash(self, hasher);
131 (span.hi - parent.lo).to_u32().stable_hash(self, hasher);
132 return;
133 }
134
135 let Some((file, line_lo, col_lo, line_hi, col_hi)) =
138 self.source_map().span_data_to_lines_and_cols(&span)
139 else {
140 Hash::hash(&TAG_INVALID_SPAN, hasher);
141 return;
142 };
143
144 if let Some(parent) = parent
145 && file.contains(parent.lo)
146 {
147 Hash::hash(&TAG_RELATIVE_SPAN, hasher);
150 Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher);
151 Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher);
152 return;
153 }
154
155 Hash::hash(&TAG_VALID_SPAN, hasher);
156 Hash::hash(&file.stable_id, hasher);
157
158 let col_line = pack_span_location(line_lo, col_lo, line_hi, col_hi);
167 let len = (span.hi - span.lo).0;
168 Hash::hash(&col_line, hasher);
169 Hash::hash(&len, hasher);
170 }
171
172 #[inline]
173 fn def_path_hash(&self, raw_def_id: RawDefId) -> Fingerprint {
174 let def_id = DefId::from_raw_def_id(raw_def_id);
175 if let Some(def_id) = def_id.as_local() {
176 self.untracked.definitions.read().def_path_hash(def_id)
177 } else {
178 self.untracked.cstore.read().def_path_hash(def_id)
179 }
180 .0
181 }
182
183 #[inline]
188 fn assert_default_stable_hash_controls(&self, msg: &str) {
189 let stable_hash_controls = self.stable_hash_controls;
190 let StableHashControls { hash_spans } = stable_hash_controls;
191
192 {
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!(
200 hash_spans, !self.incremental_ignore_spans,
201 "Attempted hashing of {msg} with non-default StableHashControls: {stable_hash_controls:?}"
202 );
203 }
204
205 #[inline]
206 fn stable_hash_controls(&self) -> StableHashControls {
207 self.stable_hash_controls
208 }
209}