diff options
-rw-r--r-- | crates/libanalysis/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/libanalysis/src/db.rs | 306 | ||||
-rw-r--r-- | crates/libanalysis/src/db/mod.rs | 196 | ||||
-rw-r--r-- | crates/libanalysis/src/db/queries.rs | 43 | ||||
-rw-r--r-- | crates/libanalysis/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/libanalysis/src/module_map_db.rs | 130 | ||||
-rw-r--r-- | crates/salsa/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/salsa/tests/integration.rs | 16 |
8 files changed, 318 insertions, 395 deletions
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml index 4c92951b1..88f29d7c8 100644 --- a/crates/libanalysis/Cargo.toml +++ b/crates/libanalysis/Cargo.toml | |||
@@ -14,6 +14,7 @@ fst = "0.3.1" | |||
14 | im = "12.0.0" | 14 | im = "12.0.0" |
15 | libsyntax2 = { path = "../libsyntax2" } | 15 | libsyntax2 = { path = "../libsyntax2" } |
16 | libeditor = { path = "../libeditor" } | 16 | libeditor = { path = "../libeditor" } |
17 | salsa = { path = "../salsa" } | ||
17 | 18 | ||
18 | [dev-dependencies] | 19 | [dev-dependencies] |
19 | test_utils = { path = "../test_utils" } | 20 | test_utils = { path = "../test_utils" } |
diff --git a/crates/libanalysis/src/db.rs b/crates/libanalysis/src/db.rs deleted file mode 100644 index d30e75fe2..000000000 --- a/crates/libanalysis/src/db.rs +++ /dev/null | |||
@@ -1,306 +0,0 @@ | |||
1 | use std::{ | ||
2 | hash::{Hash, Hasher}, | ||
3 | sync::Arc, | ||
4 | cell::RefCell, | ||
5 | fmt::Debug, | ||
6 | any::Any, | ||
7 | }; | ||
8 | use parking_lot::Mutex; | ||
9 | use libsyntax2::{File}; | ||
10 | use im; | ||
11 | use { | ||
12 | FileId, | ||
13 | imp::{FileResolverImp}, | ||
14 | }; | ||
15 | |||
16 | #[derive(Debug)] | ||
17 | pub(crate) struct DbHost { | ||
18 | db: Arc<Db>, | ||
19 | } | ||
20 | |||
21 | impl DbHost { | ||
22 | pub(crate) fn new() -> DbHost { | ||
23 | let db = Db { | ||
24 | file_resolver: FileResolverImp::default(), | ||
25 | files: im::HashMap::new(), | ||
26 | cache: Mutex::new(Cache::new()) | ||
27 | }; | ||
28 | DbHost { db: Arc::new(db) } | ||
29 | } | ||
30 | pub(crate) fn change_file(&mut self, file_id: FileId, text: Option<String>) { | ||
31 | let db = self.db_mut(); | ||
32 | match text { | ||
33 | None => { | ||
34 | db.files.remove(&file_id); | ||
35 | } | ||
36 | Some(text) => { | ||
37 | db.files.insert(file_id, Arc::new(text)); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) { | ||
42 | let db = self.db_mut(); | ||
43 | db.file_resolver = file_resolver | ||
44 | } | ||
45 | pub(crate) fn query_ctx(&self) -> QueryCtx { | ||
46 | QueryCtx { | ||
47 | db: Arc::clone(&self.db), | ||
48 | stack: RefCell::new(Vec::new()), | ||
49 | trace: RefCell::new(Vec::new()), | ||
50 | } | ||
51 | } | ||
52 | fn db_mut(&mut self) -> &mut Db { | ||
53 | // NB: this "forks" the database | ||
54 | let db = Arc::make_mut(&mut self.db); | ||
55 | db.cache.get_mut().gen += 1; | ||
56 | db | ||
57 | } | ||
58 | } | ||
59 | |||
60 | type QueryInvocationId = (u32, u64); | ||
61 | type Gen = u64; | ||
62 | type OutputHash = u64; | ||
63 | |||
64 | fn id<Q: Query>(params: &Q::Params) -> QueryInvocationId { | ||
65 | use std::collections::hash_map::DefaultHasher; | ||
66 | let mut hasher = DefaultHasher::new(); | ||
67 | params.hash(&mut hasher); | ||
68 | (Q::ID, hasher.finish()) | ||
69 | } | ||
70 | fn output_hash<Q: Query>(output: &Q::Output) -> OutputHash { | ||
71 | use std::collections::hash_map::DefaultHasher; | ||
72 | let mut hasher = DefaultHasher::new(); | ||
73 | output.hash(&mut hasher); | ||
74 | hasher.finish() | ||
75 | } | ||
76 | |||
77 | #[derive(Debug)] | ||
78 | pub(crate) struct Db { | ||
79 | file_resolver: FileResolverImp, | ||
80 | files: im::HashMap<FileId, Arc<String>>, | ||
81 | cache: Mutex<Cache>, | ||
82 | } | ||
83 | |||
84 | impl Clone for Db { | ||
85 | fn clone(&self) -> Db { | ||
86 | Db { | ||
87 | file_resolver: self.file_resolver.clone(), | ||
88 | files: self.files.clone(), | ||
89 | cache: Mutex::new(Cache::new()), | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | type QueryDeps = Vec<(QueryInvocationId, Arc<Any>, OutputHash)>; | ||
95 | |||
96 | #[derive(Default, Debug)] | ||
97 | pub(crate) struct Cache { | ||
98 | gen: Gen, | ||
99 | green: im::HashMap<QueryInvocationId, (Gen, OutputHash)>, | ||
100 | deps: im::HashMap<QueryInvocationId, QueryDeps>, | ||
101 | results: im::HashMap<QueryInvocationId, Arc<Any>>, | ||
102 | } | ||
103 | |||
104 | |||
105 | #[allow(type_alias_bounds)] | ||
106 | pub(crate) type QueryCache<Q: Query> = im::HashMap< | ||
107 | <Q as Query>::Params, | ||
108 | <Q as Query>::Output | ||
109 | >; | ||
110 | |||
111 | impl Cache { | ||
112 | fn new() -> Cache { | ||
113 | Default::default() | ||
114 | } | ||
115 | |||
116 | fn get_result<Q: Query>(&self, id: QueryInvocationId) -> Q::Output | ||
117 | where | ||
118 | Q::Output: Clone | ||
119 | { | ||
120 | let res = &self.results[&id]; | ||
121 | let res = res.downcast_ref::<Q::Output>().unwrap(); | ||
122 | res.clone() | ||
123 | } | ||
124 | } | ||
125 | |||
126 | pub(crate) struct QueryCtx { | ||
127 | db: Arc<Db>, | ||
128 | stack: RefCell<Vec<(QueryInvocationId, QueryDeps)>>, | ||
129 | pub(crate) trace: RefCell<Vec<TraceEvent>>, | ||
130 | } | ||
131 | |||
132 | #[derive(Clone, Copy, Debug)] | ||
133 | pub(crate) struct TraceEvent { | ||
134 | pub(crate) query_id: u32, | ||
135 | pub(crate) kind: TraceEventKind | ||
136 | } | ||
137 | |||
138 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
139 | pub(crate) enum TraceEventKind { | ||
140 | Start, Evaluating, Finish | ||
141 | } | ||
142 | |||
143 | impl QueryCtx { | ||
144 | pub(crate) fn get<Q: Get>(&self, params: &Q::Params) -> Q::Output { | ||
145 | let me = id::<Q>(params); | ||
146 | self.trace(TraceEvent { query_id: Q::ID, kind: TraceEventKind::Start }); | ||
147 | let res = Q::get(self, params); | ||
148 | self.trace(TraceEvent { query_id: Q::ID, kind: TraceEventKind::Finish }); | ||
149 | { | ||
150 | let mut stack = self.stack.borrow_mut(); | ||
151 | if let Some((_, ref mut deps)) = stack.last_mut() { | ||
152 | let params = Arc::new(params.clone()); | ||
153 | deps.push((me, params, output_hash::<Q>(&res))); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | res | ||
158 | } | ||
159 | fn trace(&self, event: TraceEvent) { | ||
160 | self.trace.borrow_mut().push(event) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | pub(crate) trait Query { | ||
165 | const ID: u32; | ||
166 | type Params: Hash + Eq + Debug + Clone + Any + 'static; | ||
167 | type Output: Hash + Debug + Any + 'static; | ||
168 | } | ||
169 | |||
170 | pub(crate) trait Get: Query { | ||
171 | fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output; | ||
172 | } | ||
173 | |||
174 | impl<Q: Eval> Get for Q | ||
175 | where | ||
176 | Q::Params: Clone, | ||
177 | Q::Output: Clone, | ||
178 | { | ||
179 | fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output { | ||
180 | if let Some(res) = try_reuse::<Q>(ctx, params) { | ||
181 | return res; | ||
182 | } | ||
183 | |||
184 | let me = id::<Q>(params); | ||
185 | ctx.trace(TraceEvent { query_id: Q::ID, kind: TraceEventKind::Evaluating }); | ||
186 | ctx.stack.borrow_mut().push((me, Vec::new())); | ||
187 | let res = Self::eval(ctx, params); | ||
188 | let (also_me, deps) = ctx.stack.borrow_mut().pop().unwrap(); | ||
189 | assert_eq!(also_me, me); | ||
190 | let mut cache = ctx.db.cache.lock(); | ||
191 | cache.deps.insert(me, deps); | ||
192 | let gen = cache.gen; | ||
193 | let output_hash = output_hash::<Q>(&res); | ||
194 | let id = id::<Q>(params); | ||
195 | cache.green.insert(id, (gen, output_hash)); | ||
196 | cache.results.insert(me, Arc::new(res.clone())); | ||
197 | res | ||
198 | } | ||
199 | } | ||
200 | |||
201 | fn try_reuse<Q: Eval>(ctx: &QueryCtx, params: &Q::Params) -> Option<Q::Output> | ||
202 | where | ||
203 | Q::Params: Clone, | ||
204 | Q::Output: Clone, | ||
205 | { | ||
206 | let id = id::<Q>(params); | ||
207 | let mut cache = ctx.db.cache.lock(); | ||
208 | let curr_gen = cache.gen; | ||
209 | let old_hash = match *cache.green.get(&id)? { | ||
210 | (gen, _) if gen == curr_gen => { | ||
211 | return Some(cache.get_result::<Q>(id)); | ||
212 | } | ||
213 | (_, hash) => hash, | ||
214 | }; | ||
215 | let deps_are_fresh = cache.deps[&id] | ||
216 | .iter() | ||
217 | .all(|&(dep_id, _, dep_hash)| { | ||
218 | match cache.green.get(&dep_id) { | ||
219 | //TODO: store the value of parameters, and re-execute the query | ||
220 | Some((gen, hash)) if gen == &curr_gen && hash == &dep_hash => true, | ||
221 | _ => false, | ||
222 | } | ||
223 | }); | ||
224 | if !deps_are_fresh { | ||
225 | return None; | ||
226 | } | ||
227 | cache.green.insert(id, (curr_gen, old_hash)); | ||
228 | Some(cache.get_result::<Q>(id)) | ||
229 | } | ||
230 | |||
231 | pub(crate) trait Eval: Query | ||
232 | where | ||
233 | Self::Params: Clone, | ||
234 | Self::Output: Clone, | ||
235 | { | ||
236 | fn eval(ctx: &QueryCtx, params: &Self::Params) -> Self::Output; | ||
237 | } | ||
238 | |||
239 | #[derive(Debug)] | ||
240 | pub(crate) struct DbFiles { | ||
241 | db: Arc<Db>, | ||
242 | } | ||
243 | |||
244 | impl Hash for DbFiles { | ||
245 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
246 | self.db.cache.lock().gen.hash(hasher) | ||
247 | } | ||
248 | } | ||
249 | |||
250 | impl DbFiles { | ||
251 | pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item=FileId> + 'a { | ||
252 | self.db.files.keys().cloned() | ||
253 | } | ||
254 | pub(crate) fn file_resolver(&self) -> FileResolverImp { | ||
255 | self.db.file_resolver.clone() | ||
256 | } | ||
257 | } | ||
258 | |||
259 | pub(crate) enum Files {} | ||
260 | impl Query for Files { | ||
261 | const ID: u32 = 1; | ||
262 | type Params = (); | ||
263 | type Output = DbFiles; | ||
264 | } | ||
265 | impl Get for Files { | ||
266 | fn get(ctx: &QueryCtx, params: &()) -> DbFiles { | ||
267 | let res = DbFiles { db: Arc::clone(&ctx.db) }; | ||
268 | let id = id::<Self>(params); | ||
269 | let hash = output_hash::<Self>(&res); | ||
270 | let mut cache = ctx.db.cache.lock(); | ||
271 | let gen = cache.gen; | ||
272 | cache.green.insert(id, (gen, hash)); | ||
273 | res | ||
274 | } | ||
275 | } | ||
276 | |||
277 | enum FileText {} | ||
278 | impl Query for FileText { | ||
279 | const ID: u32 = 10; | ||
280 | type Params = FileId; | ||
281 | type Output = Arc<String>; | ||
282 | } | ||
283 | impl Get for FileText { | ||
284 | fn get(ctx: &QueryCtx, file_id: &FileId) -> Arc<String> { | ||
285 | let res = ctx.db.files[file_id].clone(); | ||
286 | let id = id::<Self>(file_id); | ||
287 | let hash = output_hash::<Self>(&res); | ||
288 | let mut cache = ctx.db.cache.lock(); | ||
289 | let gen = cache.gen; | ||
290 | cache.green.insert(id, (gen, hash)); | ||
291 | res | ||
292 | } | ||
293 | } | ||
294 | |||
295 | pub(crate) enum FileSyntax {} | ||
296 | impl Query for FileSyntax { | ||
297 | const ID: u32 = 20; | ||
298 | type Params = FileId; | ||
299 | type Output = File; | ||
300 | } | ||
301 | impl Eval for FileSyntax { | ||
302 | fn eval(ctx: &QueryCtx, file_id: &FileId) -> File { | ||
303 | let text = ctx.get::<FileText>(file_id); | ||
304 | File::parse(&text) | ||
305 | } | ||
306 | } | ||
diff --git a/crates/libanalysis/src/db/mod.rs b/crates/libanalysis/src/db/mod.rs new file mode 100644 index 000000000..f68aab61c --- /dev/null +++ b/crates/libanalysis/src/db/mod.rs | |||
@@ -0,0 +1,196 @@ | |||
1 | mod queries; | ||
2 | |||
3 | use std::{ | ||
4 | hash::{Hash}, | ||
5 | sync::Arc, | ||
6 | fmt::Debug, | ||
7 | any::Any, | ||
8 | iter, | ||
9 | }; | ||
10 | use im; | ||
11 | use salsa; | ||
12 | use { | ||
13 | FileId, | ||
14 | imp::{FileResolverImp}, | ||
15 | }; | ||
16 | |||
17 | |||
18 | #[derive(Clone, Default)] | ||
19 | pub(crate) struct State { | ||
20 | pub(crate) resolver: FileResolverImp, | ||
21 | pub(crate) file_map: im::HashMap<FileId, Arc<str>>, | ||
22 | } | ||
23 | |||
24 | type Data = Arc<Any + Send + Sync + 'static>; | ||
25 | |||
26 | pub(crate) struct QueryCtx<'a> { | ||
27 | inner: &'a salsa::QueryCtx<State, Data> | ||
28 | } | ||
29 | |||
30 | pub(crate) struct Db { | ||
31 | inner: salsa::Db<State, Data> | ||
32 | } | ||
33 | |||
34 | struct GroundQuery<T, R> { | ||
35 | id: u16, | ||
36 | f: fn(&State, &T) -> R, | ||
37 | h: fn(&R) -> u64, | ||
38 | } | ||
39 | |||
40 | pub(crate) struct Query<T, R> { | ||
41 | pub(crate) id: u16, | ||
42 | pub(crate) f: fn(QueryCtx, &T) -> R, | ||
43 | } | ||
44 | |||
45 | impl Db { | ||
46 | pub(crate) fn new(state: State) -> Db { | ||
47 | Db { inner: salsa::Db::new(query_config(), state) } | ||
48 | } | ||
49 | pub(crate) fn state(&self) -> &State { | ||
50 | self.inner.ground_data() | ||
51 | } | ||
52 | pub(crate) fn with_state( | ||
53 | &self, | ||
54 | new_state: State, | ||
55 | updated_files: &[FileId], | ||
56 | file_set_changed: bool, | ||
57 | ) -> Db { | ||
58 | let mut inv = salsa::Invalidations::new(); | ||
59 | if file_set_changed { | ||
60 | inv.invalidate( | ||
61 | salsa::QueryTypeId(queries::FILE_SET.id), | ||
62 | iter::once(salsa::InputFingerprint(hash(&()))), | ||
63 | ); | ||
64 | } else { | ||
65 | inv.invalidate( | ||
66 | salsa::QueryTypeId(queries::FILE_SET.id), | ||
67 | iter::empty(), | ||
68 | ); | ||
69 | } | ||
70 | inv.invalidate( | ||
71 | salsa::QueryTypeId(queries::FILE_TEXT.id), | ||
72 | updated_files.iter().map(hash).map(salsa::InputFingerprint), | ||
73 | ); | ||
74 | Db { inner: self.inner.with_ground_data(new_state, inv) } | ||
75 | } | ||
76 | pub(crate) fn get<T, R>(&self, q: Query<T, R>, params: T) -> (Arc<R>, Vec<u16>) | ||
77 | where | ||
78 | T: Hash + Send + Sync + 'static, | ||
79 | R: Send + Sync + 'static, | ||
80 | { | ||
81 | let query_id = salsa::QueryId( | ||
82 | salsa::QueryTypeId(q.id), | ||
83 | salsa::InputFingerprint(hash(¶ms)), | ||
84 | ); | ||
85 | let params = Arc::new(params); | ||
86 | let (res, events) = self.inner.get(query_id, params); | ||
87 | let res = res.downcast().unwrap(); | ||
88 | let events = events.into_iter().map(|it| it.0).collect(); | ||
89 | (res, events) | ||
90 | } | ||
91 | |||
92 | } | ||
93 | |||
94 | impl<'a> QueryCtx<'a> { | ||
95 | fn get_g<T, R>(&self, q: GroundQuery<T, R>, params: T) -> Arc<R> | ||
96 | where | ||
97 | T: Hash + Send + Sync + 'static, | ||
98 | R: Send + Sync + 'static, | ||
99 | { | ||
100 | let query_id = salsa::QueryId( | ||
101 | salsa::QueryTypeId(q.id), | ||
102 | salsa::InputFingerprint(hash(¶ms)), | ||
103 | ); | ||
104 | let res = self.inner.get(query_id, Arc::new(params)); | ||
105 | res.downcast().unwrap() | ||
106 | } | ||
107 | pub(crate) fn get<T, R>(&self, q: Query<T, R>, params: T) -> Arc<R> | ||
108 | where | ||
109 | T: Hash + Send + Sync + 'static, | ||
110 | R: Send + Sync + 'static, | ||
111 | { | ||
112 | let query_id = salsa::QueryId( | ||
113 | salsa::QueryTypeId(q.id), | ||
114 | salsa::InputFingerprint(hash(¶ms)), | ||
115 | ); | ||
116 | let res = self.inner.get(query_id, Arc::new(params)); | ||
117 | res.downcast().unwrap() | ||
118 | } | ||
119 | } | ||
120 | |||
121 | fn query_config() -> salsa::QueryConfig<State, Data> { | ||
122 | let mut res = salsa::QueryConfig::new(); | ||
123 | let queries: Vec<SalsaGroundQuery> = vec![ | ||
124 | queries::FILE_TEXT.into(), | ||
125 | queries::FILE_SET.into(), | ||
126 | ]; | ||
127 | for q in queries { | ||
128 | res = res.with_ground_query(q.query_type, q.f) | ||
129 | } | ||
130 | let queries: Vec<SalsaQuery> = vec![ | ||
131 | queries::FILE_SYNTAX.into(), | ||
132 | ::module_map_db::MODULE_DESCR.into(), | ||
133 | ::module_map_db::RESOLVE_SUBMODULE.into(), | ||
134 | ::module_map_db::PARENT_MODULE.into(), | ||
135 | ]; | ||
136 | for q in queries { | ||
137 | res = res.with_query(q.query_type, q.f); | ||
138 | } | ||
139 | res | ||
140 | } | ||
141 | |||
142 | struct SalsaGroundQuery { | ||
143 | query_type: salsa::QueryTypeId, | ||
144 | f: Box<Fn(&State, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>, | ||
145 | } | ||
146 | |||
147 | impl<T, R> From<GroundQuery<T, R>> for SalsaGroundQuery | ||
148 | where | ||
149 | T: Send + Sync + 'static, | ||
150 | R: Send + Sync + 'static, | ||
151 | { | ||
152 | fn from(q: GroundQuery<T, R>) -> SalsaGroundQuery | ||
153 | { | ||
154 | SalsaGroundQuery { | ||
155 | query_type: salsa::QueryTypeId(q.id), | ||
156 | f: Box::new(move |state, data| { | ||
157 | let data: &T = data.downcast_ref().unwrap(); | ||
158 | let res = (q.f)(state, data); | ||
159 | let h = (q.h)(&res); | ||
160 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
161 | }) | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | struct SalsaQuery { | ||
167 | query_type: salsa::QueryTypeId, | ||
168 | f: Box<Fn(&salsa::QueryCtx<State, Data>, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>, | ||
169 | } | ||
170 | |||
171 | impl<T, R> From<Query<T, R>> for SalsaQuery | ||
172 | where | ||
173 | T: Hash + Send + Sync + 'static, | ||
174 | R: Hash + Send + Sync + 'static, | ||
175 | { | ||
176 | fn from(q: Query<T, R>) -> SalsaQuery | ||
177 | { | ||
178 | SalsaQuery { | ||
179 | query_type: salsa::QueryTypeId(q.id), | ||
180 | f: Box::new(move |ctx, data| { | ||
181 | let ctx = QueryCtx { inner: ctx }; | ||
182 | let data: &T = data.downcast_ref().unwrap(); | ||
183 | let res = (q.f)(ctx, data); | ||
184 | let h = hash(&res); | ||
185 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
186 | }) | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | fn hash<T: ::std::hash::Hash>(x: &T) -> u64 { | ||
192 | use std::hash::Hasher; | ||
193 | let mut hasher = ::std::collections::hash_map::DefaultHasher::new(); | ||
194 | ::std::hash::Hash::hash(x, &mut hasher); | ||
195 | hasher.finish() | ||
196 | } | ||
diff --git a/crates/libanalysis/src/db/queries.rs b/crates/libanalysis/src/db/queries.rs new file mode 100644 index 000000000..2d4aac6e9 --- /dev/null +++ b/crates/libanalysis/src/db/queries.rs | |||
@@ -0,0 +1,43 @@ | |||
1 | use std::sync::Arc; | ||
2 | use libsyntax2::{File}; | ||
3 | use { | ||
4 | FileId, FileResolverImp, | ||
5 | db::{Query, GroundQuery, QueryCtx, hash}, | ||
6 | }; | ||
7 | |||
8 | |||
9 | impl<'a> QueryCtx<'a> { | ||
10 | pub(crate) fn file_set(&self) -> Arc<(Vec<FileId>, FileResolverImp)> { | ||
11 | self.get_g(FILE_SET, ()) | ||
12 | } | ||
13 | pub(crate) fn file_text(&self, file_id: FileId) -> Arc<str> { | ||
14 | Arc::clone(&*self.get_g(FILE_TEXT, file_id)) | ||
15 | } | ||
16 | pub(crate) fn file_syntax(&self, file_id: FileId) -> File { | ||
17 | (&*self.get(FILE_SYNTAX, file_id)).clone() | ||
18 | } | ||
19 | } | ||
20 | |||
21 | pub(super) const FILE_TEXT: GroundQuery<FileId, Arc<str>> = GroundQuery { | ||
22 | id: 10, | ||
23 | f: |state, id| state.file_map[&id].clone(), | ||
24 | h: hash, | ||
25 | }; | ||
26 | |||
27 | pub(super) const FILE_SET: GroundQuery<(), (Vec<FileId>, FileResolverImp)> = GroundQuery { | ||
28 | id: 11, | ||
29 | f: |state, &()| { | ||
30 | let files = state.file_map.keys().cloned().collect(); | ||
31 | let resolver = state.resolver.clone(); | ||
32 | (files, resolver) | ||
33 | }, | ||
34 | h: |(files, _)| hash(files), | ||
35 | }; | ||
36 | |||
37 | pub(super) const FILE_SYNTAX: Query<FileId, File> = Query { | ||
38 | id: 20, | ||
39 | f: |ctx, file_id: &FileId| { | ||
40 | let text = ctx.file_text(*file_id); | ||
41 | File::parse(&*text) | ||
42 | } | ||
43 | }; | ||
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 3e77006c5..bca5d4c1d 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -10,6 +10,7 @@ extern crate relative_path; | |||
10 | #[macro_use] | 10 | #[macro_use] |
11 | extern crate crossbeam_channel; | 11 | extern crate crossbeam_channel; |
12 | extern crate im; | 12 | extern crate im; |
13 | extern crate salsa; | ||
13 | 14 | ||
14 | mod symbol_index; | 15 | mod symbol_index; |
15 | mod module_map; | 16 | mod module_map; |
diff --git a/crates/libanalysis/src/module_map_db.rs b/crates/libanalysis/src/module_map_db.rs index 4d4bd9104..ff69cc0d2 100644 --- a/crates/libanalysis/src/module_map_db.rs +++ b/crates/libanalysis/src/module_map_db.rs | |||
@@ -2,66 +2,55 @@ use std::sync::Arc; | |||
2 | use { | 2 | use { |
3 | FileId, | 3 | FileId, |
4 | db::{ | 4 | db::{ |
5 | Query, Eval, QueryCtx, FileSyntax, Files, | 5 | Query, QueryCtx |
6 | Cache, QueryCache, | ||
7 | }, | 6 | }, |
8 | module_map::resolve_submodule, | 7 | module_map::resolve_submodule, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | pub(crate) enum ModuleDescr {} | 10 | impl<'a> QueryCtx<'a> { |
12 | impl Query for ModuleDescr { | 11 | fn module_descr(&self, file_id: FileId) -> Arc<descr::ModuleDescr> { |
13 | const ID: u32 = 30; | 12 | self.get(MODULE_DESCR, file_id) |
14 | type Params = FileId; | 13 | } |
15 | type Output = Arc<descr::ModuleDescr>; | 14 | fn resolve_submodule(&self, file_id: FileId, submod: descr::Submodule) -> Arc<Vec<FileId>> { |
16 | } | 15 | self.get(RESOLVE_SUBMODULE, (file_id, submod)) |
17 | 16 | } | |
18 | enum ResolveSubmodule {} | ||
19 | impl Query for ResolveSubmodule { | ||
20 | const ID: u32 = 31; | ||
21 | type Params = (FileId, descr::Submodule); | ||
22 | type Output = Arc<Vec<FileId>>; | ||
23 | } | ||
24 | |||
25 | enum ParentModule {} | ||
26 | impl Query for ParentModule { | ||
27 | const ID: u32 = 40; | ||
28 | type Params = FileId; | ||
29 | type Output = Arc<Vec<FileId>>; | ||
30 | } | 17 | } |
31 | 18 | ||
32 | impl Eval for ModuleDescr { | 19 | pub(crate) const MODULE_DESCR: Query<FileId, descr::ModuleDescr> = Query { |
33 | fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<descr::ModuleDescr> { | 20 | id: 30, |
34 | let file = ctx.get::<FileSyntax>(file_id); | 21 | f: |ctx, &file_id| { |
35 | Arc::new(descr::ModuleDescr::new(file.ast())) | 22 | let file = ctx.file_syntax(file_id); |
23 | descr::ModuleDescr::new(file.ast()) | ||
36 | } | 24 | } |
37 | } | 25 | }; |
38 | 26 | ||
39 | impl Eval for ResolveSubmodule { | 27 | pub(crate) const RESOLVE_SUBMODULE: Query<(FileId, descr::Submodule), Vec<FileId>> = Query { |
40 | fn eval(ctx: &QueryCtx, &(file_id, ref submodule): &(FileId, descr::Submodule)) -> Arc<Vec<FileId>> { | 28 | id: 31, |
41 | let files = ctx.get::<Files>(&()); | 29 | f: |ctx, params| { |
42 | let res = resolve_submodule(file_id, &submodule.name, &files.file_resolver()).0; | 30 | let files = ctx.file_set(); |
43 | Arc::new(res) | 31 | resolve_submodule(params.0, ¶ms.1.name, &files.1).0 |
44 | } | 32 | } |
45 | } | 33 | }; |
46 | 34 | ||
47 | impl Eval for ParentModule { | 35 | pub(crate) const PARENT_MODULE: Query<FileId, Vec<FileId>> = Query { |
48 | fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<Vec<FileId>> { | 36 | id: 40, |
49 | let files = ctx.get::<Files>(&()); | 37 | f: |ctx, file_id| { |
50 | let res = files.iter() | 38 | let files = ctx.file_set(); |
51 | .map(|parent_id| (parent_id, ctx.get::<ModuleDescr>(&parent_id))) | 39 | let res = files.0.iter() |
40 | .map(|&parent_id| (parent_id, ctx.module_descr(parent_id))) | ||
52 | .filter(|(parent_id, descr)| { | 41 | .filter(|(parent_id, descr)| { |
53 | descr.submodules.iter() | 42 | descr.submodules.iter() |
54 | .any(|subm| { | 43 | .any(|subm| { |
55 | ctx.get::<ResolveSubmodule>(&(*parent_id, subm.clone())) | 44 | ctx.resolve_submodule(*parent_id, subm.clone()) |
56 | .iter() | 45 | .iter() |
57 | .any(|it| it == file_id) | 46 | .any(|it| it == file_id) |
58 | }) | 47 | }) |
59 | }) | 48 | }) |
60 | .map(|(id, _)| id) | 49 | .map(|(id, _)| id) |
61 | .collect(); | 50 | .collect(); |
62 | Arc::new(res) | 51 | res |
63 | } | 52 | } |
64 | } | 53 | }; |
65 | 54 | ||
66 | mod descr { | 55 | mod descr { |
67 | use libsyntax2::{ | 56 | use libsyntax2::{ |
@@ -102,7 +91,7 @@ mod tests { | |||
102 | use im; | 91 | use im; |
103 | use relative_path::{RelativePath, RelativePathBuf}; | 92 | use relative_path::{RelativePath, RelativePathBuf}; |
104 | use { | 93 | use { |
105 | db::{Query, DbHost, TraceEventKind}, | 94 | db::{Query, Db, State}, |
106 | imp::FileResolverImp, | 95 | imp::FileResolverImp, |
107 | FileId, FileResolver, | 96 | FileId, FileResolver, |
108 | }; | 97 | }; |
@@ -126,7 +115,7 @@ mod tests { | |||
126 | struct Fixture { | 115 | struct Fixture { |
127 | next_file_id: u32, | 116 | next_file_id: u32, |
128 | fm: im::HashMap<FileId, RelativePathBuf>, | 117 | fm: im::HashMap<FileId, RelativePathBuf>, |
129 | db: DbHost, | 118 | db: Db, |
130 | } | 119 | } |
131 | 120 | ||
132 | impl Fixture { | 121 | impl Fixture { |
@@ -134,7 +123,7 @@ mod tests { | |||
134 | Fixture { | 123 | Fixture { |
135 | next_file_id: 1, | 124 | next_file_id: 1, |
136 | fm: im::HashMap::new(), | 125 | fm: im::HashMap::new(), |
137 | db: DbHost::new(), | 126 | db: Db::new(State::default()), |
138 | } | 127 | } |
139 | } | 128 | } |
140 | fn add_file(&mut self, path: &str, text: &str) -> FileId { | 129 | fn add_file(&mut self, path: &str, text: &str) -> FileId { |
@@ -142,36 +131,39 @@ mod tests { | |||
142 | let file_id = FileId(self.next_file_id); | 131 | let file_id = FileId(self.next_file_id); |
143 | self.next_file_id += 1; | 132 | self.next_file_id += 1; |
144 | self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); | 133 | self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); |
145 | self.db.change_file(file_id, Some(text.to_string())); | 134 | let mut new_state = self.db.state().clone(); |
146 | self.db.set_file_resolver(FileResolverImp::new( | 135 | new_state.file_map.insert(file_id, text.to_string().into_boxed_str().into()); |
136 | new_state.resolver = FileResolverImp::new( | ||
147 | Arc::new(FileMap(self.fm.clone())) | 137 | Arc::new(FileMap(self.fm.clone())) |
148 | )); | 138 | ); |
149 | 139 | self.db = self.db.with_state(new_state, &[file_id], true); | |
150 | file_id | 140 | file_id |
151 | } | 141 | } |
152 | fn remove_file(&mut self, file_id: FileId) { | 142 | fn remove_file(&mut self, file_id: FileId) { |
153 | self.fm.remove(&file_id); | 143 | self.fm.remove(&file_id); |
154 | self.db.change_file(file_id, None); | 144 | let mut new_state = self.db.state().clone(); |
155 | self.db.set_file_resolver(FileResolverImp::new( | 145 | new_state.file_map.remove(&file_id); |
146 | new_state.resolver = FileResolverImp::new( | ||
156 | Arc::new(FileMap(self.fm.clone())) | 147 | Arc::new(FileMap(self.fm.clone())) |
157 | )) | 148 | ); |
149 | self.db = self.db.with_state(new_state, &[file_id], true); | ||
158 | } | 150 | } |
159 | fn change_file(&mut self, file_id: FileId, new_text: &str) { | 151 | fn change_file(&mut self, file_id: FileId, new_text: &str) { |
160 | self.db.change_file(file_id, Some(new_text.to_string())); | 152 | let mut new_state = self.db.state().clone(); |
153 | new_state.file_map.insert(file_id, new_text.to_string().into_boxed_str().into()); | ||
154 | self.db = self.db.with_state(new_state, &[file_id], false); | ||
161 | } | 155 | } |
162 | fn check_parent_modules( | 156 | fn check_parent_modules( |
163 | &self, | 157 | &self, |
164 | file_id: FileId, | 158 | file_id: FileId, |
165 | expected: &[FileId], | 159 | expected: &[FileId], |
166 | queries: &[(u32, u64)] | 160 | queries: &[(u16, u64)] |
167 | ) { | 161 | ) { |
168 | let ctx = self.db.query_ctx(); | 162 | let (actual, events) = self.db.get(PARENT_MODULE, file_id); |
169 | let actual = ctx.get::<ParentModule>(&file_id); | ||
170 | assert_eq!(actual.as_slice(), expected); | 163 | assert_eq!(actual.as_slice(), expected); |
171 | let mut counts = HashMap::new(); | 164 | let mut counts = HashMap::new(); |
172 | ctx.trace.borrow().iter() | 165 | events.into_iter() |
173 | .filter(|event| event.kind == TraceEventKind::Evaluating) | 166 | .for_each(|event| *counts.entry(event).or_insert(0) += 1); |
174 | .for_each(|event| *counts.entry(event.query_id).or_insert(0) += 1); | ||
175 | for &(query_id, expected_count) in queries.iter() { | 167 | for &(query_id, expected_count) in queries.iter() { |
176 | let actual_count = *counts.get(&query_id).unwrap_or(&0); | 168 | let actual_count = *counts.get(&query_id).unwrap_or(&0); |
177 | assert_eq!( | 169 | assert_eq!( |
@@ -189,25 +181,25 @@ mod tests { | |||
189 | fn test_parent_module() { | 181 | fn test_parent_module() { |
190 | let mut f = Fixture::new(); | 182 | let mut f = Fixture::new(); |
191 | let foo = f.add_file("/foo.rs", ""); | 183 | let foo = f.add_file("/foo.rs", ""); |
192 | // f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 1)]); | 184 | f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]); |
193 | 185 | ||
194 | let lib = f.add_file("/lib.rs", "mod foo;"); | 186 | let lib = f.add_file("/lib.rs", "mod foo;"); |
195 | f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 2)]); | 187 | f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]); |
196 | f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 0)]); | 188 | f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 0)]); |
197 | 189 | ||
198 | f.change_file(lib, ""); | 190 | f.change_file(lib, ""); |
199 | f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 2)]); | 191 | f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]); |
200 | 192 | ||
201 | // f.change_file(lib, "mod foo;"); | 193 | f.change_file(lib, "mod foo;"); |
202 | // f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 2)]); | 194 | f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]); |
203 | 195 | ||
204 | // f.change_file(lib, "mod bar;"); | 196 | f.change_file(lib, "mod bar;"); |
205 | // f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 2)]); | 197 | f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]); |
206 | 198 | ||
207 | // f.change_file(lib, "mod foo;"); | 199 | f.change_file(lib, "mod foo;"); |
208 | // f.check_parent_modules(foo, &[lib], &[(ModuleDescr::ID, 2)]); | 200 | f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]); |
209 | 201 | ||
210 | // f.remove_file(lib); | 202 | f.remove_file(lib); |
211 | // f.check_parent_modules(foo, &[], &[(ModuleDescr::ID, 1)]); | 203 | f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 0)]); |
212 | } | 204 | } |
213 | } | 205 | } |
diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index 5de3c7774..75815e8bd 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs | |||
@@ -8,8 +8,8 @@ use std::{ | |||
8 | }; | 8 | }; |
9 | use parking_lot::Mutex; | 9 | use parking_lot::Mutex; |
10 | 10 | ||
11 | type GroundQueryFn<T, D> = fn(&T, &D) -> (D, OutputFingerprint); | 11 | type GroundQueryFn<T, D> = Box<Fn(&T, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; |
12 | type QueryFn<T, D> = fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint); | 12 | type QueryFn<T, D> = Box<Fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; |
13 | 13 | ||
14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
15 | pub struct Db<T, D> { | 15 | pub struct Db<T, D> { |
@@ -119,7 +119,7 @@ where | |||
119 | res | 119 | res |
120 | } | 120 | } |
121 | 121 | ||
122 | pub fn get_inner( | 122 | fn get_inner( |
123 | &self, | 123 | &self, |
124 | query_id: QueryId, | 124 | query_id: QueryId, |
125 | params: D, | 125 | params: D, |
@@ -176,9 +176,9 @@ where | |||
176 | self.executed.borrow_mut().push(query_id.0); | 176 | self.executed.borrow_mut().push(query_id.0); |
177 | self.stack.borrow_mut().push(Vec::new()); | 177 | self.stack.borrow_mut().push(Vec::new()); |
178 | 178 | ||
179 | let (res, output_fingerprint) = if let Some(f) = self.ground_query_fn_by_type(query_id.0) { | 179 | let (res, output_fingerprint) = if let Some(f) = self.query_config.ground_fn.get(&query_id.0) { |
180 | f(&self.db.ground_data, ¶ms) | 180 | f(&self.db.ground_data, ¶ms) |
181 | } else if let Some(f) = self.query_fn_by_type(query_id.0) { | 181 | } else if let Some(f) = self.query_config.query_fn.get(&query_id.0) { |
182 | f(self, ¶ms) | 182 | f(self, ¶ms) |
183 | } else { | 183 | } else { |
184 | panic!("unknown query type: {:?}", query_id.0); | 184 | panic!("unknown query type: {:?}", query_id.0); |
@@ -190,12 +190,6 @@ where | |||
190 | self.db.record(query_id, params, res.clone(), output_fingerprint, deps); | 190 | self.db.record(query_id, params, res.clone(), output_fingerprint, deps); |
191 | (res, output_fingerprint) | 191 | (res, output_fingerprint) |
192 | } | 192 | } |
193 | fn ground_query_fn_by_type(&self, query_type: QueryTypeId) -> Option<GroundQueryFn<T, D>> { | ||
194 | self.query_config.ground_fn.get(&query_type).map(|&it| it) | ||
195 | } | ||
196 | fn query_fn_by_type(&self, query_type: QueryTypeId) -> Option<QueryFn<T, D>> { | ||
197 | self.query_config.query_fn.get(&query_type).map(|&it| it) | ||
198 | } | ||
199 | fn record_dep( | 193 | fn record_dep( |
200 | &self, | 194 | &self, |
201 | query_id: QueryId, | 195 | query_id: QueryId, |
@@ -239,7 +233,9 @@ where | |||
239 | query_config: Arc::new(query_config), | 233 | query_config: Arc::new(query_config), |
240 | } | 234 | } |
241 | } | 235 | } |
242 | 236 | pub fn ground_data(&self) -> &T { | |
237 | &self.db.ground_data | ||
238 | } | ||
243 | pub fn with_ground_data( | 239 | pub fn with_ground_data( |
244 | &self, | 240 | &self, |
245 | ground_data: T, | 241 | ground_data: T, |
diff --git a/crates/salsa/tests/integration.rs b/crates/salsa/tests/integration.rs index 3cec330e6..aed9219be 100644 --- a/crates/salsa/tests/integration.rs +++ b/crates/salsa/tests/integration.rs | |||
@@ -79,19 +79,19 @@ where | |||
79 | 79 | ||
80 | fn mk_queries() -> salsa::QueryConfig<State, Data> { | 80 | fn mk_queries() -> salsa::QueryConfig<State, Data> { |
81 | salsa::QueryConfig::<State, Data>::new() | 81 | salsa::QueryConfig::<State, Data>::new() |
82 | .with_ground_query(GET_TEXT, |state, id| { | 82 | .with_ground_query(GET_TEXT, Box::new(|state, id| { |
83 | mk_ground_query::<u32, String>(state, id, |state, id| state[id].clone()) | 83 | mk_ground_query::<u32, String>(state, id, |state, id| state[id].clone()) |
84 | }) | 84 | })) |
85 | .with_ground_query(GET_FILES, |state, id| { | 85 | .with_ground_query(GET_FILES, Box::new(|state, id| { |
86 | mk_ground_query::<(), Vec<u32>>(state, id, |state, &()| state.keys().cloned().collect()) | 86 | mk_ground_query::<(), Vec<u32>>(state, id, |state, &()| state.keys().cloned().collect()) |
87 | }) | 87 | })) |
88 | .with_query(FILE_NEWLINES, |query_ctx, id| { | 88 | .with_query(FILE_NEWLINES, Box::new(|query_ctx, id| { |
89 | mk_query(query_ctx, id, |query_ctx, &id| { | 89 | mk_query(query_ctx, id, |query_ctx, &id| { |
90 | let text = query_ctx.get_text(id); | 90 | let text = query_ctx.get_text(id); |
91 | text.lines().count() | 91 | text.lines().count() |
92 | }) | 92 | }) |
93 | }) | 93 | })) |
94 | .with_query(TOTAL_NEWLINES, |query_ctx, id| { | 94 | .with_query(TOTAL_NEWLINES, Box::new(|query_ctx, id| { |
95 | mk_query(query_ctx, id, |query_ctx, &()| { | 95 | mk_query(query_ctx, id, |query_ctx, &()| { |
96 | let mut total = 0; | 96 | let mut total = 0; |
97 | for &id in query_ctx.get_files().iter() { | 97 | for &id in query_ctx.get_files().iter() { |
@@ -99,7 +99,7 @@ fn mk_queries() -> salsa::QueryConfig<State, Data> { | |||
99 | } | 99 | } |
100 | total | 100 | total |
101 | }) | 101 | }) |
102 | }) | 102 | })) |
103 | } | 103 | } |
104 | 104 | ||
105 | #[test] | 105 | #[test] |