inc_complete/
computation.rs

1use crate::{Cell, Db, DbHandle};
2use std::{
3    any::{Any, TypeId},
4    collections::HashMap,
5    hash::Hash,
6};
7
8mod tuple_impls;
9
10pub trait Computation: 'static + Sized + Clone {
11    type Storage;
12    type Output: Eq;
13
14    fn run(&self, handle: &mut DbHandle<impl Computation>) -> Self::Output;
15
16    fn input_to_cell(input: &Self, storage: &Self::Storage) -> Option<Cell>;
17    fn insert_new_cell(cell: Cell, function: Self, storage: &mut Self::Storage);
18
19    fn get_function_and_output(
20        cell: Cell,
21        storage: &Self::Storage,
22    ) -> (&Self, Option<&Self::Output>);
23    fn set_output(cell: Cell, output: Self::Output, storage: &mut Self::Storage);
24
25    // We could use TypeIds but those are not stable across rustc updates.
26    // Default to 0 here and have each pair impl increment the rhs to decide IDs based
27    // on their position in the computation tuple.
28    #[inline(always)]
29    fn computation_id_of<T: Computation>() -> u32 {
30        0
31    }
32
33    fn get_storage<Concrete: Computation + 'static>(
34        computation_id: u32,
35        container: &Self::Storage,
36    ) -> &Concrete::Storage {
37        assert_eq!(computation_id, 0, "Type dispatch failed for get_storage");
38
39        assert_eq!(
40            TypeId::of::<Concrete::Storage>(),
41            TypeId::of::<Self::Storage>(),
42            "Type dispatch failed for get_storage storage type:\n    {}\n != {}",
43            std::any::type_name::<Concrete::Storage>(),
44            std::any::type_name::<Self::Storage>(),
45        );
46
47        // Safety: We confirmed above Concrete::Storage == Self::Storage and thus `&mut Concrete::Storage == &mut Self::Storage`
48        unsafe { std::mem::transmute(container) }
49    }
50
51    fn get_storage_mut<Concrete: Computation + 'static>(
52        computation_id: u32,
53        container: &mut Self::Storage,
54    ) -> &mut Concrete::Storage {
55        assert_eq!(
56            computation_id, 0,
57            "Type dispatch failed for get_storage_mut container"
58        );
59
60        assert_eq!(
61            TypeId::of::<Concrete::Storage>(),
62            TypeId::of::<Self::Storage>(),
63            "Type dispatch failed for get_storage_mut container type"
64        );
65
66        // Safety: We confirmed above Concrete::Storage == Self::Storage and thus `&mut Concrete::Storage == &mut Self::Storage`
67        unsafe { std::mem::transmute(container) }
68    }
69
70    /// True if this has any cached output
71    fn output_is_unset<FullComputation: Computation>(
72        cell: Cell,
73        computation_id: u32,
74        original_computation_id: u32,
75        db: &Db<FullComputation>,
76    ) -> bool {
77        assert_eq!(
78            computation_id, 0,
79            "Type dispatch failed for output_is_unset"
80        );
81
82        let container = FullComputation::get_storage::<Self>(original_computation_id, db.storage());
83        let unset = Self::get_function_and_output(cell, container).1.is_none();
84        println!("{} output is unset: {unset}", std::any::type_name::<Self>());
85        unset
86    }
87
88    /// Given a Cell, TypeId pair dispatch to the correct run function
89    /// and return true if the value has changed. This should also cache
90    /// the new value if it has changed.
91    /// Note that in dispatch functions `Self` is always the concrete, non-tuple type.
92    fn dispatch_run<FullComputation: Computation>(
93        cell: Cell,
94        computation_id: u32,
95        original_computation_id: u32,
96        db: &mut Db<FullComputation>,
97    ) -> bool
98    where
99        Self: Clone,
100        Self::Output: Eq,
101    {
102        assert_eq!(computation_id, 0, "Type dispatch failed for dispatch_run");
103
104        let container =
105            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
106        let function = Self::get_function_and_output(cell, container).0.clone();
107        let output = function.run(&mut db.handle(cell));
108        FullComputation::dispatch_update_output::<Self, FullComputation>(
109            cell,
110            original_computation_id,
111            original_computation_id,
112            output,
113            db,
114        )
115    }
116
117    /// Dispatch to the correct update_output function to cache the new output
118    /// and return true if the value has changed.
119    /// Note that in dispatch functions `Self` is the current type being dispatched,
120    /// `Concrete`, if present, is the non-tuple type of the target computation,
121    /// and `FullComputation` is the type of the `Db` computation parameter which is
122    /// usually a tuple of every possible computation.
123    fn dispatch_update_output<Concrete, FullComputation>(
124        cell: Cell,
125        computation_id: u32,
126        original_computation_id: u32,
127        output: Concrete::Output,
128        db: &mut Db<FullComputation>,
129    ) -> bool
130    where
131        Concrete: Computation,
132        FullComputation: Computation,
133        Self::Output: Eq,
134    {
135        assert_eq!(
136            computation_id, 0,
137            "Type dispatch failed for dispatch_update_output"
138        );
139        assert_eq!(
140            TypeId::of::<Concrete::Output>(),
141            TypeId::of::<Self::Output>(),
142            "Type dispatch failed for dispatch_update_output"
143        );
144
145        // Safety: We just checked T::Output == Self::Output above, we should be copying
146        // the same type.
147        let output2: Self::Output = unsafe { std::mem::transmute_copy(&output) };
148        std::mem::forget(output);
149
150        let container =
151            FullComputation::get_storage_mut::<Self>(original_computation_id, db.storage_mut());
152
153        let (_, previous_value) = Self::get_function_and_output(cell, container);
154        let changed = previous_value.map_or(true, |previous| output2 != *previous);
155
156        if changed {
157            Self::set_output(cell, output2, container);
158        }
159
160        changed
161    }
162
163    fn dispatch_input_to_cell<Concrete>(input: &Concrete, container: &Self::Storage) -> Option<Cell>
164    where
165        Concrete: 'static + Computation + Any,
166    {
167        assert_eq!(TypeId::of::<Concrete>(), TypeId::of::<Self>());
168        let input = (input as &dyn Any).downcast_ref().expect("T == Self");
169        Self::input_to_cell(input, container)
170    }
171
172    fn dispatch_insert_new_cell<Concrete>(cell: Cell, input: Concrete, storage: &mut Self::Storage)
173    where
174        Concrete: 'static + Computation + Any,
175        Concrete::Storage: 'static,
176    {
177        let input = transmute_copy_checked::<Concrete, Self>(input);
178        Self::insert_new_cell(cell, input, storage)
179    }
180}
181
182fn transmute_copy_checked<A: 'static, B: 'static>(x: A) -> B {
183    assert_eq!(TypeId::of::<A>(), TypeId::of::<B>());
184    // Safety: We confirmed above A == B
185    let x2: B = unsafe { std::mem::transmute_copy(&x) };
186    // x2 is copied byte by byte, we need to ensure the destructor is not run twice.
187    std::mem::forget(x);
188    x2
189}
190
191/// A helper type for defining Computations with HashMap-backed storage
192#[derive(Debug, Clone, PartialEq, Eq, Hash)]
193pub struct Cached<T>(T);
194
195impl<T> Cached<T> {
196    pub const fn new(x: T) -> Self {
197        Self(x)
198    }
199}
200
201pub trait Run {
202    type Output: Eq;
203
204    fn run(&self, handle: &mut DbHandle<impl Computation>) -> Self::Output;
205}
206
207impl<T> Computation for Cached<T>
208where
209    T: 'static + Run + Eq + Hash + Clone,
210{
211    type Output = <T as Run>::Output;
212    type Storage = (
213        HashMap<Self, Cell>,
214        HashMap<Cell, (Self, Option<Self::Output>)>,
215    );
216
217    fn run(&self, handle: &mut DbHandle<impl Computation>) -> Self::Output {
218        self.0.run(handle)
219    }
220
221    fn input_to_cell(input: &Self, (self_to_cell, _): &Self::Storage) -> Option<Cell> {
222        self_to_cell.get(input).copied()
223    }
224
225    fn get_function_and_output(
226        cell: Cell,
227        (_, cell_to_output): &Self::Storage,
228    ) -> (&Self, Option<&Self::Output>) {
229        let (this, output) = &cell_to_output[&cell];
230        (this, output.as_ref())
231    }
232
233    fn set_output(cell: Cell, new_output: Self::Output, (_, cell_to_output): &mut Self::Storage) {
234        cell_to_output.entry(cell).and_modify(|(_, output)| {
235            *output = Some(new_output);
236        });
237    }
238
239    fn insert_new_cell(cell: Cell, function: Self, storage: &mut Self::Storage) {
240        storage.0.insert(function.clone(), cell);
241        storage.1.insert(cell, (function, None));
242    }
243}
244
245/// Helper to define a Computation for a simple input type which has no fields and thus
246/// does not require a HashMap to cache each possible value. To use in a `Computation`,
247/// `T` must implement `OutputTypeForInput` to specify its output type. `T` cannot provide
248/// a `run` function since it cannot have any dependencies as an input. Its value must be
249/// manually set via `Db::update_input(db, input, value)`
250///
251/// Examples include `struct SourceFile;` or `struct Time;`
252#[derive(Debug, Clone)]
253pub struct Input<T>(std::marker::PhantomData<T>);
254
255impl<T> Input<T> {
256    pub const fn new() -> Self {
257        Self(std::marker::PhantomData)
258    }
259}
260
261pub trait OutputTypeForInput: Clone {
262    type Output: Eq;
263}
264
265impl<T: OutputTypeForInput + 'static> Computation for Input<T> {
266    type Output = <T as OutputTypeForInput>::Output;
267    type Storage = (Option<Cell>, Option<Self::Output>);
268
269    fn run(&self, _: &mut DbHandle<impl Computation>) -> Self::Output {
270        panic!(
271            "Input `{}` queried before `db.update_input(..)` called",
272            std::any::type_name::<T>()
273        )
274    }
275
276    fn input_to_cell(_: &Self, storage: &Self::Storage) -> Option<Cell> {
277        storage.0
278    }
279
280    fn get_function_and_output(_: Cell, storage: &Self::Storage) -> (&Self, Option<&Self::Output>) {
281        (&Self(std::marker::PhantomData), storage.1.as_ref())
282    }
283
284    fn set_output(_: Cell, new_output: Self::Output, storage: &mut Self::Storage) {
285        storage.1 = Some(new_output);
286    }
287
288    fn insert_new_cell(cell: Cell, _: Self, storage: &mut Self::Storage) {
289        storage.0 = Some(cell);
290        storage.1 = None;
291    }
292}