Skip to content

Commit b00266b

Browse files
Global TypeRef/TraitRef interning
1 parent 25201b2 commit b00266b

File tree

11 files changed

+205
-120
lines changed

11 files changed

+205
-120
lines changed

Cargo.lock

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

crates/hir/src/display.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl HirDisplay for Function {
9191
let ret_type = if !qual.is_async {
9292
&data.ret_type
9393
} else {
94-
match &data.ret_type {
94+
match &*data.ret_type {
9595
TypeRef::ImplTrait(bounds) => match &bounds[0] {
9696
TypeBound::Path(path) => {
9797
path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings

crates/hir/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ impl SelfParam {
957957
func_data
958958
.params
959959
.first()
960-
.map(|param| match *param {
960+
.map(|param| match &**param {
961961
TypeRef::Reference(.., mutability) => match mutability {
962962
hir_def::type_ref::Mutability::Shared => Access::Shared,
963963
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
@@ -1011,7 +1011,7 @@ impl Const {
10111011
}
10121012

10131013
pub fn type_ref(self, db: &dyn HirDatabase) -> TypeRef {
1014-
db.const_data(self.id).type_ref.clone()
1014+
db.const_data(self.id).type_ref.as_ref().clone()
10151015
}
10161016
}
10171017

@@ -1101,7 +1101,7 @@ impl TypeAlias {
11011101
}
11021102

11031103
pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1104-
db.type_alias_data(self.id).type_ref.clone()
1104+
db.type_alias_data(self.id).type_ref.as_deref().cloned()
11051105
}
11061106

11071107
pub fn ty(self, db: &dyn HirDatabase) -> Type {
@@ -1615,7 +1615,7 @@ impl Impl {
16151615
// FIXME: the return type is wrong. This should be a hir version of
16161616
// `TraitRef` (ie, resolved `TypeRef`).
16171617
pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> {
1618-
db.impl_data(self.id).target_trait.clone()
1618+
db.impl_data(self.id).target_trait.as_deref().cloned()
16191619
}
16201620

16211621
pub fn self_ty(self, db: &dyn HirDatabase) -> Type {

crates/hir_def/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ doctest = false
1111

1212
[dependencies]
1313
cov-mark = { version = "1.1", features = ["thread-local"] }
14+
dashmap = { version = "4.0.2", features = ["raw-api"] }
1415
log = "0.4.8"
1516
once_cell = "1.3.1"
1617
rustc-hash = "1.1.0"

crates/hir_def/src/adt.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
1515
use crate::{
1616
body::{CfgExpander, LowerCtx},
1717
db::DefDatabase,
18+
intern::Interned,
1819
item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
1920
src::HasChildSource,
2021
src::HasSource,
@@ -58,7 +59,7 @@ pub enum VariantData {
5859
#[derive(Debug, Clone, PartialEq, Eq)]
5960
pub struct FieldData {
6061
pub name: Name,
61-
pub type_ref: TypeRef,
62+
pub type_ref: Interned<TypeRef>,
6263
pub visibility: RawVisibility,
6364
}
6465

@@ -292,7 +293,7 @@ fn lower_struct(
292293
|| Either::Left(fd.clone()),
293294
|| FieldData {
294295
name: Name::new_tuple_field(i),
295-
type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
296+
type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
296297
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
297298
},
298299
);
@@ -309,7 +310,7 @@ fn lower_struct(
309310
|| Either::Right(fd.clone()),
310311
|| FieldData {
311312
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
312-
type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
313+
type_ref: Interned::new(TypeRef::from_ast_opt(&ctx, fd.ty())),
313314
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
314315
},
315316
);
@@ -358,7 +359,7 @@ fn lower_field(
358359
) -> FieldData {
359360
FieldData {
360361
name: field.name.clone(),
361-
type_ref: item_tree[field.type_ref].clone(),
362+
type_ref: field.type_ref.clone(),
362363
visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
363364
}
364365
}

crates/hir_def/src/data.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
attr::Attrs,
1010
body::Expander,
1111
db::DefDatabase,
12+
intern::Interned,
1213
item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
1314
type_ref::{TraitRef, TypeBound, TypeRef},
1415
visibility::RawVisibility,
@@ -19,8 +20,8 @@ use crate::{
1920
#[derive(Debug, Clone, PartialEq, Eq)]
2021
pub struct FunctionData {
2122
pub name: Name,
22-
pub params: Vec<TypeRef>,
23-
pub ret_type: TypeRef,
23+
pub params: Vec<Interned<TypeRef>>,
24+
pub ret_type: Interned<TypeRef>,
2425
pub attrs: Attrs,
2526
/// True if the first param is `self`. This is relevant to decide whether this
2627
/// can be called as a method.
@@ -57,11 +58,11 @@ impl FunctionData {
5758
params: enabled_params
5859
.clone()
5960
.filter_map(|id| match &item_tree[id] {
60-
Param::Normal(ty) => Some(item_tree[*ty].clone()),
61+
Param::Normal(ty) => Some(ty.clone()),
6162
Param::Varargs => None,
6263
})
6364
.collect(),
64-
ret_type: item_tree[func.ret_type].clone(),
65+
ret_type: func.ret_type.clone(),
6566
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
6667
has_self_param: func.has_self_param,
6768
has_body: func.has_body,
@@ -76,7 +77,7 @@ impl FunctionData {
7677
#[derive(Debug, Clone, PartialEq, Eq)]
7778
pub struct TypeAliasData {
7879
pub name: Name,
79-
pub type_ref: Option<TypeRef>,
80+
pub type_ref: Option<Interned<TypeRef>>,
8081
pub visibility: RawVisibility,
8182
pub is_extern: bool,
8283
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
@@ -94,7 +95,7 @@ impl TypeAliasData {
9495

9596
Arc::new(TypeAliasData {
9697
name: typ.name.clone(),
97-
type_ref: typ.type_ref.map(|id| item_tree[id].clone()),
98+
type_ref: typ.type_ref.clone(),
9899
visibility: item_tree[typ.visibility].clone(),
99100
is_extern: typ.is_extern,
100101
bounds: typ.bounds.to_vec(),
@@ -156,8 +157,8 @@ impl TraitData {
156157

157158
#[derive(Debug, Clone, PartialEq, Eq)]
158159
pub struct ImplData {
159-
pub target_trait: Option<TraitRef>,
160-
pub self_ty: TypeRef,
160+
pub target_trait: Option<Interned<TraitRef>>,
161+
pub self_ty: Interned<TypeRef>,
161162
pub items: Vec<AssocItemId>,
162163
pub is_negative: bool,
163164
}
@@ -169,8 +170,8 @@ impl ImplData {
169170

170171
let item_tree = impl_loc.id.item_tree(db);
171172
let impl_def = &item_tree[impl_loc.id.value];
172-
let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
173-
let self_ty = item_tree[impl_def.self_ty].clone();
173+
let target_trait = impl_def.target_trait.clone();
174+
let self_ty = impl_def.self_ty.clone();
174175
let is_negative = impl_def.is_negative;
175176
let module_id = impl_loc.container;
176177
let container = AssocContainerId::ImplId(id);
@@ -195,7 +196,7 @@ impl ImplData {
195196
pub struct ConstData {
196197
/// const _: () = ();
197198
pub name: Option<Name>,
198-
pub type_ref: TypeRef,
199+
pub type_ref: Interned<TypeRef>,
199200
pub visibility: RawVisibility,
200201
}
201202

@@ -207,7 +208,7 @@ impl ConstData {
207208

208209
Arc::new(ConstData {
209210
name: konst.name.clone(),
210-
type_ref: item_tree[konst.type_ref].clone(),
211+
type_ref: konst.type_ref.clone(),
211212
visibility: item_tree[konst.visibility].clone(),
212213
})
213214
}
@@ -216,7 +217,7 @@ impl ConstData {
216217
#[derive(Debug, Clone, PartialEq, Eq)]
217218
pub struct StaticData {
218219
pub name: Option<Name>,
219-
pub type_ref: TypeRef,
220+
pub type_ref: Interned<TypeRef>,
220221
pub visibility: RawVisibility,
221222
pub mutable: bool,
222223
pub is_extern: bool,
@@ -230,7 +231,7 @@ impl StaticData {
230231

231232
Arc::new(StaticData {
232233
name: Some(statik.name.clone()),
233-
type_ref: item_tree[statik.type_ref].clone(),
234+
type_ref: statik.type_ref.clone(),
234235
visibility: item_tree[statik.visibility].clone(),
235236
mutable: statik.mutable,
236237
is_extern: statik.is_extern,

crates/hir_def/src/intern.rs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! Global `Arc`-based object interning infrastructure.
2+
//!
3+
//! Eventually this should probably be replaced with salsa-based interning.
4+
5+
use std::{
6+
fmt::{self, Debug},
7+
hash::{BuildHasherDefault, Hash},
8+
ops::Deref,
9+
sync::Arc,
10+
};
11+
12+
use dashmap::{DashMap, SharedValue};
13+
use once_cell::sync::OnceCell;
14+
use rustc_hash::FxHasher;
15+
16+
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
17+
18+
pub struct Interned<T: Internable> {
19+
arc: Arc<T>,
20+
}
21+
22+
impl<T: Internable> Interned<T> {
23+
pub fn new(obj: T) -> Self {
24+
let storage = T::storage().get();
25+
let shard_idx = storage.determine_map(&obj);
26+
let shard = &storage.shards()[shard_idx];
27+
let shard = shard.upgradeable_read();
28+
29+
// Atomically,
30+
// - check if `obj` is already in the map
31+
// - if so, clone its `Arc` and return it
32+
// - if not, box it up, insert it, and return a clone
33+
// This needs to be atomic (locking the shard) to avoid races with other thread, which could
34+
// insert the same object between us looking it up and inserting it.
35+
36+
// FIXME: avoid double lookup by using raw entry API (once stable, or when hashbrown can be
37+
// plugged into dashmap)
38+
if let Some((arc, _)) = shard.get_key_value(&obj) {
39+
return Self { arc: arc.clone() };
40+
}
41+
42+
let arc = Arc::new(obj);
43+
let arc2 = arc.clone();
44+
45+
{
46+
let mut shard = shard.upgrade();
47+
shard.insert(arc2, SharedValue::new(()));
48+
}
49+
50+
Self { arc }
51+
}
52+
}
53+
54+
impl<T: Internable> Drop for Interned<T> {
55+
fn drop(&mut self) {
56+
// When the last `Ref` is dropped, remove the object from the global map.
57+
if Arc::strong_count(&self.arc) == 2 {
58+
// Only `self` and the global map point to the object.
59+
60+
let storage = T::storage().get();
61+
let shard_idx = storage.determine_map(&self.arc);
62+
let shard = &storage.shards()[shard_idx];
63+
let mut shard = shard.write();
64+
65+
// FIXME: avoid double lookup
66+
let (arc, _) =
67+
shard.get_key_value(&self.arc).expect("interned value removed prematurely");
68+
69+
if Arc::strong_count(arc) != 2 {
70+
// Another thread has interned another copy
71+
return;
72+
}
73+
74+
shard.remove(&self.arc);
75+
76+
// Shrink the backing storage if the shard is less than 50% occupied.
77+
if shard.len() * 2 < shard.capacity() {
78+
shard.shrink_to_fit();
79+
}
80+
}
81+
}
82+
}
83+
84+
/// Compares interned `Ref`s using pointer equality.
85+
impl<T: Internable> PartialEq for Interned<T> {
86+
#[inline]
87+
fn eq(&self, other: &Self) -> bool {
88+
Arc::ptr_eq(&self.arc, &other.arc)
89+
}
90+
}
91+
92+
impl<T: Internable> Eq for Interned<T> {}
93+
94+
impl<T: Internable> AsRef<T> for Interned<T> {
95+
#[inline]
96+
fn as_ref(&self) -> &T {
97+
&self.arc
98+
}
99+
}
100+
101+
impl<T: Internable> Deref for Interned<T> {
102+
type Target = T;
103+
104+
#[inline]
105+
fn deref(&self) -> &Self::Target {
106+
&self.arc
107+
}
108+
}
109+
110+
impl<T: Internable> Clone for Interned<T> {
111+
fn clone(&self) -> Self {
112+
Self { arc: self.arc.clone() }
113+
}
114+
}
115+
116+
impl<T: Debug + Internable> Debug for Interned<T> {
117+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118+
(*self.arc).fmt(f)
119+
}
120+
}
121+
122+
pub struct InternStorage<T> {
123+
map: OnceCell<InternMap<T>>,
124+
}
125+
126+
impl<T> InternStorage<T> {
127+
pub const fn new() -> Self {
128+
Self { map: OnceCell::new() }
129+
}
130+
}
131+
132+
impl<T: Internable> InternStorage<T> {
133+
fn get(&self) -> &InternMap<T> {
134+
self.map.get_or_init(DashMap::default)
135+
}
136+
}
137+
138+
pub trait Internable: Hash + Eq + Sized + 'static {
139+
fn storage() -> &'static InternStorage<Self>;
140+
}
141+
142+
// region:`Internable` implementations
143+
144+
macro_rules! impl_internable {
145+
( $($t:ty),+ $(,)? ) => { $(
146+
impl Internable for $t {
147+
fn storage() -> &'static InternStorage<Self> {
148+
static STORAGE: InternStorage<$t> = InternStorage::new();
149+
&STORAGE
150+
}
151+
}
152+
)+ };
153+
}
154+
155+
impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef);
156+
157+
// endregion

0 commit comments

Comments
 (0)