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