aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/Cargo.toml1
-rw-r--r--crates/libanalysis/src/db.rs306
-rw-r--r--crates/libanalysis/src/db/mod.rs196
-rw-r--r--crates/libanalysis/src/db/queries.rs43
-rw-r--r--crates/libanalysis/src/lib.rs1
-rw-r--r--crates/libanalysis/src/module_map_db.rs130
-rw-r--r--crates/salsa/src/lib.rs20
-rw-r--r--crates/salsa/tests/integration.rs16
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"
14im = "12.0.0" 14im = "12.0.0"
15libsyntax2 = { path = "../libsyntax2" } 15libsyntax2 = { path = "../libsyntax2" }
16libeditor = { path = "../libeditor" } 16libeditor = { path = "../libeditor" }
17salsa = { path = "../salsa" }
17 18
18[dev-dependencies] 19[dev-dependencies]
19test_utils = { path = "../test_utils" } 20test_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 @@
1use std::{
2 hash::{Hash, Hasher},
3 sync::Arc,
4 cell::RefCell,
5 fmt::Debug,
6 any::Any,
7};
8use parking_lot::Mutex;
9use libsyntax2::{File};
10use im;
11use {
12 FileId,
13 imp::{FileResolverImp},
14};
15
16#[derive(Debug)]
17pub(crate) struct DbHost {
18 db: Arc<Db>,
19}
20
21impl 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
60type QueryInvocationId = (u32, u64);
61type Gen = u64;
62type OutputHash = u64;
63
64fn 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}
70fn 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)]
78pub(crate) struct Db {
79 file_resolver: FileResolverImp,
80 files: im::HashMap<FileId, Arc<String>>,
81 cache: Mutex<Cache>,
82}
83
84impl 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
94type QueryDeps = Vec<(QueryInvocationId, Arc<Any>, OutputHash)>;
95
96#[derive(Default, Debug)]
97pub(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)]
106pub(crate) type QueryCache<Q: Query> = im::HashMap<
107 <Q as Query>::Params,
108 <Q as Query>::Output
109>;
110
111impl 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
126pub(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)]
133pub(crate) struct TraceEvent {
134 pub(crate) query_id: u32,
135 pub(crate) kind: TraceEventKind
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
139pub(crate) enum TraceEventKind {
140 Start, Evaluating, Finish
141}
142
143impl 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
164pub(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
170pub(crate) trait Get: Query {
171 fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
172}
173
174impl<Q: Eval> Get for Q
175where
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
201fn try_reuse<Q: Eval>(ctx: &QueryCtx, params: &Q::Params) -> Option<Q::Output>
202where
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
231pub(crate) trait Eval: Query
232where
233 Self::Params: Clone,
234 Self::Output: Clone,
235{
236 fn eval(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
237}
238
239#[derive(Debug)]
240pub(crate) struct DbFiles {
241 db: Arc<Db>,
242}
243
244impl Hash for DbFiles {
245 fn hash<H: Hasher>(&self, hasher: &mut H) {
246 self.db.cache.lock().gen.hash(hasher)
247 }
248}
249
250impl 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
259pub(crate) enum Files {}
260impl Query for Files {
261 const ID: u32 = 1;
262 type Params = ();
263 type Output = DbFiles;
264}
265impl 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
277enum FileText {}
278impl Query for FileText {
279 const ID: u32 = 10;
280 type Params = FileId;
281 type Output = Arc<String>;
282}
283impl 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
295pub(crate) enum FileSyntax {}
296impl Query for FileSyntax {
297 const ID: u32 = 20;
298 type Params = FileId;
299 type Output = File;
300}
301impl 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 @@
1mod queries;
2
3use std::{
4 hash::{Hash},
5 sync::Arc,
6 fmt::Debug,
7 any::Any,
8 iter,
9};
10use im;
11use salsa;
12use {
13 FileId,
14 imp::{FileResolverImp},
15};
16
17
18#[derive(Clone, Default)]
19pub(crate) struct State {
20 pub(crate) resolver: FileResolverImp,
21 pub(crate) file_map: im::HashMap<FileId, Arc<str>>,
22}
23
24type Data = Arc<Any + Send + Sync + 'static>;
25
26pub(crate) struct QueryCtx<'a> {
27 inner: &'a salsa::QueryCtx<State, Data>
28}
29
30pub(crate) struct Db {
31 inner: salsa::Db<State, Data>
32}
33
34struct GroundQuery<T, R> {
35 id: u16,
36 f: fn(&State, &T) -> R,
37 h: fn(&R) -> u64,
38}
39
40pub(crate) struct Query<T, R> {
41 pub(crate) id: u16,
42 pub(crate) f: fn(QueryCtx, &T) -> R,
43}
44
45impl 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(&params)),
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
94impl<'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(&params)),
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(&params)),
115 );
116 let res = self.inner.get(query_id, Arc::new(params));
117 res.downcast().unwrap()
118 }
119}
120
121fn 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
142struct SalsaGroundQuery {
143 query_type: salsa::QueryTypeId,
144 f: Box<Fn(&State, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>,
145}
146
147impl<T, R> From<GroundQuery<T, R>> for SalsaGroundQuery
148where
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
166struct SalsaQuery {
167 query_type: salsa::QueryTypeId,
168 f: Box<Fn(&salsa::QueryCtx<State, Data>, &Data) -> (Data, salsa::OutputFingerprint) + Send + Sync + 'static>,
169}
170
171impl<T, R> From<Query<T, R>> for SalsaQuery
172where
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
191fn 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 @@
1use std::sync::Arc;
2use libsyntax2::{File};
3use {
4 FileId, FileResolverImp,
5 db::{Query, GroundQuery, QueryCtx, hash},
6};
7
8
9impl<'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
21pub(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
27pub(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
37pub(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]
11extern crate crossbeam_channel; 11extern crate crossbeam_channel;
12extern crate im; 12extern crate im;
13extern crate salsa;
13 14
14mod symbol_index; 15mod symbol_index;
15mod module_map; 16mod 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;
2use { 2use {
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
11pub(crate) enum ModuleDescr {} 10impl<'a> QueryCtx<'a> {
12impl 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 }
18enum ResolveSubmodule {}
19impl Query for ResolveSubmodule {
20 const ID: u32 = 31;
21 type Params = (FileId, descr::Submodule);
22 type Output = Arc<Vec<FileId>>;
23}
24
25enum ParentModule {}
26impl Query for ParentModule {
27 const ID: u32 = 40;
28 type Params = FileId;
29 type Output = Arc<Vec<FileId>>;
30} 17}
31 18
32impl Eval for ModuleDescr { 19pub(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
39impl Eval for ResolveSubmodule { 27pub(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, &params.1.name, &files.1).0
44 } 32 }
45} 33};
46 34
47impl Eval for ParentModule { 35pub(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
66mod descr { 55mod 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};
9use parking_lot::Mutex; 9use parking_lot::Mutex;
10 10
11type GroundQueryFn<T, D> = fn(&T, &D) -> (D, OutputFingerprint); 11type GroundQueryFn<T, D> = Box<Fn(&T, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
12type QueryFn<T, D> = fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint); 12type QueryFn<T, D> = Box<Fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
13 13
14#[derive(Debug)] 14#[derive(Debug)]
15pub struct Db<T, D> { 15pub 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, &params) 180 f(&self.db.ground_data, &params)
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, &params) 182 f(self, &params)
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
80fn mk_queries() -> salsa::QueryConfig<State, Data> { 80fn 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]