Skip to content

Commit 35cd31e

Browse files
committed
Decrease generated code compilation time
It seems that many closures slow down compilation tremendously. This change provides a 10x speed-up or more depending on the number of variants.
1 parent e47ea9f commit 35cd31e

File tree

1 file changed

+57
-32
lines changed
  • enum-iterator-derive/src

1 file changed

+57
-32
lines changed

enum-iterator-derive/src/lib.rs

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ fn derive_for_struct(
5858
fields: &Fields,
5959
) -> Result<TokenStream, syn::Error> {
6060
let cardinality = tuple_cardinality(fields);
61-
let first = init_fields(fields, Direction::Forward);
62-
let last = init_fields(fields, Direction::Backward);
61+
let first = init_value(ty, None, fields, Direction::Forward);
62+
let last = init_value(ty, None, fields, Direction::Backward);
6363
let next_body = advance_struct(ty, fields, Direction::Forward);
6464
let previous_body = advance_struct(ty, fields, Direction::Backward);
6565
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@@ -92,11 +92,11 @@ fn derive_for_struct(
9292
}
9393

9494
fn first() -> ::core::option::Option<Self> {
95-
::core::option::Option::Some(#ty { #first })
95+
#first
9696
}
9797

9898
fn last() -> ::core::option::Option<Self> {
99-
::core::option::Option::Some(#ty { #last })
99+
#last
100100
}
101101
}
102102
};
@@ -209,18 +209,25 @@ fn field_id(field: &Field, index: usize) -> Member {
209209
.map_or_else(|| Member::from(index), Member::from)
210210
}
211211

212-
fn init_fields(fields: &Fields, direction: Direction) -> TokenStream {
212+
fn init_value(
213+
ty: &Ident,
214+
variant: Option<&Ident>,
215+
fields: &Fields,
216+
direction: Direction,
217+
) -> TokenStream {
213218
let reset = direction.reset();
214-
fields
215-
.iter()
216-
.enumerate()
217-
.map(|(i, field)| {
218-
let id = field_id(field, i);
219-
quote! {
220-
#id: ::enum_iterator::Sequence::#reset()?,
219+
let initialization = repeat(quote! { ::enum_iterator::Sequence::#reset() }).take(fields.len());
220+
let assignments = field_assignments(fields);
221+
let bindings = bindings().take(fields.len());
222+
let id = variant.map_or_else(|| quote! { #ty }, |v| quote! { #ty::#v });
223+
quote! {{
224+
match (#(#initialization,)*) {
225+
(#(::core::option::Option::Some(#bindings),)*) => {
226+
::core::option::Option::Some(#id { #assignments })
221227
}
222-
})
223-
.collect::<TokenStream>()
228+
_ => ::core::option::Option::None,
229+
}
230+
}}
224231
}
225232

226233
fn next_variant(
@@ -241,17 +248,17 @@ fn next_variant(
241248
};
242249
let arms = variants.iter().enumerate().map(|(i, v)| {
243250
let id = &v.ident;
244-
let init = init_fields(&v.fields, direction);
251+
let init = init_value(ty, Some(id), &v.fields, direction);
245252
quote! {
246-
#i => ::core::option::Option::Some(#ty::#id { #init })
253+
#i => #init
247254
}
248255
});
249256
quote! {
250257
loop {
251-
let next = (|| match i {
258+
let next = match i {
252259
#(#arms,)*
253260
_ => ::core::option::Option::None,
254-
})();
261+
};
255262
match next {
256263
::core::option::Option::Some(_) => break next,
257264
::core::option::Option::None => #advance,
@@ -299,12 +306,12 @@ fn advance_enum(
299306
fn advance_enum_arm(ty: &Ident, direction: Direction, i: usize, variant: &Variant) -> TokenStream {
300307
let next = match direction {
301308
Direction::Forward => match i.checked_add(1) {
302-
Some(next_i) => quote! { .or_else(|| next_variant(#next_i)) },
303-
None => quote! {},
309+
Some(next_i) => quote! { next_variant(#next_i) },
310+
None => quote! { ::core::option::Option::None },
304311
},
305312
Direction::Backward => match i.checked_sub(1) {
306-
Some(prev_i) => quote! { .or_else(|| previous_variant(#prev_i)) },
307-
None => quote! {},
313+
Some(prev_i) => quote! { previous_variant(#prev_i) },
314+
None => quote! { ::core::option::Option::None },
308315
},
309316
};
310317
let id = &variant.ident;
@@ -314,9 +321,13 @@ fn advance_enum_arm(ty: &Ident, direction: Direction, i: usize, variant: &Varian
314321
let tuple = advance_tuple(&bindings, direction);
315322
quote! {
316323
#ty::#id { #destructuring } => {
317-
#tuple
318-
.map(|(#(#bindings,)*)| #ty::#id { #assignments })
319-
#next
324+
let y = #tuple;
325+
match y {
326+
::core::option::Option::Some((#(#bindings,)*)) => {
327+
::core::option::Option::Some(#ty::#id { #assignments })
328+
}
329+
::core::option::Option::None => #next,
330+
}
320331
}
321332
}
322333
}
@@ -332,8 +343,8 @@ fn advance_tuple(bindings: &[Ident], direction: Direction) -> TokenStream {
332343
let rev_binding_head = match rev_binding_head {
333344
Some(head) => quote! {
334345
let (#head, carry) = match ::enum_iterator::Sequence::#advance(#head) {
335-
::core::option::Option::Some(#head) => (#head, false),
336-
::core::option::Option::None => (::enum_iterator::Sequence::#reset()?, true),
346+
::core::option::Option::Some(#head) => (::core::option::Option::Some(#head), false),
347+
::core::option::Option::None => (::enum_iterator::Sequence::#reset(), true),
337348
};
338349
},
339350
None => quote! {
@@ -345,17 +356,31 @@ fn advance_tuple(bindings: &[Ident], direction: Direction) -> TokenStream {
345356
#(
346357
let (#rev_binding_tail, carry) = if carry {
347358
match ::enum_iterator::Sequence::#advance(#rev_binding_tail) {
348-
::core::option::Option::Some(#rev_binding_tail) => (#rev_binding_tail, false),
349-
::core::option::Option::None => (::enum_iterator::Sequence::#reset()?, true),
359+
::core::option::Option::Some(#rev_binding_tail) => {
360+
(::core::option::Option::Some(#rev_binding_tail), false)
361+
}
362+
::core::option::Option::None => (::enum_iterator::Sequence::#reset(), true),
350363
}
351364
} else {
352-
(::core::clone::Clone::clone(#rev_binding_tail), false)
365+
(
366+
::core::option::Option::Some(::core::clone::Clone::clone(#rev_binding_tail)),
367+
false,
368+
)
353369
};
354370
)*
355-
::core::option::Option::Some((#(#bindings,)*)).filter(|_| !carry)
371+
if carry {
372+
::core::option::Option::None
373+
} else {
374+
match (#(#bindings,)*) {
375+
(#(::core::option::Option::Some(#bindings),)*) => {
376+
::core::option::Option::Some((#(#bindings,)*))
377+
}
378+
_ => ::core::option::Option::None,
379+
}
380+
}
356381
};
357382
quote! {
358-
(|| { #body })()
383+
{ #body }
359384
}
360385
}
361386

0 commit comments

Comments
 (0)