From e12fdd2a648c87af19c0e95fffff39e0d11b455f Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 13 Oct 2026 12:20:56 +0200 Subject: [PATCH 1/4] add DenseMap --- compiler/rustc_index/src/dense_map.rs | 204 ++++++++++++++++++++++++++ compiler/rustc_index/src/lib.rs | 3 + 2 files changed, 207 insertions(+) create mode 100644 compiler/rustc_index/src/dense_map.rs diff --git a/compiler/rustc_index/src/dense_map.rs b/compiler/rustc_index/src/dense_map.rs new file mode 100644 index 0000000000000..e3998a2786319 --- /dev/null +++ b/compiler/rustc_index/src/dense_map.rs @@ -0,0 +1,204 @@ +use std::fmt; +use std::marker::PhantomData; +use std::ops::{Index, IndexMut}; +use std::slice::Iter; +use std::vec::IntoIter; + +pub trait DenseKey: Copy { + fn new(i: usize) -> Self; + fn index(self) -> usize; +} + +#[derive(Clone, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct DenseMap { + raw: Vec, + _marker: PhantomData, +} + +impl Default for DenseMap { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl DenseMap { + /// Constructs a new, empty `IndexVec`. + #[inline] + pub fn new() -> Self { + Self::from_raw(Vec::new()) + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self::from_raw(Vec::with_capacity(capacity)) + } + + #[inline] + pub fn from_elem_n(elem: V, len: usize) -> Self + where + V: Clone, + { + Self::from_raw(vec![elem; len]) + } + + #[inline] + fn from_raw(raw: Vec) -> Self { + Self { raw, _marker: PhantomData } + } + + #[inline] + pub fn get(&self, key: K) -> Option<&V> { + self.raw.get(key.index()) + } + + #[inline] + pub fn get_mut(&mut self, key: K) -> Option<&mut V> { + self.raw.get_mut(key.index()) + } + + #[inline] + pub fn replace(&mut self, key: K, value: V) -> V { + assert!(key.index() < self.raw.len()); + std::mem::replace(&mut self.raw[key.index()], value) + } + + #[inline] + pub fn len(&self) -> usize { + self.raw.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.raw.is_empty() + } + + #[inline] + pub fn push(&mut self, value: V) -> K { + let idx = K::new(self.raw.len()); + self.raw.push(value); + idx + } + + #[inline] + #[rustc_lint_query_instability] + pub fn values(&self) -> impl ExactSizeIterator + DoubleEndedIterator { + self.raw.iter() + } + + #[inline] + #[rustc_lint_query_instability] + pub fn keys(&self) -> impl ExactSizeIterator { + (0..self.len()).map(|i| K::new(i)) + } + + #[inline] + #[rustc_lint_query_instability] + pub fn iter_enumerated(&self) -> impl ExactSizeIterator + DoubleEndedIterator { + #[allow(rustc::potential_query_instability)] + (0..self.len()).map(|i| K::new(i)).zip(self.values()) + } +} + +impl<'a, K: DenseKey, V> IntoIterator for &'a DenseMap { + type Item = &'a V; + type IntoIter = Iter<'a, V>; + + #[inline] + #[rustc_lint_query_instability] + fn into_iter(self) -> Iter<'a, V> { + self.raw.iter() + } +} + +impl IntoIterator for DenseMap { + type Item = V; + type IntoIter = IntoIter; + + #[inline] + #[rustc_lint_query_instability] + fn into_iter(self) -> IntoIter { + self.raw.into_iter() + } +} + +impl fmt::Debug for DenseMap { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.raw, fmt) + } +} + +impl Index for DenseMap { + type Output = V; + + #[inline] + #[track_caller] + fn index(&self, index: K) -> &V { + &self.raw[index.index()] + } +} + +impl IndexMut for DenseMap { + #[inline] + #[track_caller] + fn index_mut(&mut self, index: K) -> &mut V { + &mut self.raw[index.index()] + } +} + +pub struct DenseMapRef<'a, K: DenseKey, V> { + _marker: PhantomData, + raw: &'a [V], +} + +impl Clone for DenseMapRef<'_, K, V> { + fn clone(&self) -> Self { + Self::from_raw(self.raw) + } +} + +impl Copy for DenseMapRef<'_, K, V> {} + +impl<'a, K: DenseKey, V> DenseMapRef<'a, K, V> { + #[inline] + pub const fn empty() -> Self { + Self::from_raw(&[]) + } + + #[inline] + pub const fn from_raw(raw: &'a [V]) -> Self { + Self { _marker: PhantomData, raw } + } + + #[inline] + pub const fn len(&self) -> usize { + self.raw.len() + } + + #[inline] + pub const fn is_empty(&self) -> bool { + self.raw.is_empty() + } + + #[inline] + pub fn get(&self, key: K) -> Option<&'a V> { + self.raw.get(key.index()) + } +} + +impl fmt::Debug for DenseMapRef<'_, K, V> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.raw, fmt) + } +} + +impl<'a, K: DenseKey, V> Index for DenseMapRef<'a, K, V> { + type Output = V; + + #[inline] + #[track_caller] + fn index(&self, index: K) -> &'a V { + &self.raw[index.index()] + } +} diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index c84b06769e081..2ce98f84ec563 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(rustc_attrs)] +#![allow(internal_features)] // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] #![cfg_attr(all(feature = "nightly", test), feature(test))] @@ -8,6 +10,7 @@ pub mod bit_set; #[cfg(feature = "nightly")] pub mod interval; +pub mod dense_map; mod idx; mod slice; mod vec; From 6047b7fa4b756e2da3e09c00cb9d927994771d3c Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 13 Oct 2026 16:51:47 +0200 Subject: [PATCH 2/4] impl DenseKey for LocalDefId, add DenseMapRef --- compiler/rustc_ast_lowering/src/lib.rs | 18 +++++++++++------- compiler/rustc_crate_store/src/cstore.rs | 4 ++-- compiler/rustc_data_structures/src/sync.rs | 2 +- .../rustc_data_structures/src/sync/vec.rs | 6 +++--- .../rustc_data_structures/src/vec_cache.rs | 11 ++++++----- compiler/rustc_hir_id/src/definitions.rs | 9 ++++++--- compiler/rustc_hir_id/src/lib.rs | 4 +++- compiler/rustc_index/src/dense_map.rs | 19 +++++++++++++++++++ compiler/rustc_index_macros/src/newtype.rs | 12 ++++++++++++ compiler/rustc_interface/src/passes.rs | 4 ++-- compiler/rustc_middle/src/ich.rs | 4 ++-- compiler/rustc_middle/src/queries.rs | 5 +++-- compiler/rustc_middle/src/query/caches.rs | 4 ++-- compiler/rustc_middle/src/query/erase.rs | 5 +++++ compiler/rustc_span/src/def_id.rs | 5 ++++- 15 files changed, 81 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c68fcb18427c5..cd51517f69660 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -64,7 +64,8 @@ use rustc_hir::{ self as hir, AngleBrackets, CRATE_OWNER_ID, ConstArg, GenericArg, HirId, ItemLocalMap, LifetimeSource, LifetimeSyntax, MissingLifetimeKind, ParamName, TraitCandidate, }; -use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_index::Idx; +use rustc_index::dense_map::{DenseMap, DenseMapRef}; use rustc_macros::extension; use rustc_middle::middle::resolve::{ AstOwner, LifetimeRes, PartialRes, PerOwnerResolverData, ResolverAstLowering, @@ -590,7 +591,7 @@ enum TryBlockScope { fn index_ast<'tcx>( tcx: TyCtxt<'tcx>, (): (), -) -> &'tcx IndexSlice>, AstOwner)>>> { +) -> DenseMapRef<'tcx, LocalDefId, Option>, AstOwner)>>> { // Queries that borrow `resolver_for_lowering`. tcx.ensure_done().output_filenames(()); tcx.ensure_done().early_lint_checks(()); @@ -603,7 +604,7 @@ fn index_ast<'tcx>( let mut indexer = Indexer { owners: &resolver.owners, - index: IndexVec::new(), + index: Default::default(), next_node_id: resolver.next_node_id, }; indexer.visit_crate(&mut krate); @@ -612,20 +613,23 @@ fn index_ast<'tcx>( let index = indexer.index; let resolver = Arc::new(resolver); - return tcx.arena.alloc_index_slice_from_iter::( + return DenseMapRef::from_raw(tcx.arena.alloc_from_iter( + // We are delegating handling of stable iteration to the users of the returned + // `DenseMapRef`, it also has a warning for unstable iteration + #[allow(rustc::potential_query_instability)] index.into_iter().map(|owner| owner.map(|o| Steal::new((Arc::clone(&resolver), o)))), - ); + )); struct Indexer<'s, 'hir> { owners: &'s NodeMap>, - index: IndexVec>, + index: DenseMap>, next_node_id: NodeId, } impl Indexer<'_, '_> { fn insert(&mut self, id: NodeId, node: AstOwner) { let def_id = self.owners[&id].def_id; - self.index.insert(def_id, node); + self.index.insert(def_id, Some(node)); } fn make_dummy( diff --git a/compiler/rustc_crate_store/src/cstore.rs b/compiler/rustc_crate_store/src/cstore.rs index c8fe5562bdd59..e4bf901ad0c70 100644 --- a/compiler/rustc_crate_store/src/cstore.rs +++ b/compiler/rustc_crate_store/src/cstore.rs @@ -9,7 +9,7 @@ use std::sync::OnceLock; use rustc_abi::ExternAbi; use rustc_attr_ir::{CfgEntry, PeImportNameType}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{self, AppendOnlyIndexVec, FreezeLock}; +use rustc_data_structures::sync::{self, AppendOnlyDenseMap, FreezeLock}; use rustc_hir_id::definitions::{DefKey, DefPath, Definitions}; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; use rustc_span::def_id::{ @@ -218,7 +218,7 @@ pub type CrateStoreDyn = dyn CrateStore + sync::DynSync + sync::DynSend; pub struct Untracked { pub cstore: FreezeLock>, /// Reference span for definitions. - pub source_span: AppendOnlyIndexVec, + pub source_span: AppendOnlyDenseMap, pub definitions: FreezeLock, /// The interned [StableCrateId]s. pub stable_crate_ids: FreezeLock, diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 85e030c8aa0c5..bc1e54c14d42c 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -41,7 +41,7 @@ pub use self::parallel::{ broadcast, par_fns, par_for_each_in, par_for_each_slice, par_join, par_map, parallel_guard, spawn, try_par_for_each_in, }; -pub use self::vec::{AppendOnlyIndexVec, AppendOnlyVec}; +pub use self::vec::{AppendOnlyDenseMap, AppendOnlyVec}; pub use self::worker_local::{Registry, WorkerLocal}; pub use crate::marker::*; diff --git a/compiler/rustc_data_structures/src/sync/vec.rs b/compiler/rustc_data_structures/src/sync/vec.rs index d35e2c61f1e19..fb3b102f8b2ba 100644 --- a/compiler/rustc_data_structures/src/sync/vec.rs +++ b/compiler/rustc_data_structures/src/sync/vec.rs @@ -1,14 +1,14 @@ use std::marker::PhantomData; -use rustc_index::Idx; +use rustc_index::dense_map::DenseKey; #[derive(Default)] -pub struct AppendOnlyIndexVec { +pub struct AppendOnlyDenseMap { vec: elsa::sync::LockFreeFrozenVec, _marker: PhantomData, } -impl AppendOnlyIndexVec { +impl AppendOnlyDenseMap { pub fn new() -> Self { Self { vec: elsa::sync::LockFreeFrozenVec::new(), _marker: PhantomData } } diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 4b5e73fa880fd..d09fe2f978177 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -12,6 +12,7 @@ use std::ops::{Index, IndexMut}; use std::sync::atomic::{AtomicPtr, AtomicU32, AtomicUsize, Ordering}; use rustc_index::Idx; +use rustc_index::dense_map::DenseKey; #[cfg(test)] mod tests; @@ -221,7 +222,7 @@ impl SlotIndex { /// query dependency tracking. /// /// [#124780]: https://github.com/rust-lang/rust/pull/124780 -pub struct VecCache { +pub struct VecCache { // Entries per bucket: // Bucket 0: 4096 2^12 // Bucket 1: 4096 2^12 @@ -243,7 +244,7 @@ pub struct VecCache { key: PhantomData<(K, I)>, } -impl Default for VecCache { +impl Default for VecCache { fn default() -> Self { VecCache { buckets: Default::default(), @@ -255,7 +256,7 @@ impl Default for VecCache { } } -impl VecCache { +impl VecCache { pub fn without_track_present() -> Self { let mut this = VecCache::default(); this.track_present = false; @@ -264,7 +265,7 @@ impl VecCache { } // SAFETY: No access to `V` is made. -unsafe impl Drop for VecCache { +unsafe impl Drop for VecCache { fn drop(&mut self) { // We have unique ownership, so no locks etc. are needed. Since `K` and `V` are both `Copy`, // we are also guaranteed to just need to deallocate any large arrays (not iterate over @@ -303,7 +304,7 @@ unsafe impl Drop for VecCache { impl VecCache where - K: Eq + Idx + Copy + Debug, + K: Eq + DenseKey + Copy + Debug, V: Copy, I: Idx + Copy, { diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 52b64a2c7fcfc..e5c9aaa1f5365 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::StableHasher; use rustc_hashes::Hash64; -use rustc_index::IndexVec; +use rustc_index::dense_map::DenseMap; use rustc_macros::{BlobDecodable, Decodable, Encodable, extension}; use rustc_serialize::Encodable; pub use rustc_span::def_id::DefPathHash; @@ -96,9 +96,9 @@ impl DefPathToIndexMap { #[derive(Debug)] pub struct Definitions { stable_crate_id: StableCrateId, - def_id_to_key: IndexVec, + def_id_to_key: DenseMap, // We do only store the local hash, as all the definitions are from the current crate. - def_path_hashes: IndexVec, + def_path_hashes: DenseMap, // The first non deterministic def index, may correspond to non-allocated // def id, in this case `first_non_det_index.as_usize() == num_definitions`. first_non_det_index: Option, @@ -420,9 +420,12 @@ impl Definitions { def_id } + #[rustc_lint_query_instability] pub fn enumerated_keys_and_path_hashes( &self, ) -> impl Iterator + ExactSizeIterator { + // propagated to the caller + #[allow(rustc::potential_query_instability)] self.def_id_to_key .iter_enumerated() .map(move |(def_id, key)| (def_id.local_def_index, key, self.def_path_hash(def_id))) diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index 7cb43e9a8ccf9..fbfab6f8e3328 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -15,6 +15,7 @@ use std::fmt::{self, Debug}; use rustc_data_structures::stable_hash::{ StableHash, StableHashCtxt, StableHasher, StableOrd, ToStableHashKey, }; +use rustc_index::dense_map::DenseKey; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_serialize::ContextOrd; use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId, LocalModId}; @@ -56,7 +57,7 @@ impl OwnerId { } } -impl rustc_index::Idx for OwnerId { +impl DenseKey for OwnerId { #[inline] fn new(idx: usize) -> Self { OwnerId { def_id: LocalDefId { local_def_index: DefIndex::from_usize(idx) } } @@ -106,6 +107,7 @@ pub struct HirId { // See https://github.com/rust-lang/rust/issues/90317. impl !Ord for HirId {} impl !PartialOrd for HirId {} +impl !rustc_index::Idx for OwnerId {} impl Debug for HirId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/compiler/rustc_index/src/dense_map.rs b/compiler/rustc_index/src/dense_map.rs index e3998a2786319..b5572d6ebaac8 100644 --- a/compiler/rustc_index/src/dense_map.rs +++ b/compiler/rustc_index/src/dense_map.rs @@ -58,12 +58,31 @@ impl DenseMap { self.raw.get_mut(key.index()) } + /// Replaces the value at `key`, returning the previously stored value. #[inline] pub fn replace(&mut self, key: K, value: V) -> V { assert!(key.index() < self.raw.len()); std::mem::replace(&mut self.raw[key.index()], value) } + /// Inserts `value` at `key` resizing the backing container if needed, filling missing values with + /// `V::default()` + /// + /// Use `Self::replace` instead when `key` is known to fit + #[inline] + pub fn insert(&mut self, key: K, value: V) -> Option + where + V: Default, + { + if key.index() < self.raw.len() { + Some(self.replace(key, value)) + } else { + self.raw.resize_with(key.index() + 1, Default::default); + *self.raw.last_mut().unwrap() = value; + None + } + } + #[inline] pub fn len(&self) -> usize { self.raw.len() diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 52f1d070718e8..09aea574948b3 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -337,6 +337,18 @@ impl Parse for Newtype { } } + impl rustc_index::dense_map::DenseKey for #name { + #[inline] + fn new(value: usize) -> Self { + Self::from_usize(value) + } + + #[inline] + fn index(self) -> usize { + self.as_usize() + } + } + #step #stable_hash_impl diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 73bebb22bc508..f43b4229ff6a6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -14,7 +14,7 @@ use rustc_crate_store::Untracked; use rustc_data_structures::indexmap::IndexMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ - AppendOnlyIndexVec, DynSend, DynSync, FreezeLock, WorkerLocal, par_fns, + AppendOnlyDenseMap, DynSend, DynSync, FreezeLock, WorkerLocal, par_fns, }; use rustc_data_structures::thousands; use rustc_errors::timings::TimingSection; @@ -967,7 +967,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let stable_crate_ids = FreezeLock::new(StableCrateIdMap::default()); let untracked = Untracked { cstore, - source_span: AppendOnlyIndexVec::new(), + source_span: AppendOnlyDenseMap::new(), definitions, stable_crate_ids, local_crate_hash: OnceLock::new(), diff --git a/compiler/rustc_middle/src/ich.rs b/compiler/rustc_middle/src/ich.rs index feb162ad1e162..af556aaac5704 100644 --- a/compiler/rustc_middle/src/ich.rs +++ b/compiler/rustc_middle/src/ich.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hash::{ RawDefId, RawSpan, StableHash, StableHashControls, StableHashCtxt, StableHasher, }; -use rustc_data_structures::sync::AppendOnlyIndexVec; +use rustc_data_structures::sync::AppendOnlyDenseMap; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_session::Session; use rustc_span::source_map::SourceMap; @@ -222,7 +222,7 @@ impl SourceSpanCache { pub(crate) fn lookup( &mut self, def_id: LocalDefId, - source_span: &AppendOnlyIndexVec, + source_span: &AppendOnlyDenseMap, ) -> SpanData { if let Some(cache) = self.0 && cache.0 == def_id diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 4632c3ca1fc12..c4943abe7c65d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -69,7 +69,8 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModId}; use rustc_hir::{ItemLocalId, PreciseCapturingArgKind}; -use rustc_index::{IndexSlice, IndexVec}; +use rustc_index::IndexVec; +use rustc_index::dense_map::DenseMapRef; use rustc_lint_defs::{LintId, StableLintExpectationId}; use rustc_macros::rustc_queries; use rustc_middle::middle::resolve::TypeRelativeDelegationRes; @@ -210,7 +211,7 @@ rustc_queries! { // We want to drop it once the whole HIR has been lowered. // We rely on reference counting to know when all definitions have been stolen. // None if the `LocalDefId` is not an owner. - query index_ast(_: ()) -> &'tcx IndexSlice>, AstOwner)>>> { + query index_ast(_: ()) -> DenseMapRef<'tcx, LocalDefId, Option>, AstOwner)>>> { eval_always no_hash desc { "getting the AST for lowering" } diff --git a/compiler/rustc_middle/src/query/caches.rs b/compiler/rustc_middle/src/query/caches.rs index 53c8d10b09e6f..b6f637bbeaa19 100644 --- a/compiler/rustc_middle/src/query/caches.rs +++ b/compiler/rustc_middle/src/query/caches.rs @@ -3,7 +3,7 @@ use std::sync::OnceLock; use rustc_data_structures::sharded::ShardedHashMap; pub use rustc_data_structures::vec_cache::VecCache; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_index::Idx; +use rustc_index::dense_map::DenseKey; use rustc_span::def_id::{DefId, DefIndex}; use crate::dep_graph::DepNodeIndex; @@ -192,7 +192,7 @@ where impl QueryCache for VecCache where - K: Idx + QueryKey, + K: DenseKey + QueryKey, V: Copy, { type Key = K; diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b0281a70eeb73..67dccbd22d266 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -13,6 +13,7 @@ use std::mem::MaybeUninit; use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{DynSend, DynSync}; +use rustc_index::dense_map::{DenseKey, DenseMapRef}; use rustc_index::{Idx, IndexSlice}; use rustc_span::def_id::ModId; use rustc_span::{ErrorGuaranteed, Spanned}; @@ -123,6 +124,10 @@ impl Erasable for &'_ IndexSlice { type Storage = [u8; size_of::<&'_ [()]>()]; } +impl<'a, K: DenseKey, V> Erasable for DenseMapRef<'a, K, V> { + type Storage = [u8; size_of::<&'_ [()]>()]; +} + // Note: this impl does not overlap with the impl for `&'_ T` above because `RawList` is unsized // and does not satisfy the implicit `T: Sized` bound. // diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 7a51bc4af828a..af4527227372a 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -9,6 +9,7 @@ use rustc_data_structures::stable_hash::{ use rustc_data_structures::unhash::Unhasher; use rustc_hashes::Hash64; use rustc_index::Idx; +use rustc_index::dense_map::DenseKey; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; use rustc_serialize::{ContextOrd, Decodable, Encodable}; @@ -376,7 +377,9 @@ impl !PartialOrd for LocalDefId {} pub const CRATE_DEF_ID: LocalDefId = LocalDefId { local_def_index: CRATE_DEF_INDEX }; pub const CRATE_MOD_ID: LocalModId = LocalModId::new_unchecked(CRATE_DEF_ID); -impl Idx for LocalDefId { +impl !Idx for LocalDefId {} + +impl DenseKey for LocalDefId { #[inline] fn new(idx: usize) -> Self { LocalDefId { local_def_index: Idx::new(idx) } From 36f0170faef369cfcd3b2fea8dcdbdb1fcd499bd Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 22 Oct 2026 09:06:28 +0200 Subject: [PATCH 3/4] LocalExpnId Idx -> DenseKey --- compiler/rustc_span/src/hygiene.rs | 79 ++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index c107f6ce4f64c..20887e28d5906 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -37,7 +37,8 @@ use rustc_data_structures::stable_hash::{ use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; use rustc_hashes::Hash64; -use rustc_index::IndexVec; +use rustc_index::dense_map::{DenseKey, DenseMap}; +use rustc_index::{Idx, IndexVec}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_serialize::{ContextOrd, Decodable, Decoder, Encodable}; use tracing::{debug, trace}; @@ -131,11 +132,45 @@ impl fmt::Debug for ExpnId { } } -rustc_index::newtype_index! { - /// A unique ID associated with a macro invocation and expansion. - #[debug_format = "expn{}"] - #[default] - pub struct LocalExpnId {} +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct LocalExpnId(ExpnIndex); + +impl Default for LocalExpnId { + fn default() -> Self { + Self::ZERO + } +} + +impl fmt::Debug for LocalExpnId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10) + // Don't use debug_tuple to always keep this on one line. + write!(f, "expn({})", self.0.as_u32()) + } +} + +impl LocalExpnId { + pub const ZERO: Self = Self(ExpnIndex::ZERO); + + pub const fn from_u32(i: u32) -> Self { + Self(ExpnIndex::from_u32(i)) + } + + pub const fn as_u32(self) -> u32 { + self.0.as_u32() + } +} + +impl From for LocalExpnId { + fn from(value: u32) -> Self { + Self::from_u32(value) + } +} + +impl From for u32 { + fn from(value: LocalExpnId) -> Self { + value.as_u32() + } } // To ensure correctness of incremental compilation, @@ -143,6 +178,19 @@ rustc_index::newtype_index! { // See https://github.com/rust-lang/rust/issues/90317. impl !Ord for LocalExpnId {} impl !PartialOrd for LocalExpnId {} +impl !Idx for LocalExpnId {} + +impl DenseKey for LocalExpnId { + #[inline] + fn new(idx: usize) -> Self { + LocalExpnId(ExpnIndex::from_usize(idx)) + } + + #[inline] + fn index(self) -> usize { + self.0.as_usize() + } +} /// A unique hash value associated to an expansion. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encodable, Decodable, StableHash)] @@ -214,12 +262,12 @@ impl LocalExpnId { #[inline] fn from_raw(idx: ExpnIndex) -> LocalExpnId { - LocalExpnId::from_u32(idx.as_u32()) + LocalExpnId(idx) } #[inline] pub fn as_raw(self) -> ExpnIndex { - ExpnIndex::from_u32(self.as_u32()) + self.0 } pub fn fresh_empty() -> LocalExpnId { @@ -502,8 +550,8 @@ pub(crate) struct HygieneData { /// Each expansion should have an associated expansion data, but sometimes there's a delay /// between creation of an expansion ID and obtaining its data (e.g. macros are collected /// first and then resolved later), so we use an `Option` here. - local_expn_data: IndexVec>, - local_expn_hashes: IndexVec, + local_expn_data: DenseMap>, + local_expn_hashes: DenseMap, /// Data and hash information from external crates. We may eventually want to remove these /// maps, and fetch the information directly from the other crate's metadata like DefIds do. foreign_expn_data: FxHashMap, @@ -535,8 +583,8 @@ impl HygieneData { // mutated afterwards. The lock-free root paths on `SyntaxContext` rely on that. let root_ctxt_data = SyntaxContextData::root(); HygieneData { - local_expn_data: IndexVec::from_elem_n(Some(root_data), 1), - local_expn_hashes: IndexVec::from_elem_n(ExpnHash(Fingerprint::ZERO), 1), + local_expn_data: DenseMap::from_elem_n(Some(root_data), 1), + local_expn_hashes: DenseMap::from_elem_n(ExpnHash(Fingerprint::ZERO), 1), foreign_expn_data: FxHashMap::default(), foreign_expn_hashes: FxHashMap::default(), expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) @@ -833,6 +881,8 @@ pub fn debug_hygiene_data(verbose: bool) -> String { expn_data.kind, )) }; + // FIXME make this output reproducible + #[expect(rustc::potential_query_instability)] data.local_expn_data.iter_enumerated().for_each(|(id, expn_data)| { let expn_data = expn_data.as_ref().expect("no expansion data for an expansion ID"); debug_expn_data((&id.to_expn_id(), expn_data)) @@ -1538,10 +1588,7 @@ pub struct HygieneDecodeContext { /// Register an expansion which has been decoded from the on-disk-cache for the local crate. pub fn register_local_expn_id(data: ExpnData, hash: ExpnHash) -> ExpnId { HygieneData::with(|hygiene_data| { - let expn_id = hygiene_data.local_expn_data.next_index(); - hygiene_data.local_expn_data.push(Some(data)); - let _eid = hygiene_data.local_expn_hashes.push(hash); - debug_assert_eq!(expn_id, _eid); + let expn_id = hygiene_data.local_expn_data.push(Some(data)); let expn_id = expn_id.to_expn_id(); From 4157babb0d00f0bae48f9484d361cb9bed6e9bbb Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 22 Oct 2026 09:21:20 +0200 Subject: [PATCH 4/4] implement !Idx and DenseKey for SyntaxContext --- compiler/rustc_span/src/hygiene.rs | 52 +++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 20887e28d5906..4d9d58222ec50 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -74,6 +74,19 @@ impl Into for SyntaxContext { // See https://github.com/rust-lang/rust/issues/90317. impl !Ord for SyntaxContext {} impl !PartialOrd for SyntaxContext {} +impl !Idx for SyntaxContext {} + +impl DenseKey for SyntaxContext { + #[inline] + fn new(idx: usize) -> Self { + Self(idx.try_into().unwrap()) + } + + #[inline] + fn index(self) -> usize { + self.0.try_into().unwrap() + } +} /// If this part of two syntax contexts is equal, then the whole syntax contexts should be equal. /// The other fields are only for caching. @@ -557,7 +570,7 @@ pub(crate) struct HygieneData { foreign_expn_data: FxHashMap, foreign_expn_hashes: FxHashMap, expn_hash_to_expn_id: UnhashMap, - syntax_context_data: Vec, + syntax_context_data: DenseMap, first_non_det_syntax_context: Option, first_non_det_local_expn_id: Option, syntax_context_map: FxHashMap, @@ -589,7 +602,7 @@ impl HygieneData { foreign_expn_hashes: FxHashMap::default(), expn_hash_to_expn_id: iter::once((ExpnHash(Fingerprint::ZERO), ExpnId::root())) .collect(), - syntax_context_data: vec![root_ctxt_data], + syntax_context_data: DenseMap::from_elem_n(root_ctxt_data, 1), syntax_context_map: iter::once((root_ctxt_data.key(), SyntaxContext(0))).collect(), expn_data_disambiguators: UnhashMap::default(), first_non_det_syntax_context: None, @@ -644,30 +657,30 @@ impl HygieneData { #[inline] fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque + self.syntax_context_data[ctxt].opaque } #[inline] fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque + self.syntax_context_data[ctxt].opaque_and_semiopaque } /// See [`SyntaxContextData::outer_expn`] #[inline] fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { - self.syntax_context_data[ctxt.0 as usize].outer_expn + self.syntax_context_data[ctxt].outer_expn } /// The last macro expansion and its Transparency #[inline] fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { - let data = &self.syntax_context_data[ctxt.0 as usize]; + let data = &self.syntax_context_data[ctxt]; (data.outer_expn, data.outer_transparency) } #[inline] fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { - self.syntax_context_data[ctxt.0 as usize].parent + self.syntax_context_data[ctxt].parent } fn remove_mark(&self, ctxt: &mut SyntaxContext) -> (ExpnId, Transparency) { @@ -794,7 +807,7 @@ impl HygieneData { // Opaque and semi-opaque versions of the parent. Note that they may be equal to the // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques, // and `parent_opaque_and_semiopaque` == `parent` if the expn contains only (semi-)opaques. - let parent_data = &self.syntax_context_data[parent.0 as usize]; + let parent_data = &self.syntax_context_data[parent]; assert_ne!(parent_data.dollar_crate_name, sym::dummy); let parent_opaque = parent_data.opaque; let parent_opaque_and_semiopaque = parent_data.opaque_and_semiopaque; @@ -816,7 +829,7 @@ impl HygieneData { }; // Fill the full data, now that we have it. - self.syntax_context_data[ctxt.as_u32() as usize] = SyntaxContextData { + self.syntax_context_data[ctxt] = SyntaxContextData { outer_expn: expn_id, outer_transparency: transparency, parent, @@ -841,11 +854,13 @@ pub fn walk_chain_collapsed(span: Span, to: Span) -> Span { HygieneData::with(|data| data.walk_chain_collapsed(span, to)) } -pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { +pub fn update_dollar_crate_names(get_name: impl Fn(SyntaxContext) -> Symbol) { // The new contexts that need updating are at the end of the list and have `$crate` as a name. let mut to_update = vec![]; HygieneData::with(|data| { - for (idx, scdata) in data.syntax_context_data.iter().enumerate().rev() { + // `to_update` is later used to call `get_name`, which is an `Fn`, not an `FnMut` + #[expect(rustc::potential_query_instability)] + for (idx, scdata) in data.syntax_context_data.iter_enumerated().rev() { if scdata.dollar_crate_name == kw::DollarCrate { to_update.push((idx, kw::DollarCrate)); } else { @@ -856,7 +871,7 @@ pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symb // The callback must be called from outside of the `HygieneData` lock, // since it will try to acquire it too. for (idx, name) in &mut to_update { - *name = get_name(SyntaxContext::from_usize(*idx)); + *name = get_name(*idx); } HygieneData::with(|data| { for (idx, name) in to_update { @@ -895,10 +910,15 @@ pub fn debug_hygiene_data(verbose: bool) -> String { foreign_expn_data.sort_by_key(|(id, _)| (id.krate, id.local_id)); foreign_expn_data.into_iter().for_each(debug_expn_data); s.push_str("\n\nSyntaxContexts:"); - data.syntax_context_data.iter().enumerate().for_each(|(id, ctxt)| { + // FIXME make this output reproducible + #[expect(rustc::potential_query_instability)] + data.syntax_context_data.iter_enumerated().for_each(|(id, ctxt)| { s.push_str(&format!( "\n#{}: parent: {:?}, outer_mark: ({:?}, {:?})", - id, ctxt.parent, ctxt.outer_expn, ctxt.outer_transparency, + id.as_u32(), + ctxt.parent, + ctxt.outer_expn, + ctxt.outer_transparency, )); }); s @@ -1120,7 +1140,7 @@ impl SyntaxContext { #[inline] pub(crate) fn dollar_crate_name(self) -> Symbol { - HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) + HygieneData::with(|data| data.syntax_context_data[self].dollar_crate_name) } #[inline] @@ -1545,7 +1565,7 @@ impl HygieneEncodeContext { let latest_contexts = { mem::take(&mut h_ctxt.borrow_mut().latest_ctxts) }.into_iter(); for ctxt in latest_contexts { - let key = HygieneData::with(|data| data.syntax_context_data[ctxt.0 as usize].key()); + let key = HygieneData::with(|data| data.syntax_context_data[ctxt].key()); encode_ctxt(encoder, ctxt, &key); }