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))]
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 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, "e! { 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}