const_type_layout_derive/
lib.rs

1//! [![CI Status]][workflow] [![MSRV]][repo] [![Latest Version]][crates.io]
2//! [![Rust Doc Crate]][docs.rs] [![Rust Doc Main]][docs]
3//! [![License Status]][fossa] [![Code Coverage]][codecov]
4//! [![Gitpod Ready-to-Code]][gitpod]
5//!
6//! [CI Status]: https://img.shields.io/github/actions/workflow/status/juntyr/const-type-layout/ci.yml?branch=main
7//! [workflow]: https://github.com/juntyr/const-type-layout/actions/workflows/ci.yml?query=branch%3Amain
8//!
9//! [MSRV]: https://img.shields.io/badge/MSRV-1.78.0--nightly-orange
10//! [repo]: https://github.com/juntyr/const-type-layout
11//!
12//! [Latest Version]: https://img.shields.io/crates/v/const-type-layout
13//! [crates.io]: https://crates.io/crates/const-type-layout
14//!
15//! [Rust Doc Crate]: https://img.shields.io/docsrs/const-type-layout
16//! [docs.rs]: https://docs.rs/const-type-layout/
17//!
18//! [Rust Doc Main]: https://img.shields.io/badge/docs-main-blue
19//! [docs]: https://juntyr.github.io/const-type-layout/const_type_layout
20//!
21//! [License Status]: https://app.fossa.com/api/projects/custom%2B26490%2Fgithub.com%2Fjuntyr%2Fconst-type-layout.svg?type=shield
22//! [fossa]: https://app.fossa.com/projects/custom%2B26490%2Fgithub.com%2Fjuntyr%2Fconst-type-layout?ref=badge_shield
23//!
24//! [Code Coverage]: https://img.shields.io/codecov/c/github/juntyr/const-type-layout?token=J39WVBIMZX
25//! [codecov]: https://codecov.io/gh/juntyr/const-type-layout
26//!
27//! [Gitpod Ready-to-Code]: https://img.shields.io/badge/Gitpod-ready-blue?logo=gitpod
28//! [gitpod]: https://gitpod.io/#https://github.com/juntyr/const-type-layout
29//!
30//! `const-type-layout-derive` provides the [`#[derive(TypeLayout)`](TypeLayout)
31//! implementation for the
32//! [`const_type_layout::TypeLayout`](https://docs.rs/const-type-layout/0.3/const_type_layout/trait.TypeLayout.html)
33//! trait.
34
35#![deny(clippy::complexity)]
36#![deny(clippy::correctness)]
37#![warn(clippy::nursery)]
38#![warn(clippy::pedantic)]
39#![deny(clippy::perf)]
40#![deny(clippy::style)]
41#![deny(clippy::suspicious)]
42#![deny(missing_docs)]
43#![feature(iter_intersperse)]
44
45extern crate proc_macro;
46
47#[macro_use]
48extern crate proc_macro_error2;
49
50use proc_macro::TokenStream;
51
52use proc_macro2::Literal;
53use quote::{quote, quote_spanned};
54use syn::{parse_macro_input, spanned::Spanned};
55
56#[proc_macro_error]
57#[proc_macro_derive(TypeLayout, attributes(layout))]
58/// Provides the `#[derive(TypeLayout)]` implementation for the
59/// [`const_type_layout::TypeLayout`](https://docs.rs/const-type-layout/0.3/const_type_layout/trait.TypeLayout.html)
60/// trait.
61///
62/// The derive also accepts a `#[layout(...)]` attribute to configure the
63/// implementation as follows:
64/// - `#[layout(crate = "<crate-path>")]` changes the path to the [`const-type-layout`](https://docs.rs/const-type-layout/0.3/const_type_layout)
65///   crate that the derive uses, which by default is `const_type_layout`.
66/// - `#[layout(bound = "<where-predicate>")]` adds the provided predicate to
67///   the where clause of the trait implementation.
68/// - `#[layout(free = "<type>")]` removes the the auto-added trait bounds for
69///   the type parameter `<type>` from the trait implementation, e.g. when
70///   implementing a wrapper around [`PhantomData<T>`](std::marker::PhantomData)
71///   which should implement the trait for any `T`.
72pub fn derive_type_layout(input: TokenStream) -> TokenStream {
73    // Parse the input tokens into a syntax tree
74    let input = parse_macro_input!(input as syn::DeriveInput);
75
76    // Used in the quasi-quotation below as `#ty_name`.
77    let ty_name = input.ident;
78    let ty_generics = input.generics.split_for_impl().1;
79
80    let mut type_params = input
81        .generics
82        .type_params()
83        .map(|param| &param.ident)
84        .collect::<Vec<_>>();
85
86    let Attributes {
87        reprs,
88        extra_bounds,
89        crate_path,
90    } = parse_attributes(&input.attrs, &mut type_params);
91
92    let inhabited = inhabited_for_type(&crate_path, &input.data);
93    let layout = layout_of_type(&crate_path, &ty_name, &ty_generics, &input.data, &reprs);
94
95    let inner_types = extract_inner_types(&input.data);
96
97    let discriminant_ty = if let syn::Data::Enum(_) = input.data {
98        Some(quote! { <Self as #crate_path::ExtractDiscriminant>::Discriminant, })
99    } else {
100        None
101    };
102
103    let Generics {
104        type_layout_input_generics,
105        type_set_input_generics,
106    } = generate_generics(&crate_path, &input.generics, &extra_bounds, &type_params);
107    let (type_layout_impl_generics, type_layout_ty_generics, type_layout_where_clause) =
108        type_layout_input_generics.split_for_impl();
109    let (type_set_impl_generics, type_set_ty_generics, type_set_where_clause) =
110        type_set_input_generics.split_for_impl();
111
112    quote! {
113        unsafe impl #type_layout_impl_generics #crate_path::TypeLayout for
114            #ty_name #type_layout_ty_generics #type_layout_where_clause
115        {
116            type Inhabited = #inhabited;
117
118            const TYPE_LAYOUT: #crate_path::TypeLayoutInfo<'static> = {
119                #crate_path::TypeLayoutInfo {
120                    name: ::core::any::type_name::<Self>(),
121                    size: ::core::mem::size_of::<Self>(),
122                    alignment: ::core::mem::align_of::<Self>(),
123                    structure: #layout,
124                }
125            };
126        }
127
128        unsafe impl #type_set_impl_generics #crate_path::typeset::ComputeTypeSet for
129            #ty_name #type_set_ty_generics #type_set_where_clause
130        {
131            type Output<__TypeSetRest: #crate_path::typeset::ExpandTypeSet> =
132                #crate_path::typeset::tset![
133                    #(#inner_types,)* #discriminant_ty .. @ __TypeSetRest
134                ];
135        }
136    }
137    .into()
138}
139
140struct Attributes {
141    reprs: String,
142    extra_bounds: Vec<syn::WherePredicate>,
143    crate_path: syn::Path,
144}
145
146#[allow(clippy::too_many_lines)]
147fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>) -> Attributes {
148    // Could parse based on https://github.com/rust-lang/rust/blob/d13e8dd41d44a73664943169d5b7fe39b22c449f/compiler/rustc_attr/src/builtin.rs#L772-L781 instead
149    let mut reprs = Vec::new();
150
151    let mut extra_bounds: Vec<syn::WherePredicate> = Vec::new();
152
153    let mut crate_path = None;
154
155    for attr in attrs {
156        if attr.path.is_ident("repr") {
157            if let Ok(syn::Meta::List(syn::MetaList { nested, .. })) = attr.parse_meta() {
158                for meta in nested {
159                    reprs.push(match meta {
160                        syn::NestedMeta::Lit(lit) => lit_to_string(&lit),
161                        syn::NestedMeta::Meta(meta) => meta_to_string(&meta),
162                    });
163                }
164            } else {
165                emit_warning!(
166                    attr.span(),
167                    "[const-type-layout]: #[repr] attribute is not in meta list format."
168                );
169            }
170        } else if attr.path.is_ident("layout") {
171            if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
172                for meta in &list.nested {
173                    if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
174                        path,
175                        lit: syn::Lit::Str(s),
176                        ..
177                    })) = &meta
178                    {
179                        if path.is_ident("free") {
180                            match syn::parse_str::<syn::Ident>(&s.value()) {
181                                Ok(param) => {
182                                    type_params.iter().position(|ty| **ty == param).map_or_else(
183                                        || {
184                                            emit_error!(
185                                                s.span(),
186                                                "[const-type-layout]: Invalid #[layout(free)] \
187                                                 attribute: \"{}\" is either not a type parameter \
188                                                 or has already been freed (duplicate attribute).",
189                                                param,
190                                            );
191                                        },
192                                        |i| {
193                                            type_params.swap_remove(i);
194                                        },
195                                    );
196                                },
197                                Err(err) => emit_error!(
198                                    s.span(),
199                                    "[const-type-layout]: Invalid #[layout(free = \"<type>\")] \
200                                     attribute: {}.",
201                                    err
202                                ),
203                            }
204                        } else if path.is_ident("bound") {
205                            match syn::parse_str(&s.value()) {
206                                Ok(bound) => extra_bounds.push(bound),
207                                Err(err) => emit_error!(
208                                    s.span(),
209                                    "[const-type-layout]: Invalid #[layout(bound = \
210                                     \"<where-predicate>\")] attribute: {}.",
211                                    err
212                                ),
213                            }
214                        } else if path.is_ident("crate") {
215                            match syn::parse_str::<syn::Path>(&s.value()) {
216                                Ok(new_crate_path) => {
217                                    if crate_path.is_none() {
218                                        crate_path = Some(
219                                            syn::parse_quote_spanned! { s.span() => #new_crate_path },
220                                        );
221                                    } else {
222                                        emit_error!(
223                                            s.span(),
224                                            "[const-type-layout]: Duplicate #[layout(crate)] \
225                                             attribute: the crate path for `const-type-layout` \
226                                             can only be set once per `derive`.",
227                                        );
228                                    }
229                                },
230                                Err(err) => emit_error!(
231                                    s.span(),
232                                    "[const-type-layout]: Invalid #[layout(crate = \
233                                     \"<crate-path>\")] attribute: {}.",
234                                    err
235                                ),
236                            }
237                        } else {
238                            emit_error!(
239                                path.span(),
240                                "[const-type-layout]: Unknown attribute, use `bound`, `crate`, or \
241                                 `free`."
242                            );
243                        }
244                    } else {
245                        emit_error!(
246                            meta.span(),
247                            "[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
248                        );
249                    }
250                }
251            } else {
252                emit_error!(
253                    attr.span(),
254                    "[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
255                );
256            }
257        }
258    }
259
260    proc_macro_error2::abort_if_dirty();
261
262    reprs.sort();
263    reprs.dedup();
264
265    let reprs = reprs
266        .into_iter()
267        .intersperse(String::from(","))
268        .collect::<String>();
269
270    Attributes {
271        reprs,
272        extra_bounds,
273        crate_path: crate_path.unwrap_or_else(|| syn::parse_quote!(::const_type_layout)),
274    }
275}
276
277fn meta_to_string(meta: &syn::Meta) -> String {
278    match meta {
279        syn::Meta::List(syn::MetaList { path, nested, .. }) => {
280            let mut list = nested
281                .iter()
282                .map(|meta| match meta {
283                    syn::NestedMeta::Lit(lit) => lit_to_string(lit),
284                    syn::NestedMeta::Meta(meta) => meta_to_string(meta),
285                })
286                .collect::<Vec<_>>();
287            list.sort();
288            list.dedup();
289
290            format!(
291                "{}({})",
292                quote!(#path),
293                list.into_iter()
294                    .intersperse(String::from(","))
295                    .collect::<String>()
296            )
297        },
298        syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => {
299            format!("{}={}", quote!(#path), lit_to_string(lit))
300        },
301        syn::Meta::Path(path) => quote!(#path).to_string(),
302    }
303}
304
305fn lit_to_string(lit: &syn::Lit) -> String {
306    quote!(#lit).to_string().escape_default().to_string()
307}
308
309fn extract_inner_types(data: &syn::Data) -> Vec<&syn::Type> {
310    let mut inner_types = Vec::new();
311
312    match data {
313        syn::Data::Struct(syn::DataStruct { fields, .. }) => {
314            for field in fields {
315                inner_types.push(&field.ty);
316            }
317        },
318        syn::Data::Union(syn::DataUnion {
319            fields: syn::FieldsNamed { named: fields, .. },
320            ..
321        }) => {
322            for field in fields {
323                inner_types.push(&field.ty);
324            }
325        },
326        syn::Data::Enum(syn::DataEnum { variants, .. }) => {
327            for variant in variants {
328                for field in &variant.fields {
329                    inner_types.push(&field.ty);
330                }
331            }
332        },
333    }
334
335    inner_types
336}
337
338struct Generics {
339    type_layout_input_generics: syn::Generics,
340    type_set_input_generics: syn::Generics,
341}
342
343fn generate_generics(
344    crate_path: &syn::Path,
345    generics: &syn::Generics,
346    extra_bounds: &[syn::WherePredicate],
347    type_params: &[&syn::Ident],
348) -> Generics {
349    let mut type_layout_input_generics = generics.clone();
350    let mut type_set_input_generics = generics.clone();
351
352    for ty in type_params {
353        type_layout_input_generics
354            .make_where_clause()
355            .predicates
356            .push(syn::parse_quote! {
357                #ty: #crate_path::TypeLayout
358            });
359
360        type_set_input_generics
361            .make_where_clause()
362            .predicates
363            .push(syn::parse_quote! {
364                #ty: #crate_path::typeset::ComputeTypeSet
365            });
366    }
367
368    for bound in extra_bounds {
369        type_layout_input_generics
370            .make_where_clause()
371            .predicates
372            .push(bound.clone());
373
374        type_set_input_generics
375            .make_where_clause()
376            .predicates
377            .push(bound.clone());
378    }
379
380    Generics {
381        type_layout_input_generics,
382        type_set_input_generics,
383    }
384}
385
386fn layout_of_type(
387    crate_path: &syn::Path,
388    ty_name: &syn::Ident,
389    ty_generics: &syn::TypeGenerics,
390    data: &syn::Data,
391    reprs: &str,
392) -> proc_macro2::TokenStream {
393    match data {
394        syn::Data::Struct(data) => {
395            let fields = quote_structlike_fields(crate_path, ty_name, ty_generics, &data.fields);
396
397            quote! {
398                #crate_path::TypeStructure::Struct { repr: #reprs, fields: &[#(#fields),*] }
399            }
400        },
401        syn::Data::Enum(r#enum) => {
402            let variants = quote_enum_variants(crate_path, ty_name, ty_generics, r#enum);
403
404            quote! {
405                #crate_path::TypeStructure::Enum { repr: #reprs, variants: &[#(#variants),*] }
406            }
407        },
408        syn::Data::Union(union) => {
409            let fields = quote_structlike_fields(
410                crate_path,
411                ty_name,
412                ty_generics,
413                &syn::Fields::Named(union.fields.clone()),
414            );
415
416            quote! {
417                #crate_path::TypeStructure::Union { repr: #reprs, fields: &[#(#fields),*] }
418            }
419        },
420    }
421}
422
423fn quote_structlike_fields(
424    crate_path: &syn::Path,
425    ty_name: &syn::Ident,
426    ty_generics: &syn::TypeGenerics,
427    fields: &syn::Fields,
428) -> Vec<proc_macro2::TokenStream> {
429    match fields {
430        syn::Fields::Named(fields) => fields
431            .named
432            .iter()
433            .map(|field| {
434                let field_name = field.ident.as_ref().unwrap();
435                let field_name_str = Literal::string(&field_name.to_string());
436                let field_ty = &field.ty;
437                let field_offset = quote_structlike_field_offset(
438                    crate_path,
439                    ty_name,
440                    ty_generics,
441                    &field_name,
442                    field_ty,
443                );
444
445                quote_spanned! { field.span() =>
446                    #crate_path::Field {
447                        name: #field_name_str,
448                        offset: { #field_offset },
449                        ty: ::core::any::type_name::<#field_ty>(),
450                    }
451                }
452            })
453            .collect(),
454        syn::Fields::Unnamed(fields) => fields
455            .unnamed
456            .iter()
457            .enumerate()
458            .map(|(field_index, field)| {
459                let field_name = syn::Index::from(field_index);
460                let field_name_str = Literal::string(&field_index.to_string());
461                let field_ty = &field.ty;
462                let field_offset = quote_structlike_field_offset(
463                    crate_path,
464                    ty_name,
465                    ty_generics,
466                    &field_name,
467                    field_ty,
468                );
469
470                quote_spanned! { field.span() =>
471                    #crate_path::Field {
472                        name: #field_name_str,
473                        offset: { #field_offset },
474                        ty: ::core::any::type_name::<#field_ty>(),
475                    }
476                }
477            })
478            .collect(),
479        syn::Fields::Unit => vec![],
480    }
481}
482
483fn quote_structlike_field_offset(
484    crate_path: &syn::Path,
485    ty_name: &syn::Ident,
486    ty_generics: &syn::TypeGenerics,
487    field_name: &impl quote::ToTokens,
488    field_ty: &syn::Type,
489) -> proc_macro2::TokenStream {
490    quote! {
491        #crate_path::MaybeUninhabited::new::<#field_ty>(
492            ::core::mem::offset_of!(#ty_name #ty_generics, #field_name)
493        )
494    }
495}
496
497fn quote_enum_variants(
498    crate_path: &syn::Path,
499    ty_name: &syn::Ident,
500    ty_generics: &syn::TypeGenerics,
501    r#enum: &syn::DataEnum,
502) -> Vec<proc_macro2::TokenStream> {
503    let mut last_discriminant = syn::Expr::Lit(syn::ExprLit {
504        attrs: vec![],
505        lit: syn::Lit::Int(syn::LitInt::from(proc_macro2::Literal::usize_unsuffixed(0))),
506    });
507    let mut last_discriminant_offset = 0;
508
509    r#enum
510        .variants
511        .iter()
512        .map(|variant| {
513            let variant_name = &variant.ident;
514            let variant_name_str = Literal::string(&variant_name.to_string());
515
516            let fields = quote_variant_fields(
517                crate_path,
518                ty_name,
519                ty_generics,
520                variant_name,
521                &variant.fields,
522            );
523
524            let discriminant = match variant.discriminant.as_ref() {
525                None => {
526                    let discriminant = syn::Expr::Binary(syn::ExprBinary {
527                        attrs: vec![],
528                        left: Box::new(last_discriminant.clone()),
529                        op: syn::parse_quote!(+),
530                        right: Box::new(syn::Expr::Lit(syn::ExprLit {
531                            attrs: vec![],
532                            lit: syn::Lit::Int(syn::LitInt::from(
533                                proc_macro2::Literal::usize_unsuffixed(last_discriminant_offset),
534                            )),
535                        })),
536                    });
537                    last_discriminant_offset += 1;
538                    discriminant
539                },
540                Some((_, discriminant)) => {
541                    last_discriminant = discriminant.clone();
542                    last_discriminant_offset = 0;
543                    discriminant.clone()
544                },
545            };
546
547            // Variants are inhabited if all of their fields in inhabited
548            let variant_inhabited = match &variant.fields {
549                syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
550                | syn::Fields::Unnamed(syn::FieldsUnnamed {
551                    unnamed: fields, ..
552                }) => {
553                    let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
554
555                    quote! { #crate_path::inhabited::all![#(#field_tys),*] }
556                },
557                syn::Fields::Unit => quote! { #crate_path::inhabited::Inhabited },
558            };
559
560            let discriminant = quote! {
561                #crate_path::MaybeUninhabited::new::<#variant_inhabited>(
562                    #crate_path::Discriminant::new::<Self>(#discriminant)
563                )
564            };
565
566            quote! {
567                #crate_path::Variant {
568                    name: #variant_name_str,
569                    discriminant: #discriminant,
570                    fields: &[#(#fields),*],
571                }
572            }
573        })
574        .collect::<Vec<_>>()
575}
576
577fn quote_variant_fields(
578    crate_path: &syn::Path,
579    ty_name: &syn::Ident,
580    ty_generics: &syn::TypeGenerics,
581    variant_name: &syn::Ident,
582    variant_fields: &syn::Fields,
583) -> Vec<proc_macro2::TokenStream> {
584    match variant_fields {
585        syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) => fields
586            .iter()
587            .map(|field| {
588                let field_name_str = Literal::string(&field.ident.as_ref().unwrap().to_string());
589                let field_name = &field.ident;
590                let field_ty = &field.ty;
591
592                let offset = quote_structlike_variant_field_offset(
593                    crate_path,
594                    ty_name,
595                    ty_generics,
596                    variant_name,
597                    field_name,
598                    field_ty,
599                );
600
601                quote_spanned! { field.span() =>
602                    #crate_path::Field {
603                        name: #field_name_str,
604                        offset: #offset,
605                        ty: ::core::any::type_name::<#field_ty>(),
606                    }
607                }
608            })
609            .collect(),
610        syn::Fields::Unnamed(syn::FieldsUnnamed {
611            unnamed: fields, ..
612        }) => fields
613            .iter()
614            .enumerate()
615            .map(|(field_index, field)| {
616                let field_name_str = Literal::string(&field_index.to_string());
617                let field_index = syn::Index::from(field_index);
618                let field_ty = &field.ty;
619
620                let offset = quote_structlike_variant_field_offset(
621                    crate_path,
622                    ty_name,
623                    ty_generics,
624                    variant_name,
625                    &field_index,
626                    field_ty,
627                );
628
629                quote_spanned! { field.span() =>
630                    #crate_path::Field {
631                        name: #field_name_str,
632                        offset: #offset,
633                        ty: ::core::any::type_name::<#field_ty>(),
634                    }
635                }
636            })
637            .collect(),
638        syn::Fields::Unit => vec![],
639    }
640}
641
642fn quote_structlike_variant_field_offset(
643    crate_path: &syn::Path,
644    ty_name: &syn::Ident,
645    ty_generics: &syn::TypeGenerics,
646    variant_name: &syn::Ident,
647    field_name: &impl quote::ToTokens,
648    field_ty: &syn::Type,
649) -> proc_macro2::TokenStream {
650    quote! {
651        #crate_path::MaybeUninhabited::new::<#field_ty>(
652            ::core::mem::offset_of!(#ty_name #ty_generics, #variant_name.#field_name)
653        )
654    }
655}
656
657fn inhabited_for_type(crate_path: &syn::Path, data: &syn::Data) -> proc_macro2::TokenStream {
658    match data {
659        syn::Data::Struct(data) => {
660            // Structs are inhabited if all of their fields in inhabited
661            let fields = match &data.fields {
662                syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
663                | syn::Fields::Unnamed(syn::FieldsUnnamed {
664                    unnamed: fields, ..
665                }) => fields,
666                syn::Fields::Unit => return quote! { #crate_path::inhabited::Inhabited },
667            };
668
669            let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
670
671            quote! { #crate_path::inhabited::all![#(#field_tys),*] }
672        },
673        syn::Data::Enum(syn::DataEnum { variants, .. }) => {
674            let variants_inhabited = variants.iter().map(|syn::Variant { fields, .. }| {
675                // Variants are inhabited if all of their fields in inhabited
676                let fields = match fields {
677                    syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
678                    | syn::Fields::Unnamed(syn::FieldsUnnamed {
679                        unnamed: fields, ..
680                    }) => fields,
681                    syn::Fields::Unit => return quote! { #crate_path::inhabited::Inhabited },
682                };
683
684                let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
685
686                quote! { #crate_path::inhabited::all![#(#field_tys),*] }
687            });
688
689            // Enums are inhabited if they have at least one inhabited variant
690            quote! {
691                #crate_path::inhabited::any![#(#variants_inhabited),*]
692            }
693        },
694        syn::Data::Union(syn::DataUnion {
695            fields: syn::FieldsNamed { named: fields, .. },
696            ..
697        }) => {
698            // Unions are inhabited if they have at least one inhabited field
699            let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
700
701            quote! { #crate_path::inhabited::any![#(#field_tys),*] }
702        },
703    }
704}