abi_stable_derive/
stable_abi.rs

1use crate::*;
2
3use crate::{
4    composite_collections::{SmallCompositeVec as CompositeVec, SmallStartLen as StartLen},
5    impl_interfacetype::impl_interfacetype_tokenizer,
6    lifetimes::LifetimeIndex,
7    literals_constructors::{rslice_tokenizer, rstr_tokenizer},
8};
9
10use as_derive_utils::{
11    datastructure::{DataStructure, DataVariant},
12    gen_params_in::{GenParamsIn, InWhat},
13    return_spanned_err, return_syn_err, to_stream,
14    to_token_fn::ToTokenFnMut,
15};
16
17use syn::Ident;
18
19use proc_macro2::{Span, TokenStream as TokenStream2};
20
21use core_extensions::{IteratorExt, SelfOps};
22
23#[doc(hidden)]
24pub mod reflection;
25
26mod attribute_parsing;
27
28mod common_tokens;
29
30mod generic_params;
31
32mod nonexhaustive;
33
34mod prefix_types;
35
36mod repr_attrs;
37
38mod tl_function;
39
40mod tl_field;
41
42mod tl_multi_tl;
43
44mod shared_vars;
45
46#[cfg(test)]
47mod tests;
48
49use self::{
50    attribute_parsing::{
51        parse_attrs_for_stable_abi, ASTypeParamBound, ConstIdents, LayoutConstructor,
52        StabilityKind, StableAbiOptions,
53    },
54    common_tokens::CommonTokens,
55    nonexhaustive::{tokenize_enum_info, tokenize_nonexhaustive_items},
56    prefix_types::{prefix_type_tokenizer, PrefixTypeTokens},
57    reflection::ModReflMode,
58    shared_vars::SharedVars,
59    tl_field::CompTLField,
60    tl_function::{CompTLFunction, VisitedFieldMap},
61    tl_multi_tl::TypeLayoutRange,
62};
63
64pub(crate) fn derive(mut data: DeriveInput) -> Result<TokenStream2, syn::Error> {
65    data.generics.make_where_clause();
66
67    // println!("\nderiving for {}",data.ident);
68
69    let arenas = Arenas::default();
70    let arenas = &arenas;
71    let ctokens = CommonTokens::new(arenas);
72    let ctokens = &ctokens;
73    let ds = &DataStructure::new(&data);
74    let config = &parse_attrs_for_stable_abi(ds.attrs, ds, arenas)?;
75    let shared_vars = &mut SharedVars::new(arenas, &config.const_idents, ctokens);
76    let generics = ds.generics;
77    let name = ds.name;
78
79    let doc_hidden_attr = config.doc_hidden_attr;
80
81    // This has to come before the `VisitedFieldMap`.
82    let generic_params_tokens =
83        generic_params::GenericParams::new(ds, shared_vars, config, ctokens);
84
85    if generics.lifetimes().count() > LifetimeIndex::MAX_LIFETIME_PARAM + 1 {
86        return_syn_err!(
87            Span::call_site(),
88            "Cannot have more than {} lifetime parameter.",
89            LifetimeIndex::MAX_LIFETIME_PARAM + 1
90        );
91    }
92
93    let visited_fields = &VisitedFieldMap::new(ds, config, shared_vars, ctokens);
94    shared_vars.extract_errs()?;
95
96    let mono_type_layout = &Ident::new(&format!("_MONO_LAYOUT_{}", name), Span::call_site());
97
98    let (_, _, where_clause) = generics.split_for_impl();
99    let where_clause = (&where_clause.expect("BUG").predicates).into_iter();
100    let where_clause_b = where_clause.clone();
101
102    let ty_generics = GenParamsIn::new(generics, InWhat::ItemUse);
103
104    let impld_stable_abi_trait = match &config.kind {
105        StabilityKind::Value {
106            impl_prefix_stable_abi: false,
107        }
108        | StabilityKind::NonExhaustive { .. } => Ident::new("StableAbi", Span::call_site()),
109        StabilityKind::Value {
110            impl_prefix_stable_abi: true,
111        }
112        | StabilityKind::Prefix { .. } => Ident::new("PrefixStableAbi", Span::call_site()),
113    };
114
115    // The type that implements StableAbi
116    let impl_ty = match &config.kind {
117        StabilityKind::Value { .. } => quote!(#name <#ty_generics> ),
118        StabilityKind::Prefix(prefix) => {
119            let n = &prefix.prefix_fields_struct;
120            quote!(#n <#ty_generics> )
121        }
122        StabilityKind::NonExhaustive(nonexhaustive) => {
123            let marker = nonexhaustive.nonexhaustive_marker;
124            quote!(#marker < #name  <#ty_generics> , __Storage > )
125        }
126    };
127
128    let mut prefix_type_trait_bound = None;
129    let mut prefix_bounds: &[_] = &[];
130
131    // The type whose size and alignment that is stored in the type layout.
132    let size_align_for = match &config.kind {
133        StabilityKind::NonExhaustive(_) => {
134            quote!(__Storage)
135        }
136        StabilityKind::Prefix(prefix) => {
137            let prefix_fields_struct = prefix.prefix_fields_struct;
138
139            prefix_type_trait_bound = Some(quote!(
140                #name <#ty_generics>:__sabi_re::PrefixTypeTrait,
141            ));
142            prefix_bounds = &prefix.prefix_bounds;
143
144            quote!( #prefix_fields_struct <#ty_generics> )
145        }
146        StabilityKind::Value { .. } => quote!(Self),
147    };
148
149    let repr = config.repr;
150
151    let is_transparent = config.repr.is_repr_transparent();
152    let is_enum = ds.data_variant == DataVariant::Enum;
153    let prefix = match &config.kind {
154        StabilityKind::Prefix(prefix) => Some(prefix),
155        _ => None,
156    };
157    let nonexh_opt = match &config.kind {
158        StabilityKind::NonExhaustive(nonexhaustive) => Some(nonexhaustive),
159        _ => None,
160    };
161
162    let tags_const;
163    let tags_arg;
164    // tokenizes the `Tag` data structure associated with this type.
165    match &config.tags {
166        Some(tag) => {
167            tags_const = quote!( const __SABI_TAG: &'static __sabi_re::Tag = &#tag; );
168            tags_arg = quote!(Some(Self::__SABI_TAG));
169        }
170        None => {
171            tags_const = TokenStream2::new();
172            tags_arg = quote!(None);
173        }
174    }
175
176    let extra_checks_const;
177    let extra_checks_arg;
178    match &config.extra_checks {
179        Some(extra_checks) => {
180            extra_checks_const = quote!(
181                const __SABI_EXTRA_CHECKS:
182                    &'static ::std::mem::ManuallyDrop<__sabi_re::StoredExtraChecks>
183                =
184                    &std::mem::ManuallyDrop::new(
185                        __sabi_re::StoredExtraChecks::from_const(
186                            &#extra_checks,
187                            __sabi_re::TD_Opaque,
188                        )
189                    );
190            );
191
192            extra_checks_arg = quote!(Some(Self::__SABI_EXTRA_CHECKS));
193        }
194        None => {
195            extra_checks_const = TokenStream2::new();
196            extra_checks_arg = quote!(None);
197        }
198    };
199
200    let variant_names_start_len = if is_enum {
201        let mut variant_names = String::new();
202        for variant in &ds.variants {
203            use std::fmt::Write;
204            let _ = write!(variant_names, "{};", variant.name);
205        }
206        shared_vars.push_str(&variant_names, None)
207    } else {
208        StartLen::EMPTY
209    };
210
211    // tokenizes the items for nonexhaustive enums outside of the module this generates.
212    let nonexhaustive_items = tokenize_nonexhaustive_items(ds, config, ctokens);
213
214    // tokenizes the items for nonexhaustive enums inside of the module this generates.
215    let nonexhaustive_tokens = tokenize_enum_info(ds, variant_names_start_len, config, ctokens)?;
216
217    let is_nonzero = if is_transparent && !visited_fields.map.is_empty() {
218        let visited_field = &visited_fields.map[0];
219
220        let is_opaque_field = visited_field.layout_ctor.is_opaque();
221        if visited_field.comp_field.is_function() {
222            quote!(__sabi_re::True)
223        } else if is_opaque_field {
224            quote!(__sabi_re::False)
225        } else {
226            let ty = visited_field.comp_field.type_(shared_vars);
227            quote!( <#ty as __StableAbi>::IsNonZeroType )
228        }
229    } else {
230        quote!(__sabi_re::False)
231    };
232
233    let ct = ctokens;
234    // The tokens for the MonoTLData stored in the TypeLayout
235    let mono_tl_data;
236    // The tokens for the GenericTLData stored in the TypeLayout
237    let generic_tl_data;
238
239    match (is_enum, prefix) {
240        (false, None) => {
241            mono_tl_data = {
242                let fields = fields_tokenizer(ds, visited_fields, ct);
243                match ds.data_variant {
244                    DataVariant::Struct => quote!( __sabi_re::MonoTLData::derive_struct(#fields) ),
245                    DataVariant::Union => quote!( __sabi_re::MonoTLData::derive_union(#fields) ),
246                    DataVariant::Enum => unreachable!(),
247                }
248            };
249            generic_tl_data = {
250                match ds.data_variant {
251                    DataVariant::Struct => quote!(__sabi_re::GenericTLData::Struct),
252                    DataVariant::Union => quote!(__sabi_re::GenericTLData::Union),
253                    DataVariant::Enum => unreachable!(),
254                }
255            };
256        }
257        (true, None) => {
258            let vn_sl = variant_names_start_len;
259            mono_tl_data = {
260                let mono_enum_tokenizer =
261                    tokenize_mono_enum(ds, vn_sl, nonexh_opt, config, visited_fields, shared_vars);
262                quote!( __sabi_re::MonoTLData::Enum(#mono_enum_tokenizer) )
263            };
264            generic_tl_data = {
265                let generic_enum_tokenizer =
266                    tokenize_generic_enum(ds, vn_sl, nonexh_opt, config, visited_fields, ct);
267                quote!( __sabi_re::GenericTLData::Enum(#generic_enum_tokenizer) )
268            };
269        }
270        (false, Some(prefix)) => {
271            if is_transparent {
272                return_spanned_err!(name, "repr(transparent) prefix types not supported")
273            }
274
275            mono_tl_data = {
276                let first_suffix_field = prefix.first_suffix_field.field_pos;
277                let fields = fields_tokenizer(ds, visited_fields, ct);
278                let prefix_field_conditionality_mask = prefix.prefix_field_conditionality_mask;
279                quote!(
280                    __sabi_re::MonoTLData::prefix_type_derive(
281                        #first_suffix_field,
282                        #prefix_field_conditionality_mask,
283                        #fields
284                    )
285                )
286            };
287            generic_tl_data = {
288                quote!(
289                    __sabi_re::GenericTLData::prefix_type_derive(
290                        <#name <#ty_generics> as
291                            __sabi_re::PrefixTypeTrait
292                        >::PT_FIELD_ACCESSIBILITY,
293                    )
294                )
295            };
296        }
297        (true, Some(_)) => {
298            return_spanned_err!(name, "enum prefix types not supported");
299        }
300    };
301
302    let lifetimes = &generics
303        .lifetimes()
304        .map(|x| &x.lifetime)
305        .collect::<Vec<_>>();
306    let type_params = &generics.type_params().map(|x| &x.ident).collect::<Vec<_>>();
307    let const_params = &generics
308        .const_params()
309        .map(|x| &x.ident)
310        .collect::<Vec<_>>();
311
312    // For `type StaticEquivalent= ... ;`
313    let lifetimes_s = lifetimes.iter().map(|_| &ctokens.static_lt);
314    let type_params_s = ToTokenFnMut::new(|ts| {
315        let ct = ctokens;
316
317        for (ty_param, bounds) in config.type_param_bounds.iter() {
318            match bounds {
319                ASTypeParamBound::NoBound => {
320                    ct.empty_tuple.to_tokens(ts);
321                }
322                ASTypeParamBound::GetStaticEquivalent | ASTypeParamBound::StableAbi => {
323                    to_stream!(ts; ct.static_equivalent, ct.lt, ty_param, ct.gt);
324                }
325            }
326            ct.comma.to_tokens(ts);
327        }
328    });
329    let const_params_s = &const_params;
330
331    // The name of the struct this generates,
332    // to use as the `GetStaticEquivalent_::StaticEquivalent` associated type.
333    let static_struct_name = Ident::new(&format!("_static_{}", name), Span::call_site());
334
335    let item_info_const = Ident::new(&format!("_item_info_const_{}", name), Span::call_site());
336
337    let static_struct_decl = {
338        let const_param_name = generics.const_params().map(|c| &c.ident);
339        let const_param_type = generics.const_params().map(|c| &c.ty);
340
341        let lifetimes_a = lifetimes;
342
343        let type_params_a = type_params;
344
345        quote! {
346            #doc_hidden_attr
347            pub struct #static_struct_name<
348                #(#lifetimes_a,)*
349                #(#type_params_a:?Sized,)*
350                #(const #const_param_name:#const_param_type,)*
351            >(
352                #(& #lifetimes_a (),)*
353                extern "C" fn(#(&#type_params_a,)*)
354            );
355        }
356    };
357
358    // if the `#[sabi(impl_InterfaceType())]` attribute was used:
359    // tokenizes the implementation of `InterfaceType` for `#name #ty_params`
360    let interfacetype_tokenizer =
361        impl_interfacetype_tokenizer(ds.name, ds.generics, config.impl_interfacetype.as_ref());
362
363    let stringified_name = rstr_tokenizer(name.to_string());
364
365    let mut stable_abi_bounded = Vec::new();
366    let mut static_equiv_bounded = Vec::new();
367
368    for (ident, bounds) in config.type_param_bounds.iter() {
369        let list = match bounds {
370            ASTypeParamBound::NoBound => None,
371            ASTypeParamBound::GetStaticEquivalent => Some(&mut static_equiv_bounded),
372            ASTypeParamBound::StableAbi => Some(&mut stable_abi_bounded),
373        };
374        if let Some(list) = list {
375            list.push(ident);
376        }
377    }
378
379    let stable_abi_bounded = &stable_abi_bounded;
380    let static_equiv_bounded = &static_equiv_bounded;
381
382    let extra_bounds = &config.extra_bounds;
383
384    let PrefixTypeTokens {
385        prefixref_types,
386        prefixref_impls,
387    } = prefix_type_tokenizer(mono_type_layout, ds, config, ctokens)?;
388
389    let mod_refl_mode = match config.mod_refl_mode {
390        ModReflMode::Module => quote!(__ModReflMode::Module),
391        ModReflMode::Opaque => quote!(__ModReflMode::Opaque),
392        ModReflMode::DelegateDeref(field_index) => {
393            quote!(
394                __ModReflMode::DelegateDeref{
395                    phantom_field_index:#field_index
396                }
397            )
398        }
399    };
400
401    let phantom_field_tys = config.phantom_fields.iter().map(|x| x.1);
402
403    // This has to be collected into a Vec ahead of time,
404    // so that the names and types are stored in SharedVars.
405    let phantom_fields = config
406        .phantom_fields
407        .iter()
408        .map(|(name, ty)| {
409            CompTLField::from_expanded_std_field(
410                name,
411                std::iter::empty(),
412                shared_vars.push_type(LayoutConstructor::Regular, ty),
413                shared_vars,
414            )
415        })
416        .collect::<Vec<CompTLField>>();
417    let phantom_fields = rslice_tokenizer(&phantom_fields);
418
419    // The storage type parameter that is added if this is a nonexhaustive enum.
420    let storage_opt = nonexh_opt.map(|_| &ctokens.und_storage);
421    let generics_header =
422        GenParamsIn::with_after_types(ds.generics, InWhat::ImplHeader, storage_opt);
423
424    shared_vars.extract_errs()?;
425
426    let mono_shared_vars_tokenizer = shared_vars.mono_shared_vars_tokenizer();
427
428    let strings_const = &config.const_idents.strings;
429    let strings = shared_vars.strings().piped(rstr_tokenizer);
430
431    let shared_vars_tokenizer = shared_vars.shared_vars_tokenizer(mono_type_layout);
432
433    // drop(_measure_time0);
434
435    let shared_where_preds = quote!(
436        #(#where_clause_b,)*
437        #(#stable_abi_bounded:__StableAbi,)*
438        #(#static_equiv_bounded:__GetStaticEquivalent_,)*
439        #(#extra_bounds,)*
440        #(#prefix_bounds,)*
441        #prefix_type_trait_bound
442    );
443
444    let stable_abi_where_preds = shared_where_preds.clone().mutated(|ts| {
445        ts.extend(quote!(
446            #(#phantom_field_tys:__StableAbi,)*
447        ))
448    });
449
450    let prefix_ref_impls = if let StabilityKind::Prefix(prefix) = &config.kind {
451        let prefix_ref = &prefix.prefix_ref;
452        let prefix_fields_struct = &prefix.prefix_fields_struct;
453        let lifetimes_s = lifetimes_s.clone();
454
455        quote!(
456            unsafe impl<#generics_header> __sabi_re::GetStaticEquivalent_
457            for #prefix_ref <#ty_generics>
458            where
459                #shared_where_preds
460            {
461                type StaticEquivalent =
462                    __sabi_re::PrefixRef<
463                        #static_struct_name <
464                            #(#lifetimes_s,)*
465                            #type_params_s
466                            #({#const_params_s}),*
467                        >
468                    >;
469            }
470
471            unsafe impl<#generics_header> __sabi_re::StableAbi for #prefix_ref <#ty_generics>
472            where
473                #stable_abi_where_preds
474            {
475                type IsNonZeroType = __sabi_re::True;
476
477                const LAYOUT: &'static __sabi_re::TypeLayout =
478                    <__sabi_re::PrefixRef<#prefix_fields_struct <#ty_generics>>
479                        as __sabi_re::StableAbi
480                    >::LAYOUT;
481            }
482        )
483    } else {
484        TokenStream2::new()
485    };
486
487    quote!(
488        #prefixref_types
489
490        #nonexhaustive_items
491
492        const _: () = {
493            use ::abi_stable;
494
495            #[allow(unused_imports)]
496            use ::abi_stable::pmr::{
497                self as __sabi_re,
498                renamed::*,
499            };
500
501            #prefixref_impls
502
503            const #item_info_const: abi_stable::type_layout::ItemInfo=
504                abi_stable::make_item_info!();
505
506            const #strings_const: ::abi_stable::std_types::RStr<'static>=#strings;
507
508
509            #static_struct_decl
510
511            #nonexhaustive_tokens
512
513            #interfacetype_tokenizer
514
515            #prefix_ref_impls
516
517            unsafe impl <#generics_header> __GetStaticEquivalent_ for #impl_ty
518            where
519                #shared_where_preds
520            {
521                type StaticEquivalent=#static_struct_name <
522                    #(#lifetimes_s,)*
523                    #type_params_s
524                    #({#const_params_s}),*
525                >;
526            }
527
528            #[doc(hidden)]
529            const #mono_type_layout:&'static __sabi_re::MonoTypeLayout=
530                &__sabi_re::MonoTypeLayout::from_derive(
531                    __sabi_re::_private_MonoTypeLayoutDerive{
532                        name: #stringified_name,
533                        item_info: #item_info_const,
534                        data: #mono_tl_data,
535                        generics: #generic_params_tokens,
536                        mod_refl_mode:#mod_refl_mode,
537                        repr_attr:#repr,
538                        phantom_fields:#phantom_fields,
539                        shared_vars: #mono_shared_vars_tokenizer,
540                    }
541                );
542
543            impl <#generics_header> #impl_ty
544            where
545                #stable_abi_where_preds
546            {
547                #shared_vars_tokenizer
548
549                #extra_checks_const
550
551                #tags_const
552            }
553
554            unsafe impl <#generics_header> __sabi_re::#impld_stable_abi_trait for #impl_ty
555            where
556                #stable_abi_where_preds
557
558            {
559                type IsNonZeroType=#is_nonzero;
560
561                const LAYOUT: &'static __sabi_re::TypeLayout = {
562                    &__sabi_re::TypeLayout::from_derive::<#size_align_for>(
563                        __sabi_re::_private_TypeLayoutDerive {
564                            shared_vars: Self::__SABI_SHARED_VARS,
565                            mono:#mono_type_layout,
566                            abi_consts: Self::ABI_CONSTS,
567                            data:#generic_tl_data,
568                            tag: #tags_arg,
569                            extra_checks: #extra_checks_arg,
570                        }
571                    )
572                };
573            }
574
575        };
576    )
577    .observe(|tokens| {
578        // drop(_measure_time1);
579        if config.debug_print {
580            panic!("\n\n\n{}\n\n\n", tokens);
581        }
582    })
583    .piped(Ok)
584}
585
586// Tokenizes a `MonoTLEnum{ .. }`
587fn tokenize_mono_enum<'a>(
588    ds: &'a DataStructure<'a>,
589    variant_names_start_len: StartLen,
590    _nonexhaustive_opt: Option<&'a nonexhaustive::NonExhaustive<'a>>,
591    _config: &'a StableAbiOptions<'a>,
592    visited_fields: &'a VisitedFieldMap<'a>,
593    shared_vars: &mut SharedVars<'a>,
594) -> impl ToTokens + 'a {
595    let ct = shared_vars.ctokens();
596
597    ToTokenFnMut::new(move |ts| {
598        let variant_names_start_len = variant_names_start_len.tokenizer(ct.as_ref());
599
600        let variant_lengths = ds.variants.iter().map(|x| {
601            assert!(
602                x.fields.len() < 256,
603                "variant '{}' has more than 255 fields.",
604                x.name
605            );
606            x.fields.len() as u8
607        });
608
609        let fields = fields_tokenizer(ds, visited_fields, ct);
610
611        quote!(
612            __sabi_re::MonoTLEnum::new(
613                #variant_names_start_len,
614                abi_stable::rslice![#( #variant_lengths ),*],
615                #fields,
616            )
617        )
618        .to_tokens(ts);
619    })
620}
621
622// Tokenizes a `GenericTLEnum{ .. }`
623fn tokenize_generic_enum<'a>(
624    ds: &'a DataStructure<'a>,
625    _variant_names_start_len: StartLen,
626    nonexhaustive_opt: Option<&'a nonexhaustive::NonExhaustive<'a>>,
627    config: &'a StableAbiOptions<'a>,
628    _visited_fields: &'a VisitedFieldMap<'a>,
629    ct: &'a CommonTokens<'a>,
630) -> impl ToTokens + 'a {
631    ToTokenFnMut::new(move |ts| {
632        let is_exhaustive = match nonexhaustive_opt {
633            Some(_) => {
634                let name = ds.name;
635
636                let ty_generics = GenParamsIn::new(ds.generics, InWhat::ItemUse);
637                // let (_, ty_generics,_) = ds.generics.split_for_impl();
638                quote!(nonexhaustive(
639                    &__sabi_re::MakeTLNonExhaustive::< #name <#ty_generics> >::NEW
640                ))
641            }
642            None => quote!(exhaustive()),
643        };
644
645        let discriminants = ds.variants.iter().map(|x| x.discriminant);
646        let discriminants = config.repr.tokenize_discriminant_exprs(discriminants, ct);
647
648        quote!(
649            __sabi_re::GenericTLEnum::new(
650                __IsExhaustive::#is_exhaustive,
651                #discriminants,
652            )
653        )
654        .to_tokens(ts);
655    })
656}
657
658/// Tokenizes a TLFields,
659fn fields_tokenizer<'a>(
660    ds: &'a DataStructure<'a>,
661    visited_fields: &'a VisitedFieldMap<'a>,
662    ctokens: &'a CommonTokens<'a>,
663) -> impl ToTokens + 'a {
664    ToTokenFnMut::new(move |ts| {
665        to_stream!(ts;ctokens.comp_tl_fields,ctokens.colon2,ctokens.new);
666        ctokens.paren.surround(ts, |ts| {
667            fields_tokenizer_inner(ds, visited_fields, ctokens, ts);
668        });
669    })
670}
671
672fn fields_tokenizer_inner<'a>(
673    ds: &'a DataStructure<'a>,
674    visited_fields: &'a VisitedFieldMap<'a>,
675    ct: &'a CommonTokens<'a>,
676    ts: &mut TokenStream2,
677) {
678    let iter = visited_fields.map.iter().map(|field| field.comp_field);
679    rslice_tokenizer(iter).to_tokens(ts);
680
681    ct.comma.to_tokens(ts);
682
683    if visited_fields.fn_ptr_count == 0 {
684        ct.none.to_tokens(ts);
685    } else {
686        to_stream!(ts;ct.some);
687        ct.paren.surround(ts, |ts| {
688            ct.and_.to_tokens(ts);
689            tokenize_tl_functions(ds, visited_fields, ct, ts);
690        });
691    }
692    to_stream! {ts; ct.comma };
693}
694
695/// Tokenizes a TLFunctions
696fn tokenize_tl_functions<'a>(
697    ds: &'a DataStructure<'a>,
698    visited_fields: &'a VisitedFieldMap<'a>,
699    _ct: &'a CommonTokens<'a>,
700    ts: &mut TokenStream2,
701) {
702    let mut functions =
703        CompositeVec::<&'a CompTLFunction>::with_capacity(visited_fields.fn_ptr_count);
704    let mut field_fn_ranges = Vec::<StartLen>::with_capacity(ds.field_count);
705
706    visited_fields
707        .map
708        .iter()
709        .map(|field| functions.extend(&field.functions))
710        .extending(&mut field_fn_ranges);
711
712    let functions = functions.into_inner();
713
714    let field_fn_ranges = field_fn_ranges.into_iter().map(|sl| sl.to_u32());
715
716    quote!({
717        const TLF_A: &[__CompTLFunction] = &[#(#functions),*];
718        const TLF_B: __TLFunctions = __TLFunctions::new(
719            __sabi_re::RSlice::from_slice(TLF_A),
720            abi_stable::rslice![#(#field_fn_ranges),*],
721        );
722        TLF_B
723    })
724    .to_tokens(ts);
725}