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