Skip to content

Commit d63a8d9

Browse files
committed
Auto merge of rust-lang#92278 - Aaron1011:fix-fingerprint-caching, r=michaelwoerister
Ensure that `Fingerprint` caching respects hashing configuration Fixes rust-lang#92266 In some `HashStable` impls, we use a cache to avoid re-computing the same `Fingerprint` from the same structure (e.g. an `AdtDef`). However, the `StableHashingContext` used can be configured to perform hashing in different ways (e.g. skipping `Span`s). This configuration information is not included in the cache key, which will cause an incorrect `Fingerprint` to be used if we hash the same structure with different `StableHashingContext` settings. To fix this, the configuration settings of `StableHashingContext` are split out into a separate `HashingControls` struct. This struct is used as part of the cache key, ensuring that our caches always produce the correct result for the given settings. With this in place, we now turn off `Span` hashing during the entire process of computing the hash included in legacy symbols. This current has no effect, but will matter when a future PR starts hashing more `Span`s that we currently skip.
2 parents 092e1c9 + 4ca275a commit d63a8d9

File tree

9 files changed

+118
-46
lines changed

9 files changed

+118
-46
lines changed

compiler/rustc_data_structures/src/stable_hasher.rs

+19
Original file line numberDiff line numberDiff line change
@@ -583,3 +583,22 @@ fn stable_hash_reduce<HCX, I, C, F>(
583583
}
584584
}
585585
}
586+
587+
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
588+
pub enum NodeIdHashingMode {
589+
Ignore,
590+
HashDefPath,
591+
}
592+
593+
/// Controls what data we do or not not hash.
594+
/// Whenever a `HashStable` implementation caches its
595+
/// result, it needs to include `HashingControls` as part
596+
/// of the key, to ensure that is does not produce an incorrect
597+
/// result (for example, using a `Fingerprint` produced while
598+
/// hashing `Span`s when a `Fingeprint` without `Span`s is
599+
/// being requested)
600+
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
601+
pub struct HashingControls {
602+
pub hash_spans: bool,
603+
pub node_id_hashing_mode: NodeIdHashingMode,
604+
}

compiler/rustc_middle/src/ty/adt.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::ty::util::{Discr, IntTypeExt};
44
use rustc_data_structures::captures::Captures;
55
use rustc_data_structures::fingerprint::Fingerprint;
66
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_data_structures::stable_hasher::HashingControls;
78
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
89
use rustc_errors::ErrorReported;
910
use rustc_hir as hir;
@@ -136,12 +137,13 @@ impl Hash for AdtDef {
136137
impl<'a> HashStable<StableHashingContext<'a>> for AdtDef {
137138
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
138139
thread_local! {
139-
static CACHE: RefCell<FxHashMap<usize, Fingerprint>> = Default::default();
140+
static CACHE: RefCell<FxHashMap<(usize, HashingControls), Fingerprint>> = Default::default();
140141
}
141142

142143
let hash: Fingerprint = CACHE.with(|cache| {
143144
let addr = self as *const AdtDef as usize;
144-
*cache.borrow_mut().entry(addr).or_insert_with(|| {
145+
let hashing_controls = hcx.hashing_controls();
146+
*cache.borrow_mut().entry((addr, hashing_controls)).or_insert_with(|| {
145147
let ty::AdtDef { did, ref variants, ref flags, ref repr } = *self;
146148

147149
let mut hasher = StableHasher::new();

compiler/rustc_middle/src/ty/impls_ty.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::mir;
66
use crate::ty;
77
use rustc_data_structures::fingerprint::Fingerprint;
88
use rustc_data_structures::fx::FxHashMap;
9+
use rustc_data_structures::stable_hasher::HashingControls;
910
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
1011
use rustc_query_system::ich::StableHashingContext;
1112
use std::cell::RefCell;
@@ -17,12 +18,12 @@ where
1718
{
1819
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
1920
thread_local! {
20-
static CACHE: RefCell<FxHashMap<(usize, usize), Fingerprint>> =
21+
static CACHE: RefCell<FxHashMap<(usize, usize, HashingControls), Fingerprint>> =
2122
RefCell::new(Default::default());
2223
}
2324

2425
let hash = CACHE.with(|cache| {
25-
let key = (self.as_ptr() as usize, self.len());
26+
let key = (self.as_ptr() as usize, self.len(), hcx.hashing_controls());
2627
if let Some(&hash) = cache.borrow().get(&key) {
2728
return hash;
2829
}

compiler/rustc_query_system/src/ich/hcx.rs

+32-18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_ast as ast;
33
use rustc_data_structures::fx::FxHashSet;
44
use rustc_data_structures::sorted_map::SortedMap;
55
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
6+
use rustc_data_structures::stable_hasher::{HashingControls, NodeIdHashingMode};
67
use rustc_data_structures::sync::Lrc;
78
use rustc_hir as hir;
89
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -26,20 +27,15 @@ fn compute_ignored_attr_names() -> FxHashSet<Symbol> {
2627
pub struct StableHashingContext<'a> {
2728
definitions: &'a Definitions,
2829
cstore: &'a dyn CrateStore,
30+
// The value of `-Z incremental-ignore-spans`.
31+
// This field should only be used by `debug_opts_incremental_ignore_span`
32+
incremental_ignore_spans: bool,
2933
pub(super) body_resolver: BodyResolver<'a>,
30-
hash_spans: bool,
31-
pub(super) node_id_hashing_mode: NodeIdHashingMode,
32-
3334
// Very often, we are hashing something that does not need the
3435
// `CachingSourceMapView`, so we initialize it lazily.
3536
raw_source_map: &'a SourceMap,
3637
caching_source_map: Option<CachingSourceMapView<'a>>,
37-
}
38-
39-
#[derive(PartialEq, Eq, Clone, Copy)]
40-
pub enum NodeIdHashingMode {
41-
Ignore,
42-
HashDefPath,
38+
pub(super) hashing_controls: HashingControls,
4339
}
4440

4541
/// The `BodyResolver` allows mapping a `BodyId` to the corresponding `hir::Body`.
@@ -70,10 +66,13 @@ impl<'a> StableHashingContext<'a> {
7066
body_resolver: BodyResolver::Forbidden,
7167
definitions,
7268
cstore,
69+
incremental_ignore_spans: sess.opts.debugging_opts.incremental_ignore_spans,
7370
caching_source_map: None,
7471
raw_source_map: sess.source_map(),
75-
hash_spans: hash_spans_initial,
76-
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
72+
hashing_controls: HashingControls {
73+
hash_spans: hash_spans_initial,
74+
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
75+
},
7776
}
7877
}
7978

@@ -133,10 +132,10 @@ impl<'a> StableHashingContext<'a> {
133132

134133
#[inline]
135134
pub fn while_hashing_spans<F: FnOnce(&mut Self)>(&mut self, hash_spans: bool, f: F) {
136-
let prev_hash_spans = self.hash_spans;
137-
self.hash_spans = hash_spans;
135+
let prev_hash_spans = self.hashing_controls.hash_spans;
136+
self.hashing_controls.hash_spans = hash_spans;
138137
f(self);
139-
self.hash_spans = prev_hash_spans;
138+
self.hashing_controls.hash_spans = prev_hash_spans;
140139
}
141140

142141
#[inline]
@@ -145,10 +144,10 @@ impl<'a> StableHashingContext<'a> {
145144
mode: NodeIdHashingMode,
146145
f: F,
147146
) {
148-
let prev = self.node_id_hashing_mode;
149-
self.node_id_hashing_mode = mode;
147+
let prev = self.hashing_controls.node_id_hashing_mode;
148+
self.hashing_controls.node_id_hashing_mode = mode;
150149
f(self);
151-
self.node_id_hashing_mode = prev;
150+
self.hashing_controls.node_id_hashing_mode = prev;
152151
}
153152

154153
#[inline]
@@ -183,6 +182,11 @@ impl<'a> StableHashingContext<'a> {
183182
}
184183
IGNORED_ATTRIBUTES.with(|attrs| attrs.contains(&name))
185184
}
185+
186+
#[inline]
187+
pub fn hashing_controls(&self) -> HashingControls {
188+
self.hashing_controls.clone()
189+
}
186190
}
187191

188192
impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId {
@@ -195,7 +199,12 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::NodeId {
195199
impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
196200
#[inline]
197201
fn hash_spans(&self) -> bool {
198-
self.hash_spans
202+
self.hashing_controls.hash_spans
203+
}
204+
205+
#[inline]
206+
fn debug_opts_incremental_ignore_spans(&self) -> bool {
207+
self.incremental_ignore_spans
199208
}
200209

201210
#[inline]
@@ -215,6 +224,11 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> {
215224
) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)> {
216225
self.source_map().span_data_to_lines_and_cols(span)
217226
}
227+
228+
#[inline]
229+
fn hashing_controls(&self) -> HashingControls {
230+
self.hashing_controls.clone()
231+
}
218232
}
219233

220234
impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {}

compiler/rustc_query_system/src/ich/impls_hir.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {
1111
#[inline]
1212
fn hash_hir_id(&mut self, hir_id: hir::HirId, hasher: &mut StableHasher) {
1313
let hcx = self;
14-
match hcx.node_id_hashing_mode {
14+
match hcx.hashing_controls.node_id_hashing_mode {
1515
NodeIdHashingMode::Ignore => {
1616
// Don't do anything.
1717
}
@@ -89,12 +89,12 @@ impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {
8989

9090
#[inline]
9191
fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F) {
92-
let prev_hash_node_ids = self.node_id_hashing_mode;
93-
self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
92+
let prev_hash_node_ids = self.hashing_controls.node_id_hashing_mode;
93+
self.hashing_controls.node_id_hashing_mode = NodeIdHashingMode::Ignore;
9494

9595
f(self);
9696

97-
self.node_id_hashing_mode = prev_hash_node_ids;
97+
self.hashing_controls.node_id_hashing_mode = prev_hash_node_ids;
9898
}
9999

100100
#[inline]

compiler/rustc_query_system/src/ich/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! ICH - Incremental Compilation Hash
22
3-
pub use self::hcx::{NodeIdHashingMode, StableHashingContext};
3+
pub use self::hcx::StableHashingContext;
4+
pub use rustc_data_structures::stable_hasher::NodeIdHashingMode;
45
use rustc_span::symbol::{sym, Symbol};
56

67
mod hcx;

compiler/rustc_span/src/hygiene.rs

+30
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::{HashStableContext, Span, DUMMY_SP};
3232
use crate::def_id::{CrateNum, DefId, StableCrateId, CRATE_DEF_ID, LOCAL_CRATE};
3333
use rustc_data_structures::fingerprint::Fingerprint;
3434
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
35+
use rustc_data_structures::stable_hasher::HashingControls;
3536
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
3637
use rustc_data_structures::sync::{Lock, Lrc};
3738
use rustc_data_structures::unhash::UnhashMap;
@@ -88,6 +89,33 @@ rustc_index::newtype_index! {
8889
}
8990
}
9091

92+
/// Assert that the provided `HashStableContext` is configured with the 'default'
93+
/// `HashingControls`. We should always have bailed out before getting to here
94+
/// with a non-default mode. With this check in place, we can avoid the need
95+
/// to maintain separate versions of `ExpnData` hashes for each permutation
96+
/// of `HashingControls` settings.
97+
fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) {
98+
match ctx.hashing_controls() {
99+
// Ideally, we would also check that `node_id_hashing_mode` was always
100+
// `NodeIdHashingMode::HashDefPath`. However, we currently end up hashing
101+
// `Span`s in this mode, and there's not an easy way to change that.
102+
// All of the span-related data that we hash is pretty self-contained
103+
// (in particular, we don't hash any `HirId`s), so this shouldn't result
104+
// in any caching problems.
105+
// FIXME: Enforce that we don't end up transitively hashing any `HirId`s,
106+
// or ensure that this method is always invoked with the same
107+
// `NodeIdHashingMode`
108+
//
109+
// Note that we require that `hash_spans` be set according to the global
110+
// `-Z incremental-ignore-spans` option. Normally, this option is disabled,
111+
// which will cause us to require that this method always be called with `Span` hashing
112+
// enabled.
113+
HashingControls { hash_spans, node_id_hashing_mode: _ }
114+
if hash_spans == !ctx.debug_opts_incremental_ignore_spans() => {}
115+
other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other),
116+
}
117+
}
118+
91119
/// A unique hash value associated to an expansion.
92120
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, HashStable_Generic)]
93121
pub struct ExpnHash(Fingerprint);
@@ -1444,6 +1472,7 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut ctx: impl HashStableContex
14441472
"Already set disambiguator for ExpnData: {:?}",
14451473
expn_data
14461474
);
1475+
assert_default_hashing_controls(&ctx, "ExpnData (disambiguator)");
14471476
let mut expn_hash = expn_data.hash_expn(&mut ctx);
14481477

14491478
let disambiguator = HygieneData::with(|data| {
@@ -1493,6 +1522,7 @@ impl<CTX: HashStableContext> HashStable<CTX> for SyntaxContext {
14931522

14941523
impl<CTX: HashStableContext> HashStable<CTX> for ExpnId {
14951524
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
1525+
assert_default_hashing_controls(ctx, "ExpnId");
14961526
let hash = if *self == ExpnId::root() {
14971527
// Avoid fetching TLS storage for a trivial often-used value.
14981528
Fingerprint::ZERO

compiler/rustc_span/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub mod hygiene;
4242
use hygiene::Transparency;
4343
pub use hygiene::{DesugaringKind, ExpnKind, MacroKind};
4444
pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext};
45+
use rustc_data_structures::stable_hasher::HashingControls;
4546
pub mod def_id;
4647
use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, LOCAL_CRATE};
4748
pub mod lev_distance;
@@ -2057,11 +2058,15 @@ impl InnerSpan {
20572058
pub trait HashStableContext {
20582059
fn def_path_hash(&self, def_id: DefId) -> DefPathHash;
20592060
fn hash_spans(&self) -> bool;
2061+
/// Accesses `sess.opts.debugging_opts.incremental_ignore_spans` since
2062+
/// we don't have easy access to a `Session`
2063+
fn debug_opts_incremental_ignore_spans(&self) -> bool;
20602064
fn def_span(&self, def_id: LocalDefId) -> Span;
20612065
fn span_data_to_lines_and_cols(
20622066
&mut self,
20632067
span: &SpanData,
20642068
) -> Option<(Lrc<SourceFile>, usize, BytePos, usize, BytePos)>;
2069+
fn hashing_controls(&self) -> HashingControls;
20652070
}
20662071

20672072
impl<CTX> HashStable<CTX> for Span

compiler/rustc_symbol_mangling/src/legacy.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -113,29 +113,29 @@ fn get_symbol_hash<'tcx>(
113113
hcx.while_hashing_spans(false, |hcx| {
114114
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
115115
item_type.hash_stable(hcx, &mut hasher);
116-
});
117-
});
118116

119-
// If this is a function, we hash the signature as well.
120-
// This is not *strictly* needed, but it may help in some
121-
// situations, see the `run-make/a-b-a-linker-guard` test.
122-
if let ty::FnDef(..) = item_type.kind() {
123-
item_type.fn_sig(tcx).hash_stable(&mut hcx, &mut hasher);
124-
}
117+
// If this is a function, we hash the signature as well.
118+
// This is not *strictly* needed, but it may help in some
119+
// situations, see the `run-make/a-b-a-linker-guard` test.
120+
if let ty::FnDef(..) = item_type.kind() {
121+
item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher);
122+
}
125123

126-
// also include any type parameters (for generic items)
127-
substs.hash_stable(&mut hcx, &mut hasher);
124+
// also include any type parameters (for generic items)
125+
substs.hash_stable(hcx, &mut hasher);
128126

129-
if let Some(instantiating_crate) = instantiating_crate {
130-
tcx.def_path_hash(instantiating_crate.as_def_id())
131-
.stable_crate_id()
132-
.hash_stable(&mut hcx, &mut hasher);
133-
}
127+
if let Some(instantiating_crate) = instantiating_crate {
128+
tcx.def_path_hash(instantiating_crate.as_def_id())
129+
.stable_crate_id()
130+
.hash_stable(hcx, &mut hasher);
131+
}
134132

135-
// We want to avoid accidental collision between different types of instances.
136-
// Especially, `VtableShim`s and `ReifyShim`s may overlap with their original
137-
// instances without this.
138-
discriminant(&instance.def).hash_stable(&mut hcx, &mut hasher);
133+
// We want to avoid accidental collision between different types of instances.
134+
// Especially, `VtableShim`s and `ReifyShim`s may overlap with their original
135+
// instances without this.
136+
discriminant(&instance.def).hash_stable(hcx, &mut hasher);
137+
});
138+
});
139139
});
140140

141141
// 64 bits should be enough to avoid collisions.

0 commit comments

Comments
 (0)