From c2825ca7738171e75b52dcb466446690d7825f7d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 17 Oct 2026 20:18:21 +1100 Subject: [PATCH 1/3] clippy: Avoid hashing FileName This will make it possible to remove the compiler's Hash impl for FileName. --- src/tools/clippy/clippy_lints/src/inherent_impl.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 5790c3cce2606..db0f046d72811 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{LocalDefId, LocalModId}; use rustc_hir::{Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; -use rustc_span::{FileName, Span}; +use rustc_span::{Span, StableSourceFileId}; use std::collections::hash_map::Entry; declare_clippy_lint! { @@ -63,7 +63,7 @@ impl MultipleInherentImpl { #[derive(Hash, Eq, PartialEq, Clone)] enum Criterion { Module(LocalModId), - File(FileName), + File(StableSourceFileId), Crate, } @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { InherentImplLintScope::Module => Criterion::Module(cx.tcx.parent_module(hir_id)), InherentImplLintScope::File => { let span = cx.tcx.hir_span(hir_id); - Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone()) + Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).stable_id) }, InherentImplLintScope::Crate => Criterion::Crate, }; From 2cc6930ef390297f86b16905d52b8cd1b2125962 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 17 Oct 2026 17:58:49 +1100 Subject: [PATCH 2/3] Sort `impl_dep_tracking_hash_via_hash!` --- compiler/rustc_session/src/config.rs | 98 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 7e274c66f6f75..3aeff8e5f0999 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3433,73 +3433,75 @@ pub(crate) mod dep_tracking { } impl_dep_tracking_hash_via_hash!( + // tidy-alphabetical-start (), + Align, AnnotateMoves, AutoDiff, - Offload, - bool, - usize, - NonZero, - u64, - Hash64, - String, - PathBuf, - lint::Level, - WasiExecModel, - u32, - FramePointer, - RelocModel, + BranchProtection, + CFGuard, + CFProtection, CodeModel, - TlsModel, - InstrumentCoverage, + CodegenRetagOptions, + CollapseMacroDebuginfo, CoverageOptions, + CrateType, + DebugInfo, + DebugInfoCompression, + Edition, + FmtDebug, + FramePointer, + FunctionReturn, + Hash64, + InliningThreshold, + InstrumentCoverage, InstrumentMcount, InstrumentMcountOpts, InstrumentXRay, - CrateType, + LanguageIdentifier, + LinkerPluginLto, + LocationDetail, + LtoCli, MergeFunctions, + MirStripDebugInfo, + NativeLib, + NextSolverConfig, + NonZero, + Offload, OnBrokenPipe, + OptLevel, + OutFileName, + OutputType, PanicStrategy, + PatchableFunctionEntry, + PathBuf, + PointerAuthOption, + Polonius, + RealFileName, + RelocModel, RelroLevel, - OptLevel, - LtoCli, - DebugInfo, - DebugInfoCompression, - MirStripDebugInfo, - CollapseMacroDebuginfo, - UnstableFeatures, - NativeLib, - SanitizerSet, - CFGuard, - CFProtection, - TargetTuple, - Edition, - LinkerPluginLto, + RemapPathScopeComponents, ResolveDocLinks, + RustcVersion, + SanitizerSet, + SourceFileHashAlgorithm, SplitDebuginfo, SplitDwarfKind, StackProtector, + String, SwitchWithOptPath, SymbolManglingVersion, SymbolVisibility, - RemapPathScopeComponents, - SourceFileHashAlgorithm, - OutFileName, - OutputType, - RealFileName, - LocationDetail, - FmtDebug, - BranchProtection, - LanguageIdentifier, - NextSolverConfig, - PatchableFunctionEntry, - Polonius, - InliningThreshold, - FunctionReturn, - Align, - CodegenRetagOptions, - RustcVersion, - PointerAuthOption, + TargetTuple, + TlsModel, + UnstableFeatures, + WasiExecModel, + bool, + lint::Level, + u32, + u64, + usize, + // tidy-alphabetical-end ); impl DepTrackingHash for (T1, T2) From 643f17d837ed9ca99be76872eb77391618f9d335 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 17 Oct 2026 18:14:45 +1100 Subject: [PATCH 3/3] Avoid stdlib's `impl Hash for Path` for stable hashing A subtle consequence of the stage0 redesign is that the compiler must take care to avoid relying on any stdlib-provided Hash impl when computing stable hashes, or at least any such impl that is actually liable to change. This was noticed when trying to strengthen the provided Hash impl for Path, which triggered baffling CI failures because the stable hashes produced by the stage1 compiler (built against stage0 stdlib) were inconsistent with the ones that would have been produced by the stage2 compiler (built against in-tree stdlib). This PR aims to unblock changes to `impl Hash for Path`, by making the compiler not rely on it for stable hashing. It does not attempt to solve the broader problem of the compiler relying on stdlib hash details. --- .../rustc_data_structures/src/stable_hash.rs | 100 +++++++++++++++++- compiler/rustc_session/src/config.rs | 26 ++++- compiler/rustc_span/src/lib.rs | 22 ++-- 3 files changed, 132 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hash.rs b/compiler/rustc_data_structures/src/stable_hash.rs index 8e7ca5a75d861..a270a87e7ea7e 100644 --- a/compiler/rustc_data_structures/src/stable_hash.rs +++ b/compiler/rustc_data_structures/src/stable_hash.rs @@ -5,6 +5,7 @@ use std::num::NonZero; use rustc_index::bit_set::{self, DenseBitSet}; use rustc_index::{Idx, IndexSlice, IndexVec}; +use rustc_macros::StableHash; use smallvec::SmallVec; use thin_vec::ThinVec; @@ -37,6 +38,25 @@ pub trait StableHashCtxt { fn assert_default_stable_hash_controls(&self, msg: &str); } +/// Dummy impl of [`StableHashCtxt`], for code that can't provide a real context, +/// and only wants to compute stable hashes for types that don't rely on the context. +pub struct DummyStableHashCtxt; + +impl StableHashCtxt for DummyStableHashCtxt { + fn stable_hash_span(&mut self, _span: RawSpan, _hasher: &mut StableHasher) { + unimplemented!() + } + fn def_path_hash(&self, _def_id: RawDefId) -> Fingerprint { + unimplemented!() + } + fn stable_hash_controls(&self) -> StableHashControls { + unimplemented!() + } + fn assert_default_stable_hash_controls(&self, _msg: &str) { + unimplemented!() + } +} + // A type used to work around `Span` not being visible in this crate. It is the same size as // `Span` but has a different alignment. pub struct RawSpan(pub u64); @@ -575,8 +595,84 @@ impl StableHash for bit_set::BitMatrix { impl_stable_traits_for_trivial_type!(::std::ffi::OsStr); -impl_stable_traits_for_trivial_type!(::std::path::Path); -impl_stable_traits_for_trivial_type!(::std::path::PathBuf); +impl StableHash for std::path::Path { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + use std::path::{Component, Prefix}; + + #[derive(StableHash)] + enum Tag { + PrefixVerbatim, + PrefixVerbatimUNC, + PrefixVerbatimDisk, + PrefixDeviceNS, + PrefixUNC, + PrefixDisk, + RootDir, + CurDir, + ParentDir, + Normal, + } + + for component in self.components() { + match component { + Component::Prefix(prefix) => match prefix.kind() { + Prefix::Verbatim(os_str) => { + Tag::PrefixVerbatim.stable_hash(hcx, hasher); + os_str.stable_hash(hcx, hasher); + } + Prefix::VerbatimUNC(host, share) => { + Tag::PrefixVerbatimUNC.stable_hash(hcx, hasher); + host.stable_hash(hcx, hasher); + share.stable_hash(hcx, hasher); + } + Prefix::VerbatimDisk(letter) => { + Tag::PrefixVerbatimDisk.stable_hash(hcx, hasher); + letter.stable_hash(hcx, hasher); + } + Prefix::DeviceNS(os_str) => { + Tag::PrefixDeviceNS.stable_hash(hcx, hasher); + os_str.stable_hash(hcx, hasher); + } + Prefix::UNC(host, share) => { + Tag::PrefixUNC.stable_hash(hcx, hasher); + host.stable_hash(hcx, hasher); + share.stable_hash(hcx, hasher); + } + Prefix::Disk(letter) => { + Tag::PrefixDisk.stable_hash(hcx, hasher); + letter.stable_hash(hcx, hasher); + } + }, + Component::RootDir => Tag::RootDir.stable_hash(hcx, hasher), + Component::CurDir => Tag::CurDir.stable_hash(hcx, hasher), + Component::ParentDir => Tag::ParentDir.stable_hash(hcx, hasher), + Component::Normal(os_str) => { + Tag::Normal.stable_hash(hcx, hasher); + os_str.stable_hash(hcx, hasher); + } + } + } + } +} + +impl StableOrd for std::path::Path { + /// FIXME(Zalathar): Unstably sorting paths is pre-existing behavior, but potentially wrong. + const CAN_USE_UNSTABLE_SORT: bool = true; + /// Stdlib path sorting should (hopefully) not change. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); +} + +impl StableHash for std::path::PathBuf { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.as_path().stable_hash(hcx, hasher); + } +} + +impl StableOrd for std::path::PathBuf { + const CAN_USE_UNSTABLE_SORT: bool = ::CAN_USE_UNSTABLE_SORT; + /// Stdlib path sorting should (hopefully) not change. + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); +} // It is not safe to implement StableHash for HashSet, HashMap or any other collection type // with unstable but observable iteration order. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 3aeff8e5f0999..2ccdbcf9bea18 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3370,7 +3370,7 @@ pub(crate) mod dep_tracking { use rustc_abi::Align; use rustc_ast::attr::version::RustcVersion; use rustc_data_structures::fx::FxIndexMap; - use rustc_data_structures::stable_hash::StableHasher; + use rustc_data_structures::stable_hash::{DummyStableHashCtxt, StableHash, StableHasher}; use rustc_errors::LanguageIdentifier; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; @@ -3474,10 +3474,8 @@ pub(crate) mod dep_tracking { OutputType, PanicStrategy, PatchableFunctionEntry, - PathBuf, PointerAuthOption, Polonius, - RealFileName, RelocModel, RelroLevel, RemapPathScopeComponents, @@ -3590,6 +3588,28 @@ pub(crate) mod dep_tracking { } } + impl DepTrackingHash for PathBuf { + fn hash( + &self, + hasher: &mut StableHasher, + _error_format: ErrorOutputType, + _for_crate_hash: bool, + ) { + self.stable_hash(&mut DummyStableHashCtxt, hasher); + } + } + + impl DepTrackingHash for RealFileName { + fn hash( + &self, + hasher: &mut StableHasher, + _error_format: ErrorOutputType, + _for_crate_hash: bool, + ) { + self.stable_hash(&mut DummyStableHashCtxt, hasher); + } + } + // This is a stable hash because BTreeMap is a sorted container pub(crate) fn stable_hash( sub_hashes: BTreeMap<&'static str, &dyn DepTrackingHash>, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f69efebf5c950..64e6670e3e6ed 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -32,7 +32,7 @@ // this crate without this line making `rustc_span` available. extern crate self as rustc_span; -use rustc_data_structures::stable_hash::StableHashCtxt; +use rustc_data_structures::stable_hash::{DummyStableHashCtxt, StableHashCtxt}; use rustc_data_structures::{AtomicRef, outline}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_serialize::opaque::mem_encoder::MemEncoder; @@ -315,7 +315,7 @@ pub struct RealFileName { /// The inner workings of `RealFileName`. /// /// It contains the `name`, `working_directory` and `embeddable_name` components. -#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable, Encodable, Hash)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, StableHash, Decodable, Encodable)] struct InnerRealFileName { /// The name. name: PathBuf, @@ -325,18 +325,18 @@ struct InnerRealFileName { embeddable_name: PathBuf, } -impl Hash for RealFileName { - #[inline] - fn hash(&self, state: &mut H) { +impl StableHash for RealFileName { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + let RealFileName { local, maybe_remapped, scopes } = self; // To prevent #70924 from happening again we should only hash the // remapped path if that exists. This is because remapped paths to // sysroot crates (/rust/$hash or /rust/$version) remain stable even // if the corresponding local path changes. if !self.was_fully_remapped() { - self.local.hash(state); + local.stable_hash(hcx, hasher); } - self.maybe_remapped.hash(state); - self.scopes.bits().hash(state); + maybe_remapped.stable_hash(hcx, hasher); + scopes.bits().stable_hash(hcx, hasher); } } @@ -505,7 +505,7 @@ impl RealFileName { } /// Differentiates between real files and common virtual files. -#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, Decodable, Encodable)] +#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, StableHash, Decodable, Encodable)] pub enum FileName { Real(RealFileName), /// Strings provided as `--cfg [cfgspec]`. @@ -2160,8 +2160,8 @@ impl StableSourceFileId { stable_crate_id: Option, ) -> Self { let mut hasher = StableHasher::new(); - filename.hash(&mut hasher); - stable_crate_id.hash(&mut hasher); + filename.stable_hash(&mut DummyStableHashCtxt, &mut hasher); + stable_crate_id.stable_hash(&mut DummyStableHashCtxt, &mut hasher); StableSourceFileId(hasher.finish()) } }