@@ -15,24 +15,47 @@ mod track;
1515
1616use proc_macro:: TokenStream as BoundaryStream ;
1717use proc_macro2:: TokenStream ;
18- use quote:: { quote, quote_spanned, ToTokens } ;
18+ use quote:: { quote, quote_spanned} ;
1919use syn:: spanned:: Spanned ;
2020use syn:: { parse_quote, Error , Result } ;
2121
2222/// Memoize a function.
2323///
24- /// Memoized functions can take two kinds of arguments:
24+ /// This attribute can be applied to free-standing functions as well as methods
25+ /// in inherent and trait impls.
26+ ///
27+ /// # Kinds of arguments
28+ /// Memoized functions can take three different kinds of arguments:
29+ ///
2530/// - _Hashed:_ This is the default. These arguments are hashed into a
26- /// high-quality 128-bit hash, which is used as a cache key. For this to be
27- /// correct, the hash implementations of your arguments **must feed all the
28- /// information your arguments expose to the hasher**. Otherwise, memoized
29- /// results might get reused invalidly.
30- /// - _Tracked:_ The argument is of the form `Tracked<T>`. These arguments enjoy
31- /// fine-grained access tracking and needn't be exactly the same for a cache
32- /// hit to occur. They only need to be used equivalently.
31+ /// high-quality 128-bit hash, which is used as a cache key.
32+ ///
33+ /// - _Immutably tracked:_ The argument is of the form `Tracked<T>`. These
34+ /// arguments enjoy fine-grained access tracking. This allows cache hits to
35+ /// occur even if the value of `T` is different than previously as long as the
36+ /// difference isn't observed.
37+ ///
38+ /// - _Mutably tracked:_ The argument is of the form `TrackedMut<T>`. Through
39+ /// this type, you can safely mutate an argument from within a memoized
40+ /// function. If there is a cache hit, comemo will replay all mutations.
41+ /// Mutable tracked methods can also have return values that are tracked just
42+ /// like immutable methods.
43+ ///
44+ /// # Restrictions
45+ /// The following restrictions apply to memoized functions:
46+ ///
47+ /// - For the memoization to be correct, the [`Hash`](std::hash::Hash)
48+ /// implementations of your arguments **must feed all the information they
49+ /// expose to the hasher**. Otherwise, memoized results might get reused
50+ /// invalidly.
51+ ///
52+ /// - The **only obversable impurity memoized functions may exhibit are
53+ /// mutations through `TrackedMut<T>` arguments.** Comemo stops you from using
54+ /// basic mutable arguments, but it cannot determine all sources of impurity,
55+ /// so this is your responsibility.
3356///
34- /// You can also add the `#[memoize]` attribute to methods in inherent and trait
35- /// impls .
57+ /// Furthermore, memoized functions cannot use destructuring patterns in their
58+ /// arguments .
3659///
3760/// # Example
3861/// ```
@@ -50,14 +73,6 @@ use syn::{parse_quote, Error, Result};
5073/// }
5174/// ```
5275///
53- /// # Restrictions
54- /// There are certain restrictions that apply to memoized functions. Most of
55- /// these are checked by comemo, but some are your responsibility:
56- /// - They must be **pure**, that is, **free of observable side effects**. This
57- /// is **your responsibility** as comemo can't check it.
58- /// - They must have an explicit return type.
59- /// - They cannot have mutable parameters (conflicts with purity).
60- /// - They cannot use destructuring patterns in their arguments.
6176#[ proc_macro_attribute]
6277pub fn memoize ( _: BoundaryStream , stream : BoundaryStream ) -> BoundaryStream {
6378 let func = syn:: parse_macro_input!( stream as syn:: Item ) ;
@@ -68,13 +83,48 @@ pub fn memoize(_: BoundaryStream, stream: BoundaryStream) -> BoundaryStream {
6883
6984/// Make a type trackable.
7085///
71- /// Adding this to an impl block of a type `T` implements the `Track` trait for
72- /// `T`. This lets you call `.track()` on that type, producing a `Tracked<T>`.
73- /// When such a tracked type is used an argument to a memoized function it
74- /// enjoys fine-grained access tracking instead of being bluntly hashed.
86+ /// This attribute can be applied to an inherent implementation block or trait
87+ /// definition. It implements the `Track` trait for the type or trait object.
88+ ///
89+ /// # Tracking immutably and mutably
90+ /// This allows you to
91+ ///
92+ /// - call `track()` on that type, producing a `Tracked<T>` container. Used as
93+ /// an argument to a memoized function, these containers enjoy fine-grained
94+ /// access tracking instead of blunt hashing.
95+ ///
96+ /// - call `track_mut()` on that type, producing a `TrackedMut<T>`. For mutable
97+ /// arguments, tracking is the only option, so that comemo can replay the side
98+ /// effects when there is a cache hit.
99+ ///
100+ /// If you attempt to track any mutable methods, your type must implement
101+ /// [`Clone`] so that comemo can roll back attempted mutations which did not
102+ /// result in a cache hit.
103+ ///
104+ /// # Restrictions
105+ /// Tracked impl blocks or traits may not be generic and may only contain
106+ /// methods. Just like with memoized functions, certain restrictions apply to
107+ /// tracked methods:
75108///
76- /// You can also add the `#[track]` attribute to a trait to make its trait
77- /// object trackable.
109+ /// - The **only obversable impurity tracked methods may exhibit are mutations
110+ /// through `&mut self`.** Comemo stops you from using basic mutable arguments
111+ /// and return values, but it cannot determine all sources of impurity, so
112+ /// this is your responsibility. Tracked methods also must not return mutable
113+ /// references or other types which allow untracked mutation. You _are_
114+ /// allowed to use interior mutability if it is not observable (even in
115+ /// immutable methods, as long as they stay idempotent).
116+ ///
117+ /// - The return values of tracked methods must implement
118+ /// [`Hash`](std::hash::Hash) and **must feed all the information they expose
119+ /// to the hasher**. Otherwise, memoized results might get reused invalidly.
120+ ///
121+ /// Furthermore:
122+ /// - Tracked methods cannot be generic.
123+ /// - They cannot be `unsafe`, `async` or `const`.
124+ /// - They must take an `&self` or `&mut self` parameter.
125+ /// - Their arguments must implement [`ToOwned`](std::borrow::ToOwned).
126+ /// - Their return values must implement [`Hash`](std::hash::Hash).
127+ /// - They cannot use destructuring patterns in their arguments.
78128///
79129/// # Example
80130/// ```
@@ -96,24 +146,6 @@ pub fn memoize(_: BoundaryStream, stream: BoundaryStream) -> BoundaryStream {
96146/// }
97147/// }
98148/// ```
99- ///
100- /// # Restrictions
101- /// Tracked impl blocks or traits may not be generic and may only contain
102- /// methods. Just like with memoized functions, certain restrictions apply to
103- /// tracked methods:
104- /// - They must be **pure**, that is, **free of observable side effects**. This
105- /// is **your responsibility** as comemo can't check it. You can use interior
106- /// mutability as long as the method stays idempotent.
107- /// - Their **return values must implement `Hash`** and **must feed all the
108- /// information they expose to the hasher**. Otherwise, memoized results might
109- /// get reused invalidly.
110- /// - They cannot be generic.
111- /// - They can only be private or public not `pub(...)`.
112- /// - They cannot be `unsafe`, `async` or `const`.
113- /// - They must take an `&self` parameter.
114- /// - They must have an explicit return type.
115- /// - They cannot have mutable parameters (conflicts with purity).
116- /// - They cannot use destructuring patterns in their arguments.
117149#[ proc_macro_attribute]
118150pub fn track ( _: BoundaryStream , stream : BoundaryStream ) -> BoundaryStream {
119151 let block = syn:: parse_macro_input!( stream as syn:: Item ) ;
0 commit comments