33//! type, and vice versa.
44
55use rustc_arena:: DroplessArena ;
6- use rustc_data_structures:: fx:: FxHashMap ;
6+ use rustc_data_structures:: fx:: FxIndexSet ;
77use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher , ToStableHashKey } ;
88use rustc_data_structures:: sync:: Lock ;
99use rustc_macros:: HashStable_Generic ;
@@ -2076,43 +2076,33 @@ impl<CTX> ToStableHashKey<CTX> for Symbol {
20762076 }
20772077}
20782078
2079- #[ derive( Default ) ]
20802079pub ( crate ) struct Interner ( Lock < InternerInner > ) ;
20812080
20822081// The `&'static str`s in this type actually point into the arena.
20832082//
2084- // The `FxHashMap`+`Vec` pair could be replaced by `FxIndexSet`, but #75278
2085- // found that to regress performance up to 2% in some cases. This might be
2086- // revisited after further improvements to `indexmap`.
2087- //
20882083// This type is private to prevent accidentally constructing more than one
20892084// `Interner` on the same thread, which makes it easy to mix up `Symbol`s
20902085// between `Interner`s.
2091- #[ derive( Default ) ]
20922086struct InternerInner {
20932087 arena : DroplessArena ,
2094- names : FxHashMap < & ' static str , Symbol > ,
2095- strings : Vec < & ' static str > ,
2088+ strings : FxIndexSet < & ' static str > ,
20962089}
20972090
20982091impl Interner {
20992092 fn prefill ( init : & [ & ' static str ] ) -> Self {
21002093 Interner ( Lock :: new ( InternerInner {
2101- strings : init. into ( ) ,
2102- names : init. iter ( ) . copied ( ) . zip ( ( 0 ..) . map ( Symbol :: new) ) . collect ( ) ,
2103- ..Default :: default ( )
2094+ arena : Default :: default ( ) ,
2095+ strings : init. iter ( ) . copied ( ) . collect ( ) ,
21042096 } ) )
21052097 }
21062098
21072099 #[ inline]
21082100 fn intern ( & self , string : & str ) -> Symbol {
21092101 let mut inner = self . 0 . lock ( ) ;
2110- if let Some ( & name ) = inner. names . get ( string) {
2111- return name ;
2102+ if let Some ( idx ) = inner. strings . get_index_of ( string) {
2103+ return Symbol :: new ( idx as u32 ) ;
21122104 }
21132105
2114- let name = Symbol :: new ( inner. strings . len ( ) as u32 ) ;
2115-
21162106 // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena,
21172107 // and immediately convert the clone back to `&[u8]`, all because there
21182108 // is no `inner.arena.alloc_str()` method. This is clearly safe.
@@ -2122,20 +2112,21 @@ impl Interner {
21222112 // SAFETY: we can extend the arena allocation to `'static` because we
21232113 // only access these while the arena is still alive.
21242114 let string: & ' static str = unsafe { & * ( string as * const str ) } ;
2125- inner. strings . push ( string) ;
21262115
21272116 // This second hash table lookup can be avoided by using `RawEntryMut`,
21282117 // but this code path isn't hot enough for it to be worth it. See
21292118 // #91445 for details.
2130- inner. names . insert ( string, name) ;
2131- name
2119+ let ( idx, is_new) = inner. strings . insert_full ( string) ;
2120+ debug_assert ! ( is_new) ; // due to the get_index_of check above
2121+
2122+ Symbol :: new ( idx as u32 )
21322123 }
21332124
21342125 /// Get the symbol as a string.
21352126 ///
21362127 /// [`Symbol::as_str()`] should be used in preference to this function.
21372128 fn get ( & self , symbol : Symbol ) -> & str {
2138- self . 0 . lock ( ) . strings [ symbol. 0 . as_usize ( ) ]
2129+ self . 0 . lock ( ) . strings . get_index ( symbol. 0 . as_usize ( ) ) . unwrap ( )
21392130 }
21402131}
21412132
0 commit comments