Skip to content

Commit 86a5ecb

Browse files
committed
Refactor anymap to its own module
1 parent de49cc3 commit 86a5ecb

File tree

5 files changed

+105
-30
lines changed

5 files changed

+105
-30
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"rust-analyzer.rustc.source": "discover"
2+
"rust-analyzer.rustc.source": "discover",
3+
"rust-analyzer.imports.granularity.group": "module"
34
}

src/ctxt.rs

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// SPDX-License-Identifier: MIT OR Apache-2.0
44

5-
use std::any::{Any, TypeId};
5+
use std::any::Any;
66
use std::sync::Arc;
77

88
use rusqlite::{Connection, OptionalExtension};
@@ -14,6 +14,7 @@ use rustc_serialize::{Decodable, Encodable};
1414
use rustc_span::{DUMMY_SP, Span, sym};
1515

1616
use crate::diagnostic::use_stack::UseSite;
17+
use crate::utils::anymap::AnyMap;
1718

1819
pub(crate) trait Query: 'static {
1920
const NAME: &'static str;
@@ -54,7 +55,7 @@ pub struct AnalysisCtxt<'tcx> {
5455
pub sql_conn: RwLock<FxHashMap<CrateNum, Option<Arc<MTLock<Connection>>>>>,
5556

5657
pub call_stack: RwLock<Vec<UseSite<'tcx>>>,
57-
pub query_cache: RwLock<FxHashMap<TypeId, Arc<dyn Any + DynSend + DynSync>>>,
58+
pub query_cache: RwLock<AnyMap<dyn Any + DynSend + DynSync>>,
5859
}
5960

6061
// Everything in `AnalysisCtxt` is either `DynSend/DynSync` or `Send/Sync`, but since there're no relation between two right now compiler cannot infer this.
@@ -113,38 +114,17 @@ impl Drop for AnalysisCtxt<'_> {
113114
}
114115
}
115116

116-
// Used when parallel compiler is used.
117-
trait ArcDowncast: Sized {
118-
fn downcast<T: Any>(self) -> Result<Arc<T>, Self>;
119-
}
120-
121-
impl ArcDowncast for Arc<dyn Any> {
122-
fn downcast<T: Any>(self) -> Result<Arc<T>, Self> {
123-
if (*self).is::<T>() {
124-
Ok(unsafe { Arc::from_raw(Arc::into_raw(self) as _) })
125-
} else {
126-
Err(self)
127-
}
128-
}
129-
}
130-
131117
impl<'tcx> AnalysisCtxt<'tcx> {
132118
pub(crate) fn query_cache<Q: Query>(
133119
&self,
134120
) -> Arc<RwLock<FxHashMap<Q::Key<'tcx>, Q::Value<'tcx>>>> {
135-
let key = TypeId::of::<Q>();
136121
let mut guard = self.query_cache.borrow_mut();
137-
let cache = (guard
138-
.entry(key)
139-
.or_insert_with(|| {
140-
let cache = Arc::new(RwLock::new(
141-
FxHashMap::<Q::Key<'static>, Q::Value<'static>>::default(),
142-
));
143-
cache
144-
})
145-
.clone() as Arc<dyn Any>)
146-
.downcast::<RwLock<FxHashMap<Q::Key<'static>, Q::Value<'static>>>>()
147-
.unwrap();
122+
let cache = guard.entry().or_insert_with(|| {
123+
let cache = Arc::new(RwLock::new(
124+
FxHashMap::<Q::Key<'static>, Q::Value<'static>>::default(),
125+
));
126+
cache
127+
});
148128
// Everything stored inside query_cache is conceptually `'tcx`, but due to limitation
149129
// of `Any` we hack around the lifetime.
150130
unsafe { std::mem::transmute(cache) }

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#![feature(once_cell_get_mut)]
1313
// Used in symbol.rs
1414
#![feature(macro_metavar_expr)]
15+
#![feature(unsize)]
1516
#![warn(rustc::internal)]
1617

1718
#[macro_use]

src/utils/anymap.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::any::{Any, TypeId};
2+
use std::collections::hash_map as map;
3+
use std::marker::{PhantomData, Unsize};
4+
5+
use rustc_data_structures::fx::FxHashMap;
6+
7+
/// Map that can store data for arbitrary types.
8+
pub struct AnyMap<U: ?Sized> {
9+
// This is basically `FxHashMap<TypeId, Box<dyn Any>>`
10+
//
11+
// The generic `U` is present to capture auto trait bounds.
12+
map: FxHashMap<TypeId, Box<U>>,
13+
}
14+
15+
pub struct OccupiedEntry<'a, U: ?Sized, T> {
16+
entry: map::OccupiedEntry<'a, TypeId, Box<U>>,
17+
phantom: PhantomData<T>,
18+
}
19+
20+
impl<'a, U: Any + ?Sized + 'static, T: 'static> OccupiedEntry<'a, U, T> {
21+
pub fn into_mut(self) -> &'a mut T
22+
where
23+
T: Unsize<U>,
24+
{
25+
let any_ref = &mut **self.entry.into_mut();
26+
debug_assert_eq!((*any_ref).type_id(), TypeId::of::<T>());
27+
// SAFETY: by type invariant, `any_ref` is a `&mut T`.
28+
unsafe { &mut *(any_ref as *mut U as *mut T) }
29+
}
30+
}
31+
32+
pub struct VacantEntry<'a, U: ?Sized, T> {
33+
entry: map::VacantEntry<'a, TypeId, Box<U>>,
34+
phantom: PhantomData<T>,
35+
}
36+
37+
impl<'a, U: Any + ?Sized, T> VacantEntry<'a, U, T> {
38+
pub fn insert(self, value: T) -> &'a mut T
39+
where
40+
T: Unsize<U>,
41+
{
42+
let any_ref = &mut **self.entry.insert(Box::new(value) as _);
43+
// SAFETY: we just inserted it and we know the type is `Box<T>`.
44+
unsafe { &mut *(any_ref as *mut U as *mut T) }
45+
}
46+
}
47+
48+
pub enum Entry<'a, U: ?Sized, T> {
49+
Occupied(OccupiedEntry<'a, U, T>),
50+
Vacant(VacantEntry<'a, U, T>),
51+
}
52+
53+
impl<'a, U: Any + ?Sized, T: 'static> Entry<'a, U, T> {
54+
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T
55+
where
56+
T: Unsize<U>,
57+
{
58+
match self {
59+
Entry::Occupied(entry) => entry.into_mut(),
60+
Entry::Vacant(entry) => entry.insert(default()),
61+
}
62+
}
63+
}
64+
65+
impl<U: ?Sized> Default for AnyMap<U> {
66+
fn default() -> Self {
67+
Self::new()
68+
}
69+
}
70+
71+
impl<U: ?Sized> AnyMap<U> {
72+
pub fn new() -> Self {
73+
Self {
74+
map: Default::default(),
75+
}
76+
}
77+
}
78+
79+
impl<U: Any + ?Sized> AnyMap<U> {
80+
pub fn entry<T: 'static>(&mut self) -> Entry<'_, U, T> {
81+
match self.map.entry(TypeId::of::<T>()) {
82+
map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry {
83+
entry,
84+
phantom: PhantomData,
85+
}),
86+
map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
87+
entry,
88+
phantom: PhantomData,
89+
}),
90+
}
91+
}
92+
}

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod anymap;

0 commit comments

Comments
 (0)