Skip to content

Commit 0c141bb

Browse files
committed
Lifetimes and performance
- Relaxes the 'static bound on tracked types, they can now have lifetimes - Improves performance through hash-based constraint generation and validation acceleration - Fixes possible name clashes in tracked methods
1 parent 2b1d9dd commit 0c141bb

File tree

8 files changed

+583
-345
lines changed

8 files changed

+583
-345
lines changed

macros/src/memoize.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ fn prepare_arg(input: &syn::FnArg) -> Result<Argument> {
6767
mutability: Some(_), ..
6868
}) = typed.ty.as_ref()
6969
{
70-
bail!(
71-
typed.ty,
72-
"memoized functions cannot have mutable parameters"
73-
)
70+
bail!(typed.ty, "memoized functions cannot have mutable parameters")
7471
}
7572

7673
Argument::Ident(mutability.clone(), ident.clone())
@@ -127,6 +124,7 @@ fn process(function: &Function) -> Result<TokenStream> {
127124
::comemo::internal::memoized(
128125
::core::any::TypeId::of::<#unique>(),
129126
::comemo::internal::Args(#arg_tuple),
127+
&::core::default::Default::default(),
130128
#closure,
131129
)
132130
} };

macros/src/track.rs

Lines changed: 117 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@ pub fn expand(item: &syn::Item) -> Result<TokenStream> {
55
// Preprocess and validate the methods.
66
let mut methods = vec![];
77

8-
let (ty, trait_) = match item {
8+
let (ty, generics, trait_) = match item {
99
syn::Item::Impl(item) => {
1010
for param in item.generics.params.iter() {
11-
bail!(param, "tracked impl blocks cannot be generic")
11+
match param {
12+
syn::GenericParam::Lifetime(_) => {}
13+
syn::GenericParam::Type(_) => {
14+
bail!(param, "tracked impl blocks cannot use type generics")
15+
}
16+
syn::GenericParam::Const(_) => {
17+
bail!(param, "tracked impl blocks cannot use const generics")
18+
}
19+
}
1220
}
1321

1422
for item in &item.items {
1523
methods.push(prepare_impl_method(&item)?);
1624
}
1725

1826
let ty = item.self_ty.as_ref().clone();
19-
(ty, None)
27+
(ty, &item.generics, None)
2028
}
2129
syn::Item::Trait(item) => {
2230
for param in item.generics.params.iter() {
@@ -28,17 +36,14 @@ pub fn expand(item: &syn::Item) -> Result<TokenStream> {
2836
}
2937

3038
let name = &item.ident;
31-
let ty = parse_quote! { dyn #name };
32-
(ty, Some(name.clone()))
39+
let ty = parse_quote! { dyn #name + '__comemo_dynamic };
40+
(ty, &item.generics, Some(name.clone()))
3341
}
34-
_ => bail!(
35-
item,
36-
"`track` can only be applied to impl blocks and traits"
37-
),
42+
_ => bail!(item, "`track` can only be applied to impl blocks and traits"),
3843
};
3944

4045
// Produce the necessary items for the type to become trackable.
41-
let scope = create(&ty, trait_, &methods)?;
46+
let scope = create(&ty, generics, trait_, &methods)?;
4247

4348
Ok(quote! {
4449
#item
@@ -172,104 +177,152 @@ fn prepare_method(vis: syn::Visibility, sig: &syn::Signature) -> Result<Method>
172177
/// Produce the necessary items for a type to become trackable.
173178
fn create(
174179
ty: &syn::Type,
180+
generics: &syn::Generics,
175181
trait_: Option<syn::Ident>,
176182
methods: &[Method],
177183
) -> Result<TokenStream> {
178-
let prefix = trait_.map(|name| quote! { #name for });
179-
let variants = methods.iter().map(create_variant);
180-
let validations = methods.iter().map(create_validation);
184+
let t: syn::GenericParam = parse_quote! { '__comemo_tracked };
185+
let r: syn::GenericParam = parse_quote! { '__comemo_retrack };
186+
let d: syn::GenericParam = parse_quote! { '__comemo_dynamic };
187+
let maybe_cloned = if methods.iter().any(|it| it.mutable) {
188+
quote! { ::core::clone::Clone::clone(self) }
189+
} else {
190+
quote! { self }
191+
};
192+
193+
// Prepare generics.
194+
let (impl_gen, type_gen, where_clause) = generics.split_for_impl();
195+
let mut impl_params: syn::Generics = parse_quote! { #impl_gen };
196+
let mut type_params: syn::Generics = parse_quote! { #type_gen };
197+
if trait_.is_some() {
198+
impl_params.params.push(d.clone());
199+
type_params.params.push(d.clone());
200+
}
201+
202+
let mut impl_params_t: syn::Generics = impl_params.clone();
203+
let mut type_params_t: syn::Generics = type_params.clone();
204+
impl_params_t.params.push(t.clone());
205+
type_params_t.params.push(t.clone());
206+
207+
// Prepare validations.
208+
let prefix = trait_.as_ref().map(|name| quote! { #name for });
209+
let validations: Vec<_> = methods.iter().map(create_validation).collect();
210+
let validate = if !methods.is_empty() {
211+
quote! {
212+
let mut this = #maybe_cloned;
213+
constraint.validate(|call| match &call.0 { #(#validations,)* })
214+
}
215+
} else {
216+
quote! { true }
217+
};
218+
let validate_with_id = if !methods.is_empty() {
219+
quote! {
220+
let mut this = #maybe_cloned;
221+
constraint.validate_with_id(
222+
|call| match &call.0 { #(#validations,)* },
223+
id,
224+
)
225+
}
226+
} else {
227+
quote! { true }
228+
};
229+
230+
// Prepare replying.
181231
let replays = methods.iter().map(create_replay);
232+
let replay = methods.iter().any(|m| m.mutable).then(|| {
233+
quote! {
234+
constraint.replay(|call| match &call.0 { #(#replays,)* });
235+
}
236+
});
237+
238+
// Prepare variants and wrapper methods.
239+
let variants = methods.iter().map(create_variant);
182240
let wrapper_methods = methods
183241
.iter()
184242
.filter(|m| !m.mutable)
185243
.map(|m| create_wrapper(m, false));
186244
let wrapper_methods_mut = methods.iter().map(|m| create_wrapper(m, true));
187-
let maybe_cloned = if methods.iter().any(|it| it.mutable) {
188-
quote! { ::std::clone::Clone::clone(self) }
189-
} else {
190-
quote! { self }
191-
};
192245

193246
Ok(quote! {
194-
#[derive(Clone, PartialEq)]
195-
pub struct __ComemoCall(__ComemoVariant);
247+
impl #impl_params ::comemo::Track for #ty #where_clause {}
196248

197-
#[derive(Clone, PartialEq)]
198-
#[allow(non_camel_case_types)]
199-
enum __ComemoVariant {
200-
#(#variants,)*
201-
}
249+
impl #impl_params ::comemo::Validate for #ty #where_clause {
250+
type Constraint = ::comemo::internal::Constraint<__ComemoCall>;
202251

203-
impl ::comemo::Track for #ty {
204252
#[inline]
205-
fn valid(&self, constraint: &::comemo::Constraint<Self>) -> bool {
206-
let mut this = #maybe_cloned;
207-
constraint.valid(|call| match &call.0 { #(#validations,)* })
253+
fn validate(&self, constraint: &Self::Constraint) -> bool {
254+
#validate
208255
}
209-
}
210256

211-
#[doc(hidden)]
212-
impl ::comemo::internal::Trackable for #ty {
213-
type Call = __ComemoCall;
214-
type Surface = __ComemoSurfaceFamily;
215-
type SurfaceMut = __ComemoSurfaceMutFamily;
257+
#[inline]
258+
fn validate_with_id(&self, constraint: &Self::Constraint, id: usize) -> bool {
259+
#validate_with_id
260+
}
216261

217262
#[inline]
218263
#[allow(unused_variables)]
219-
fn replay(&mut self, constraint: &::comemo::Constraint<Self>) {
220-
constraint.replay(|call| match &call.0 { #(#replays,)* });
264+
fn replay(&mut self, constraint: &Self::Constraint) {
265+
#replay
221266
}
267+
}
268+
269+
#[derive(Clone, PartialEq, Hash)]
270+
pub struct __ComemoCall(__ComemoVariant);
271+
272+
#[derive(Clone, PartialEq, Hash)]
273+
#[allow(non_camel_case_types)]
274+
enum __ComemoVariant {
275+
#(#variants,)*
276+
}
277+
278+
#[doc(hidden)]
279+
impl #impl_params ::comemo::internal::Surfaces for #ty #where_clause {
280+
type Surface<#t> = __ComemoSurface #type_params_t where Self: #t;
281+
type SurfaceMut<#t> = __ComemoSurfaceMut #type_params_t where Self: #t;
222282

223283
#[inline]
224-
fn surface_ref<'a, 'r>(
225-
tracked: &'r ::comemo::Tracked<'a, Self>,
226-
) -> &'r __ComemoSurface<'a> {
284+
fn surface_ref<#t, #r>(
285+
tracked: &#r ::comemo::Tracked<#t, Self>,
286+
) -> &#r Self::Surface<#t> {
227287
// Safety: __ComemoSurface is repr(transparent).
228288
unsafe { &*(tracked as *const _ as *const _) }
229289
}
230290

231291
#[inline]
232-
fn surface_mut_ref<'a, 'r>(
233-
tracked: &'r ::comemo::TrackedMut<'a, Self>,
234-
) -> &'r __ComemoSurfaceMut<'a> {
292+
fn surface_mut_ref<#t, #r>(
293+
tracked: &#r ::comemo::TrackedMut<#t, Self>,
294+
) -> &#r Self::SurfaceMut<#t> {
235295
// Safety: __ComemoSurfaceMut is repr(transparent).
236296
unsafe { &*(tracked as *const _ as *const _) }
237297
}
238298

239299
#[inline]
240-
fn surface_mut_mut<'a, 'r>(
241-
tracked: &'r mut ::comemo::TrackedMut<'a, Self>,
242-
) -> &'r mut __ComemoSurfaceMut<'a> {
300+
fn surface_mut_mut<#t, #r>(
301+
tracked: &#r mut ::comemo::TrackedMut<#t, Self>,
302+
) -> &#r mut Self::SurfaceMut<#t> {
243303
// Safety: __ComemoSurfaceMut is repr(transparent).
244304
unsafe { &mut *(tracked as *mut _ as *mut _) }
245305
}
246306
}
247307

248308
#[repr(transparent)]
249-
pub struct __ComemoSurface<'a>(::comemo::Tracked<'a, #ty>);
309+
pub struct __ComemoSurface #impl_params_t(::comemo::Tracked<#t, #ty>)
310+
#where_clause;
250311

251312
#[allow(dead_code)]
252-
impl #prefix __ComemoSurface<'_> {
313+
impl #impl_params_t #prefix __ComemoSurface #type_params_t {
253314
#(#wrapper_methods)*
254315
}
255316

256-
pub enum __ComemoSurfaceFamily {}
257-
impl<'a> ::comemo::internal::Family<'a> for __ComemoSurfaceFamily {
258-
type Out = __ComemoSurface<'a>;
259-
}
260-
261317
#[repr(transparent)]
262-
pub struct __ComemoSurfaceMut<'a>(::comemo::TrackedMut<'a, #ty>);
318+
pub struct __ComemoSurfaceMut #impl_params_t(::comemo::TrackedMut<#t, #ty>)
319+
#where_clause;
263320

264321
#[allow(dead_code)]
265-
impl #prefix __ComemoSurfaceMut<'_> {
322+
impl #impl_params_t #prefix __ComemoSurfaceMut #type_params_t {
266323
#(#wrapper_methods_mut)*
267324
}
268325

269-
pub enum __ComemoSurfaceMutFamily {}
270-
impl<'a> ::comemo::internal::Family<'a> for __ComemoSurfaceMutFamily {
271-
type Out = __ComemoSurfaceMut<'a>;
272-
}
273326
})
274327
}
275328

@@ -328,12 +381,12 @@ fn create_wrapper(method: &Method, tracked_mut: bool) -> TokenStream {
328381
#[track_caller]
329382
#[inline]
330383
#vis #sig {
331-
let call = __ComemoVariant::#name(#(#args.to_owned()),*);
332-
let (value, constraint) = ::comemo::internal::#to_parts;
333-
let output = value.#name(#(#args,)*);
334-
if let Some(constraint) = constraint {
384+
let __comemo_variant = __ComemoVariant::#name(#(#args.to_owned()),*);
385+
let (__comemo_value, __comemo_constraint) = ::comemo::internal::#to_parts;
386+
let output = __comemo_value.#name(#(#args,)*);
387+
if let Some(constraint) = __comemo_constraint {
335388
constraint.push(
336-
__ComemoCall(call),
389+
__ComemoCall(__comemo_variant),
337390
::comemo::internal::hash(&output),
338391
#mutable,
339392
);

0 commit comments

Comments
 (0)