scopegraphs/completeness/
mod.rs

1//! This module contains several utilities to guarantee query stability.
2//!
3//! Query stability means that the result of a query, once resolved, will remain valid after future
4//! additions to the scope graph.
5//! This allows safe interleaving of name resolution and scope graph construction.
6//!
7//! The main trait of this module is [`Completeness`]. An implementation of this trait should be passed
8//! to [`ScopeGraph::new`](super::ScopeGraph::new) to obtain a scope graph instance.
9//!
10//! Currently, the module contains three safe implementations.
11//! * [`ImplicitClose`] is the easiest to use, and most likely the preferred choice for simple
12//!   sequential type checkers.
13//! * [`ExplicitClose`] requires some manual bookkeeping, but allows more flexible handling of
14//!   queries. This is the most suitable choice for type checkers that need to do dynamic scheduling.
15//!   Running queries can return an error, because scopes relevant to the query weren't closed yet.
16//! * [`FutureCompleteness`] is like [`ExplicitClose`], except queries can no longer error. Instead,
17//!   queries return a [`Future`](std::future::Future) that resolves when all scopes related to the query are closed.
18
19mod future;
20
21pub use future::*;
22
23mod critical_edge;
24pub use critical_edge::*;
25mod explicit;
26pub use explicit::*;
27mod implicit;
28pub use implicit::*;
29mod unchecked;
30pub use unchecked::*;
31
32mod private {
33    pub trait Sealed {}
34}
35
36use crate::scopegraph::{InnerScopeGraph, Scope};
37use crate::{Label, ScopeGraph};
38use private::Sealed;
39use std::rc::Rc;
40use std::{fmt::Debug, hash::Hash, marker::PhantomData};
41
42/*** Completeness trait ***/
43
44/// Types implementing this trait have the responsibility to guarantee query stability.
45///
46/// This means that the result of a query, once computed, may not be invalidated by later extensions
47/// of the scope graph. See [`crate::concepts::completeness`]
48///
49/// A [`super::ScopeGraph`] will call the handlers of its completeness on each operation.
50/// For scope addition and data access, it can update some internal state.
51/// For edge addition and retrieval, it can override/augment the default result as well.
52///
53/// The way query stability is guaranteed is very flexible. Current implementations use
54/// - critical edges, explicitly closed by the client ([`ExplicitClose`]).
55/// - critical edges, implicitly closed when retrieved ([`ImplicitClose`]).
56///
57/// Future implementations may also:
58/// - delay retrieval of edges until it is closed (by returning a future). TODO: [this is implemented now](`FutureCompleteness`)
59/// - guarantee stability by retaining residual queries, and checking those do not return any
60///   results for later additions.
61/// - ...
62///
63/// This trait is sealed.
64/// You cannot define your own completeness strategies to ensure that only verified implementations are available.
65// TODO: @Aron could you document this?
66#[allow(missing_docs)]
67pub trait Completeness<LABEL: Label, DATA>: Sealed {
68    fn cmpl_new_scope(&self, inner_scope_graph: &InnerScopeGraph<LABEL, DATA>, scope: Scope);
69
70    /// Should initialize a scope without possibility to extend it with edges
71    fn cmpl_new_complete_scope(
72        &self,
73        inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
74        scope: Scope,
75    );
76
77    type NewEdgeResult;
78    fn cmpl_new_edge(
79        &self,
80        inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
81        src: Scope,
82        lbl: LABEL,
83        dst: Scope,
84    ) -> Self::NewEdgeResult;
85
86    // type GetDataResult;
87    // fn get_data(&mut self, inner_scope_graph: &InnerScopeGraph<LABEL, DATA>, scope: Scope) -> Self::GetDataResult;
88
89    type GetEdgesResult<'rslv>
90    where
91        Self: 'rslv,
92        LABEL: 'rslv,
93        DATA: 'rslv;
94    fn cmpl_get_edges<'rslv>(
95        &'rslv self,
96        inner_scope_graph: &'rslv InnerScopeGraph<LABEL, DATA>,
97        src: Scope,
98        lbl: LABEL,
99    ) -> Self::GetEdgesResult<'rslv>
100    where
101        LABEL: 'rslv,
102        DATA: 'rslv;
103}
104
105// There are two main ways of creating edges, which depend on the completeness mode:
106// - user-closed: requires the user to close each open edge exactly once; extension based on `ScopeExt`
107// - implicit: user does not have explicit control over closing edges
108// These are marker traits, that are used in impl block that provide different APIs to different
109
110#[doc(hidden)]
111pub struct Witness(pub(crate) ());
112
113/// Marker trait for completeness strategies that require edges to be closed explicitly.
114pub trait UserClosed<LABEL: Label, DATA>: Completeness<LABEL, DATA> {
115    #[doc(hidden)]
116    fn close(&self, scope: Scope, label: &LABEL, witness: Witness);
117}
118
119/// Marker trait for completeness strategies that do not require edges to be closed explicitly.
120///
121/// Used to make the correct API's available on scope graphs.
122pub trait Implicit<LABEL: Label, DATA>: Completeness<LABEL, DATA> {}
123
124// Edge extension permissions for UserClosed Completenesses
125#[derive(Debug)]
126struct ScopeExtPermInner<
127    'ext,
128    LABEL: Hash + Label + Debug, /* <= Bound here required for Drop implementation */
129    DATA,
130    CMPL: UserClosed<LABEL, DATA>, // Bound required for Drop implementation
131> {
132    /// Scope for which this object witnesses the permission to extend.
133    scope: Scope,
134    /// Label with which [scope] may be extended
135    label: LABEL,
136    /// Scope graph in which the scope may be extended.
137    sg: &'ext CMPL,
138    _data: PhantomData<DATA>, // FIXME: required for using `where CMPL: UserClosed<LABEL, DATA>` in impl blocks. Can it be removed some way?
139}
140
141impl<LABEL: Hash + Label + Debug, DATA, CMPL> Drop for ScopeExtPermInner<'_, LABEL, DATA, CMPL>
142where
143    CMPL: UserClosed<LABEL, DATA>,
144{
145    /// This is the trick! When the permission is dropped, we know for sure that no future extensions will be made (otherwise, [self] should have been kept alive)
146    /// Thus, we can close the critical edge.
147    fn drop(&mut self) {
148        self.sg.close(self.scope, &self.label, Witness(()))
149    }
150}
151
152/// Represents the permission to extend a scope with a particular label.
153///
154/// Do not instantiate this struct manually, but use the [add_scope] macro instead.
155#[derive(Debug)]
156pub struct ScopeExtPerm<'ext, LABEL: Hash + Label + Debug, DATA, CMPL: UserClosed<LABEL, DATA>>(
157    Rc<ScopeExtPermInner<'ext, LABEL, DATA, CMPL>>,
158);
159
160impl<LABEL: Hash + Label + Debug, DATA, CMPL: UserClosed<LABEL, DATA>> Clone
161    for ScopeExtPerm<'_, LABEL, DATA, CMPL>
162{
163    fn clone(&self) -> Self {
164        Self(self.0.clone())
165    }
166}
167
168impl<'ext, LABEL: Hash + Label + Debug, DATA, CMPL: UserClosed<LABEL, DATA>>
169    ScopeExtPerm<'ext, LABEL, DATA, CMPL>
170where
171    CMPL: UserClosed<LABEL, DATA>,
172{
173    pub(super) fn scope(&self) -> &Scope {
174        &self.0.scope
175    }
176
177    pub(super) fn label(&self) -> &LABEL {
178        &self.0.label
179    }
180
181    /// This is an implementation detail of the [add_scope!] macro and should not be called directly!
182    #[doc(hidden)]
183    pub unsafe fn init<'storage>(
184        scope: Scope,
185        label: LABEL,
186        sg: &'ext ScopeGraph<'storage, LABEL, DATA, CMPL>,
187    ) -> ScopeExtPerm<'ext, LABEL, DATA, CMPL> {
188        ScopeExtPerm(Rc::new(ScopeExtPermInner {
189            scope,
190            label,
191            sg: &sg.completeness,
192            _data: PhantomData,
193        }))
194    }
195
196    /// Closes the edge, meaning that it cannot be extended anymore.
197    /// // TODO: fix this sentence
198    /// Closes an edge, (i.e., prohibit future new extensions, by losing ownership of the extends permission).
199    ///
200    /// For example, the following program will return an error.
201    /// ```compile_fail
202    /// # use scopegraphs::completeness::ExplicitClose;
203    /// # use scopegraphs::Label;
204    /// # use scopegraphs::Storage;
205    /// # use scopegraphs::ScopeGraph;
206    /// # use scopegraphs::add_scope;
207    ///
208    /// # #[derive(Eq, Hash, PartialEq, Label, Clone, Copy)] enum Lbl { Def }
209    /// # use Lbl::*;
210    /// let storage = Storage::new();
211    /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
212    ///
213    /// let (s1, s1_def) = add_scope!(&sg, 0, [Def]);
214    /// let s2 = sg.add_scope_closed(42);
215    ///
216    /// s1_def.close();
217    /// sg.ext_edge(&s1_def, s2).expect_err("cannot add edge after closing edge");
218    /// ```
219    ///
220    /// Closing is required to permit queries to traverse these edges:
221    /// ```
222    ///
223    /// # use scopegraphs::completeness::ExplicitClose;
224    /// # use scopegraphs::ScopeGraph;
225    /// # use scopegraphs::resolve::{DefaultDataEquivalence, DefaultLabelOrder, EdgeOrData, Resolve};
226    /// # use scopegraphs_macros::{compile_regex, Label};
227    /// # use scopegraphs::Storage;
228    /// # use scopegraphs::add_scope;
229    /// #
230    /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
231    /// # enum Lbl { Def }
232    /// # use Lbl::*;
233    /// # type LblD = EdgeOrData<Lbl>;
234    /// #
235    /// # compile_regex!(type Regex<Lbl> = Def);
236    /// let storage = Storage::new();
237    /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
238    ///
239    /// let (s1, s1_def) = add_scope!(&sg, 0, [Def]);
240    /// let s2 = sg.add_scope_closed(42);
241    ///
242    /// // Note: not calling `s1_def.close()`
243    ///
244    /// let query_result = sg.query()
245    ///     .with_path_wellformedness(Regex::new()) // regex: `Def`
246    ///     .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
247    ///     .resolve(s1);
248    ///
249    /// query_result.expect_err("require s1/Def to be closed");
250    /// ```
251    ///
252    /// Closing allows queries to resolve:
253    /// ```
254    ///
255    /// # use scopegraphs::completeness::ExplicitClose;
256    /// # use scopegraphs::ScopeGraph;
257    /// # use scopegraphs::resolve::{DefaultDataEquivalence, DefaultLabelOrder, EdgeOrData, Resolve};
258    /// # use scopegraphs_macros::{compile_regex, Label};
259    /// # use scopegraphs::Storage;
260    /// # use scopegraphs::add_scope;
261    /// #
262    /// # #[derive(Eq, Hash, PartialEq, Label, Debug, Copy, Clone)]
263    /// # enum Lbl { Def }
264    /// # use Lbl::*;
265    /// # type LblD = EdgeOrData<Lbl>;
266    /// #
267    /// # compile_regex!(type Regex<Lbl> = Def);
268    /// let storage = Storage::new();
269    /// let mut sg = ScopeGraph::<Lbl, usize, _>::new(&storage, ExplicitClose::default());
270    ///
271    /// let (s1, s1_def) = add_scope!(&sg, [Def]);
272    /// let s2 = sg.add_scope_closed(42);
273    ///
274    /// // Note: closing the edge *after* creating all edges, *before* doing the query
275    /// s1_def.close();
276    ///
277    /// let query_result = sg.query()
278    ///     .with_path_wellformedness(Regex::new()) // regex: `Def`
279    ///     .with_data_wellformedness(|x: &usize| *x == 42) // match `42`
280    ///     .resolve(s1);
281    ///
282    /// query_result.expect("query should return result");
283    /// ```
284    pub fn close(self) {
285        // self dropped at the end of this block
286    }
287}
288
289/// Creates a scope (with some data if specified), and permission to extend it for each label specified in the label list argument.
290///
291/// TODO: Better documentation, examples.
292#[macro_export]
293macro_rules! add_scope {
294  ($sg:expr, $data:expr, [ $($lbl:expr),* ]) => {
295    {
296        // put initialized code in block
297        let sg = $sg;       // evaluate scope graph expression
298        let data = $data;   // evaluate data expression
299
300        // create new scope
301        let scope = sg.add_scope_with(data, [$($lbl),*]);
302
303        // return the scope, and the extension permissions
304        (scope, $(unsafe { $crate::completeness::ScopeExtPerm::init(scope, $lbl, sg) }),*)
305    }
306  };
307
308  // case when no data is given: falls back on [Default] value for data
309  ($sg:expr, [$($lbl:expr),* ]) => { add_scope!($sg, Default::default(), [$($lbl),*]) };
310
311  // case when no data nor labels are given
312  ($sg:expr) => { add_scope!($sg, Default::default(), []).0 };
313}
314
315/// Adds an edge to the scope graph
316// TODO: better explanation, (link to) examples.
317#[macro_export]
318macro_rules! add_edge {
319    // case for call with extension permission
320    ($sg:expr, $ext:expr, $tgt:expr) => {
321        sg.ext_edge($ext, $tgt)
322    };
323
324    // case when giving source and target scopes explicitly
325    ($sg:expr, $src:expr, $lbl:expr, $tgt:expr) => {
326        sg.add_edge($src, $lbl, $tgt)
327    };
328}
329
330// TODO: Residual-query-based Completeness requires access to query resolution