From febf082f0a9f28fcbb0024eb2418c17f2ab0809e Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 6 Oct 2026 18:59:07 +0300 Subject: [PATCH 01/11] Implement remapping for `SyntaxContext`s and `LocalExpnId`s --- compiler/rustc_hir_id/src/definitions.rs | 6 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 59 +++++- compiler/rustc_span/src/hygiene.rs | 184 +++++++++++++------ 3 files changed, 187 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 92c8aeb43f283..f8f12bafbcc0a 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -18,6 +18,7 @@ pub use rustc_span::def_id::DefPathHash; use rustc_span::def_id::{ CRATE_DEF_INDEX, CrateNum, DefIndex, LOCAL_CRATE, LocalDefId, LocalDefIdMap, StableCrateId, }; +use rustc_span::hygiene::{HygieneEntityRemapper, LocalExpansionRemapper, SyntaxContextRemapper}; use rustc_span::{SpanEncoder, Symbol, kw, sym}; use tracing::{debug, instrument}; @@ -308,7 +309,10 @@ impl Definitions { assert!( self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()).is_none(), "this function should be called only once" - ) + ); + + SyntaxContextRemapper::with(|mut r| r.commit_end_of_determinism()); + LocalExpansionRemapper::with(|mut r| r.commit_end_of_determinism()); } #[inline(always)] diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f9f4c52b79d3b..02d4595736aff 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -36,7 +36,9 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{OptLevel, OutputType, TargetModifier}; use rustc_span::def_id::CRATE_MOD_ID; -use rustc_span::hygiene::HygieneEncodeContext; +use rustc_span::hygiene::{ + HygieneEncodeContext, HygieneEntityRemapper, LocalExpansionRemapper, SyntaxContextRemapper, +}; use rustc_span::{ ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, Symbol, SyntaxContext, bug, span_bug, sym, @@ -49,6 +51,26 @@ use crate::diagnostics::{FailCreateFileEncoder, FailWriteFile}; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::*; +struct HygieneRemappingResults { + remapping: FxHashMap, + first_non_det_index: u32, +} + +impl HygieneRemappingResults { + fn new((idx, remapping): (u32, FxHashMap)) -> HygieneRemappingResults { + HygieneRemappingResults { remapping, first_non_det_index: idx } + } + + #[inline] + fn map_id(&self, idx: u32) -> u32 { + if idx < self.first_non_det_index { + idx + } else { + self.remapping.get(&idx).copied().unwrap_or(idx) + } + } +} + pub(super) struct EncodeContext<'a, 'tcx> { opaque: FileEncoder<'a>, metadata_hasher: Arc>, @@ -78,6 +100,8 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: Rc>, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, + s_ctxt_remapping: HygieneRemappingResults, + local_expn_remapping: HygieneRemappingResults, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -170,7 +194,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { let idx = self.hygiene_ctxt.borrow_mut().get_syntax_ctxt_encoding_index(syntax_context); - idx.encode(self); + self.map_syntax_context(idx).encode(self); } fn encode_expn_id(&mut self, expn_id: ExpnId) { @@ -182,7 +206,12 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { self.hygiene_ctxt.borrow_mut().schedule_expn_data_for_encoding(expn_id); } expn_id.krate.encode(self); - expn_id.local_id.encode(self); + + if let Some(local_expn) = expn_id.as_local() { + self.map_local_expn(local_expn.as_u32()).encode(self); + } else { + expn_id.local_id.encode(self); + } } fn encode_span(&mut self, span: Span) { @@ -469,6 +498,16 @@ macro_rules! record_defaulted_array { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { + #[inline] + fn map_syntax_context(&self, idx: u32) -> u32 { + self.s_ctxt_remapping.map_id(idx) + } + + #[inline] + fn map_local_expn(&self, idx: u32) -> u32 { + self.local_expn_remapping.map_id(idx) + } + fn emit_lazy_distance(&mut self, position: NonZero) { let pos = position.get(); let distance = match self.lazy_state { @@ -2042,14 +2081,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &Rc::clone(&self.hygiene_ctxt), &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), |(this, syntax_contexts, _, _), index, ctxt_data| { + let index = this.map_syntax_context(index); syntax_contexts.set_some(index, this.lazy(ctxt_data)); }, |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { - if let Some(index) = index.as_local() { - expn_data_table - .set_some(index.as_raw(), this.lazy(expn_data.expect("local expn"))); + if let Some(idx) = index.as_local() { + let idx = this.map_local_expn(idx.as_u32()).into(); - expn_hash_table.set_some(index.as_raw(), this.lazy(hash)); + expn_data_table.set_some(idx, this.lazy(expn_data.expect("local expn"))); + expn_hash_table.set_some(idx, this.lazy(hash)); } }, ); @@ -2718,6 +2758,9 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); + let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); + let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); + let mut ecx = EncodeContext { opaque: encoder, metadata_hasher: Arc::clone(&metadata_hasher), @@ -2734,6 +2777,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), + s_ctxt_remapping: HygieneRemappingResults::new(s_ctxt_remapping), + local_expn_remapping: HygieneRemappingResults::new(l_expn_remapping), }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a081bd1b65e92..fd5ff99ce35c7 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -25,7 +25,7 @@ // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) use std::cell::RefCell; -use std::hash::Hash; +use std::hash::{DefaultHasher, Hash, Hasher}; use std::sync::Arc; use std::{fmt, iter, mem}; @@ -54,6 +54,13 @@ use crate::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, with_session_globals}; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); +impl From for SyntaxContext { + #[inline] + fn from(raw_id: u32) -> Self { + SyntaxContext(raw_id) + } +} + // To ensure correctness of incremental compilation, // `SyntaxContext` must not implement `Ord` or `PartialOrd`. // See https://github.com/rust-lang/rust/issues/90317. @@ -341,6 +348,102 @@ impl ExpnId { } } +pub trait HygieneEntityRemapper + Copy> { + fn hash(&self, id: TIdx) -> u64; + fn len(&self) -> usize; + fn first_non_det_index_mut(&mut self) -> &mut Option; + fn first_non_det_index(&self) -> u32; + + fn commit_end_of_determinism(&mut self) { + let len = self.len() as u32; + assert!( + self.first_non_det_index_mut().replace(len).is_none(), + "this function can be called only once" + ) + } + + fn create_remapping(&self) -> (u32, FxHashMap) { + let start = self.first_non_det_index(); + let range = start..self.len() as u32; + + let start = range.start; + + let mut indices_to_remap = range.collect::>(); + indices_to_remap.sort_by_key(|&idx| self.hash(From::from(idx))); + + let mut remapping = FxHashMap::::default(); + + for (idx, id) in indices_to_remap.into_iter().enumerate() { + let from = id; + let to = start + idx as u32; + + if from != to { + remapping.insert(from, to); + } + } + + (start, remapping) + } +} + +pub struct SyntaxContextRemapper<'a>(&'a mut HygieneData); +impl SyntaxContextRemapper<'_> { + pub fn with(f: impl FnOnce(SyntaxContextRemapper<'_>) -> T) -> T { + HygieneData::with(|data| f(SyntaxContextRemapper(data))) + } +} + +pub struct LocalExpansionRemapper<'a>(&'a mut HygieneData); +impl LocalExpansionRemapper<'_> { + pub fn with(f: impl FnOnce(LocalExpansionRemapper<'_>) -> T) -> T { + HygieneData::with(|data| f(LocalExpansionRemapper(data))) + } +} + +impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { + #[inline] + fn hash(&self, ctxt: SyntaxContext) -> u64 { + ctxt.hash_for_det_sorting(self.0) + } + + #[inline] + fn len(&self) -> usize { + self.0.syntax_context_data.len() + } + + #[inline] + fn first_non_det_index_mut(&mut self) -> &mut Option { + &mut self.0.first_s_ctxt_non_det_index + } + + #[inline] + fn first_non_det_index(&self) -> u32 { + self.0.first_s_ctxt_non_det_index.expect("must be set at this point") + } +} + +impl HygieneEntityRemapper for LocalExpansionRemapper<'_> { + #[inline] + fn hash(&self, id: LocalExpnId) -> u64 { + self.0.local_expn_hashes[id].local_hash().as_u64() + } + + #[inline] + fn len(&self) -> usize { + self.0.local_expn_data.len() + } + + #[inline] + fn first_non_det_index_mut(&mut self) -> &mut Option { + &mut self.0.first_local_expn_non_det_index + } + + #[inline] + fn first_non_det_index(&self) -> u32 { + self.0.first_local_expn_non_det_index.expect("must be set at this point") + } +} + #[derive(Debug)] pub(crate) struct HygieneData { /// Each expansion should have an associated expansion data, but sometimes there's a delay @@ -354,6 +457,8 @@ pub(crate) struct HygieneData { foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, + first_s_ctxt_non_det_index: Option, + first_local_expn_non_det_index: Option, syntax_context_map: FxHashMap, /// Maps the `local_hash` of an `ExpnData` to the next disambiguator value. /// This is used by `update_disambiguator` to keep track of which `ExpnData`s @@ -386,6 +491,8 @@ impl HygieneData { syntax_context_data: vec![root_ctxt_data], syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(), expn_data_disambiguators: UnhashMap::default(), + first_s_ctxt_non_det_index: None, + first_local_expn_non_det_index: None, } } @@ -1289,43 +1396,10 @@ impl DesugaringKind { pub struct HygieneEncodeContext { /// All `SyntaxContexts` for which we have written `SyntaxContextData` into crate metadata. serialized_ctxts: FxHashSet, - /// The `SyntaxContexts` that we have serialized (e.g. as a result of encoding `Spans`) - /// in the most recent 'round' of serializing. Serializing `SyntaxContextData` - /// may cause us to serialize more `SyntaxContext`s, so serialize in a loop - /// until we reach a fixed point. - latest_ctxts: Vec<(u32 /* Encoding index */, SyntaxContext)>, + latest_ctxts: Vec, serialized_expns: FxHashSet, latest_expns: Vec, - - /// Maps every `SyntaxContext` into its encoding index. - /// Earlier the `ctxt.0` was used when writing metadata, however, - /// this results into non-deterministic metadata (see #129094). - /// The non-determinism is encountered when decoding syntax contexts - /// in `decode_syntax_context` function below. The syntax contexts from - /// other crate metadata can be decoded in different order, which results - /// into different ids assigned to decoded syntax contexts. - /// First invocation: - /// (ALLOC - syntax context id, ORIG - original id of decoded syntax context: - /// `raw_id` in `decode_syntax_context`) - /// ALLOC: #3, ORIG: 1 - /// ALLOC: #9, ORIG: 18769 - /// ALLOC: #10, ORIG: 25868 - /// ALLOC: #11, ORIG: 18822 - /// ALLOC: #12, ORIG: 23092 - /// - /// Second invocation: - /// ALLOC: #3, ORIG: 1 - /// ALLOC: #9, ORIG: 25868 - /// ALLOC: #10, ORIG: 18769 - /// ALLOC: #11, ORIG: 18822 - /// ALLOC: #12, ORIG: 23092 - /// - /// We see that `18769` and `25868` assigned different syntax context ids, - /// however, the order of encoding is deterministic, so we can remap allocated - /// syntax context ids into encoding indices and use them, thus outputting - /// same metadata. - encoding_indices: FxHashMap, } impl Default for HygieneEncodeContext { @@ -1335,20 +1409,11 @@ impl Default for HygieneEncodeContext { latest_ctxts: Default::default(), serialized_expns: Default::default(), latest_expns: Default::default(), - // Zero is taken by root syntax context. - encoding_indices: FxHashMap::from_iter(iter::once((SyntaxContext::root(), 0))), } } } impl HygieneEncodeContext { - #[inline] - fn get_encoding_index(&mut self, ctxt: SyntaxContext) -> u32 { - let map = &mut self.encoding_indices; - let len = map.len(); - *map.entry(ctxt).or_insert(len as u32) - } - /// Record the fact that we need to serialize the corresponding `ExpnData`. #[inline] pub fn schedule_expn_data_for_encoding(&mut self, expn: ExpnId) { @@ -1381,9 +1446,9 @@ impl HygieneEncodeContext { #[allow(rustc::potential_query_instability)] let latest_contexts = { mem::take(&mut h_ctxt.borrow_mut().latest_ctxts) }.into_iter(); - for (idx, ctxt) in latest_contexts { + for ctxt in latest_contexts { let key = HygieneData::with(|data| data.syntax_context_data[ctxt.0 as usize].key()); - encode_ctxt(encoder, idx, &key); + encode_ctxt(encoder, ctxt.0, &key); } // Same as above, but for expansions instead of syntax contexts. @@ -1408,17 +1473,11 @@ impl HygieneEncodeContext { #[inline] pub fn get_syntax_ctxt_encoding_index(&mut self, ctxt: SyntaxContext) -> u32 { - let index = self.get_encoding_index(ctxt); if self.serialized_ctxts.insert(ctxt) { - // If we created new encoding index then it is greater - // than any previous index, so this vector is in ascending order. - // We can't push existing, possibly out-of-order, index - // as we check if we already saw this syntax context above. - // This property is important for deterministic output (see #129094). - self.latest_ctxts.push((index, ctxt)); + self.latest_ctxts.push(ctxt); } - index + ctxt.0 } } @@ -1606,6 +1665,23 @@ impl StableHash for SyntaxContext { } } +impl SyntaxContext { + fn hash_for_det_sorting(self, data: &HygieneData) -> u64 { + if self.is_root() { + return 0; + } + + let (expn_id, transparency) = data.outer_mark(self); + let hash = data.expn_hash(expn_id).0; + + let mut state = DefaultHasher::new(); + hash.hash(&mut state); + transparency.hash(&mut state); + + state.finish() + } +} + impl StableHash for ExpnId { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { hcx.assert_default_stable_hash_controls("ExpnId"); From d13d31f2ab0a5b94a1bcc4045f119f5f274d7ebb Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 8 Oct 2026 14:34:09 +0300 Subject: [PATCH 02/11] Small fixes --- compiler/rustc_span/src/hygiene.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index fd5ff99ce35c7..65c6fa24125fa 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -366,9 +366,7 @@ pub trait HygieneEntityRemapper + Copy> { let start = self.first_non_det_index(); let range = start..self.len() as u32; - let start = range.start; - - let mut indices_to_remap = range.collect::>(); + let mut indices_to_remap = range.collect::>(); indices_to_remap.sort_by_key(|&idx| self.hash(From::from(idx))); let mut remapping = FxHashMap::::default(); From 1e1c7321868b2a9523cb97759ddacb4f79b71f05 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 9 Oct 2026 14:06:44 +0300 Subject: [PATCH 03/11] Use vectors for remapping, initialize remapping structs in `EncodeContext` only when it is needed --- compiler/rustc_metadata/src/rmeta/encoder.rs | 27 ++++++++++++-------- compiler/rustc_span/src/hygiene.rs | 17 +++++------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 02d4595736aff..2b88e26ad1add 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -51,22 +51,26 @@ use crate::diagnostics::{FailCreateFileEncoder, FailWriteFile}; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::*; +#[derive(Default)] struct HygieneRemappingResults { - remapping: FxHashMap, + remapping: Vec, first_non_det_index: u32, + last_idx: u32, } impl HygieneRemappingResults { - fn new((idx, remapping): (u32, FxHashMap)) -> HygieneRemappingResults { - HygieneRemappingResults { remapping, first_non_det_index: idx } + fn new((idx, last_idx, remapping): (u32, u32, Vec)) -> HygieneRemappingResults { + HygieneRemappingResults { remapping, last_idx, first_non_det_index: idx } } #[inline] fn map_id(&self, idx: u32) -> u32 { - if idx < self.first_non_det_index { + // New hygiene entities can be created during metadata encoding, + // so we need both checks. + if idx < self.first_non_det_index || idx > self.last_idx { idx } else { - self.remapping.get(&idx).copied().unwrap_or(idx) + self.remapping[(idx - self.first_non_det_index) as usize] } } } @@ -2668,6 +2672,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { with_encode_metadata_header(tcx, path, |ecx| { // Encode all the entries and extra information in the crate, // culminating in the `CrateRoot` which points to all of it. + let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); + let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); + + ecx.s_ctxt_remapping = HygieneRemappingResults::new(s_ctxt_remapping); + ecx.local_expn_remapping = HygieneRemappingResults::new(l_expn_remapping); + let (root, unhashed) = ecx.encode_crate_root(); // Flush buffer to ensure backing file has the correct size. @@ -2758,9 +2768,6 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); - let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); - let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); - let mut ecx = EncodeContext { opaque: encoder, metadata_hasher: Arc::clone(&metadata_hasher), @@ -2777,8 +2784,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), - s_ctxt_remapping: HygieneRemappingResults::new(s_ctxt_remapping), - local_expn_remapping: HygieneRemappingResults::new(l_expn_remapping), + s_ctxt_remapping: Default::default(), + local_expn_remapping: Default::default(), }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 65c6fa24125fa..88ecb3c7b2989 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -362,25 +362,20 @@ pub trait HygieneEntityRemapper + Copy> { ) } - fn create_remapping(&self) -> (u32, FxHashMap) { + fn create_remapping(&self) -> (u32, u32, Vec) { let start = self.first_non_det_index(); - let range = start..self.len() as u32; + let end = self.len() as u32; - let mut indices_to_remap = range.collect::>(); + let mut indices_to_remap = (start..end).collect::>(); indices_to_remap.sort_by_key(|&idx| self.hash(From::from(idx))); - let mut remapping = FxHashMap::::default(); + let mut remapping = vec![0; indices_to_remap.len()]; for (idx, id) in indices_to_remap.into_iter().enumerate() { - let from = id; - let to = start + idx as u32; - - if from != to { - remapping.insert(from, to); - } + remapping[(id - start) as usize] = start + idx as u32; } - (start, remapping) + (start, end - 1, remapping) } } From ead8ac45aa2e9d144205a90daefc9208e6793516 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 13 Oct 2026 12:52:53 +0300 Subject: [PATCH 04/11] Revert "Use vectors for remapping, initialize remapping structs in `EncodeContext` only when it is needed" This reverts commit 9c7b7fdbcb50d869d9ddae2957e736e62f13385c. --- compiler/rustc_metadata/src/rmeta/encoder.rs | 27 ++++++++------------ compiler/rustc_span/src/hygiene.rs | 17 +++++++----- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2b88e26ad1add..02d4595736aff 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -51,26 +51,22 @@ use crate::diagnostics::{FailCreateFileEncoder, FailWriteFile}; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::*; -#[derive(Default)] struct HygieneRemappingResults { - remapping: Vec, + remapping: FxHashMap, first_non_det_index: u32, - last_idx: u32, } impl HygieneRemappingResults { - fn new((idx, last_idx, remapping): (u32, u32, Vec)) -> HygieneRemappingResults { - HygieneRemappingResults { remapping, last_idx, first_non_det_index: idx } + fn new((idx, remapping): (u32, FxHashMap)) -> HygieneRemappingResults { + HygieneRemappingResults { remapping, first_non_det_index: idx } } #[inline] fn map_id(&self, idx: u32) -> u32 { - // New hygiene entities can be created during metadata encoding, - // so we need both checks. - if idx < self.first_non_det_index || idx > self.last_idx { + if idx < self.first_non_det_index { idx } else { - self.remapping[(idx - self.first_non_det_index) as usize] + self.remapping.get(&idx).copied().unwrap_or(idx) } } } @@ -2672,12 +2668,6 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { with_encode_metadata_header(tcx, path, |ecx| { // Encode all the entries and extra information in the crate, // culminating in the `CrateRoot` which points to all of it. - let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); - let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); - - ecx.s_ctxt_remapping = HygieneRemappingResults::new(s_ctxt_remapping); - ecx.local_expn_remapping = HygieneRemappingResults::new(l_expn_remapping); - let (root, unhashed) = ecx.encode_crate_root(); // Flush buffer to ensure backing file has the correct size. @@ -2768,6 +2758,9 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); + let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); + let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); + let mut ecx = EncodeContext { opaque: encoder, metadata_hasher: Arc::clone(&metadata_hasher), @@ -2784,8 +2777,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), - s_ctxt_remapping: Default::default(), - local_expn_remapping: Default::default(), + s_ctxt_remapping: HygieneRemappingResults::new(s_ctxt_remapping), + local_expn_remapping: HygieneRemappingResults::new(l_expn_remapping), }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 88ecb3c7b2989..65c6fa24125fa 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -362,20 +362,25 @@ pub trait HygieneEntityRemapper + Copy> { ) } - fn create_remapping(&self) -> (u32, u32, Vec) { + fn create_remapping(&self) -> (u32, FxHashMap) { let start = self.first_non_det_index(); - let end = self.len() as u32; + let range = start..self.len() as u32; - let mut indices_to_remap = (start..end).collect::>(); + let mut indices_to_remap = range.collect::>(); indices_to_remap.sort_by_key(|&idx| self.hash(From::from(idx))); - let mut remapping = vec![0; indices_to_remap.len()]; + let mut remapping = FxHashMap::::default(); for (idx, id) in indices_to_remap.into_iter().enumerate() { - remapping[(id - start) as usize] = start + idx as u32; + let from = id; + let to = start + idx as u32; + + if from != to { + remapping.insert(from, to); + } } - (start, end - 1, remapping) + (start, remapping) } } From 6321315ed61391f288e3d08c381204fee973b95a Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 13 Oct 2026 19:07:16 +0300 Subject: [PATCH 05/11] Better perf maybe --- compiler/rustc_span/src/hygiene.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 65c6fa24125fa..4d3b3c9f1e59d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -54,13 +54,6 @@ use crate::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, with_session_globals}; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); -impl From for SyntaxContext { - #[inline] - fn from(raw_id: u32) -> Self { - SyntaxContext(raw_id) - } -} - // To ensure correctness of incremental compilation, // `SyntaxContext` must not implement `Ord` or `PartialOrd`. // See https://github.com/rust-lang/rust/issues/90317. @@ -348,8 +341,8 @@ impl ExpnId { } } -pub trait HygieneEntityRemapper + Copy> { - fn hash(&self, id: TIdx) -> u64; +pub trait HygieneEntityRemapper { + fn hash(&self, raw_id: u32) -> u64; fn len(&self) -> usize; fn first_non_det_index_mut(&mut self) -> &mut Option; fn first_non_det_index(&self) -> u32; @@ -367,7 +360,7 @@ pub trait HygieneEntityRemapper + Copy> { let range = start..self.len() as u32; let mut indices_to_remap = range.collect::>(); - indices_to_remap.sort_by_key(|&idx| self.hash(From::from(idx))); + indices_to_remap.sort_by_cached_key(|&idx| self.hash(idx)); let mut remapping = FxHashMap::::default(); @@ -398,10 +391,10 @@ impl LocalExpansionRemapper<'_> { } } -impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { +impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { #[inline] - fn hash(&self, ctxt: SyntaxContext) -> u64 { - ctxt.hash_for_det_sorting(self.0) + fn hash(&self, raw_id: u32) -> u64 { + SyntaxContext(raw_id).hash_for_det_sorting(self.0) } #[inline] @@ -420,10 +413,10 @@ impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { } } -impl HygieneEntityRemapper for LocalExpansionRemapper<'_> { +impl HygieneEntityRemapper for LocalExpansionRemapper<'_> { #[inline] - fn hash(&self, id: LocalExpnId) -> u64 { - self.0.local_expn_hashes[id].local_hash().as_u64() + fn hash(&self, raw_id: u32) -> u64 { + self.0.local_expn_hashes[LocalExpnId::from_u32(raw_id)].local_hash().as_u64() } #[inline] From 309f5aede2af7e797e1833d5d3c96cfb89543118 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 13:18:06 +0300 Subject: [PATCH 06/11] Address some review comments --- compiler/rustc_hir_id/src/definitions.rs | 10 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 71 ++++---------- .../rustc_middle/src/query/on_disk_cache.rs | 8 +- compiler/rustc_span/src/hygiene.rs | 98 ++++++++++++------- 4 files changed, 94 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index f8f12bafbcc0a..aeb6b188ddb48 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -18,7 +18,7 @@ pub use rustc_span::def_id::DefPathHash; use rustc_span::def_id::{ CRATE_DEF_INDEX, CrateNum, DefIndex, LOCAL_CRATE, LocalDefId, LocalDefIdMap, StableCrateId, }; -use rustc_span::hygiene::{HygieneEntityRemapper, LocalExpansionRemapper, SyntaxContextRemapper}; +use rustc_span::hygiene::{HygieneRemapper, LocalExpnIdRemapper, SyntaxContextRemapper}; use rustc_span::{SpanEncoder, Symbol, kw, sym}; use tracing::{debug, instrument}; @@ -306,13 +306,11 @@ impl Definitions { /// This function indicates that the order of def id allocations /// may be non-deterministic after it was called. pub fn commit_end_of_determinism(&mut self) { - assert!( - self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()).is_none(), - "this function should be called only once" - ); + let prev = self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()); + assert!(prev.is_none(), "this function should be called only once"); SyntaxContextRemapper::with(|mut r| r.commit_end_of_determinism()); - LocalExpansionRemapper::with(|mut r| r.commit_end_of_determinism()); + LocalExpnIdRemapper::with(|mut r| r.commit_end_of_determinism()); } #[inline(always)] diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 02d4595736aff..830cadef2087c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -37,11 +37,12 @@ use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{OptLevel, OutputType, TargetModifier}; use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::hygiene::{ - HygieneEncodeContext, HygieneEntityRemapper, LocalExpansionRemapper, SyntaxContextRemapper, + HygieneEncodeContext, HygieneRemapper, HygieneRemappingResults, LocalExpnIdRemapper, + SyntaxContextRemapper, }; use rustc_span::{ - ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, - Symbol, SyntaxContext, bug, span_bug, sym, + ByteSymbol, ExternalSource, FileName, LocalExpnId, SourceFile, SpanData, SpanEncoder, + StableSourceFileId, Symbol, SyntaxContext, bug, span_bug, sym, }; use rustc_structures::CrateType; use tracing::{debug, instrument, trace}; @@ -51,26 +52,6 @@ use crate::diagnostics::{FailCreateFileEncoder, FailWriteFile}; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::*; -struct HygieneRemappingResults { - remapping: FxHashMap, - first_non_det_index: u32, -} - -impl HygieneRemappingResults { - fn new((idx, remapping): (u32, FxHashMap)) -> HygieneRemappingResults { - HygieneRemappingResults { remapping, first_non_det_index: idx } - } - - #[inline] - fn map_id(&self, idx: u32) -> u32 { - if idx < self.first_non_det_index { - idx - } else { - self.remapping.get(&idx).copied().unwrap_or(idx) - } - } -} - pub(super) struct EncodeContext<'a, 'tcx> { opaque: FileEncoder<'a>, metadata_hasher: Arc>, @@ -100,8 +81,8 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: Rc>, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, - s_ctxt_remapping: HygieneRemappingResults, - local_expn_remapping: HygieneRemappingResults, + s_ctxt_remapping: HygieneRemappingResults, + l_expn_remapping: HygieneRemappingResults, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -193,8 +174,8 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { - let idx = self.hygiene_ctxt.borrow_mut().get_syntax_ctxt_encoding_index(syntax_context); - self.map_syntax_context(idx).encode(self); + self.hygiene_ctxt.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); + self.emit_u32(self.s_ctxt_remapping.map_id(syntax_context).into()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { @@ -208,7 +189,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { expn_id.krate.encode(self); if let Some(local_expn) = expn_id.as_local() { - self.map_local_expn(local_expn.as_u32()).encode(self); + self.l_expn_remapping.map_id(local_expn).as_u32().encode(self); } else { expn_id.local_id.encode(self); } @@ -498,16 +479,6 @@ macro_rules! record_defaulted_array { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - #[inline] - fn map_syntax_context(&self, idx: u32) -> u32 { - self.s_ctxt_remapping.map_id(idx) - } - - #[inline] - fn map_local_expn(&self, idx: u32) -> u32 { - self.local_expn_remapping.map_id(idx) - } - fn emit_lazy_distance(&mut self, position: NonZero) { let pos = position.get(); let distance = match self.lazy_state { @@ -2080,16 +2051,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { HygieneEncodeContext::encode( &Rc::clone(&self.hygiene_ctxt), &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), - |(this, syntax_contexts, _, _), index, ctxt_data| { - let index = this.map_syntax_context(index); - syntax_contexts.set_some(index, this.lazy(ctxt_data)); + |(this, syntax_contexts, _, _), context, ctxt_data| { + let context = this.s_ctxt_remapping.map_id(context); + syntax_contexts.set_some(context.into(), this.lazy(ctxt_data)); }, - |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { - if let Some(idx) = index.as_local() { - let idx = this.map_local_expn(idx.as_u32()).into(); + |(this, _, expn_data_table, expn_hash_table), expn_id, expn_data, hash| { + if let Some(expn_id) = expn_id.as_local() { + let expn_idx = this.l_expn_remapping.map_id(expn_id).as_raw(); - expn_data_table.set_some(idx, this.lazy(expn_data.expect("local expn"))); - expn_hash_table.set_some(idx, this.lazy(hash)); + expn_data_table.set_some(expn_idx, this.lazy(expn_data.expect("local expn"))); + expn_hash_table.set_some(expn_idx, this.lazy(hash)); } }, ); @@ -2758,8 +2729,8 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); - let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.create_remapping()); - let l_expn_remapping = LocalExpansionRemapper::with(|r| r.create_remapping()); + let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.remapping_results()); + let l_expn_remapping = LocalExpnIdRemapper::with(|r| r.remapping_results()); let mut ecx = EncodeContext { opaque: encoder, @@ -2777,8 +2748,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), - s_ctxt_remapping: HygieneRemappingResults::new(s_ctxt_remapping), - local_expn_remapping: HygieneRemappingResults::new(l_expn_remapping), + s_ctxt_remapping, + l_expn_remapping, }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 7383ad23273be..21b25ce4c0450 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -283,10 +283,10 @@ impl OnDiskCache { HygieneEncodeContext::encode( &Rc::clone(&encoder.hygiene_context), &mut encoder, - |encoder, index, ctxt_data| { + |encoder, context, ctxt_data| { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); - syntax_contexts.insert(index, pos); + syntax_contexts.insert(context.into(), pos); }, |encoder, expn_id, data, hash| { if expn_id.krate == LOCAL_CRATE { @@ -838,8 +838,8 @@ impl<'tcx> CacheEncoder<'tcx> { impl<'tcx> SpanEncoder for CacheEncoder<'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { - let idx = self.hygiene_context.borrow_mut().get_syntax_ctxt_encoding_index(syntax_context); - idx.encode(self); + self.hygiene_context.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); + self.emit_u32(syntax_context.into()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 4d3b3c9f1e59d..4540167b94075 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -54,6 +54,20 @@ use crate::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, with_session_globals}; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); +impl From for SyntaxContext { + #[inline(always)] + fn from(value: u32) -> SyntaxContext { + SyntaxContext(value) + } +} + +impl Into for SyntaxContext { + #[inline(always)] + fn into(self) -> u32 { + self.0 + } +} + // To ensure correctness of incremental compilation, // `SyntaxContext` must not implement `Ord` or `PartialOrd`. // See https://github.com/rust-lang/rust/issues/90317. @@ -341,39 +355,56 @@ impl ExpnId { } } -pub trait HygieneEntityRemapper { - fn hash(&self, raw_id: u32) -> u64; +pub struct HygieneRemappingResults> { + pub remapping: FxHashMap, + pub first_non_det_index: T, +} + +impl + Copy> HygieneRemappingResults { + #[inline] + pub fn map_id(&self, idx: T) -> T { + if idx.into() < self.first_non_det_index.into() { + idx + } else { + self.remapping.get(&idx).copied().unwrap_or(idx) + } + } +} + +/// Includes common logic for remapping both expansion ids and syntax contexts. +pub trait HygieneRemapper + Into + Hash + Eq + Copy> { + fn hash(&self, key: TKey) -> u64; fn len(&self) -> usize; - fn first_non_det_index_mut(&mut self) -> &mut Option; + fn first_non_det_index_mut(&mut self) -> &mut Option; fn first_non_det_index(&self) -> u32; fn commit_end_of_determinism(&mut self) { let len = self.len() as u32; assert!( - self.first_non_det_index_mut().replace(len).is_none(), + self.first_non_det_index_mut().replace(TKey::from(len)).is_none(), "this function can be called only once" ) } - fn create_remapping(&self) -> (u32, FxHashMap) { + fn remapping_results(&self) -> HygieneRemappingResults { let start = self.first_non_det_index(); let range = start..self.len() as u32; - let mut indices_to_remap = range.collect::>(); - indices_to_remap.sort_by_cached_key(|&idx| self.hash(idx)); + let mut indices_to_remap = range.map(|raw_id| TKey::from(raw_id)).collect::>(); + indices_to_remap.sort_by_cached_key(|&key| self.hash(key)); - let mut remapping = FxHashMap::::default(); + let mut remapping = FxHashMap::::default(); - for (idx, id) in indices_to_remap.into_iter().enumerate() { - let from = id; - let to = start + idx as u32; + for (idx, key) in indices_to_remap.into_iter().enumerate() { + let from = key; + let to = TKey::from(start + idx as u32); if from != to { remapping.insert(from, to); } } - (start, remapping) + HygieneRemappingResults { remapping, first_non_det_index: TKey::from(start) } } } @@ -384,17 +415,17 @@ impl SyntaxContextRemapper<'_> { } } -pub struct LocalExpansionRemapper<'a>(&'a mut HygieneData); -impl LocalExpansionRemapper<'_> { - pub fn with(f: impl FnOnce(LocalExpansionRemapper<'_>) -> T) -> T { - HygieneData::with(|data| f(LocalExpansionRemapper(data))) +pub struct LocalExpnIdRemapper<'a>(&'a mut HygieneData); +impl LocalExpnIdRemapper<'_> { + pub fn with(f: impl FnOnce(LocalExpnIdRemapper<'_>) -> T) -> T { + HygieneData::with(|data| f(LocalExpnIdRemapper(data))) } } -impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { +impl HygieneRemapper for SyntaxContextRemapper<'_> { #[inline] - fn hash(&self, raw_id: u32) -> u64 { - SyntaxContext(raw_id).hash_for_det_sorting(self.0) + fn hash(&self, key: SyntaxContext) -> u64 { + key.hash_for_det_sorting(self.0) } #[inline] @@ -403,20 +434,20 @@ impl HygieneEntityRemapper for SyntaxContextRemapper<'_> { } #[inline] - fn first_non_det_index_mut(&mut self) -> &mut Option { + fn first_non_det_index_mut(&mut self) -> &mut Option { &mut self.0.first_s_ctxt_non_det_index } #[inline] fn first_non_det_index(&self) -> u32 { - self.0.first_s_ctxt_non_det_index.expect("must be set at this point") + self.0.first_s_ctxt_non_det_index.map(|s| s.as_u32()).expect("must be set at this point") } } -impl HygieneEntityRemapper for LocalExpansionRemapper<'_> { +impl HygieneRemapper for LocalExpnIdRemapper<'_> { #[inline] - fn hash(&self, raw_id: u32) -> u64 { - self.0.local_expn_hashes[LocalExpnId::from_u32(raw_id)].local_hash().as_u64() + fn hash(&self, key: LocalExpnId) -> u64 { + self.0.local_expn_hashes[key].local_hash().as_u64() } #[inline] @@ -425,13 +456,16 @@ impl HygieneEntityRemapper for LocalExpansionRemapper<'_> { } #[inline] - fn first_non_det_index_mut(&mut self) -> &mut Option { + fn first_non_det_index_mut(&mut self) -> &mut Option { &mut self.0.first_local_expn_non_det_index } #[inline] fn first_non_det_index(&self) -> u32 { - self.0.first_local_expn_non_det_index.expect("must be set at this point") + self.0 + .first_local_expn_non_det_index + .map(|l| l.as_u32()) + .expect("must be set at this point") } } @@ -448,8 +482,8 @@ pub(crate) struct HygieneData { foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, - first_s_ctxt_non_det_index: Option, - first_local_expn_non_det_index: Option, + first_s_ctxt_non_det_index: Option, + first_local_expn_non_det_index: Option, syntax_context_map: FxHashMap, /// Maps the `local_hash` of an `ExpnData` to the next disambiguator value. /// This is used by `update_disambiguator` to keep track of which `ExpnData`s @@ -1416,7 +1450,7 @@ impl HygieneEncodeContext { pub fn encode( h_ctxt: &RefCell, encoder: &mut T, - mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextKey), + mut encode_ctxt: impl FnMut(&mut T, SyntaxContext, &SyntaxContextKey), mut encode_expn: impl FnMut(&mut T, ExpnId, Option<&ExpnData>, ExpnHash), ) { // When we serialize a `SyntaxContextData`, we may end up serializing @@ -1439,7 +1473,7 @@ impl HygieneEncodeContext { for ctxt in latest_contexts { let key = HygieneData::with(|data| data.syntax_context_data[ctxt.0 as usize].key()); - encode_ctxt(encoder, ctxt.0, &key); + encode_ctxt(encoder, ctxt, &key); } // Same as above, but for expansions instead of syntax contexts. @@ -1463,12 +1497,10 @@ impl HygieneEncodeContext { } #[inline] - pub fn get_syntax_ctxt_encoding_index(&mut self, ctxt: SyntaxContext) -> u32 { + pub fn enqueue_syntax_context_for_encoding(&mut self, ctxt: SyntaxContext) { if self.serialized_ctxts.insert(ctxt) { self.latest_ctxts.push(ctxt); } - - ctxt.0 } } From 17dc5b6133c21c87de683533369da9f177d9b3c6 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 13:37:09 +0300 Subject: [PATCH 07/11] Address review comments --- compiler/rustc_metadata/src/rmeta/encoder.rs | 25 ++++++++-------- compiler/rustc_span/src/hygiene.rs | 31 ++++++++------------ 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 830cadef2087c..9fadafa787674 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -81,8 +81,8 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: Rc>, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, - s_ctxt_remapping: HygieneRemappingResults, - l_expn_remapping: HygieneRemappingResults, + syntax_context_remapping: HygieneRemappingResults, + local_expn_id_remapping: HygieneRemappingResults, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -175,7 +175,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { self.hygiene_ctxt.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); - self.emit_u32(self.s_ctxt_remapping.map_id(syntax_context).into()); + self.emit_u32(self.syntax_context_remapping.map_id(syntax_context).into()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { @@ -188,11 +188,12 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } expn_id.krate.encode(self); - if let Some(local_expn) = expn_id.as_local() { - self.l_expn_remapping.map_id(local_expn).as_u32().encode(self); - } else { - expn_id.local_id.encode(self); - } + let expn_index = match expn_id.as_local() { + Some(local_expn) => self.local_expn_id_remapping.map_id(local_expn).as_raw(), + None => expn_id.local_id, + }; + + expn_index.encode(self); } fn encode_span(&mut self, span: Span) { @@ -2052,12 +2053,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &Rc::clone(&self.hygiene_ctxt), &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), |(this, syntax_contexts, _, _), context, ctxt_data| { - let context = this.s_ctxt_remapping.map_id(context); + let context = this.syntax_context_remapping.map_id(context); syntax_contexts.set_some(context.into(), this.lazy(ctxt_data)); }, |(this, _, expn_data_table, expn_hash_table), expn_id, expn_data, hash| { if let Some(expn_id) = expn_id.as_local() { - let expn_idx = this.l_expn_remapping.map_id(expn_id).as_raw(); + let expn_idx = this.local_expn_id_remapping.map_id(expn_id).as_raw(); expn_data_table.set_some(expn_idx, this.lazy(expn_data.expect("local expn"))); expn_hash_table.set_some(expn_idx, this.lazy(hash)); @@ -2748,8 +2749,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), - s_ctxt_remapping, - l_expn_remapping, + syntax_context_remapping: s_ctxt_remapping, + local_expn_id_remapping: l_expn_remapping, }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 4540167b94075..54afb64c79b0f 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -423,9 +423,19 @@ impl LocalExpnIdRemapper<'_> { } impl HygieneRemapper for SyntaxContextRemapper<'_> { - #[inline] fn hash(&self, key: SyntaxContext) -> u64 { - key.hash_for_det_sorting(self.0) + if key.is_root() { + return 0; + } + + let (expn_id, transparency) = self.0.outer_mark(key); + let hash = self.0.expn_hash(expn_id).0; + + let mut state = DefaultHasher::new(); + hash.hash(&mut state); + transparency.hash(&mut state); + + state.finish() } #[inline] @@ -1688,23 +1698,6 @@ impl StableHash for SyntaxContext { } } -impl SyntaxContext { - fn hash_for_det_sorting(self, data: &HygieneData) -> u64 { - if self.is_root() { - return 0; - } - - let (expn_id, transparency) = data.outer_mark(self); - let hash = data.expn_hash(expn_id).0; - - let mut state = DefaultHasher::new(); - hash.hash(&mut state); - transparency.hash(&mut state); - - state.finish() - } -} - impl StableHash for ExpnId { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { hcx.assert_default_stable_hash_controls("ExpnId"); From 1d7ab7887375e7e4664018eaedebd4806fe71e71 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 13:40:07 +0300 Subject: [PATCH 08/11] Small cleanups --- compiler/rustc_span/src/hygiene.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 54afb64c79b0f..24300982881a1 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -380,10 +380,9 @@ pub trait HygieneRemapper + Into + Hash + Eq + Copy> { fn commit_end_of_determinism(&mut self) { let len = self.len() as u32; - assert!( - self.first_non_det_index_mut().replace(TKey::from(len)).is_none(), - "this function can be called only once" - ) + + let prev = self.first_non_det_index_mut().replace(TKey::from(len)); + assert!(prev.is_none(), "this function can be called only once") } fn remapping_results(&self) -> HygieneRemappingResults { From 90dbc88ef73cdc01bf565ed0820c55d96bf71686 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 13:51:09 +0300 Subject: [PATCH 09/11] Connect different hashings of syntax context --- compiler/rustc_span/src/hygiene.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 24300982881a1..b13291815be14 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -427,8 +427,7 @@ impl HygieneRemapper for SyntaxContextRemapper<'_> { return 0; } - let (expn_id, transparency) = self.0.outer_mark(key); - let hash = self.0.expn_hash(expn_id).0; + let (hash, transparency) = key.get_data_for_hashing(self.0); let mut state = DefaultHasher::new(); hash.hash(&mut state); @@ -1675,6 +1674,13 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut hcx: impl StableHashCtxt) ExpnHash::new(LOCAL_CRATE.as_def_id().to_stable_hash_key(&mut hcx).stable_crate_id(), expn_hash) } +impl SyntaxContext { + fn get_data_for_hashing(&self, data: &HygieneData) -> (Fingerprint, Transparency) { + let (expn_id, transparency) = data.outer_mark(*self); + (data.expn_hash(expn_id).0, transparency) + } +} + impl StableHash for SyntaxContext { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { const TAG_EXPANSION: u8 = 0; @@ -1687,10 +1693,7 @@ impl StableHash for SyntaxContext { // This duplicates `ExpnId::stable_hash`, but reads the outer mark and // expansion hash in a single `HygieneData::with` call to avoid extra locking. hcx.assert_default_stable_hash_controls("ExpnId"); - let (hash, transparency) = HygieneData::with(|data| { - let (expn_id, transparency) = data.outer_mark(*self); - (data.expn_hash(expn_id).0, transparency) - }); + let (hash, transparency) = HygieneData::with(|data| self.get_data_for_hashing(data)); hash.stable_hash(hcx, hasher); transparency.stable_hash(hcx, hasher); } From e0cfd893315a9314bcb0abf981370bdd10c34fdd Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 15:44:07 +0300 Subject: [PATCH 10/11] Address review comments --- compiler/rustc_metadata/src/rmeta/encoder.rs | 22 +++++----- .../rustc_middle/src/query/on_disk_cache.rs | 6 +-- compiler/rustc_span/src/hygiene.rs | 41 +++++++++---------- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 9fadafa787674..b1ded8a18fa00 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -175,7 +175,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { self.hygiene_ctxt.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); - self.emit_u32(self.syntax_context_remapping.map_id(syntax_context).into()); + self.emit_u32(self.syntax_context_remapping.map_id(syntax_context).as_u32()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { @@ -2052,16 +2052,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { HygieneEncodeContext::encode( &Rc::clone(&self.hygiene_ctxt), &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), - |(this, syntax_contexts, _, _), context, ctxt_data| { - let context = this.syntax_context_remapping.map_id(context); - syntax_contexts.set_some(context.into(), this.lazy(ctxt_data)); + |(this, syntax_contexts, _, _), ctxt, ctxt_data| { + let context = this.syntax_context_remapping.map_id(ctxt); + syntax_contexts.set_some(context.as_u32(), this.lazy(ctxt_data)); }, |(this, _, expn_data_table, expn_hash_table), expn_id, expn_data, hash| { if let Some(expn_id) = expn_id.as_local() { - let expn_idx = this.local_expn_id_remapping.map_id(expn_id).as_raw(); + let expn_index = this.local_expn_id_remapping.map_id(expn_id).as_raw(); - expn_data_table.set_some(expn_idx, this.lazy(expn_data.expect("local expn"))); - expn_hash_table.set_some(expn_idx, this.lazy(hash)); + expn_data_table.set_some(expn_index, this.lazy(expn_data.expect("local expn"))); + expn_hash_table.set_some(expn_index, this.lazy(hash)); } }, ); @@ -2730,8 +2730,8 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); - let s_ctxt_remapping = SyntaxContextRemapper::with(|r| r.remapping_results()); - let l_expn_remapping = LocalExpnIdRemapper::with(|r| r.remapping_results()); + let syntax_context_remapping = SyntaxContextRemapper::with(|r| r.remapping_results()); + let local_expn_id_remapping = LocalExpnIdRemapper::with(|r| r.remapping_results()); let mut ecx = EncodeContext { opaque: encoder, @@ -2749,8 +2749,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: Default::default(), symbol_index_table: Default::default(), - syntax_context_remapping: s_ctxt_remapping, - local_expn_id_remapping: l_expn_remapping, + syntax_context_remapping, + local_expn_id_remapping, }; // Encode the rustc version string in a predictable location. diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 21b25ce4c0450..7658112358d7c 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -283,10 +283,10 @@ impl OnDiskCache { HygieneEncodeContext::encode( &Rc::clone(&encoder.hygiene_context), &mut encoder, - |encoder, context, ctxt_data| { + |encoder, ctxt, ctxt_data| { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); - syntax_contexts.insert(context.into(), pos); + syntax_contexts.insert(ctxt.as_u32(), pos); }, |encoder, expn_id, data, hash| { if expn_id.krate == LOCAL_CRATE { @@ -839,7 +839,7 @@ impl<'tcx> CacheEncoder<'tcx> { impl<'tcx> SpanEncoder for CacheEncoder<'tcx> { fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { self.hygiene_context.borrow_mut().enqueue_syntax_context_for_encoding(syntax_context); - self.emit_u32(syntax_context.into()); + self.emit_u32(syntax_context.as_u32()); } fn encode_expn_id(&mut self, expn_id: ExpnId) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index b13291815be14..2302eb811f56b 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -355,7 +355,7 @@ impl ExpnId { } } -pub struct HygieneRemappingResults> { +pub struct HygieneRemappingResults { pub remapping: FxHashMap, pub first_non_det_index: T, } @@ -372,38 +372,38 @@ impl + Copy> HygieneRemappingResults { } /// Includes common logic for remapping both expansion ids and syntax contexts. -pub trait HygieneRemapper + Into + Hash + Eq + Copy> { - fn hash(&self, key: TKey) -> u64; +pub trait HygieneRemapper + Hash + Eq + Copy> { + fn hash(&self, key: T) -> u64; fn len(&self) -> usize; - fn first_non_det_index_mut(&mut self) -> &mut Option; + fn first_non_det_index_mut(&mut self) -> &mut Option; fn first_non_det_index(&self) -> u32; fn commit_end_of_determinism(&mut self) { let len = self.len() as u32; - let prev = self.first_non_det_index_mut().replace(TKey::from(len)); + let prev = self.first_non_det_index_mut().replace(T::from(len)); assert!(prev.is_none(), "this function can be called only once") } - fn remapping_results(&self) -> HygieneRemappingResults { + fn remapping_results(&self) -> HygieneRemappingResults { let start = self.first_non_det_index(); let range = start..self.len() as u32; - let mut indices_to_remap = range.map(|raw_id| TKey::from(raw_id)).collect::>(); + let mut indices_to_remap = range.map(|raw_id| T::from(raw_id)).collect::>(); indices_to_remap.sort_by_cached_key(|&key| self.hash(key)); - let mut remapping = FxHashMap::::default(); + let mut remapping = FxHashMap::::default(); for (idx, key) in indices_to_remap.into_iter().enumerate() { let from = key; - let to = TKey::from(start + idx as u32); + let to = T::from(start + idx as u32); if from != to { remapping.insert(from, to); } } - HygieneRemappingResults { remapping, first_non_det_index: TKey::from(start) } + HygieneRemappingResults { remapping, first_non_det_index: T::from(start) } } } @@ -443,12 +443,12 @@ impl HygieneRemapper for SyntaxContextRemapper<'_> { #[inline] fn first_non_det_index_mut(&mut self) -> &mut Option { - &mut self.0.first_s_ctxt_non_det_index + &mut self.0.first_non_det_syntax_context } #[inline] fn first_non_det_index(&self) -> u32 { - self.0.first_s_ctxt_non_det_index.map(|s| s.as_u32()).expect("must be set at this point") + self.0.first_non_det_syntax_context.map(|s| s.as_u32()).expect("must be set at this point") } } @@ -465,15 +465,12 @@ impl HygieneRemapper for LocalExpnIdRemapper<'_> { #[inline] fn first_non_det_index_mut(&mut self) -> &mut Option { - &mut self.0.first_local_expn_non_det_index + &mut self.0.first_non_det_local_expn_id } #[inline] fn first_non_det_index(&self) -> u32 { - self.0 - .first_local_expn_non_det_index - .map(|l| l.as_u32()) - .expect("must be set at this point") + self.0.first_non_det_local_expn_id.map(|l| l.as_u32()).expect("must be set at this point") } } @@ -490,8 +487,8 @@ pub(crate) struct HygieneData { foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, syntax_context_data: Vec, - first_s_ctxt_non_det_index: Option, - first_local_expn_non_det_index: Option, + first_non_det_syntax_context: Option, + first_non_det_local_expn_id: Option, syntax_context_map: FxHashMap, /// Maps the `local_hash` of an `ExpnData` to the next disambiguator value. /// This is used by `update_disambiguator` to keep track of which `ExpnData`s @@ -524,8 +521,8 @@ impl HygieneData { syntax_context_data: vec![root_ctxt_data], syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(), expn_data_disambiguators: UnhashMap::default(), - first_s_ctxt_non_det_index: None, - first_local_expn_non_det_index: None, + first_non_det_syntax_context: None, + first_non_det_local_expn_id: None, } } @@ -848,7 +845,7 @@ impl SyntaxContext { } #[inline] - pub(crate) const fn as_u32(self) -> u32 { + pub const fn as_u32(self) -> u32 { self.0 } From 14980208231dc61eb12898e2ecbd4281464c2c68 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 15 Oct 2026 16:22:17 +0300 Subject: [PATCH 11/11] Try stable hash --- compiler/rustc_metadata/src/rmeta/encoder.rs | 9 +- compiler/rustc_span/src/hygiene.rs | 97 ++++++++++++++------ 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b1ded8a18fa00..f0c9e3990437c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2730,8 +2730,13 @@ fn with_encode_metadata_header( let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); - let syntax_context_remapping = SyntaxContextRemapper::with(|r| r.remapping_results()); - let local_expn_id_remapping = LocalExpnIdRemapper::with(|r| r.remapping_results()); + let (syntax_context_remapping, local_expn_id_remapping) = + tcx.with_stable_hashing_context(|mut ctx| { + ( + SyntaxContextRemapper::with(|r| r.remapping_results(&mut ctx)), + LocalExpnIdRemapper::with(|r| r.remapping_results(&mut ctx)), + ) + }); let mut ecx = EncodeContext { opaque: encoder, diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 2302eb811f56b..b7afcf9bab5ca 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -25,7 +25,7 @@ // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) use std::cell::RefCell; -use std::hash::{DefaultHasher, Hash, Hasher}; +use std::hash::{Hash, Hasher}; use std::sync::Arc; use std::{fmt, iter, mem}; @@ -372,8 +372,8 @@ impl + Copy> HygieneRemappingResults { } /// Includes common logic for remapping both expansion ids and syntax contexts. -pub trait HygieneRemapper + Hash + Eq + Copy> { - fn hash(&self, key: T) -> u64; +pub trait HygieneRemapper + Hash + StableHash + Eq + Copy> { + fn hash(&self, key: T, hcx: &mut impl StableHashCtxt) -> u64; fn len(&self) -> usize; fn first_non_det_index_mut(&mut self) -> &mut Option; fn first_non_det_index(&self) -> u32; @@ -385,12 +385,12 @@ pub trait HygieneRemapper + Hash + Eq + Copy> { assert!(prev.is_none(), "this function can be called only once") } - fn remapping_results(&self) -> HygieneRemappingResults { + fn remapping_results(&self, hcx: &mut impl StableHashCtxt) -> HygieneRemappingResults { let start = self.first_non_det_index(); let range = start..self.len() as u32; let mut indices_to_remap = range.map(|raw_id| T::from(raw_id)).collect::>(); - indices_to_remap.sort_by_cached_key(|&key| self.hash(key)); + indices_to_remap.sort_by_cached_key(|&key| self.hash(key, hcx)); let mut remapping = FxHashMap::::default(); @@ -422,18 +422,11 @@ impl LocalExpnIdRemapper<'_> { } impl HygieneRemapper for SyntaxContextRemapper<'_> { - fn hash(&self, key: SyntaxContext) -> u64 { - if key.is_root() { - return 0; - } - - let (hash, transparency) = key.get_data_for_hashing(self.0); - - let mut state = DefaultHasher::new(); - hash.hash(&mut state); - transparency.hash(&mut state); + fn hash(&self, key: SyntaxContext, hcx: &mut impl StableHashCtxt) -> u64 { + let hasher = &mut StableHasher::new(); + key.stable_hash_internal(Some(self.0), hcx, hasher); - state.finish() + hasher.finish() } #[inline] @@ -454,8 +447,11 @@ impl HygieneRemapper for SyntaxContextRemapper<'_> { impl HygieneRemapper for LocalExpnIdRemapper<'_> { #[inline] - fn hash(&self, key: LocalExpnId) -> u64 { - self.0.local_expn_hashes[key].local_hash().as_u64() + fn hash(&self, key: LocalExpnId, hcx: &mut impl StableHashCtxt) -> u64 { + let hasher = &mut StableHasher::new(); + key.stable_hash_internal(Some(self.0), hcx, hasher); + + hasher.finish() } #[inline] @@ -1672,14 +1668,18 @@ fn update_disambiguator(expn_data: &mut ExpnData, mut hcx: impl StableHashCtxt) } impl SyntaxContext { + #[inline] fn get_data_for_hashing(&self, data: &HygieneData) -> (Fingerprint, Transparency) { let (expn_id, transparency) = data.outer_mark(*self); (data.expn_hash(expn_id).0, transparency) } -} -impl StableHash for SyntaxContext { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + fn stable_hash_internal( + &self, + data: Option<&HygieneData>, + hcx: &mut Hcx, + hasher: &mut StableHasher, + ) { const TAG_EXPANSION: u8 = 0; const TAG_NO_EXPANSION: u8 = 1; @@ -1690,29 +1690,74 @@ impl StableHash for SyntaxContext { // This duplicates `ExpnId::stable_hash`, but reads the outer mark and // expansion hash in a single `HygieneData::with` call to avoid extra locking. hcx.assert_default_stable_hash_controls("ExpnId"); - let (hash, transparency) = HygieneData::with(|data| self.get_data_for_hashing(data)); + + let (hash, transp) = match data { + Some(data) => self.get_data_for_hashing(data), + None => HygieneData::with(|data| self.get_data_for_hashing(data)), + }; + hash.stable_hash(hcx, hasher); - transparency.stable_hash(hcx, hasher); + transp.stable_hash(hcx, hasher); } } } -impl StableHash for ExpnId { +impl StableHash for SyntaxContext { + #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.stable_hash_internal(None, hcx, hasher); + } +} + +impl ExpnId { + #[inline] + fn get_data_for_hashing(&self, data: &HygieneData) -> Fingerprint { + data.expn_hash(*self).0 + } + + fn stable_hash_internal( + &self, + data: Option<&HygieneData>, + hcx: &mut Hcx, + hasher: &mut StableHasher, + ) { hcx.assert_default_stable_hash_controls("ExpnId"); let hash = if *self == ExpnId::root() { // Avoid fetching TLS storage for a trivial often-used value. Fingerprint::ZERO } else { - self.expn_hash().0 + match data { + Some(data) => self.get_data_for_hashing(data), + None => HygieneData::with(|data| self.get_data_for_hashing(data)), + } }; hash.stable_hash(hcx, hasher); } } +impl StableHash for ExpnId { + #[inline] + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.stable_hash_internal(None, hcx, hasher); + } +} + +impl LocalExpnId { + #[inline] + fn stable_hash_internal( + self, + data: Option<&HygieneData>, + hcx: &mut Hcx, + hasher: &mut StableHasher, + ) { + self.to_expn_id().stable_hash_internal(data, hcx, hasher) + } +} + impl StableHash for LocalExpnId { + #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - self.to_expn_id().stable_hash(hcx, hasher); + self.to_expn_id().stable_hash_internal(None, hcx, hasher); } }