Skip to content

Commit 7ef7246

Browse files
committed
Implement rough symbol interning infra
1 parent 7fa20cb commit 7ef7246

File tree

5 files changed

+2253
-50
lines changed

5 files changed

+2253
-50
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-expand/src/name.rs

Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::fmt;
44

5+
use intern::Symbol;
56
use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr};
67

78
/// `Name` is a wrapper around string, which is used in hir for both references
@@ -12,7 +13,10 @@ use syntax::{ast, format_smolstr, utils::is_raw_identifier, SmolStr};
1213
/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
1314
/// name without "r#".
1415
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
15-
pub struct Name(Repr);
16+
pub struct Name {
17+
symbol: Symbol,
18+
span: (),
19+
}
1620

1721
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
1822
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -28,15 +32,11 @@ impl UnescapedName<'_> {
2832
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
2933
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
3034
pub fn to_smol_str(&self) -> SmolStr {
31-
match &self.0 .0 {
32-
Repr::Text(it) => {
33-
if let Some(stripped) = it.strip_prefix("r#") {
34-
SmolStr::new(stripped)
35-
} else {
36-
it.clone()
37-
}
38-
}
39-
Repr::TupleField(it) => SmolStr::new(it.to_string()),
35+
let it = self.0.symbol.as_str();
36+
if let Some(stripped) = it.strip_prefix("r#") {
37+
SmolStr::new(stripped)
38+
} else {
39+
it.into()
4040
}
4141
}
4242

@@ -51,34 +51,39 @@ impl Name {
5151
/// Hopefully, this should allow us to integrate hygiene cleaner in the
5252
/// future, and to switch to interned representation of names.
5353
const fn new_text(text: SmolStr) -> Name {
54-
Name(Repr::Text(text))
54+
Name { symbol: Symbol::intern(text.as_str()), span: () }
5555
}
5656

5757
// FIXME: See above, unfortunately some places really need this right now
5858
#[doc(hidden)]
5959
pub const fn new_text_dont_use(text: SmolStr) -> Name {
60-
Name(Repr::Text(text))
60+
Name { symbol: Symbol::intern(text.as_str()), span: () }
6161
}
6262

6363
pub fn new_tuple_field(idx: usize) -> Name {
64-
Name(Repr::TupleField(idx))
64+
Name { symbol: Symbol::intern(&idx.to_string()), span: () }
6565
}
6666

6767
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
68-
Self::new_text(lt.text().into())
68+
Name { symbol: Symbol::intern(lt.text().as_str()), span: () }
6969
}
7070

7171
/// Shortcut to create a name from a string literal.
7272
const fn new_static(text: &'static str) -> Name {
73-
Name::new_text(SmolStr::new_static(text))
73+
todo!()
74+
}
75+
76+
/// Shortcut to create a name from a string literal.
77+
fn new_ref(text: &str) -> Name {
78+
Name { symbol: Symbol::intern(text), span: () }
7479
}
7580

7681
/// Resolve a name from the text of token.
7782
fn resolve(raw_text: &str) -> Name {
7883
match raw_text.strip_prefix("r#") {
7984
// When `raw_text` starts with "r#" but the name does not coincide with any
8085
// keyword, we never need the prefix so we strip it.
81-
Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
86+
Some(text) if !is_raw_identifier(text) => Name::new_ref(text),
8287
// Keywords (in the current edition) *can* be used as a name in earlier editions of
8388
// Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
8489
// escaped form.
@@ -120,47 +125,32 @@ impl Name {
120125

121126
/// Returns the tuple index this name represents if it is a tuple field.
122127
pub fn as_tuple_index(&self) -> Option<usize> {
123-
match self.0 {
124-
Repr::TupleField(idx) => Some(idx),
125-
_ => None,
126-
}
128+
self.symbol.as_str().parse().ok()
127129
}
128130

129131
/// Returns the text this name represents if it isn't a tuple field.
130-
pub fn as_text(&self) -> Option<SmolStr> {
131-
match &self.0 {
132-
Repr::Text(it) => Some(it.clone()),
133-
_ => None,
134-
}
132+
pub fn as_text(&self) -> SmolStr {
133+
self.symbol.as_str().into()
135134
}
136135

137136
/// Returns the text this name represents if it isn't a tuple field.
138-
pub fn as_str(&self) -> Option<&str> {
139-
match &self.0 {
140-
Repr::Text(it) => Some(it),
141-
_ => None,
142-
}
137+
pub fn as_str(&self) -> &str {
138+
self.symbol.as_str()
143139
}
144140

145141
/// Returns the textual representation of this name as a [`SmolStr`].
146142
/// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in
147143
/// the general case.
148144
pub fn to_smol_str(&self) -> SmolStr {
149-
match &self.0 {
150-
Repr::Text(it) => it.clone(),
151-
Repr::TupleField(it) => SmolStr::new(it.to_string()),
152-
}
145+
self.as_text()
153146
}
154147

155148
pub fn unescaped(&self) -> UnescapedName<'_> {
156149
UnescapedName(self)
157150
}
158151

159152
pub fn is_escaped(&self) -> bool {
160-
match &self.0 {
161-
Repr::Text(it) => it.starts_with("r#"),
162-
Repr::TupleField(_) => false,
163-
}
153+
self.symbol.as_str().starts_with("r#")
164154
}
165155

166156
pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
@@ -175,10 +165,7 @@ struct Display<'a> {
175165

176166
impl fmt::Display for Display<'_> {
177167
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178-
match &self.name.0 {
179-
Repr::Text(text) => fmt::Display::fmt(&text, f),
180-
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
181-
}
168+
fmt::Display::fmt(self.name.symbol.as_str(), f)
182169
}
183170
}
184171

@@ -188,13 +175,9 @@ struct UnescapedDisplay<'a> {
188175

189176
impl fmt::Display for UnescapedDisplay<'_> {
190177
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191-
match &self.name.0 .0 {
192-
Repr::Text(text) => {
193-
let text = text.strip_prefix("r#").unwrap_or(text);
194-
fmt::Display::fmt(&text, f)
195-
}
196-
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
197-
}
178+
let symbol = &self.name.0.symbol.as_str();
179+
let text = symbol.strip_prefix("r#").unwrap_or(symbol);
180+
fmt::Display::fmt(&text, f)
198181
}
199182
}
200183

crates/intern/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dashmap.workspace = true
1818
hashbrown.workspace = true
1919
rustc-hash.workspace = true
2020
triomphe.workspace = true
21+
sptr = "0.3.2"
2122

2223
[lints]
23-
workspace = true
24+
workspace = true

crates/intern/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ type Guard<T> = dashmap::RwLockWriteGuard<
2020
HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>,
2121
>;
2222

23+
mod symbol;
24+
pub use self::symbol::{symbols, Symbol};
25+
2326
pub struct Interned<T: Internable + ?Sized> {
2427
arc: Arc<T>,
2528
}

0 commit comments

Comments
 (0)