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}