comemo/
track.rs

1use std::fmt::{self, Debug, Formatter};
2use std::hash::Hash;
3use std::ops::{Deref, DerefMut};
4
5use crate::accelerate;
6
7/// A trackable type.
8///
9/// This is implemented by types that have an implementation block annotated
10/// with `#[track]` and for trait objects whose traits are annotated with
11/// `#[track]`. For more details, see [its documentation](macro@crate::track).
12pub trait Track: Surfaces {
13    /// An enumeration of possible tracked calls that can be performed on this
14    /// tracked type.
15    type Call: Call;
16
17    /// Performs a call on the value and returns the hash of its results.
18    fn call(&self, call: &Self::Call) -> u128;
19
20    /// Performs a mutable call on the value.
21    fn call_mut(&mut self, call: &Self::Call);
22
23    /// Start tracking all accesses to a value.
24    #[inline]
25    fn track(&self) -> Tracked<'_, Self> {
26        Tracked { value: self, sink: None, id: accelerate::id() }
27    }
28
29    /// Start tracking all accesses and mutations to a value.
30    #[inline]
31    fn track_mut(&mut self) -> TrackedMut<'_, Self> {
32        TrackedMut { value: self, sink: None }
33    }
34
35    /// Start tracking all accesses into a sink.
36    #[inline]
37    fn track_with<'a>(
38        &'a self,
39        sink: &'a dyn Sink<Call = Self::Call>,
40    ) -> Tracked<'a, Self> {
41        Tracked {
42            value: self,
43            sink: Some(sink),
44            id: accelerate::id(),
45        }
46    }
47
48    /// Start tracking all accesses and mutations into a sink.
49    #[inline]
50    fn track_mut_with<'a>(
51        &'a mut self,
52        sink: &'a dyn Sink<Call = Self::Call>,
53    ) -> TrackedMut<'a, Self> {
54        TrackedMut { value: self, sink: Some(sink) }
55    }
56}
57
58/// A destination to which recorded tracked calls can be sent.
59pub trait Sink: Send + Sync {
60    /// An enumeration of possible tracked calls that can be sent to this sink.
61    type Call;
62
63    /// Emit a call and its return hash to the sink.
64    ///
65    /// Returns `false` if the call was deduplicated, so that callers can avoid
66    /// sending it to other sinks higher up the hierarchy.
67    fn emit(&self, call: Self::Call, ret: u128) -> bool;
68}
69
70impl<S: Sink> Sink for &S {
71    type Call = S::Call;
72
73    fn emit(&self, call: Self::Call, ret: u128) -> bool {
74        (*self).emit(call, ret)
75    }
76}
77
78/// A call to a tracked function.
79pub trait Call: Clone + PartialEq + Hash + Send + Sync {
80    /// Whether the call is mutable.
81    fn is_mutable(&self) -> bool;
82}
83
84/// This implementation is used for hashed types in the `Input` trait.
85impl Call for () {
86    fn is_mutable(&self) -> bool {
87        false
88    }
89}
90
91/// This type's tracked surfaces.
92pub trait Surfaces {
93    /// The tracked API surface of this type.
94    type Surface<'a>
95    where
96        Self: 'a;
97
98    /// The mutable tracked API surface of this type.
99    type SurfaceMut<'a>
100    where
101        Self: 'a;
102
103    /// Access the immutable surface from a `Tracked`.
104    fn surface_ref<'a, 't>(tracked: &'t Tracked<'a, Self>) -> &'t Self::Surface<'a>
105    where
106        Self: Track;
107
108    /// Access the immutable surface from a `TrackedMut`.
109    fn surface_mut_ref<'a, 't>(
110        tracked: &'t TrackedMut<'a, Self>,
111    ) -> &'t Self::SurfaceMut<'a>
112    where
113        Self: Track;
114
115    /// Access the mutable surface from a `TrackedMut`.
116    fn surface_mut_mut<'a, 't>(
117        tracked: &'t mut TrackedMut<'a, Self>,
118    ) -> &'t mut Self::SurfaceMut<'a>
119    where
120        Self: Track;
121}
122
123/// Tracks accesses to a value.
124///
125/// Encapsulates a reference to a value and tracks all accesses to it. The only
126/// methods accessible on `Tracked<T>` are those defined in an implementation
127/// block or trait for `T` annotated with `#[track]`. For more details, see [its
128/// documentation](macro@crate::track).
129///
130/// ## Variance
131/// Typically you can ignore the defaulted `C` parameter. However, due to
132/// compiler limitations, this type will then be invariant over `T`. This limits
133/// how it can be used. In particular, invariance prevents you from creating a
134/// usable _chain_ of tracked types.
135///
136/// ```
137/// # use comemo::{Track, Tracked};
138/// struct Chain<'a> {
139///     outer: Tracked<'a, Self>,
140///     data: u32, // some data for the chain link
141/// }
142/// # #[comemo::track] impl<'a> Chain<'a> {}
143/// ```
144///
145/// However, this is sometimes a useful pattern (for example, it allows you to
146/// detect cycles in memoized recursive algorithms). If you want to create a
147/// tracked chain or need covariance for another reason, you need to manually
148/// specify the call type like so:
149///
150/// ```
151/// # use comemo::{Track, Tracked};
152/// struct Chain<'a> {
153///     outer: Tracked<'a, Self, <Chain<'static> as Track>::Call>,
154///     data: u32, // some data for the chain link
155/// }
156/// # #[comemo::track] impl<'a> Chain<'a> {}
157/// ```
158///
159/// Notice the `'static` lifetime: This makes the compiler understand that no
160/// strange business that depends on `'a` is happening in the associated
161/// constraint type. (In fact, all constraints are `'static`.)
162pub struct Tracked<'a, T, C = <T as Track>::Call>
163where
164    T: Track + ?Sized,
165{
166    /// A reference to the tracked value.
167    pub(crate) value: &'a T,
168    /// A constraint that is generated for T by the tracked methods on T's
169    /// surface type.
170    ///
171    /// Starts out as `None` and is set to a stack-stored constraint in the
172    /// preamble of memoized functions.
173    pub(crate) sink: Option<&'a dyn Sink<Call = C>>,
174    /// A unique ID for validation acceleration.
175    pub(crate) id: usize,
176}
177
178// The type `Tracked<T>` automatically dereferences to T's generated surface
179// type. This makes all tracked methods available, but leaves all other ones
180// unaccessible.
181impl<'a, T> Deref for Tracked<'a, T>
182where
183    T: Track + ?Sized,
184{
185    type Target = T::Surface<'a>;
186
187    #[inline]
188    fn deref(&self) -> &Self::Target {
189        T::surface_ref(self)
190    }
191}
192
193impl<T> Debug for Tracked<'_, T>
194where
195    T: Track + ?Sized,
196{
197    #[inline]
198    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
199        f.pad("Tracked(..)")
200    }
201}
202
203impl<'a, T> Copy for Tracked<'a, T> where T: Track + ?Sized {}
204
205impl<'a, T> Clone for Tracked<'a, T>
206where
207    T: Track + ?Sized,
208{
209    #[inline]
210    fn clone(&self) -> Self {
211        *self
212    }
213}
214
215/// Tracks accesses and mutations to a value.
216///
217/// Encapsulates a mutable reference to a value and tracks all accesses to it.
218/// The only methods accessible on `TrackedMut<T>` are those defined in an
219/// implementation block or trait for `T` annotated with `#[track]`. For more
220/// details, see [its documentation](macro@crate::track).
221///
222/// For more details, see [`Tracked`].
223pub struct TrackedMut<'a, T, C = <T as Track>::Call>
224where
225    T: Track + ?Sized,
226{
227    /// A reference to the tracked value.
228    pub(crate) value: &'a mut T,
229    /// A constraint that is generated for T by the tracked methods on T's
230    /// surface type.
231    ///
232    /// Starts out as `None` and is set to a stack-stored constraint in the
233    /// preamble of memoized functions.
234    pub(crate) sink: Option<&'a dyn Sink<Call = C>>,
235}
236
237impl<'a, T> TrackedMut<'a, T>
238where
239    T: Track + ?Sized,
240{
241    /// Downgrade to an immutable reference.
242    ///
243    /// This is an associated function as to not interfere with any methods
244    /// defined on `T`. It should be called as `TrackedMut::downgrade(...)`.
245    #[inline]
246    pub fn downgrade(this: Self) -> Tracked<'a, T> {
247        Tracked {
248            value: this.value,
249            sink: this.sink,
250            id: accelerate::id(),
251        }
252    }
253
254    /// Reborrow with a shorter lifetime.
255    ///
256    /// This is an associated function as to not interfere with any methods
257    /// defined on `T`. It should be called as `TrackedMut::reborrow(...)`.
258    #[inline]
259    pub fn reborrow(this: &Self) -> Tracked<'_, T> {
260        Tracked {
261            value: this.value,
262            sink: this.sink,
263            id: accelerate::id(),
264        }
265    }
266
267    /// Reborrow mutably with a shorter lifetime.
268    ///
269    /// This is an associated function as to not interfere with any methods
270    /// defined on `T`. It should be called as `TrackedMut::reborrow_mut(...)`.
271    #[inline]
272    pub fn reborrow_mut(this: &mut Self) -> TrackedMut<'_, T> {
273        TrackedMut { value: this.value, sink: this.sink }
274    }
275}
276
277impl<'a, T> Deref for TrackedMut<'a, T>
278where
279    T: Track + ?Sized,
280{
281    type Target = T::SurfaceMut<'a>;
282
283    #[inline]
284    fn deref(&self) -> &Self::Target {
285        T::surface_mut_ref(self)
286    }
287}
288
289impl<'a, T> DerefMut for TrackedMut<'a, T>
290where
291    T: Track + ?Sized,
292{
293    #[inline]
294    fn deref_mut(&mut self) -> &mut Self::Target {
295        T::surface_mut_mut(self)
296    }
297}
298
299impl<T> Debug for TrackedMut<'_, T>
300where
301    T: Track + ?Sized,
302{
303    #[inline]
304    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
305        f.pad("TrackedMut(..)")
306    }
307}
308
309/// Destructure a `Tracked<_>` into its parts.
310#[inline]
311pub fn to_parts_ref<T>(tracked: Tracked<'_, T>) -> (&T, Option<&dyn Sink<Call = T::Call>>)
312where
313    T: Track + ?Sized,
314{
315    (tracked.value, tracked.sink)
316}
317
318/// Destructure a `TrackedMut<_>` into its parts.
319#[inline]
320pub fn to_parts_mut_ref<'a, T>(
321    tracked: &'a TrackedMut<T>,
322) -> (&'a T, Option<&'a dyn Sink<Call = T::Call>>)
323where
324    T: Track + ?Sized,
325{
326    (tracked.value, tracked.sink)
327}
328
329/// Destructure a `TrackedMut<_>` into its parts.
330#[inline]
331pub fn to_parts_mut_mut<'a, T>(
332    tracked: &'a mut TrackedMut<T>,
333) -> (&'a mut T, Option<&'a dyn Sink<Call = T::Call>>)
334where
335    T: Track + ?Sized,
336{
337    (tracked.value, tracked.sink)
338}