comemo/
track.rs

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