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.custom_niche && !matches!(&input.data, syn::Data::Struct(_)) {
359        return Err(syn::Error::new_spanned(
360            name,
361            "`custom_niche` is only supported on structs",
362        ));
363    }
364    let rust_spec_impl = match &input.data {
365        syn::Data::Struct(data) => {
366            gen_struct_impl(repr, alignment, name, generics, &data.fields, attrs)
367        }
368        syn::Data::Enum(data) if is_fieldless_enum(data) => {
369            gen_fieldless_enum_impl(repr, alignment, name, generics, &data.variants, attrs)
370        }
371        syn::Data::Enum(data)
372            if matches!(repr, Some(ReprKind::Transparent))
373                || repr.is_none() && data.variants.len() == 1 =>
374        {
375            let Some(variant) = data.variants.first() else {
376                return Ok(quote! {});
377            };
378
379            gen_struct_impl(repr, alignment, name, generics, &variant.fields, attrs)
380        }
381        syn::Data::Enum(data) => {
382            gen_enum_impl(repr, alignment, name, generics, &data.variants, attrs)
383        }
384        syn::Data::Union(data) if matches!(repr, Some(ReprKind::Transparent)) => {
385            let fields = data
386                .fields
387                .named
388                .iter()
389                .map(|field| &field.ty)
390                .collect::<Vec<_>>();
391
392            gen_struct_fields_impl(
393                Some(&ReprKind::Transparent),
394                alignment,
395                name,
396                generics,
397                &fields,
398                attrs,
399            )
400        }
401        syn::Data::Union(data) => {
402            gen_union_impl(repr, alignment, name, generics, &data.fields, attrs)
403        }
404    };
405
406    let drop_assert = gen_drop_assert(name, generics, attrs.custom_drop);
407    Ok(quote! { #drop_assert #rust_spec_impl })
408}
409
410fn gen_drop_assert(
411    name: &syn::Ident,
412    generics: &syn::Generics,
413    custom_drop: bool,
414) -> proc_macro2::TokenStream {
415    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
416    if custom_drop {
417        quote! {
418            const _: () = {
419                trait AssertHasDrop { fn check(); }
420                impl #impl_generics AssertHasDrop for #name #ty_generics #where_clause {
421                    fn check() {
422                        fn require_drop<T: core::ops::Drop>() {}
423                        let _ = require_drop::<#name #ty_generics>;
424                    }
425                }
426            };
427        }
428    } else {
429        quote! {
430            const _: () = {
431                trait AssertNoDrop { fn check(); }
432                impl #impl_generics AssertNoDrop for #name #ty_generics #where_clause {
433                    fn check() {
434                        trait DropImplIsNotSupported<A> { fn check() {} }
435                        impl<T: ?Sized> DropImplIsNotSupported<()> for T {}
436                        struct DropDetected;
437                        impl<T: ?Sized + core::ops::Drop> DropImplIsNotSupported<DropDetected> for T {}
438                        let _ = <#name #ty_generics as DropImplIsNotSupported<_>>::check;
439                    }
440                }
441            };
442        }
443    }
444}
445
446#[derive(Clone, Copy, Default)]
447struct RustSpecAttrs {
448    custom_niche: bool,
449    custom_drop: bool,
450}
451
452fn parse_rust_spec_attrs(attrs: &[syn::Attribute]) -> syn::Result<RustSpecAttrs> {
453    let mut result = RustSpecAttrs::default();
454
455    for attr in attrs
456        .iter()
457        .filter(|attr| attr.path().is_ident("rust_spec"))
458    {
459        attr.parse_nested_meta(|meta| {
460            if meta.path.is_ident("custom_niche") {
461                if result.custom_niche {
462                    return Err(meta.error("duplicate `custom_niche` within attribute"));
463                }
464                result.custom_niche = true;
465                return Ok(());
466            }
467            if meta.path.is_ident("custom_drop") {
468                if result.custom_drop {
469                    return Err(meta.error("duplicate `custom_drop` within attribute"));
470                }
471                result.custom_drop = true;
472                return Ok(());
473            }
474            Err(meta.error("unknown rust_spec attribute"))
475        })?;
476    }
477
478    Ok(result)
479}
480
481fn is_fieldless_enum(data: &syn::DataEnum) -> bool {
482    data.variants
483        .iter()
484        .all(|variant| matches!(variant.fields, syn::Fields::Unit))
485}
486
487fn field_types(fields: &syn::Fields) -> Vec<&syn::Type> {
488    fields.iter().map(|field| &field.ty).collect()
489}
490
491fn variant_field_types(variants: &Punctuated<syn::Variant, Token![,]>) -> Vec<&syn::Type> {
492    variants
493        .iter()
494        .flat_map(|variant| field_types(&variant.fields))
495        .collect()
496}
497
498fn gen_struct_impl(
499    repr: Option<&ReprKind>,
500    repr_alignment: Option<usize>,
501    name: &syn::Ident,
502    generics: &syn::Generics,
503    fields: &syn::Fields,
504    attrs: RustSpecAttrs,
505) -> proc_macro2::TokenStream {
506    let fields = field_types(fields);
507
508    gen_struct_fields_impl(repr, repr_alignment, name, generics, &fields, attrs)
509}
510
511fn gen_struct_fields_impl(
512    repr: Option<&ReprKind>,
513    alignment: Option<usize>,
514    name: &syn::Ident,
515    generics: &syn::Generics,
516    fields: &[&syn::Type],
517    attrs: RustSpecAttrs,
518) -> proc_macro2::TokenStream {
519    let layout = if repr.is_some() {
520        gen_stable_layout_family(generics, fields)
521    } else {
522        gen_unstable_layout_family()
523    };
524    let size = gen_size_family(generics, fields);
525    let alignment = apply_repr_alignment(gen_alignment_family(generics, fields, None), alignment);
526    let trap = gen_trap_family(generics, fields, false);
527    let niche = if attrs.custom_niche {
528        let crate_ = crate_path();
529        AggregateFamily::fixed(quote! { #crate_::niche::WithNiche<#crate_::Unstable> })
530    } else if let Some(ReprKind::Transparent) = repr {
531        gen_transparent_niche_family(generics, fields)
532    } else {
533        gen_niche_family(generics, fields)
534    };
535
536    let drop = gen_drop_family(generics, fields, attrs.custom_drop);
537    let mutability = gen_single_field_mutability_family(generics, fields);
538    let indirect_trap = gen_indirect_trap_family(generics, fields);
539
540    let spec = TypeSpecFamilies {
541        layout,
542        size,
543        alignment,
544        trap,
545        niche,
546        drop,
547        mutability,
548        indirect_trap,
549    };
550
551    gen_type_spec_impl(name, generics, fields, spec, quote! {})
552}
553
554fn gen_enum_impl(
555    repr: Option<&ReprKind>,
556    repr_alignment: Option<usize>,
557    name: &syn::Ident,
558    generics: &syn::Generics,
559    variants: &Punctuated<syn::Variant, Token![,]>,
560    attrs: RustSpecAttrs,
561) -> proc_macro2::TokenStream {
562    if matches!(repr, Some(ReprKind::C(None))) {
563        return gen_plain_c_enum_impls(repr_alignment, name, generics, variants, attrs);
564    }
565
566    let crate_ = crate_path();
567    let fields = variant_field_types(variants);
568    let has_trap_tag_values = match repr {
569        None => rust_tag_has_traps(variants.len()),
570        Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
571            primitive_tag_has_traps(tag, variants.len())
572        }
573        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
574        Some(ReprKind::Transparent) => false,
575    };
576    let layout = if repr.is_some() {
577        gen_enum_layout_family(generics, &fields)
578    } else {
579        gen_unstable_layout_family()
580    };
581    let size = AggregateFamily::fixed(quote! {
582        #crate_::size::Sized<#crate_::Gt<#crate_::Zero>>
583    });
584    let tag = match repr {
585        Some(ReprKind::Primitive(tag) | ReprKind::C(Some(tag))) => Some(tag.as_ref().clone()),
586        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
587        Some(ReprKind::Transparent) => None,
588        None => Some(infer_repr(variants.len())),
589    };
590    let alignment = apply_repr_alignment(
591        gen_alignment_family(generics, &fields, tag.as_ref()),
592        repr_alignment,
593    );
594    let trap = gen_trap_family(generics, &fields, has_trap_tag_values);
595
596    let niche = gen_enum_niche_family(has_trap_tag_values);
597    let drop = gen_drop_family(generics, &fields, attrs.custom_drop);
598    let mutability = if matches!(repr, None | Some(ReprKind::Transparent)) && variants.len() == 1 {
599        gen_single_field_mutability_family(generics, &fields)
600    } else {
601        gen_exclusive_mutability_family()
602    };
603    let indirect_trap = gen_indirect_trap_family(generics, &fields);
604
605    let spec = TypeSpecFamilies {
606        layout,
607        size,
608        alignment,
609        trap,
610        niche,
611        drop,
612        mutability,
613        indirect_trap,
614    };
615
616    gen_type_spec_impl(name, generics, &fields, spec, quote! {})
617}
618
619fn gen_union_impl(
620    repr: Option<&ReprKind>,
621    repr_alignment: Option<usize>,
622    name: &syn::Ident,
623    generics: &syn::Generics,
624    fields: &syn::FieldsNamed,
625    attrs: RustSpecAttrs,
626) -> proc_macro2::TokenStream {
627    let crate_ = crate_path();
628
629    let fields = fields
630        .named
631        .iter()
632        .map(|field| &field.ty)
633        .collect::<Vec<_>>();
634
635    let layout = if repr.is_some() {
636        gen_stable_layout_family(generics, &fields)
637    } else {
638        gen_unstable_layout_family()
639    };
640    let size = gen_size_family(generics, &fields);
641    let alignment = apply_repr_alignment(
642        gen_alignment_family(generics, &fields, None),
643        repr_alignment,
644    );
645    let trap = AggregateFamily::fixed(quote! {
646        #crate_::layout::Robust
647    });
648    let niche = AggregateFamily::fixed(quote! {
649        #crate_::niche::WithoutNiche
650    });
651    let drop = gen_drop_family(generics, &fields, attrs.custom_drop);
652
653    let mutability = gen_exclusive_mutability_family();
654    let indirect_trap = AggregateFamily::fixed(quote! {
655        #crate_::layout::Robust
656    });
657
658    let spec = TypeSpecFamilies {
659        layout,
660        size,
661        alignment,
662        trap,
663        niche,
664        drop,
665        mutability,
666        indirect_trap,
667    };
668
669    gen_type_spec_impl(name, generics, &fields, spec, quote! {})
670}
671
672fn gen_fieldless_enum_impl(
673    repr: Option<&ReprKind>,
674    repr_alignment: Option<usize>,
675    name: &syn::Ident,
676    generics: &syn::Generics,
677    variants: &Punctuated<syn::Variant, Token![,]>,
678    attrs: RustSpecAttrs,
679) -> proc_macro2::TokenStream {
680    if matches!(repr, Some(ReprKind::C(None))) {
681        return gen_plain_c_enum_impls(repr_alignment, name, generics, variants, attrs);
682    }
683
684    let crate_ = crate_path();
685    let layout_kind = match repr {
686        None => quote! { #crate_::Unstable },
687        Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
688        Some(ReprKind::Transparent) => quote! {
689            #crate_::Stable
690        },
691        Some(ReprKind::C(Some(_)) | ReprKind::Primitive(_)) => {
692            quote! { #crate_::Stable }
693        }
694    };
695    let has_tag = !variants.is_empty()
696        && match repr {
697            Some(ReprKind::C(_) | ReprKind::Primitive(_)) => true,
698            Some(ReprKind::Transparent) | None => variants.len() > 1,
699        };
700
701    let size = if has_tag {
702        AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::Gt<#crate_::Zero>> })
703    } else {
704        AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::size::Zero> })
705    };
706
707    let niche = if has_tag {
708        let has_trap_tag_values = match repr {
709            None => rust_tag_has_traps(variants.len()),
710            Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
711                primitive_tag_has_traps(tag, variants.len())
712            }
713            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
714            Some(ReprKind::Transparent) => false,
715        };
716        gen_enum_niche_family(has_trap_tag_values)
717    } else {
718        AggregateFamily::fixed(quote! { #crate_::niche::WithoutNiche })
719    };
720
721    let has_trap_tag_values = has_tag
722        && match repr {
723            None => rust_tag_has_traps(variants.len()),
724            Some(ReprKind::C(Some(tag)) | ReprKind::Primitive(tag)) => {
725                primitive_tag_has_traps(tag, variants.len())
726            }
727            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
728            Some(ReprKind::Transparent) => false,
729        };
730    let layout = AggregateFamily::fixed(layout_kind);
731    let alignment = if has_tag {
732        let tag = match repr {
733            Some(ReprKind::Primitive(tag) | ReprKind::C(Some(tag))) => tag.as_ref().clone(),
734            Some(ReprKind::C(None)) => unreachable!("handled by gen_plain_c_enum_impls"),
735            Some(ReprKind::Transparent) => infer_repr(variants.len()),
736            None => infer_repr(variants.len()),
737        };
738        gen_alignment_family(generics, &[], Some(&tag))
739    } else {
740        gen_alignment_family(generics, &[], None)
741    };
742    let alignment = apply_repr_alignment(alignment, repr_alignment);
743    let trap = AggregateFamily::fixed(if has_trap_tag_values {
744        quote! { #crate_::layout::NonRobust }
745    } else {
746        quote! { #crate_::layout::Robust }
747    });
748    let mutability = gen_exclusive_mutability_family();
749    let drop = gen_drop_family(generics, &[], attrs.custom_drop);
750    let indirect_trap = AggregateFamily::fixed(quote! { #crate_::layout::Robust });
751
752    let spec = TypeSpecFamilies {
753        layout,
754        size,
755        alignment,
756        trap,
757        niche,
758        drop,
759        mutability,
760        indirect_trap,
761    };
762
763    gen_type_spec_impl(name, generics, &[], spec, quote! {})
764}
765
766fn gen_unstable_layout_family() -> AggregateFamily {
767    let crate_ = crate_path();
768    AggregateFamily::fixed(quote! { #crate_::Unstable })
769}
770
771fn gen_stable_layout_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
772    let crate_ = crate_path();
773
774    AggregateFamily::aggregate(
775        quote! { core::ops::Add },
776        quote! { Layout },
777        generics,
778        fields,
779    )
780    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::Stable }))
781}
782
783fn gen_enum_layout_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
784    gen_stable_layout_family(generics, fields)
785}
786
787fn gen_plain_c_enum_impls(
788    repr_alignment: Option<usize>,
789    name: &syn::Ident,
790    generics: &syn::Generics,
791    variants: &Punctuated<syn::Variant, Token![,]>,
792    attrs: RustSpecAttrs,
793) -> proc_macro2::TokenStream {
794    let crate_ = crate_path();
795    let fields = variant_field_types(variants);
796
797    let eight_bit_targets = quote! {
798        #[cfg(all(
799            target_arch = "arm",
800            any(target_os = "none", target_os = "nuttx", target_os = "rtems"),
801        ))]
802    };
803    let sixteen_bit_targets = quote! {
804        #[cfg(any(target_arch = "avr", target_arch = "msp430"))]
805    };
806    let normal_targets = quote! {
807        #[cfg(not(any(
808            all(
809                target_arch = "arm",
810                any(target_os = "none", target_os = "nuttx", target_os = "rtems"),
811            ),
812            target_arch = "avr",
813            target_arch = "msp430",
814        )))]
815    };
816
817    [
818        (eight_bit_targets, 8),
819        (sixteen_bit_targets, 16),
820        (normal_targets, 32),
821    ]
822    .into_iter()
823    .map(|(cfg, bits)| {
824        let tag = c_tag_type(bits);
825        let has_trap_tag_values = primitive_tag_has_traps(&tag, variants.len());
826        let layout = gen_stable_layout_family(generics, &fields);
827        let niche = gen_enum_niche_family(has_trap_tag_values);
828        let spec = TypeSpecFamilies {
829            layout,
830            size: AggregateFamily::fixed(quote! {
831                #crate_::size::Sized<#crate_::Gt<#crate_::Zero>>
832            }),
833            alignment: apply_repr_alignment(
834                gen_alignment_family(generics, &fields, Some(&tag)),
835                repr_alignment,
836            ),
837            trap: gen_trap_family(generics, &fields, has_trap_tag_values),
838            niche,
839            drop: gen_drop_family(generics, &fields, attrs.custom_drop),
840            mutability: gen_exclusive_mutability_family(),
841            indirect_trap: gen_indirect_trap_family(generics, &fields),
842        };
843
844        gen_type_spec_impl(name, generics, &fields, spec, cfg)
845    })
846    .collect()
847}
848
849fn gen_indirect_trap_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
850    let crate_ = crate_path();
851
852    AggregateFamily::aggregate(
853        quote! { core::ops::Add },
854        quote! { __IndirectTrap },
855        generics,
856        fields,
857    )
858    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::layout::Robust }))
859}
860
861fn gen_drop_family(
862    generics: &syn::Generics,
863    fields: &[&syn::Type],
864    custom_drop: bool,
865) -> AggregateFamily {
866    let crate_ = crate_path();
867    let seed = if custom_drop {
868        quote! { #crate_::drop::CustomDrop<#crate_::drop::NoDrop> }
869    } else {
870        quote! { #crate_::drop::NoDrop }
871    };
872    AggregateFamily::aggregate_with_seed(
873        quote! { core::ops::Add },
874        quote! { Drop },
875        seed,
876        generics,
877        fields,
878    )
879}
880
881fn gen_trap_family(
882    generics: &syn::Generics,
883    fields: &[&syn::Type],
884    has_trap_values: bool,
885) -> AggregateFamily {
886    let crate_ = crate_path();
887
888    let init = if has_trap_values {
889        quote! { #crate_::layout::NonRobust }
890    } else {
891        quote! { #crate_::layout::Robust }
892    };
893
894    AggregateFamily::aggregate_with_seed(
895        quote! { core::ops::Add },
896        quote! { Trap },
897        init,
898        generics,
899        fields,
900    )
901}
902
903fn gen_size_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
904    let crate_ = crate_path();
905
906    AggregateFamily::aggregate(quote! { core::ops::Add }, quote! { Size }, generics, fields)
907        .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::size::Sized<#crate_::Zero> }))
908}
909
910fn gen_alignment_family(
911    generics: &syn::Generics,
912    fields: &[&syn::Type],
913    tag: Option<&syn::Type>,
914) -> AggregateFamily {
915    let crate_ = crate_path();
916
917    let mut family = AggregateFamily::aggregate(
918        quote! { #crate_::Max },
919        quote! { Alignment },
920        generics,
921        fields,
922    )
923    .unwrap_or_else(|| AggregateFamily::fixed(quote! { #crate_::One }));
924
925    if let Some(tag) = tag {
926        let kind = family.kind;
927
928        let tag_kind = quote! { <#tag as #crate_::RustSpec>::Alignment };
929        family.kind = quote! { <#tag_kind as #crate_::Max<#kind>>::Output };
930    }
931
932    family
933}
934
935fn apply_repr_alignment(mut family: AggregateFamily, align: Option<usize>) -> AggregateFamily {
936    let crate_ = crate_path();
937
938    if matches!(align, Some(value) if value > 1) {
939        let minimum = quote! { #crate_::Gt<#crate_::One> };
940        let kind = family.kind;
941        family.kind = quote! { <#minimum as #crate_::Max<#kind>>::Output };
942    }
943
944    family
945}
946
947fn gen_niche_family(generics: &syn::Generics, fields: &[&syn::Type]) -> AggregateFamily {
948    let crate_ = crate_path();
949
950    AggregateFamily::aggregate_with_seed(
951        quote! { core::ops::Add },
952        quote! { Niche },
953        quote! { #crate_::niche::WithoutNiche },
954        generics,
955        fields,
956    )
957}
958
959fn gen_transparent_niche_family(
960    generics: &syn::Generics,
961    fields: &[&syn::Type],
962) -> AggregateFamily {
963    match fields {
964        [] => {
965            let crate_ = crate_path();
966            AggregateFamily::fixed(quote! { #crate_::niche::WithoutNiche })
967        }
968        [field] => AggregateFamily::fixed(field_axis_kind(field, 0, &quote! { Niche }, generics)),
969        _ => gen_niche_family(generics, fields),
970    }
971}
972
973fn gen_single_field_mutability_family(
974    generics: &syn::Generics,
975    fields: &[&syn::Type],
976) -> AggregateFamily {
977    match fields {
978        [field] => {
979            AggregateFamily::fixed(field_axis_kind(field, 0, &quote! { Mutability }, generics))
980        }
981        _ => gen_exclusive_mutability_family(),
982    }
983}
984
985fn gen_exclusive_mutability_family() -> AggregateFamily {
986    let crate_ = crate_path();
987    AggregateFamily::fixed(quote! { #crate_::mutability::Exclusive })
988}
989
990fn gen_type_spec_impl(
991    name: &syn::Ident,
992    generics: &syn::Generics,
993    fields: &[&syn::Type],
994    families: TypeSpecFamilies,
995    attrs: proc_macro2::TokenStream,
996) -> proc_macro2::TokenStream {
997    let TypeSpecFamilies {
998        layout,
999        size,
1000        alignment,
1001        trap,
1002        niche,
1003        drop,
1004        mutability,
1005        indirect_trap,
1006    } = families;
1007
1008    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1009    let predicates = where_clause
1010        .as_ref()
1011        .map(|where_clause| &where_clause.predicates);
1012
1013    let crate_ = crate_path();
1014    let hrtb_axes = fields
1015        .iter()
1016        .filter_map(|field| {
1017            let lifetimes = hrtb_lifetimes(field, generics);
1018            (!lifetimes.is_empty()).then_some(lifetimes)
1019        })
1020        .collect::<Vec<_>>();
1021
1022    let field_bounds = fields.iter().enumerate().flat_map(|(index, field)| {
1023        if field_has_type_params(field, generics) {
1024            vec![quote! { #field: #crate_::RustSpec }]
1025        } else {
1026            hrtb_lifetimes(field, generics)
1027                .into_iter()
1028                .map(|lifetimes| quote! { #lifetimes Self: #crate_::__HrtbAxes<#index> })
1029                .collect()
1030        }
1031    });
1032
1033    let hrtb_axes_impls = fields
1034        .iter()
1035        .enumerate()
1036        .filter_map(|(index, field)| hrtb_axes_impl(name, generics, field, index));
1037
1038    let aggregate_bounds = layout
1039        .aggregate_bounds
1040        .into_iter()
1041        .chain(size.aggregate_bounds)
1042        .chain(alignment.aggregate_bounds)
1043        .chain(trap.aggregate_bounds)
1044        .chain(niche.aggregate_bounds)
1045        .chain(drop.aggregate_bounds)
1046        .chain(mutability.aggregate_bounds)
1047        .chain(indirect_trap.aggregate_bounds)
1048        .flat_map(|bound| {
1049            if !bound.to_string().contains("__HrtbAxes") {
1050                return vec![bound.clone()];
1051            }
1052
1053            hrtb_axes
1054                .iter()
1055                .flatten()
1056                .map(|lifetimes| quote! { #lifetimes #bound })
1057                .collect()
1058        });
1059
1060    let layout_kind = layout.kind;
1061    let size_kind = size.kind;
1062    let alignment_kind = alignment.kind;
1063    let trap_kind = trap.kind;
1064    let niche_kind = niche.kind;
1065    let drop_kind = drop.kind;
1066    let mutability_kind = mutability.kind;
1067    let indirect_trap_kind = indirect_trap.kind;
1068
1069    quote! {
1070        #(#hrtb_axes_impls)*
1071
1072        #attrs
1073        unsafe impl #impl_generics #crate_::RustSpec for #name #ty_generics where
1074            #(#field_bounds,)*
1075            #(#aggregate_bounds,)*
1076            #predicates
1077        {
1078            type Layout = #layout_kind;
1079            type Size = #size_kind;
1080            type Alignment = #alignment_kind;
1081            type Trap = #trap_kind;
1082            type Niche = #niche_kind;
1083            type Drop = #drop_kind;
1084            type Mutability = #mutability_kind;
1085            type __IndirectTrap = #indirect_trap_kind;
1086        }
1087    }
1088}
1089
1090fn gen_enum_niche_family(has_trap_tag_values: bool) -> AggregateFamily {
1091    let crate_ = crate_path();
1092
1093    let niche_kind = if has_trap_tag_values {
1094        quote! { #crate_::niche::WithNiche<#crate_::Unstable> }
1095    } else {
1096        quote! { #crate_::niche::WithoutNiche }
1097    };
1098
1099    AggregateFamily::fixed(niche_kind)
1100}
1101
1102fn primitive_tag_has_traps(tag: &syn::Type, variant_count: usize) -> bool {
1103    !is_exhaustive_enum(variant_count, tag)
1104}
1105
1106fn rust_tag_has_traps(variant_count: usize) -> bool {
1107    let tag = infer_repr(variant_count);
1108    primitive_tag_has_traps(&tag, variant_count)
1109}
1110
1111fn c_tag_type(c_enum_bits: u32) -> syn::Type {
1112    match c_enum_bits {
1113        8 => syn::parse_quote!(u8),
1114        16 => syn::parse_quote!(u16),
1115        32 => syn::parse_quote!(u32),
1116        _ => unreachable!("unsupported C enum width"),
1117    }
1118}