1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::completeness::private::Sealed;
use crate::completeness::{Completeness, CriticalEdgeBasedCompleteness, Delay, ExplicitClose};
use crate::future_wrapper::FutureWrapper;
use crate::label::Label;
use crate::scopegraph::{InnerScopeGraph, Scope};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::future::poll_fn;
use std::hash::Hash;
use std::task::{Poll, Waker};

/// A completeness strategy that makes queries return a [`Future`](std::future::Future)
/// in case one of the scopes that the query runs over was not yet closed.
/// The future resolves once all such scopes *are* closed.
///
/// Using [`FutureCompleteness`], you can somewhat delegate the task of scheduling
/// typechecking to one of rust's executors, be it [`tokio`](https://docs.rs/tokio)
/// or [`futures::block_on`](https://docs.rs/futures/latest/futures/executor/fn.block_on.html).
// TODO: add practical lessons from Lace.
///
/// Extends, and contains an instance of, [`ExplicitClose`].
#[derive(Debug)]
pub struct FutureCompleteness<LABEL> {
    explicit_close: ExplicitClose<LABEL>,
    wakers: RefCell<HashMap<Delay<LABEL>, Vec<Waker>>>,
}

impl<LABEL> Default for FutureCompleteness<LABEL> {
    fn default() -> Self {
        Self {
            explicit_close: ExplicitClose::<LABEL>::default(),
            wakers: RefCell::new(HashMap::default()),
        }
    }
}

impl<LABEL> Sealed for FutureCompleteness<LABEL> {}

impl<LABEL: Hash + Eq + Label + Copy, DATA> Completeness<LABEL, DATA>
    for FutureCompleteness<LABEL>
{
    fn cmpl_new_scope(&self, inner_scope_graph: &InnerScopeGraph<LABEL, DATA>, scope: Scope) {
        self.explicit_close.cmpl_new_scope(inner_scope_graph, scope)
    }

    type NewEdgeResult = <ExplicitClose<LABEL> as Completeness<LABEL, DATA>>::NewEdgeResult;

    fn cmpl_new_edge(
        &self,
        inner_scope_graph: &InnerScopeGraph<LABEL, DATA>,
        src: Scope,
        lbl: LABEL,
        dst: Scope,
    ) -> Self::NewEdgeResult {
        self.explicit_close
            .cmpl_new_edge(inner_scope_graph, src, lbl, dst)
    }

    type GetEdgesResult<'rslv> = FutureWrapper<'rslv, Vec<Scope>> where Self: 'rslv, LABEL: 'rslv, DATA: 'rslv;

    fn cmpl_get_edges<'rslv>(
        &'rslv self,
        inner_scope_graph: &'rslv InnerScopeGraph<LABEL, DATA>,
        src: Scope,
        lbl: LABEL,
    ) -> Self::GetEdgesResult<'rslv>
    where
        LABEL: 'rslv,
        DATA: 'rslv,
    {
        FutureWrapper::new(poll_fn(move |cx| {
            match self
                .explicit_close
                .cmpl_get_edges(inner_scope_graph, src, lbl)
            {
                Ok(scopes) => Poll::Ready(scopes),
                Err(delay) => {
                    self.wakers
                        .borrow_mut()
                        .entry(delay)
                        .or_default()
                        .push(cx.waker().clone());
                    Poll::Pending
                }
            }
        }))
    }
}

impl<LABEL: Hash + Eq + Copy> FutureCompleteness<LABEL> {
    pub(crate) fn close(&self, scope: Scope, label: &LABEL) {
        self.explicit_close.close(scope, label);
        for waker in self
            .wakers
            .borrow()
            .get(&Delay {
                scope,
                label: *label,
            })
            .into_iter()
            .flatten()
        {
            waker.wake_by_ref()
        }
    }
}

impl<LABEL: Hash + Eq + Label + Copy, DATA> CriticalEdgeBasedCompleteness<LABEL, DATA>
    for FutureCompleteness<LABEL>
{
    fn init_scope_with(&self, open_edges: HashSet<LABEL>) {
        <ExplicitClose<LABEL> as CriticalEdgeBasedCompleteness<LABEL, DATA>>::init_scope_with(
            &self.explicit_close,
            open_edges,
        );
    }
}