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