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}