@@ -15,24 +15,47 @@ mod track;
15
15
16
16
use proc_macro:: TokenStream as BoundaryStream ;
17
17
use proc_macro2:: TokenStream ;
18
- use quote:: { quote, quote_spanned, ToTokens } ;
18
+ use quote:: { quote, quote_spanned} ;
19
19
use syn:: spanned:: Spanned ;
20
20
use syn:: { parse_quote, Error , Result } ;
21
21
22
22
/// Memoize a function.
23
23
///
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
+ ///
25
30
/// - _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.
33
56
///
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 .
36
59
///
37
60
/// # Example
38
61
/// ```
@@ -50,14 +73,6 @@ use syn::{parse_quote, Error, Result};
50
73
/// }
51
74
/// ```
52
75
///
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.
61
76
#[ proc_macro_attribute]
62
77
pub fn memoize ( _: BoundaryStream , stream : BoundaryStream ) -> BoundaryStream {
63
78
let func = syn:: parse_macro_input!( stream as syn:: Item ) ;
@@ -68,13 +83,48 @@ pub fn memoize(_: BoundaryStream, stream: BoundaryStream) -> BoundaryStream {
68
83
69
84
/// Make a type trackable.
70
85
///
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:
75
108
///
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.
78
128
///
79
129
/// # Example
80
130
/// ```
@@ -96,24 +146,6 @@ pub fn memoize(_: BoundaryStream, stream: BoundaryStream) -> BoundaryStream {
96
146
/// }
97
147
/// }
98
148
/// ```
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.
117
149
#[ proc_macro_attribute]
118
150
pub fn track ( _: BoundaryStream , stream : BoundaryStream ) -> BoundaryStream {
119
151
let block = syn:: parse_macro_input!( stream as syn:: Item ) ;
0 commit comments