1use std::hash::Hash;
23use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher};
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_hir::definitions::DefPathHash;
6use rustc_session::Session;
7use rustc_session::cstore::Untracked;
8use rustc_span::source_map::SourceMap;
9use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span};
1011// Very often, we are hashing something that does not need the `CachingSourceMapView`, so we
12// initialize it lazily.
13#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for CachingSourceMap<'a> {
#[inline]
fn clone(&self) -> CachingSourceMap<'a> {
match self {
CachingSourceMap::Unused(__self_0) =>
CachingSourceMap::Unused(::core::clone::Clone::clone(__self_0)),
CachingSourceMap::InUse(__self_0) =>
CachingSourceMap::InUse(::core::clone::Clone::clone(__self_0)),
}
}
}Clone)]
14enum CachingSourceMap<'a> {
15 Unused(&'a SourceMap),
16 InUse(CachingSourceMapView<'a>),
17}
1819/// This is the context state available during incr. comp. hashing. It contains
20/// enough information to transform `DefId`s and `HirId`s into stable `DefPath`s (i.e.,
21/// a reference to the `TyCtxt`) and it holds a few caches for speeding up various
22/// things (e.g., each `DefId`/`DefPath` is only hashed once).
23#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for StableHashingContext<'a> {
#[inline]
fn clone(&self) -> StableHashingContext<'a> {
StableHashingContext {
untracked: ::core::clone::Clone::clone(&self.untracked),
incremental_ignore_spans: ::core::clone::Clone::clone(&self.incremental_ignore_spans),
caching_source_map: ::core::clone::Clone::clone(&self.caching_source_map),
hashing_controls: ::core::clone::Clone::clone(&self.hashing_controls),
}
}
}Clone)]
24pub struct StableHashingContext<'a> {
25 untracked: &'a Untracked,
26// The value of `-Z incremental-ignore-spans`.
27 // This field should only be used by `unstable_opts_incremental_ignore_span`
28incremental_ignore_spans: bool,
29 caching_source_map: CachingSourceMap<'a>,
30 hashing_controls: HashingControls,
31}
3233impl<'a> StableHashingContext<'a> {
34#[inline]
35pub fn new(sess: &'a Session, untracked: &'a Untracked) -> Self {
36let hash_spans_initial = !sess.opts.unstable_opts.incremental_ignore_spans;
3738StableHashingContext {
39untracked,
40 incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans,
41 caching_source_map: CachingSourceMap::Unused(sess.source_map()),
42 hashing_controls: HashingControls { hash_spans: hash_spans_initial },
43 }
44 }
4546#[inline]
47pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) {
48let prev_hash_spans = self.hashing_controls.hash_spans;
49self.hashing_controls.hash_spans = hash_spans;
50f(self);
51self.hashing_controls.hash_spans = prev_hash_spans;
52 }
5354#[inline]
55fn source_map(&mut self) -> &mut CachingSourceMapView<'a> {
56match self.caching_source_map {
57 CachingSourceMap::InUse(ref mut sm) => sm,
58 CachingSourceMap::Unused(sm) => {
59self.caching_source_map = CachingSourceMap::InUse(CachingSourceMapView::new(sm));
60self.source_map() // this recursive call will hit the `InUse` case
61}
62 }
63 }
6465#[inline]
66fn def_span(&self, def_id: LocalDefId) -> Span {
67self.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP)
68 }
6970#[inline]
71pub fn hashing_controls(&self) -> HashingControls {
72self.hashing_controls
73 }
74}
7576impl<'a> rustc_span::HashStableContextfor StableHashingContext<'a> {
77/// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that
78 /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`).
79 /// Instead, we hash the (file name, line, column) triple, which stays the same even if the
80 /// containing `SourceFile` has moved within the `SourceMap`.
81 ///
82 /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets.
83 /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we
84 /// avoid doing it twice when the span starts and ends in the same file, which is almost always
85 /// the case.
86 ///
87 /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`.
88#[inline]
89fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) {
90const TAG_VALID_SPAN: u8 = 0;
91const TAG_INVALID_SPAN: u8 = 1;
92const TAG_RELATIVE_SPAN: u8 = 2;
9394if !self.hashing_controls().hash_spans {
95return;
96 }
9798let span = span.data_untracked();
99span.ctxt.hash_stable(self, hasher);
100span.parent.hash_stable(self, hasher);
101102if span.is_dummy() {
103 Hash::hash(&TAG_INVALID_SPAN, hasher);
104return;
105 }
106107let parent = span.parent.map(|parent| self.def_span(parent).data_untracked());
108if let Some(parent) = parent109 && parent.contains(span)
110 {
111// This span is enclosed in a definition: only hash the relative position. This catches
112 // a subset of the cases from the `file.contains(parent.lo)`. But we can do this check
113 // cheaply without the expensive `span_data_to_lines_and_cols` query.
114Hash::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);
117return;
118 }
119120// If this is not an empty or invalid span, we want to hash the last position that belongs
121 // to it, as opposed to hashing the first position past it.
122let Some((file, line_lo, col_lo, line_hi, col_hi)) =
123self.source_map().span_data_to_lines_and_cols(&span)
124else {
125 Hash::hash(&TAG_INVALID_SPAN, hasher);
126return;
127 };
128129if let Some(parent) = parent130 && file.contains(parent.lo)
131 {
132// This span is relative to another span in the same file,
133 // only hash the relative position.
134Hash::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);
137return;
138 }
139140 Hash::hash(&TAG_VALID_SPAN, hasher);
141 Hash::hash(&file.stable_id, hasher);
142143// Hash both the length and the end location (line/column) of a span. If we hash only the
144 // length, for example, then two otherwise equal spans with different end locations will
145 // have the same hash. This can cause a problem during incremental compilation wherein a
146 // previous result for a query that depends on the end location of a span will be
147 // incorrectly reused when the end location of the span it depends on has changed (see
148 // issue #74890). A similar analysis applies if some query depends specifically on the
149 // length of the span, but we only hash the end location. So hash both.
150151let col_lo_trunc = (col_lo.0 as u64) & 0xFF;
152let line_lo_trunc = ((line_loas u64) & 0xFF_FF_FF) << 8;
153let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32;
154let line_hi_trunc = ((line_hias u64) & 0xFF_FF_FF) << 40;
155let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc;
156let len = (span.hi - span.lo).0;
157 Hash::hash(&col_line, hasher);
158 Hash::hash(&len, hasher);
159 }
160161#[inline]
162fn def_path_hash(&self, def_id: DefId) -> DefPathHash {
163if let Some(def_id) = def_id.as_local() {
164self.untracked.definitions.read().def_path_hash(def_id)
165 } else {
166self.untracked.cstore.read().def_path_hash(def_id)
167 }
168 }
169170/// Assert that the provided `HashStableContext` is configured with the default
171 /// `HashingControls`. We should always have bailed out before getting to here with a
172 /// non-default mode. With this check in place, we can avoid the need to maintain separate
173 /// versions of `ExpnData` hashes for each permutation of `HashingControls` settings.
174#[inline]
175fn assert_default_hashing_controls(&self, msg: &str) {
176let hashing_controls = self.hashing_controls;
177let HashingControls { hash_spans } = hashing_controls;
178179// Note that we require that `hash_spans` be the inverse of the global `-Z
180 // incremental-ignore-spans` option. Normally, this option is disabled, in which case
181 // `hash_spans` must be true.
182 //
183 // Span hashing can also be disabled without `-Z incremental-ignore-spans`. This is the
184 // case for instance when building a hash for name mangling. Such configuration must not be
185 // used for metadata.
186match (&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!(
187 hash_spans, !self.incremental_ignore_spans,
188"Attempted hashing of {msg} with non-default HashingControls: {hashing_controls:?}"
189);
190 }
191}
192193impl<'a> rustc_abi::HashStableContextfor StableHashingContext<'a> {}
194impl<'a> rustc_ast::HashStableContextfor StableHashingContext<'a> {}
195impl<'a> rustc_hir::HashStableContextfor StableHashingContext<'a> {}
196impl<'a> rustc_session::HashStableContextfor StableHashingContext<'a> {}