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.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::WithDrop<#crate_::drop::Custom> }
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, "e! { 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, "e! { 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}