Skip to main content

rust_spec_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::{ToTokens, format_ident, quote};
3use syn::{Token, punctuated::Punctuated};
4
5use crate::repr::{ReprKind, infer_repr, is_exhaustive_enum, parse_repr};
6
7mod repr;
8
9/// Derives `RustSpec` for a struct, enum, or union.
10///
11/// Add `#[rust_spec(custom_drop)]` if the type itself implements [`Drop`].
12/// Add `#[rust_spec(custom_niche)]` if a struct has a custom niche.
13#[proc_macro_derive(RustSpec, attributes(rust_spec))]
14pub fn rust_spec(item: TokenStream) -> TokenStream {
15    let input = syn::parse_macro_input!(item as syn::DeriveInput);
16    expand(&input)
17        .unwrap_or_else(syn::Error::into_compile_error)
18        .into()
19}
20
21fn crate_path() -> proc_macro2::TokenStream {
22    let rust_spec = proc_macro_crate::crate_name("rust-spec");
23    match rust_spec {
24        Ok(proc_macro_crate::FoundCrate::Itself) => return quote! { rust_spec },
25        Ok(proc_macro_crate::FoundCrate::Name(name)) => {
26            let name = format_ident!("{}", name);
27            return quote! { #name };
28        }
29        Err(_) => {}
30    }
31
32    match proc_macro_crate::crate_name("co3") {
33        Ok(proc_macro_crate::FoundCrate::Itself) => quote! { crate::rust_spec },
34        Ok(proc_macro_crate::FoundCrate::Name(name)) => {
35            let name = format_ident!("{}", name);
36            quote! { #name::rust_spec }
37        }
38        Err(_) => quote! { co3::rust_spec },
39    }
40}
41
42struct AggregateFamily {
43    kind: proc_macro2::TokenStream,
44    aggregate_bounds: Vec<proc_macro2::TokenStream>,
45}
46
47struct TypeSpecFamilies {
48    layout: AggregateFamily,
49    size: AggregateFamily,
50    alignment: AggregateFamily,
51    trap: AggregateFamily,
52    niche: AggregateFamily,
53    drop: AggregateFamily,
54    mutability: AggregateFamily,
55    indirect_trap: AggregateFamily,
56}
57
58impl AggregateFamily {
59    fn fixed(kind: proc_macro2::TokenStream) -> Self {
60        Self {
61            kind,
62            aggregate_bounds: Vec::new(),
63        }
64    }
65
66    fn aggregate_parts(
67        operator: proc_macro2::TokenStream,
68        axis: proc_macro2::TokenStream,
69        generics: &syn::Generics,
70        fields: &[&syn::Type],
71    ) -> (
72        Option<proc_macro2::TokenStream>,
73        Option<proc_macro2::TokenStream>,
74        Vec<proc_macro2::TokenStream>,
75    ) {
76        let mut aggregate_bounds = Vec::new();
77
78        let (concrete_fields, parameterized_fields): (Vec<_>, Vec<_>) = fields
79            .iter()
80            .enumerate()
81            .map(|(index, &field)| (index, field))
82            .partition(|(_, field)| !field_needs_bounds(field, generics));
83
84        let mut concrete_fields = concrete_fields.into_iter();
85        let concrete_kind = concrete_fields.next().map(|(index, field)| {
86            let mut kind = field_axis_kind(field, index, &axis, generics);
87
88            for (index, field) in concrete_fields {
89                let field_kind = field_axis_kind(field, index, &axis, generics);
90                kind = quote! { <#kind as #operator<#field_kind>>::Output };
91            }
92
93            kind
94        });
95
96        let mut parameterized_fields = parameterized_fields.into_iter();
97        let parameterized_kind = parameterized_fields.next().map(|(index, field)| {
98            let mut kind = field_axis_kind(field, index, &axis, generics);
99
100            for (index, field) in parameterized_fields {
101                let field_kind = field_axis_kind(field, index, &axis, generics);
102
103                aggregate_bounds.push(quote! { #kind: #operator<#field_kind> });
104                kind = quote! { <#kind as #operator<#field_kind>>::Output };
105            }
106
107            kind
108        });
109
110        (concrete_kind, parameterized_kind, aggregate_bounds)
111    }
112
113    fn aggregate(
114        operator: proc_macro2::TokenStream,
115        axis: proc_macro2::TokenStream,
116        generics: &syn::Generics,
117        fields: &[&syn::Type],
118    ) -> Option<Self> {
119        let (concrete_kind, parameterized_kind, mut aggregate_bounds) =
120            Self::aggregate_parts(operator.clone(), axis, generics, fields);
121        let kind = match (concrete_kind, parameterized_kind) {
122            (Some(concrete), Some(parameterized)) => {
123                aggregate_bounds.push(quote! { #concrete: #operator<#parameterized> });
124
125                quote! { <#concrete as #operator<#parameterized>>::Output }
126            }
127            (Some(concrete), None) => concrete,
128            (None, Some(parameterized)) => parameterized,
129            (None, None) => return None,
130        };
131
132        Some(Self {
133            kind,
134            aggregate_bounds,
135        })
136    }
137
138    fn aggregate_with_seed(
139        operator: proc_macro2::TokenStream,
140        axis: proc_macro2::TokenStream,
141        seed: proc_macro2::TokenStream,
142        generics: &syn::Generics,
143        fields: &[&syn::Type],
144    ) -> Self {
145        let (concrete_kind, parameterized_kind, mut aggregate_bounds) =
146            Self::aggregate_parts(operator.clone(), axis, generics, fields);
147
148        let kind = match (concrete_kind, parameterized_kind) {
149            (Some(concrete), Some(parameterized)) => {
150                aggregate_bounds.push(
151                    quote! { <#seed as #operator<#concrete>>::Output: #operator<#parameterized> },
152                );
153                quote! {
154                    <<#seed as #operator<#concrete>>::Output as #operator<#parameterized>>::Output
155                }
156            }
157            (Some(concrete), None) => quote! { <#seed as #operator<#concrete>>::Output },
158            (None, Some(parameterized)) => {
159                aggregate_bounds.push(quote! { #seed: #operator<#parameterized> });
160                quote! { <#seed as #operator<#parameterized>>::Output }
161            }
162            (None, None) => return Self::fixed(seed),
163        };
164
165        Self {
166            kind,
167            aggregate_bounds,
168        }
169    }
170}
171
172fn hrtb_projection_bound(field: &syn::Type, generics: &syn::Generics) -> Vec<syn::WherePredicate> {
173    use syn::visit::Visit;
174
175    #[derive(Default)]
176    struct ProjectionVisitor<'a> {
177        projections: Vec<&'a syn::TypePath>,
178    }
179
180    impl<'ast> Visit<'ast> for ProjectionVisitor<'ast> {
181        fn visit_type_path(&mut self, type_path: &'ast syn::TypePath) {
182            if type_path.qself.is_some() {
183                self.projections.push(type_path);
184                for segment in &type_path.path.segments {
185                    self.visit_path_arguments(&segment.arguments);
186                }
187                return;
188            }
189            syn::visit::visit_type_path(self, type_path);
190        }
191    }
192
193    let mut visitor = ProjectionVisitor::default();
194    visitor.visit_type(field);
195    let crate_ = crate_path();
196
197    let mut bounds = Vec::new();
198    for projection in &visitor.projections {
199        let Some(qself) = projection.qself.as_ref() else {
200            continue;
201        };
202        let trait_path = syn::Path {
203            leading_colon: projection.path.leading_colon,
204            segments: projection
205                .path
206                .segments
207                .iter()
208                .take(qself.position)
209                .cloned()
210                .collect(),
211        };
212
213        for predicate in generics
214            .where_clause
215            .iter()
216            .flat_map(|where_clause| &where_clause.predicates)
217        {
218            let syn::WherePredicate::Type(predicate) = predicate else {
219                continue;
220            };
221            if predicate.lifetimes.is_none()
222                || predicate.bounded_ty.to_token_stream().to_string()
223                    != qself.ty.to_token_stream().to_string()
224            {
225                continue;
226            }
227
228            let Some(bound_index) = predicate.bounds.iter().position(|bound| {
229                matches!(bound, syn::TypeParamBound::Trait(bound) if bound.path.to_token_stream().to_string() == trait_path.to_token_stream().to_string())
230            }) else {
231                continue;
232            };
233            let Some(associated_type) = projection.path.segments.iter().nth(qself.position) else {
234                continue;
235            };
236            let mut predicate = predicate.clone();
237            let syn::TypeParamBound::Trait(bound) = &mut predicate.bounds[bound_index] else {
238                unreachable!("matched a trait bound");
239            };
240            *bound = syn::parse_quote! { #trait_path<#associated_type: #crate_::RustSpec> };
241
242            bounds.push(syn::WherePredicate::Type(predicate));
243        }
244    }
245
246    bounds
247}
248
249fn hrtb_lifetimes(field: &syn::Type, generics: &syn::Generics) -> Vec<syn::BoundLifetimes> {
250    hrtb_projection_bound(field, generics)
251        .into_iter()
252        .filter_map(|predicate| match predicate {
253            syn::WherePredicate::Type(predicate) => predicate.lifetimes,
254            _ => None,
255        })
256        .collect()
257}
258
259fn field_axis_kind(
260    field: &syn::Type,
261    index: usize,
262    axis: &proc_macro2::TokenStream,
263    generics: &syn::Generics,
264) -> proc_macro2::TokenStream {
265    let crate_ = crate_path();
266
267    if !hrtb_projection_bound(field, generics).is_empty() {
268        return quote! { <Self as #crate_::__HrtbAxes<#index>>::#axis<'static> };
269    }
270
271    quote! { <#field as #crate_::RustSpec>::#axis }
272}
273
274fn hrtb_axes_impl(
275    name: &syn::Ident,
276    generics: &syn::Generics,
277    field: &syn::Type,
278    index: usize,
279) -> Option<proc_macro2::TokenStream> {
280    let hrtb_bounds = hrtb_projection_bound(field, generics);
281
282    let crate_ = crate_path();
283    if hrtb_bounds.is_empty() {
284        return None;
285    }
286
287    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
288    let predicates = where_clause.map(|w| &w.predicates);
289
290    Some(quote! {
291        #[doc(hidden)]
292        impl #impl_generics #crate_::__HrtbAxes<#index> for #name #ty_generics
293        where
294            #(#hrtb_bounds,)*
295            #predicates
296        {
297            type Layout<'__rust_spec> = <#field as #crate_::RustSpec>::Layout;
298            type Size<'__rust_spec> = <#field as #crate_::RustSpec>::Size;
299            type Alignment<'__rust_spec> = <#field as #crate_::RustSpec>::Alignment;
300            type Trap<'__rust_spec> = <#field as #crate_::RustSpec>::Trap;
301            type Niche<'__rust_spec> = <#field as #crate_::RustSpec>::Niche;
302            type Drop<'__rust_spec> = <#field as #crate_::RustSpec>::Drop;
303            type Mutability<'__rust_spec> = <#field as #crate_::RustSpec>::Mutability;
304            type __IndirectTrap<'__rust_spec> = <#field as #crate_::RustSpec>::__IndirectTrap;
305        }
306    })
307}
308
309fn field_has_type_params(ty: &syn::Type, generics: &syn::Generics) -> bool {
310    use syn::visit::Visit;
311
312    struct Visitor<'a> {
313        type_params: Vec<&'a syn::Ident>,
314        found: bool,
315    }
316
317    impl<'a> Visitor<'a> {
318        fn new(generics: &'a syn::Generics) -> Self {
319            Self {
320                type_params: generics.type_params().map(|p| &p.ident).collect(),
321                found: false,
322            }
323        }
324    }
325
326    impl syn::visit::Visit<'_> for Visitor<'_> {
327        fn visit_type_path(&mut self, type_path: &syn::TypePath) {
328            if type_path.qself.is_none()
329                && let Some(first_segment) = type_path.path.segments.first()
330                && self.type_params.contains(&&first_segment.ident)
331            {
332                self.found = true;
333            }
334
335            syn::visit::visit_type_path(self, type_path);
336        }
337
338        // Raw pointers have fixed classifications and do not follow their pointees.
339        fn visit_type_ptr(&mut self, _: &syn::TypePtr) {}
340    }
341
342    let mut visitor = Visitor::new(generics);
343    visitor.visit_type(ty);
344    visitor.found
345}
346
347fn field_needs_bounds(field: &syn::Type, generics: &syn::Generics) -> bool {
348    field_has_type_params(field, generics) || !hrtb_projection_bound(field, generics).is_empty()
349}
350
351fn expand(input: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
352    let attrs = parse_rust_spec_attrs(&input.attrs)?;
353    let repr = parse_repr(&input.attrs)?;
354    let alignment = repr.align;
355    let repr = repr.kind.as_ref();
356    let name = &input.ident;
357    let generics = &input.generics;
358    if attrs.opaque {
359        let crate_ = crate_path();
360        let (opaque_impl_generics, opaque_ty_generics, opaque_where_clause) =
361            generics.split_for_impl();
362        let mut rust_spec_generics = generics.clone();
363        rust_spec_generics
364            .make_where_clause()
365            .predicates
366            .push(syn::parse_quote!(Self: Sized));
367        let (impl_generics, ty_generics, where_clause) = rust_spec_generics.split_for_impl();
368        return Ok(quote! {
369            unsafe impl #opaque_impl_generics #crate_::Opaque for #name #opaque_ty_generics #opaque_where_clause {}
370
371            unsafe impl #impl_generics #crate_::RustSpec for #name #ty_generics #where_clause {
372                type Layout = #crate_::Stable;
373                type Size = #crate_::size::Sized<#crate_::Gt<#crate_::Zero>>;
374                type Alignment = #crate_::One;
375                type Trap = #crate_::layout::Robust;
376                type Niche = #crate_::niche::WithoutNiche;
377                type Drop = #crate_::drop::NoDrop;
378                type Mutability = #crate_::mutability::Exclusive;
379                type __IndirectTrap = #crate_::layout::Robust;
380            }
381        });
382    }
383    if attrs.custom_niche && !matches!(&input.data, syn::Data::Struct(_)) {
384        return Err(syn::Error::new_spanned(
385            name,
386            "`custom_niche` is only supported on structs",
387        ));
388    }
389    let rust_spec_impl = match &input.data {
390        syn::Data::Struct(data) => {
391            gen_struct_impl(repr, alignment, name, generics, &data.fields, attrs)
392        }
393        syn::Data::Enum(data) if is_fieldless_enum(data) => {
394            gen_fieldless_enum_impl(repr, alignment, name, generics, &data.variants, attrs)
395        }
396        syn::Data::Enum(data)
397            if matches!(repr, Some(ReprKind::Transparent))
398                || repr.is_none() && data.variants.len() == 1 =>
399        {
400            let Some(variant) = data.variants.first() else {
401                return Ok(quote! {});
402            };
403
404            let fields = field_types(&variant.fields);
405            gen_struct_fields_impl(
406                repr,
407                alignment,
408                name,
409                generics,
410                &fields,
411                attrs,
412                gen_exclusive_mutability_family(),
413            )
414        }
415        syn::Data::Enum(data) => {
416            gen_enum_impl(repr, alignment, name, generics, &data.variants, attrs)
417        }
418        syn::Data::Union(data) if matches!(repr, Some(ReprKind::Transparent)) => {
419            let fields = data
420                .fields
421                .named
422                .iter()
423                .map(|field| &field.ty)
424                .collect::<Vec<_>>();
425
426            gen_struct_fields_impl(
427                Some(&ReprKind::Transparent),
428                alignment,
429                name,
430                generics,
431                &fields,
432                attrs,
433                gen_mutability_family(generics, &fields),
434            )
435        }
436        syn::Data::Union(data) => {
437            gen_union_impl(repr, alignment, name, generics, &data.fields, attrs)
438        }
439    };
440
441    let drop_assert = gen_drop_assert(name, generics, attrs.custom_drop);
442    Ok(quote! { #drop_assert #rust_spec_impl })
443}
444
445fn gen_drop_assert(
446    name: &syn::Ident,
447    generics: &syn::Generics,
448    custom_drop: bool,
449) -> proc_macro2::TokenStream {
450    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
451    if custom_drop {
452        quote! {
453            const _: () = {
454                trait AssertHasDrop { fn check(); }
455                impl #impl_generics AssertHasDrop for #name #ty_generics #where_clause {
456                    fn check() {
457                        fn require_drop<T: core::ops::Drop>() {}
458                        let _ = require_drop::<#name #ty_generics>;
459                    }
460                }
461            };
462        }
463    } else {
464        quote! {
465            const _: () = {
466                trait AssertNoDrop { fn check(); }
467                impl #impl_generics AssertNoDrop for #name #ty_generics #where_clause {
468                    fn check() {
469                        trait DropImplIsNotSupported<A> { fn check() {} }
470                        impl<T: ?Sized> DropImplIsNotSupported<()> for T {}
471                        struct DropDetected;
472                        impl<T: ?Sized + core::ops::Drop> DropImplIsNotSupported<DropDetected> for T {}
473                        let _ = <#name #ty_generics as DropImplIsNotSupported<_>>::check;
474                    }
475                }
476            };
477        }
478    }
479}
480
481#[derive(Clone, Copy, Default)]
482struct RustSpecAttrs {
483    custom_niche: bool,
484    custom_drop: bool,
485    opaque: bool,
486}
487
488fn parse_rust_spec_attrs(attrs: &[syn::Attribute]) -> syn::Result<RustSpecAttrs> {
489    let mut result = RustSpecAttrs::default();
490
491    for attr in attrs
492        .iter()
493        .filter(|attr| attr.path().is_ident("rust_spec"))
494    {
495        attr.parse_nested_meta(|meta| {
496            if meta.path.is_ident("opaque") {
497                if result.opaque {
498                    return Err(meta.error("duplicate `opaque` within attribute"));
499                }
500                result.opaque = true;
501                return Ok(());
502            }
503            if meta.path.is_ident("custom_niche") {
504                if result.custom_niche {
505                    return Err(meta.error("duplicate `custom_niche` within attribute"));
506                }
507                result.custom_niche = true;
508                return Ok(());
509            }
510            if meta.path.is_ident("custom_drop") {
511                if result.custom_drop {
512                    return Err(meta.error("duplicate `custom_drop` within attribute"));
513                }
514                result.custom_drop = true;
515                return Ok(());
516            }
517            Err(meta.error("unknown rust_spec attribute"))
518        })?;
519    }
520
521    Ok(result)
522}
523
524fn is_fieldless_enum(data: &syn::DataEnum) -> bool {
525    data.variants
526        .iter()
527        .all(|variant| matches!(variant.fields, syn::Fields::Unit))
528}
529
530fn field_types(fields: &syn::Fields) -> Vec<&syn::Type> {
531    fields.iter().map(|field| &field.ty).collect()
532}
533
534fn variant_field_types(variants: &Punctuated<syn::Variant, Token![,]>) -> Vec<&syn::Type> {
535    variants
536        .iter()
537        .flat_map(|variant| field_types(&variant.fields))
538        .collect()
539}
540
541fn gen_struct_impl(
542    repr: Option<&ReprKind>,
543    repr_alignment: Option<usize>,
544    name: &syn::Ident,
545    generics: &syn::Generics,
546    fields: &syn::Fields,
547    attrs: RustSpecAttrs,
548) -> proc_macro2::TokenStream {
549    let fields = field_types(fields);
550
551    let mutability = gen_mutability_family(generics, &fields);
552    gen_struct_fields_impl(
553        repr,
554        repr_alignment,
555        name,
556        generics,
557        &fields,
558        attrs,
559        mutability,
560    )
561}
562
563fn gen_struct_fields_impl(
564    repr: Option<&ReprKind>,
565    alignment: Option<usize>,
566    name: &syn::Ident,
567    generics: &syn::Generics,
568    fields: &[&syn::Type],
569    attrs: RustSpecAttrs,
570    mutability: AggregateFamily,
571) -> proc_macro2::TokenStream {
572    let layout = if repr.is_some() {
573        gen_stable_layout_family(generics, fields)
574    } else {
575        gen_unstable_layout_family()
576    };
577    let size = gen_size_family(generics, fields);
578    let alignment = apply_repr_alignment(gen_alignment_family(generics, fields, None), alignment);
579    let trap = gen_trap_family(generics, fields, false);
580    let niche = if attrs.custom_niche {
581        let crate_ = crate_path();
582        AggregateFamily::fixed(quote! { #crate_::niche::WithNiche<#crate_::Unstable> })
583    } else if let Some(ReprKind::Transparent) = repr {
584        gen_transparent_niche_family(generics, fields)
585    } else {
586        gen_niche_family(generics, fields)
587    };
588
589    let drop = gen_drop_family(generics, fields, attrs.custom_drop);
590    let indirect_trap = gen_indirect_trap_family(generics, fields);
591
592    let spec = TypeSpecFamilies {
593        layout,
594        size,
595        alignment,
596        trap,
597        niche,
598        drop,
599        mutability,
600        indirect_trap,
601    };
602
603    gen_type_spec_impl(name, generics, fields, spec, quote! {})
604}
605
606fn gen_enum_impl(
607    repr: Option<&ReprKind>,
608    repr_alignment: Option<usize>,
609    name: &syn::Ident,
610    generics: &syn::Generics,
611    variants: &Punctuated<syn::Variant, Token![,]>,
612    attrs: RustSpecAttrs,
613) -> proc_macro2::TokenStream {
614    if matches!(repr, Some(ReprKind::C(None))) {
615        return gen_plain_c_enum_impls(repr_alignment, name, generics, variants, attrs);
616    }
617
618    let crate_ = crate_path();
619    let fields = variant_field_types(variants);
620    let has_trap_tag_values = match repr {
621        None => rust_tag_has_traps(variants.len()),
622        Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
623            primitive_tag_has_traps(tag, variants.len())
624        }
625        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
626        Some(ReprKind::Transparent) => false,
627    };
628    let layout = if repr.is_some() {
629        gen_enum_layout_family(generics, &fields)
630    } else {
631        gen_unstable_layout_family()
632    };
633    let size = AggregateFamily::fixed(quote! {
634        #crate_::size::Sized<#crate_::Gt<#crate_::Zero>>
635    });
636    let tag = match repr {
637        Some(ReprKind::Primitive(tag) | ReprKind::C(Some(tag))) => Some(tag.as_ref().clone()),
638        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
639        Some(ReprKind::Transparent) => None,
640        None => Some(infer_repr(variants.len())),
641    };
642    let alignment = apply_repr_alignment(
643        gen_alignment_family(generics, &fields, tag.as_ref()),
644        repr_alignment,
645    );
646    let trap = gen_trap_family(generics, &fields, has_trap_tag_values);
647
648    let niche = gen_enum_niche_family(has_trap_tag_values);
649    let drop = gen_drop_family(generics, &fields, attrs.custom_drop);
650    let mutability = gen_exclusive_mutability_family();
651    let indirect_trap = gen_indirect_trap_family(generics, &fields);
652
653    let spec = TypeSpecFamilies {
654        layout,
655        size,
656        alignment,
657        trap,
658        niche,
659        drop,
660        mutability,
661        indirect_trap,
662    };
663
664    gen_type_spec_impl(name, generics, &fields, spec, quote! {})
665}
666
667fn gen_union_impl(
668    repr: Option<&ReprKind>,
669    repr_alignment: Option<usize>,
670    name: &syn::Ident,
671    generics: &syn::Generics,
672    fields: &syn::FieldsNamed,
673    attrs: RustSpecAttrs,
674) -> proc_macro2::TokenStream {
675    let crate_ = crate_path();
676
677    let fields = fields
678        .named
679        .iter()
680        .map(|field| &field.ty)
681        .collect::<Vec<_>>();
682
683    let layout = if repr.is_some() {
684        gen_stable_layout_family(generics, &fields)
685    } else {
686        gen_unstable_layout_family()
687    };
688    let size = gen_size_family(generics, &fields);
689    let alignment = apply_repr_alignment(
690        gen_alignment_family(generics, &fields, None),
691        repr_alignment,
692    );
693    let trap = AggregateFamily::fixed(quote! {
694        #crate_::layout::Robust
695    });
696    let niche = AggregateFamily::fixed(quote! {
697        #crate_::niche::WithoutNiche
698    });
699    let drop = gen_drop_family(generics, &fields, attrs.custom_drop);
700
701    let mutability = gen_mutability_family(generics, &fields);
702    let indirect_trap = AggregateFamily::fixed(quote! {
703        #crate_::layout::Robust
704    });
705
706    let spec = TypeSpecFamilies {
707        layout,
708        size,
709        alignment,
710        trap,
711        niche,
712        drop,
713        mutability,
714        indirect_trap,
715    };
716
717    gen_type_spec_impl(name, generics, &fields, spec, quote! {})
718}
719
720fn gen_fieldless_enum_impl(
721    repr: Option<&ReprKind>,
722    repr_alignment: Option<usize>,
723    name: &syn::Ident,
724    generics: &syn::Generics,
725    variants: &Punctuated<syn::Variant, Token![,]>,
726    attrs: RustSpecAttrs,
727) -> proc_macro2::TokenStream {
728    if matches!(repr, Some(ReprKind::C(None))) {
729        return gen_plain_c_enum_impls(repr_alignment, name, generics, variants, attrs);
730    }
731
732    let crate_ = crate_path();
733    let layout_kind = match repr {
734        None => quote! { #crate_::Unstable },
735        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
736        Some(ReprKind::Transparent) => quote! {
737            #crate_::Stable
738        },
739        Some(ReprKind::C(Some(_)) | ReprKind::Primitive(_)) => {
740            quote! { #crate_::Stable }
741        }
742    };
743    let has_tag = !variants.is_empty()
744        && match repr {
745            Some(ReprKind::C(_) | ReprKind::Primitive(_)) => true,
746            Some(ReprKind::Transparent) | None => variants.len() > 1,
747        };
748
749    let size = if has_tag {
750        AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::Gt<#crate_::Zero>> })
751    } else {
752        AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::size::Zero> })
753    };
754
755    let niche = if has_tag {
756        let has_trap_tag_values = match repr {
757            None => rust_tag_has_traps(variants.len()),
758            Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
759                primitive_tag_has_traps(tag, variants.len())
760            }
761            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
762            Some(ReprKind::Transparent) => false,
763        };
764        gen_enum_niche_family(has_trap_tag_values)
765    } else {
766        AggregateFamily::fixed(quote! { #crate_::niche::WithoutNiche })
767    };
768
769    let has_trap_tag_values = has_tag
770        && match repr {
771            None => rust_tag_has_traps(variants.len()),
772            Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
773                primitive_tag_has_traps(tag, variants.len())
774            }
775            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
776            Some(ReprKind::Transparent) => false,
777        };
778    let layout = AggregateFamily::fixed(layout_kind);
779    let alignment = if has_tag {
780        let tag = match repr {
781            Some(ReprKind::Primitive(tag) | ReprKind::C(Some(tag))) => tag.as_ref().clone(),
782            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
783            Some(ReprKind::Transparent) => infer_repr(variants.len()),
784            None => infer_repr(variants.len()),
785        };
786        gen_alignment_family(generics, &[], Some(&tag))
787    } else {
788        gen_alignment_family(generics, &[], None)
789    };
790    let alignment = apply_repr_alignment(alignment, repr_alignment);
791    let trap = AggregateFamily::fixed(if has_trap_tag_values {
792        quote! { #crate_::layout::NonRobust }
793    } else {
794        quote! { #crate_::layout::Robust }
795    });
796    let mutability = gen_exclusive_mutability_family();
797    let drop = gen_drop_family(generics, &[], attrs.custom_drop);
798    let indirect_trap = AggregateFamily::fixed(quote! { #crate_::layout::Robust });
799
800    let spec = TypeSpecFamilies {
801        layout,
802        size,
803        alignment,
804        trap,
805        niche,
806        drop,
807        mutability,
808        indirect_trap,
809    };
810
811    gen_type_spec_impl(name, generics, &[], spec, quote! {})
812}
813
814fn gen_unstable_layout_family() -> AggregateFamily {
815    let crate_ = crate_path();
816    AggregateFamily::fixed(quote! { #crate_::Unstable })
817}
818
819fn gen_stable_layout_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
820    let crate_ = crate_path();
821
822    AggregateFamily::aggregate(
823        quote! { core::ops::Add },
824        quote! { Layout },
825        generics,
826        fields,
827    )
828    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::Stable }))
829}
830
831fn gen_enum_layout_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
832    gen_stable_layout_family(generics, fields)
833}
834
835fn gen_plain_c_enum_impls(
836    repr_alignment: Option<usize>,
837    name: &syn::Ident,
838    generics: &syn::Generics,
839    variants: &Punctuated<syn::Variant, Token![,]>,
840    attrs: RustSpecAttrs,
841) -> proc_macro2::TokenStream {
842    let crate_ = crate_path();
843    let fields = variant_field_types(variants);
844
845    let eight_bit_targets = quote! {
846        #[cfg(all(
847            target_arch = "arm",
848            any(target_os = "none", target_os = "nuttx", target_os = "rtems"),
849        ))]
850    };
851    let sixteen_bit_targets = quote! {
852        #[cfg(any(target_arch = "avr", target_arch = "msp430"))]
853    };
854    let normal_targets = quote! {
855        #[cfg(not(any(
856            all(
857                target_arch = "arm",
858                any(target_os = "none", target_os = "nuttx", target_os = "rtems"),
859            ),
860            target_arch = "avr",
861            target_arch = "msp430",
862        )))]
863    };
864
865    [
866        (eight_bit_targets, 8),
867        (sixteen_bit_targets, 16),
868        (normal_targets, 32),
869    ]
870    .into_iter()
871    .map(|(cfg, bits)| {
872        let tag = c_tag_type(bits);
873        let has_trap_tag_values = primitive_tag_has_traps(&tag, variants.len());
874        let layout = gen_stable_layout_family(generics, &fields);
875        let niche = gen_enum_niche_family(has_trap_tag_values);
876        let spec = TypeSpecFamilies {
877            layout,
878            size: AggregateFamily::fixed(quote! {
879                #crate_::size::Sized<#crate_::Gt<#crate_::Zero>>
880            }),
881            alignment: apply_repr_alignment(
882                gen_alignment_family(generics, &fields, Some(&tag)),
883                repr_alignment,
884            ),
885            trap: gen_trap_family(generics, &fields, has_trap_tag_values),
886            niche,
887            drop: gen_drop_family(generics, &fields, attrs.custom_drop),
888            mutability: gen_exclusive_mutability_family(),
889            indirect_trap: gen_indirect_trap_family(generics, &fields),
890        };
891
892        gen_type_spec_impl(name, generics, &fields, spec, cfg)
893    })
894    .collect()
895}
896
897fn gen_indirect_trap_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
898    let crate_ = crate_path();
899
900    AggregateFamily::aggregate(
901        quote! { core::ops::Add },
902        quote! { __IndirectTrap },
903        generics,
904        fields,
905    )
906    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::layout::Robust }))
907}
908
909fn gen_drop_family(
910    generics: &syn::Generics,
911    fields: &[&syn::Type],
912    custom_drop: bool,
913) -> AggregateFamily {
914    let crate_ = crate_path();
915    let seed = if custom_drop {
916        quote! { #crate_::drop::CustomDrop<#crate_::drop::NoDrop> }
917    } else {
918        quote! { #crate_::drop::NoDrop }
919    };
920    AggregateFamily::aggregate_with_seed(
921        quote! { core::ops::Add },
922        quote! { Drop },
923        seed,
924        generics,
925        fields,
926    )
927}
928
929fn gen_trap_family(
930    generics: &syn::Generics,
931    fields: &[&syn::Type],
932    has_trap_values: bool,
933) -> AggregateFamily {
934    let crate_ = crate_path();
935
936    let init = if has_trap_values {
937        quote! { #crate_::layout::NonRobust }
938    } else {
939        quote! { #crate_::layout::Robust }
940    };
941
942    AggregateFamily::aggregate_with_seed(
943        quote! { core::ops::Add },
944        quote! { Trap },
945        init,
946        generics,
947        fields,
948    )
949}
950
951fn gen_size_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
952    let crate_ = crate_path();
953
954    AggregateFamily::aggregate(quote! { core::ops::Add }, quote! { Size }, generics, fields)
955        .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::Zero> }))
956}
957
958fn gen_alignment_family(
959    generics: &syn::Generics,
960    fields: &[&syn::Type],
961    tag: Option<&syn::Type>,
962) -> AggregateFamily {
963    let crate_ = crate_path();
964
965    let mut family = AggregateFamily::aggregate(
966        quote! { #crate_::Max },
967        quote! { Alignment },
968        generics,
969        fields,
970    )
971    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::One }));
972
973    if let Some(tag) = tag {
974        let kind = family.kind;
975
976        let tag_kind = quote! { <#tag as #crate_::RustSpec>::Alignment };
977        family.kind = quote! { <#tag_kind as #crate_::Max<#kind>>::Output };
978    }
979
980    family
981}
982
983fn apply_repr_alignment(mut family: AggregateFamily, align: Option<usize>) -> AggregateFamily {
984    let crate_ = crate_path();
985
986    if matches!(align, Some(value) if value > 1) {
987        let minimum = quote! { #crate_::Gt<#crate_::One> };
988        let kind = family.kind;
989        family.kind = quote! { <#minimum as #crate_::Max<#kind>>::Output };
990    }
991
992    family
993}
994
995fn gen_niche_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
996    let crate_ = crate_path();
997
998    AggregateFamily::aggregate_with_seed(
999        quote! { core::ops::Add },
1000        quote! { Niche },
1001        quote! { #crate_::niche::WithoutNiche },
1002        generics,
1003        fields,
1004    )
1005}
1006
1007fn gen_transparent_niche_family(
1008    generics: &syn::Generics,
1009    fields: &[&syn::Type],
1010) -> AggregateFamily {
1011    match fields {
1012        [] => {
1013            let crate_ = crate_path();
1014            AggregateFamily::fixed(quote! { #crate_::niche::WithoutNiche })
1015        }
1016        [field] => AggregateFamily::fixed(field_axis_kind(field, 0, &quote! { Niche }, generics)),
1017        _ => gen_niche_family(generics, fields),
1018    }
1019}
1020
1021fn gen_mutability_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
1022    AggregateFamily::aggregate(
1023        quote! { core::ops::Add },
1024        quote! { Mutability },
1025        generics,
1026        fields,
1027    )
1028    .unwrap_or_else(gen_exclusive_mutability_family)
1029}
1030
1031fn gen_exclusive_mutability_family() -> AggregateFamily {
1032    let crate_ = crate_path();
1033    AggregateFamily::fixed(quote! { #crate_::mutability::Exclusive })
1034}
1035
1036fn gen_type_spec_impl(
1037    name: &syn::Ident,
1038    generics: &syn::Generics,
1039    fields: &[&syn::Type],
1040    families: TypeSpecFamilies,
1041    attrs: proc_macro2::TokenStream,
1042) -> proc_macro2::TokenStream {
1043    let TypeSpecFamilies {
1044        layout,
1045        size,
1046        alignment,
1047        trap,
1048        niche,
1049        drop,
1050        mutability,
1051        indirect_trap,
1052    } = families;
1053
1054    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1055    let predicates = where_clause
1056        .as_ref()
1057        .map(|where_clause| &where_clause.predicates);
1058
1059    let crate_ = crate_path();
1060    let hrtb_axes = fields
1061        .iter()
1062        .filter_map(|field| {
1063            let lifetimes = hrtb_lifetimes(field, generics);
1064            (!lifetimes.is_empty()).then_some(lifetimes)
1065        })
1066        .collect::<Vec<_>>();
1067
1068    let field_bounds = fields.iter().enumerate().flat_map(|(index, field)| {
1069        if field_has_type_params(field, generics) {
1070            vec![quote! { #field: #crate_::RustSpec }]
1071        } else {
1072            hrtb_lifetimes(field, generics)
1073                .into_iter()
1074                .map(|lifetimes| quote! { #lifetimes Self: #crate_::__HrtbAxes<#index> })
1075                .collect()
1076        }
1077    });
1078
1079    let hrtb_axes_impls = fields
1080        .iter()
1081        .enumerate()
1082        .filter_map(|(index, field)| hrtb_axes_impl(name, generics, field, index));
1083
1084    let aggregate_bounds = layout
1085        .aggregate_bounds
1086        .into_iter()
1087        .chain(size.aggregate_bounds)
1088        .chain(alignment.aggregate_bounds)
1089        .chain(trap.aggregate_bounds)
1090        .chain(niche.aggregate_bounds)
1091        .chain(drop.aggregate_bounds)
1092        .chain(mutability.aggregate_bounds)
1093        .chain(indirect_trap.aggregate_bounds)
1094        .flat_map(|bound| {
1095            if !bound.to_string().contains("__HrtbAxes") {
1096                return vec![bound.clone()];
1097            }
1098
1099            hrtb_axes
1100                .iter()
1101                .flatten()
1102                .map(|lifetimes| quote! { #lifetimes #bound })
1103                .collect()
1104        });
1105
1106    let layout_kind = layout.kind;
1107    let size_kind = size.kind;
1108    let alignment_kind = alignment.kind;
1109    let trap_kind = trap.kind;
1110    let niche_kind = niche.kind;
1111    let drop_kind = drop.kind;
1112    let mutability_kind = mutability.kind;
1113    let indirect_trap_kind = indirect_trap.kind;
1114
1115    quote! {
1116        #(#hrtb_axes_impls)*
1117
1118        #attrs
1119        unsafe impl #impl_generics #crate_::RustSpec for #name #ty_generics where
1120            #(#field_bounds,)*
1121            #(#aggregate_bounds,)*
1122            #predicates
1123        {
1124            type Layout = #layout_kind;
1125            type Size = #size_kind;
1126            type Alignment = #alignment_kind;
1127            type Trap = #trap_kind;
1128            type Niche = #niche_kind;
1129            type Drop = #drop_kind;
1130            type Mutability = #mutability_kind;
1131            type __IndirectTrap = #indirect_trap_kind;
1132        }
1133    }
1134}
1135
1136fn gen_enum_niche_family(has_trap_tag_values: bool) -> AggregateFamily {
1137    let crate_ = crate_path();
1138
1139    let niche_kind = if has_trap_tag_values {
1140        quote! { #crate_::niche::WithNiche<#crate_::Unstable> }
1141    } else {
1142        quote! { #crate_::niche::WithoutNiche }
1143    };
1144
1145    AggregateFamily::fixed(niche_kind)
1146}
1147
1148fn primitive_tag_has_traps(tag: &syn::Type, variant_count: usize) -> bool {
1149    !is_exhaustive_enum(variant_count, tag)
1150}
1151
1152fn rust_tag_has_traps(variant_count: usize) -> bool {
1153    let tag = infer_repr(variant_count);
1154    primitive_tag_has_traps(&tag, variant_count)
1155}
1156
1157fn c_tag_type(c_enum_bits: u32) -> syn::Type {
1158    match c_enum_bits {
1159        8 => syn::parse_quote!(u8),
1160        16 => syn::parse_quote!(u16),
1161        32 => syn::parse_quote!(u32),
1162        _ => unreachable!("unsupported C enum width"),
1163    }
1164}