sway_core/query_engine/
mod.rs

1use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
2use std::{
3    collections::HashMap,
4    ops::{Deref, DerefMut},
5    path::PathBuf,
6    sync::Arc,
7    time::SystemTime,
8};
9use sway_error::{error::CompileError, warning::CompileWarning};
10use sway_types::{IdentUnique, ProgramId, SourceId, Spanned};
11
12use crate::{
13    decl_engine::{DeclId, DeclRef},
14    language::ty::{TyFunctionDecl, TyFunctionSig, TyModule},
15    {Engines, Programs},
16};
17
18#[derive(Debug, Clone, Hash, PartialEq, Eq)]
19pub struct ModuleCacheKey {
20    pub path: Arc<PathBuf>,
21    pub include_tests: bool,
22}
23
24impl ModuleCacheKey {
25    pub fn new(path: Arc<PathBuf>, include_tests: bool) -> Self {
26        Self {
27            path,
28            include_tests,
29        }
30    }
31}
32
33#[derive(Clone, Debug)]
34pub struct ModuleCommonInfo {
35    pub path: Arc<PathBuf>,
36    pub hash: u64,
37    pub include_tests: bool,
38    pub dependencies: Vec<Arc<PathBuf>>,
39}
40
41#[derive(Clone, Debug)]
42pub struct ParsedModuleInfo {
43    pub modified_time: Option<SystemTime>,
44    pub version: Option<u64>,
45}
46
47#[derive(Clone, Debug)]
48pub struct TypedModuleInfo {
49    pub module: Arc<TyModule>,
50    pub version: Option<u64>,
51}
52
53#[derive(Clone, Debug)]
54pub struct ModuleCacheEntry {
55    pub common: ModuleCommonInfo,
56    pub parsed: ParsedModuleInfo,
57    pub typed: Option<TypedModuleInfo>,
58}
59
60impl ModuleCacheEntry {
61    pub fn new(common: ModuleCommonInfo, parsed: ParsedModuleInfo) -> Self {
62        Self {
63            common,
64            parsed,
65            typed: None,
66        }
67    }
68
69    pub fn is_typed(&self) -> bool {
70        self.typed.is_some()
71    }
72
73    pub fn set_typed(&mut self, typed: TypedModuleInfo) {
74        self.typed = Some(typed);
75    }
76
77    pub fn update_common(&mut self, new_common: ModuleCommonInfo) {
78        self.common = new_common;
79    }
80
81    pub fn update_parsed(&mut self, new_parsed: ParsedModuleInfo) {
82        self.parsed = new_parsed;
83    }
84
85    pub fn update_parsed_and_common(
86        &mut self,
87        new_common: ModuleCommonInfo,
88        new_parsed: ParsedModuleInfo,
89    ) {
90        self.common = new_common;
91        self.parsed = new_parsed;
92    }
93}
94
95#[derive(Debug, Default, Clone)]
96pub struct ModuleCacheMap(HashMap<ModuleCacheKey, ModuleCacheEntry>);
97
98impl Deref for ModuleCacheMap {
99    type Target = HashMap<ModuleCacheKey, ModuleCacheEntry>;
100    fn deref(&self) -> &Self::Target {
101        &self.0
102    }
103}
104
105impl DerefMut for ModuleCacheMap {
106    fn deref_mut(&mut self) -> &mut Self::Target {
107        &mut self.0
108    }
109}
110
111impl ModuleCacheMap {
112    pub fn update_entry(
113        &mut self,
114        key: &ModuleCacheKey,
115        new_common: ModuleCommonInfo,
116        new_parsed: ParsedModuleInfo,
117    ) {
118        if let Some(entry) = self.get_mut(key) {
119            entry.update_parsed_and_common(new_common, new_parsed);
120        } else {
121            self.insert(key.clone(), ModuleCacheEntry::new(new_common, new_parsed));
122        }
123    }
124}
125
126pub type ProgramsCacheMap = HashMap<Arc<PathBuf>, ProgramsCacheEntry>;
127pub type FunctionsCacheMap = HashMap<(IdentUnique, String), FunctionCacheEntry>;
128
129#[derive(Clone, Debug)]
130pub struct ProgramsCacheEntry {
131    pub path: Arc<PathBuf>,
132    pub programs: Programs,
133    pub handler_data: (Vec<CompileError>, Vec<CompileWarning>),
134}
135
136#[derive(Clone, Debug)]
137pub struct FunctionCacheEntry {
138    pub fn_decl: DeclRef<DeclId<TyFunctionDecl>>,
139}
140
141#[derive(Debug, Default)]
142pub struct QueryEngine {
143    // We want the below types wrapped in Arcs to optimize cloning from LSP.
144    programs_cache: CowCache<ProgramsCacheMap>,
145    pub module_cache: CowCache<ModuleCacheMap>,
146    // NOTE: Any further AstNodes that are cached need to have garbage collection applied, see clear_module()
147    function_cache: CowCache<FunctionsCacheMap>,
148}
149
150impl Clone for QueryEngine {
151    fn clone(&self) -> Self {
152        Self {
153            programs_cache: CowCache::new(self.programs_cache.read().clone()),
154            module_cache: CowCache::new(self.module_cache.read().clone()),
155            function_cache: CowCache::new(self.function_cache.read().clone()),
156        }
157    }
158}
159
160impl QueryEngine {
161    pub fn update_or_insert_parsed_module_cache_entry(&self, entry: ModuleCacheEntry) {
162        let path = entry.common.path.clone();
163        let include_tests = entry.common.include_tests;
164        let key = ModuleCacheKey::new(path, include_tests);
165        let mut cache = self.module_cache.write();
166        cache.update_entry(&key, entry.common, entry.parsed);
167    }
168
169    pub fn update_typed_module_cache_entry(&self, key: &ModuleCacheKey, entry: TypedModuleInfo) {
170        let mut cache = self.module_cache.write();
171        cache.get_mut(key).unwrap().set_typed(entry);
172    }
173
174    pub fn get_programs_cache_entry(&self, path: &Arc<PathBuf>) -> Option<ProgramsCacheEntry> {
175        let cache = self.programs_cache.read();
176        cache.get(path).cloned()
177    }
178
179    pub fn insert_programs_cache_entry(&self, entry: ProgramsCacheEntry) {
180        let mut cache = self.programs_cache.write();
181        cache.insert(entry.path.clone(), entry);
182    }
183
184    pub fn get_function(
185        &self,
186        engines: &Engines,
187        ident: IdentUnique,
188        sig: TyFunctionSig,
189    ) -> Option<DeclRef<DeclId<TyFunctionDecl>>> {
190        let cache = self.function_cache.read();
191        cache
192            .get(&(ident, sig.get_type_str(engines)))
193            .map(|s| s.fn_decl.clone())
194    }
195
196    pub fn insert_function(
197        &self,
198        engines: &Engines,
199        ident: IdentUnique,
200        sig: TyFunctionSig,
201        fn_decl: DeclRef<DeclId<TyFunctionDecl>>,
202    ) {
203        let mut cache = self.function_cache.write();
204        cache.insert(
205            (ident, sig.get_type_str(engines)),
206            FunctionCacheEntry { fn_decl },
207        );
208    }
209
210    /// Removes all data associated with the `source_id` from the function cache.
211    pub fn clear_module(&mut self, source_id: &SourceId) {
212        self.function_cache
213            .write()
214            .retain(|(ident, _), _| (ident.span().source_id() != Some(source_id)));
215    }
216
217    /// Removes all data associated with the `program_id` from the function cache.
218    pub fn clear_program(&mut self, program_id: &ProgramId) {
219        self.function_cache.write().retain(|(ident, _), _| {
220            ident
221                .span()
222                .source_id()
223                .is_none_or(|id| id.program_id() != *program_id)
224        });
225    }
226
227    ///  Commits all changes to their respective caches.
228    pub fn commit(&self) {
229        self.programs_cache.commit();
230        self.module_cache.commit();
231        self.function_cache.commit();
232    }
233}
234
235/// Thread-safe, copy-on-write cache optimized for LSP operations.
236///
237/// Addresses key LSP challenges:
238/// 1. Concurrent read access to shared data
239/// 2. Local modifications for cancellable operations (e.g., compilation)
240/// 3. Prevents incomplete results from affecting shared state
241/// 4. Maintains consistency via explicit commit step
242///
243/// Uses `Arc<RwLock<T>>` for shared state and `RwLock<Option<T>>` for local changes.
244/// Suitable for interactive sessions with frequent file changes.
245#[derive(Debug, Default)]
246pub struct CowCache<T: Clone> {
247    inner: Arc<RwLock<T>>,
248    local: RwLock<Option<T>>,
249}
250
251impl<T: Clone> CowCache<T> {
252    /// Creates a new `CowCache` with the given initial value.
253    ///
254    /// The value is wrapped in an `Arc<RwLock<T>>` to allow shared access across threads.
255    pub fn new(value: T) -> Self {
256        Self {
257            inner: Arc::new(RwLock::new(value)),
258            local: RwLock::new(None),
259        }
260    }
261
262    /// Provides read access to the cached value.
263    ///
264    /// If a local modification exists, it returns a reference to the local copy.
265    /// Otherwise, it returns a reference to the shared state.
266    ///
267    /// This method is optimized for concurrent read access in LSP operations.
268    pub fn read(&self) -> impl Deref<Target = T> + '_ {
269        if self.local.read().is_some() {
270            ReadGuard::Local(self.local.read())
271        } else {
272            ReadGuard::Shared(self.inner.read())
273        }
274    }
275
276    /// Provides write access to a local copy of the cached value.
277    ///
278    /// In LSP, this is used for operations like compilation tasks that may be cancelled.
279    /// It allows modifications without affecting the shared state until explicitly committed.
280    pub fn write(&self) -> impl DerefMut<Target = T> + '_ {
281        let mut local = self.local.write();
282        if local.is_none() {
283            *local = Some(self.inner.read().clone());
284        }
285        WriteGuard(local)
286    }
287
288    /// Commits local modifications to the shared state.
289    ///
290    /// Called after successful completion of a compilation task.
291    /// If a task is cancelled, not calling this method effectively discards local changes.
292    pub fn commit(&self) {
293        if let Some(local) = self.local.write().take() {
294            *self.inner.write() = local;
295        }
296    }
297}
298
299/// A guard type that provides read access to either the local or shared state.
300enum ReadGuard<'a, T: Clone> {
301    Local(RwLockReadGuard<'a, Option<T>>),
302    Shared(RwLockReadGuard<'a, T>),
303}
304
305impl<T: Clone> Deref for ReadGuard<'_, T> {
306    type Target = T;
307
308    fn deref(&self) -> &Self::Target {
309        match self {
310            ReadGuard::Local(r) => r.as_ref().unwrap(),
311            ReadGuard::Shared(guard) => guard.deref(),
312        }
313    }
314}
315
316/// A guard type that provides write access to the local state.
317struct WriteGuard<'a, T: Clone>(RwLockWriteGuard<'a, Option<T>>);
318
319impl<T: Clone> Deref for WriteGuard<'_, T> {
320    type Target = T;
321
322    fn deref(&self) -> &Self::Target {
323        self.0.as_ref().unwrap()
324    }
325}
326
327impl<T: Clone> DerefMut for WriteGuard<'_, T> {
328    fn deref_mut(&mut self) -> &mut Self::Target {
329        self.0.as_mut().unwrap()
330    }
331}