@@ -5,6 +5,7 @@ use std::num::NonZero;
55
66use rustc_index:: bit_set:: { self , DenseBitSet } ;
77use rustc_index:: { Idx , IndexSlice , IndexVec } ;
8+ use rustc_macros:: StableHash ;
89use smallvec:: SmallVec ;
910use thin_vec:: ThinVec ;
1011
@@ -37,6 +38,25 @@ pub trait StableHashCtxt {
3738 fn assert_default_stable_hash_controls ( & self , msg : & str ) ;
3839}
3940
41+ /// Dummy impl of [`StableHashCtxt`], for code that can't provide a real context,
42+ /// and only wants to compute stable hashes for types that don't rely on the context.
43+ pub struct DummyStableHashCtxt ;
44+
45+ impl StableHashCtxt for DummyStableHashCtxt {
46+ fn stable_hash_span ( & mut self , _span : RawSpan , _hasher : & mut StableHasher ) {
47+ panic ! ( "method on dummy stable-hash context was unexpectedly called" ) ;
48+ }
49+ fn def_path_hash ( & self , _def_id : RawDefId ) -> Fingerprint {
50+ panic ! ( "method on dummy stable-hash context was unexpectedly called" ) ;
51+ }
52+ fn stable_hash_controls ( & self ) -> StableHashControls {
53+ panic ! ( "method on dummy stable-hash context was unexpectedly called" ) ;
54+ }
55+ fn assert_default_stable_hash_controls ( & self , _msg : & str ) {
56+ panic ! ( "method on dummy stable-hash context was unexpectedly called" ) ;
57+ }
58+ }
59+
4060// A type used to work around `Span` not being visible in this crate. It is the same size as
4161// `Span` but has a different alignment.
4262pub struct RawSpan ( pub u64 ) ;
@@ -71,6 +91,18 @@ pub struct RawDefId(pub u32, pub u32);
7191/// - `stable_hash()` must be independent of the host architecture. The
7292/// `StableHasher` takes care of endianness and `isize`/`usize` platform
7393/// differences.
94+ ///
95+ /// # Consistency across compiler stages
96+ ///
97+ /// Because the stage1 compiler is built against the bootstrap stdlib, but the stage2
98+ /// compiler uses in-tree stdlib, implementations of `StableHash` must not rely on `Hash`
99+ /// impls that were provided or derived by the standard library. Doing so leads to weird
100+ /// bugs and CI failures that can block changes to those stdlib hash impls.
101+ ///
102+ /// (Unfortunately, as of late 2026 we have many stable-hash impls that _do_
103+ /// leak details of stdlib hash impls.)
104+ ///
105+ /// See <https://github.com/rust-lang/rust/pull/164458> for more context.
74106pub trait StableHash {
75107 fn stable_hash < Hcx : StableHashCtxt > ( & self , hcx : & mut Hcx , hasher : & mut StableHasher ) ;
76108}
@@ -575,8 +607,97 @@ impl<R: Idx, C: Idx> StableHash for bit_set::BitMatrix<R, C> {
575607
576608impl_stable_traits_for_trivial_type ! ( :: std:: ffi:: OsStr ) ;
577609
578- impl_stable_traits_for_trivial_type ! ( :: std:: path:: Path ) ;
579- impl_stable_traits_for_trivial_type ! ( :: std:: path:: PathBuf ) ;
610+ impl StableHash for std:: path:: Path {
611+ fn stable_hash < Hcx : StableHashCtxt > ( & self , hcx : & mut Hcx , hasher : & mut StableHasher ) {
612+ use std:: path:: { Component , Prefix } ;
613+
614+ /// Tag value to be mixed in with each kind of component/prefix.
615+ #[ derive( StableHash ) ]
616+ enum Tag {
617+ StartOfPath ,
618+ PrefixVerbatim ,
619+ PrefixVerbatimUNC ,
620+ PrefixVerbatimDisk ,
621+ PrefixDeviceNS ,
622+ PrefixUNC ,
623+ PrefixDisk ,
624+ RootDir ,
625+ CurDir ,
626+ ParentDir ,
627+ Normal ,
628+ EndOfPath ,
629+ }
630+
631+ // Explicitly tag the start of each path, so that empty paths always hash something.
632+ Tag :: StartOfPath . stable_hash ( hcx, hasher) ;
633+
634+ for ( i, component) in self . components ( ) . enumerate ( ) {
635+ // Mix in the index of each component, to avoid potentially trivial collisions
636+ // between inputs like `("foo/bar", "baz")` and `("foo", "bar/baz")`.
637+ i. stable_hash ( hcx, hasher) ;
638+ match component {
639+ Component :: Prefix ( prefix) => match prefix. kind ( ) {
640+ Prefix :: Verbatim ( os_str) => {
641+ Tag :: PrefixVerbatim . stable_hash ( hcx, hasher) ;
642+ os_str. stable_hash ( hcx, hasher) ;
643+ }
644+ Prefix :: VerbatimUNC ( host, share) => {
645+ Tag :: PrefixVerbatimUNC . stable_hash ( hcx, hasher) ;
646+ host. stable_hash ( hcx, hasher) ;
647+ share. stable_hash ( hcx, hasher) ;
648+ }
649+ Prefix :: VerbatimDisk ( letter) => {
650+ Tag :: PrefixVerbatimDisk . stable_hash ( hcx, hasher) ;
651+ letter. stable_hash ( hcx, hasher) ;
652+ }
653+ Prefix :: DeviceNS ( os_str) => {
654+ Tag :: PrefixDeviceNS . stable_hash ( hcx, hasher) ;
655+ os_str. stable_hash ( hcx, hasher) ;
656+ }
657+ Prefix :: UNC ( host, share) => {
658+ Tag :: PrefixUNC . stable_hash ( hcx, hasher) ;
659+ host. stable_hash ( hcx, hasher) ;
660+ share. stable_hash ( hcx, hasher) ;
661+ }
662+ Prefix :: Disk ( letter) => {
663+ Tag :: PrefixDisk . stable_hash ( hcx, hasher) ;
664+ letter. stable_hash ( hcx, hasher) ;
665+ }
666+ } ,
667+ Component :: RootDir => Tag :: RootDir . stable_hash ( hcx, hasher) ,
668+ Component :: CurDir => Tag :: CurDir . stable_hash ( hcx, hasher) ,
669+ Component :: ParentDir => Tag :: ParentDir . stable_hash ( hcx, hasher) ,
670+ Component :: Normal ( os_str) => {
671+ Tag :: Normal . stable_hash ( hcx, hasher) ;
672+ os_str. stable_hash ( hcx, hasher) ;
673+ }
674+ }
675+ }
676+
677+ // Explicitly tag the end of each path, so that empty paths always hash something.
678+ // (Tagging the start and the end and the component-index is potentially overkill.)
679+ Tag :: EndOfPath . stable_hash ( hcx, hasher) ;
680+ }
681+ }
682+
683+ impl StableOrd for std:: path:: Path {
684+ /// FIXME(Zalathar): Unstably sorting paths is pre-existing behavior, but potentially wrong.
685+ const CAN_USE_UNSTABLE_SORT : bool = true ;
686+ /// Stdlib path sorting should (hopefully) not change.
687+ const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED : ( ) = ( ) ;
688+ }
689+
690+ impl StableHash for std:: path:: PathBuf {
691+ fn stable_hash < Hcx : StableHashCtxt > ( & self , hcx : & mut Hcx , hasher : & mut StableHasher ) {
692+ self . as_path ( ) . stable_hash ( hcx, hasher) ;
693+ }
694+ }
695+
696+ impl StableOrd for std:: path:: PathBuf {
697+ const CAN_USE_UNSTABLE_SORT : bool = <std:: path:: Path as StableOrd >:: CAN_USE_UNSTABLE_SORT ;
698+ /// Stdlib path sorting should (hopefully) not change.
699+ const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED : ( ) = ( ) ;
700+ }
580701
581702// It is not safe to implement StableHash for HashSet, HashMap or any other collection type
582703// with unstable but observable iteration order.
0 commit comments