1#![deny(clippy::complexity)]
36#![deny(clippy::correctness)]
37#![warn(clippy::nursery)]
38#![warn(clippy::pedantic)]
39#![deny(clippy::perf)]
40#![deny(clippy::style)]
41#![deny(clippy::suspicious)]
42#![deny(missing_docs)]
43#![feature(iter_intersperse)]
44
45extern crate proc_macro;
46
47#[macro_use]
48extern crate proc_macro_error2;
49
50use proc_macro::TokenStream;
51
52use proc_macro2::Literal;
53use quote::{quote, quote_spanned};
54use syn::{parse_macro_input, spanned::Spanned};
55
56#[proc_macro_error]
57#[proc_macro_derive(TypeLayout, attributes(layout))]
58pub fn derive_type_layout(input: TokenStream) -> TokenStream {
73 let input = parse_macro_input!(input as syn::DeriveInput);
75
76 let ty_name = input.ident;
78 let ty_generics = input.generics.split_for_impl().1;
79
80 let mut type_params = input
81 .generics
82 .type_params()
83 .map(|param| ¶m.ident)
84 .collect::<Vec<_>>();
85
86 let Attributes {
87 reprs,
88 extra_bounds,
89 crate_path,
90 } = parse_attributes(&input.attrs, &mut type_params);
91
92 let inhabited = inhabited_for_type(&crate_path, &input.data);
93 let layout = layout_of_type(&crate_path, &ty_name, &ty_generics, &input.data, &reprs);
94
95 let inner_types = extract_inner_types(&input.data);
96
97 let discriminant_ty = if let syn::Data::Enum(_) = input.data {
98 Some(quote! { <Self as #crate_path::ExtractDiscriminant>::Discriminant, })
99 } else {
100 None
101 };
102
103 let Generics {
104 type_layout_input_generics,
105 type_set_input_generics,
106 } = generate_generics(&crate_path, &input.generics, &extra_bounds, &type_params);
107 let (type_layout_impl_generics, type_layout_ty_generics, type_layout_where_clause) =
108 type_layout_input_generics.split_for_impl();
109 let (type_set_impl_generics, type_set_ty_generics, type_set_where_clause) =
110 type_set_input_generics.split_for_impl();
111
112 quote! {
113 unsafe impl #type_layout_impl_generics #crate_path::TypeLayout for
114 #ty_name #type_layout_ty_generics #type_layout_where_clause
115 {
116 type Inhabited = #inhabited;
117
118 const TYPE_LAYOUT: #crate_path::TypeLayoutInfo<'static> = {
119 #crate_path::TypeLayoutInfo {
120 name: ::core::any::type_name::<Self>(),
121 size: ::core::mem::size_of::<Self>(),
122 alignment: ::core::mem::align_of::<Self>(),
123 structure: #layout,
124 }
125 };
126 }
127
128 unsafe impl #type_set_impl_generics #crate_path::typeset::ComputeTypeSet for
129 #ty_name #type_set_ty_generics #type_set_where_clause
130 {
131 type Output<__TypeSetRest: #crate_path::typeset::ExpandTypeSet> =
132 #crate_path::typeset::tset![
133 #(#inner_types,)* #discriminant_ty .. @ __TypeSetRest
134 ];
135 }
136 }
137 .into()
138}
139
140struct Attributes {
141 reprs: String,
142 extra_bounds: Vec<syn::WherePredicate>,
143 crate_path: syn::Path,
144}
145
146#[allow(clippy::too_many_lines)]
147fn parse_attributes(attrs: &[syn::Attribute], type_params: &mut Vec<&syn::Ident>) -> Attributes {
148 let mut reprs = Vec::new();
150
151 let mut extra_bounds: Vec<syn::WherePredicate> = Vec::new();
152
153 let mut crate_path = None;
154
155 for attr in attrs {
156 if attr.path.is_ident("repr") {
157 if let Ok(syn::Meta::List(syn::MetaList { nested, .. })) = attr.parse_meta() {
158 for meta in nested {
159 reprs.push(match meta {
160 syn::NestedMeta::Lit(lit) => lit_to_string(&lit),
161 syn::NestedMeta::Meta(meta) => meta_to_string(&meta),
162 });
163 }
164 } else {
165 emit_warning!(
166 attr.span(),
167 "[const-type-layout]: #[repr] attribute is not in meta list format."
168 );
169 }
170 } else if attr.path.is_ident("layout") {
171 if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
172 for meta in &list.nested {
173 if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
174 path,
175 lit: syn::Lit::Str(s),
176 ..
177 })) = &meta
178 {
179 if path.is_ident("free") {
180 match syn::parse_str::<syn::Ident>(&s.value()) {
181 Ok(param) => {
182 type_params.iter().position(|ty| **ty == param).map_or_else(
183 || {
184 emit_error!(
185 s.span(),
186 "[const-type-layout]: Invalid #[layout(free)] \
187 attribute: \"{}\" is either not a type parameter \
188 or has already been freed (duplicate attribute).",
189 param,
190 );
191 },
192 |i| {
193 type_params.swap_remove(i);
194 },
195 );
196 },
197 Err(err) => emit_error!(
198 s.span(),
199 "[const-type-layout]: Invalid #[layout(free = \"<type>\")] \
200 attribute: {}.",
201 err
202 ),
203 }
204 } else if path.is_ident("bound") {
205 match syn::parse_str(&s.value()) {
206 Ok(bound) => extra_bounds.push(bound),
207 Err(err) => emit_error!(
208 s.span(),
209 "[const-type-layout]: Invalid #[layout(bound = \
210 \"<where-predicate>\")] attribute: {}.",
211 err
212 ),
213 }
214 } else if path.is_ident("crate") {
215 match syn::parse_str::<syn::Path>(&s.value()) {
216 Ok(new_crate_path) => {
217 if crate_path.is_none() {
218 crate_path = Some(
219 syn::parse_quote_spanned! { s.span() => #new_crate_path },
220 );
221 } else {
222 emit_error!(
223 s.span(),
224 "[const-type-layout]: Duplicate #[layout(crate)] \
225 attribute: the crate path for `const-type-layout` \
226 can only be set once per `derive`.",
227 );
228 }
229 },
230 Err(err) => emit_error!(
231 s.span(),
232 "[const-type-layout]: Invalid #[layout(crate = \
233 \"<crate-path>\")] attribute: {}.",
234 err
235 ),
236 }
237 } else {
238 emit_error!(
239 path.span(),
240 "[const-type-layout]: Unknown attribute, use `bound`, `crate`, or \
241 `free`."
242 );
243 }
244 } else {
245 emit_error!(
246 meta.span(),
247 "[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
248 );
249 }
250 }
251 } else {
252 emit_error!(
253 attr.span(),
254 "[const-type-layout]: Expected #[layout(attr = \"value\")] syntax."
255 );
256 }
257 }
258 }
259
260 proc_macro_error2::abort_if_dirty();
261
262 reprs.sort();
263 reprs.dedup();
264
265 let reprs = reprs
266 .into_iter()
267 .intersperse(String::from(","))
268 .collect::<String>();
269
270 Attributes {
271 reprs,
272 extra_bounds,
273 crate_path: crate_path.unwrap_or_else(|| syn::parse_quote!(::const_type_layout)),
274 }
275}
276
277fn meta_to_string(meta: &syn::Meta) -> String {
278 match meta {
279 syn::Meta::List(syn::MetaList { path, nested, .. }) => {
280 let mut list = nested
281 .iter()
282 .map(|meta| match meta {
283 syn::NestedMeta::Lit(lit) => lit_to_string(lit),
284 syn::NestedMeta::Meta(meta) => meta_to_string(meta),
285 })
286 .collect::<Vec<_>>();
287 list.sort();
288 list.dedup();
289
290 format!(
291 "{}({})",
292 quote!(#path),
293 list.into_iter()
294 .intersperse(String::from(","))
295 .collect::<String>()
296 )
297 },
298 syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => {
299 format!("{}={}", quote!(#path), lit_to_string(lit))
300 },
301 syn::Meta::Path(path) => quote!(#path).to_string(),
302 }
303}
304
305fn lit_to_string(lit: &syn::Lit) -> String {
306 quote!(#lit).to_string().escape_default().to_string()
307}
308
309fn extract_inner_types(data: &syn::Data) -> Vec<&syn::Type> {
310 let mut inner_types = Vec::new();
311
312 match data {
313 syn::Data::Struct(syn::DataStruct { fields, .. }) => {
314 for field in fields {
315 inner_types.push(&field.ty);
316 }
317 },
318 syn::Data::Union(syn::DataUnion {
319 fields: syn::FieldsNamed { named: fields, .. },
320 ..
321 }) => {
322 for field in fields {
323 inner_types.push(&field.ty);
324 }
325 },
326 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
327 for variant in variants {
328 for field in &variant.fields {
329 inner_types.push(&field.ty);
330 }
331 }
332 },
333 }
334
335 inner_types
336}
337
338struct Generics {
339 type_layout_input_generics: syn::Generics,
340 type_set_input_generics: syn::Generics,
341}
342
343fn generate_generics(
344 crate_path: &syn::Path,
345 generics: &syn::Generics,
346 extra_bounds: &[syn::WherePredicate],
347 type_params: &[&syn::Ident],
348) -> Generics {
349 let mut type_layout_input_generics = generics.clone();
350 let mut type_set_input_generics = generics.clone();
351
352 for ty in type_params {
353 type_layout_input_generics
354 .make_where_clause()
355 .predicates
356 .push(syn::parse_quote! {
357 #ty: #crate_path::TypeLayout
358 });
359
360 type_set_input_generics
361 .make_where_clause()
362 .predicates
363 .push(syn::parse_quote! {
364 #ty: #crate_path::typeset::ComputeTypeSet
365 });
366 }
367
368 for bound in extra_bounds {
369 type_layout_input_generics
370 .make_where_clause()
371 .predicates
372 .push(bound.clone());
373
374 type_set_input_generics
375 .make_where_clause()
376 .predicates
377 .push(bound.clone());
378 }
379
380 Generics {
381 type_layout_input_generics,
382 type_set_input_generics,
383 }
384}
385
386fn layout_of_type(
387 crate_path: &syn::Path,
388 ty_name: &syn::Ident,
389 ty_generics: &syn::TypeGenerics,
390 data: &syn::Data,
391 reprs: &str,
392) -> proc_macro2::TokenStream {
393 match data {
394 syn::Data::Struct(data) => {
395 let fields = quote_structlike_fields(crate_path, ty_name, ty_generics, &data.fields);
396
397 quote! {
398 #crate_path::TypeStructure::Struct { repr: #reprs, fields: &[#(#fields),*] }
399 }
400 },
401 syn::Data::Enum(r#enum) => {
402 let variants = quote_enum_variants(crate_path, ty_name, ty_generics, r#enum);
403
404 quote! {
405 #crate_path::TypeStructure::Enum { repr: #reprs, variants: &[#(#variants),*] }
406 }
407 },
408 syn::Data::Union(union) => {
409 let fields = quote_structlike_fields(
410 crate_path,
411 ty_name,
412 ty_generics,
413 &syn::Fields::Named(union.fields.clone()),
414 );
415
416 quote! {
417 #crate_path::TypeStructure::Union { repr: #reprs, fields: &[#(#fields),*] }
418 }
419 },
420 }
421}
422
423fn quote_structlike_fields(
424 crate_path: &syn::Path,
425 ty_name: &syn::Ident,
426 ty_generics: &syn::TypeGenerics,
427 fields: &syn::Fields,
428) -> Vec<proc_macro2::TokenStream> {
429 match fields {
430 syn::Fields::Named(fields) => fields
431 .named
432 .iter()
433 .map(|field| {
434 let field_name = field.ident.as_ref().unwrap();
435 let field_name_str = Literal::string(&field_name.to_string());
436 let field_ty = &field.ty;
437 let field_offset = quote_structlike_field_offset(
438 crate_path,
439 ty_name,
440 ty_generics,
441 &field_name,
442 field_ty,
443 );
444
445 quote_spanned! { field.span() =>
446 #crate_path::Field {
447 name: #field_name_str,
448 offset: { #field_offset },
449 ty: ::core::any::type_name::<#field_ty>(),
450 }
451 }
452 })
453 .collect(),
454 syn::Fields::Unnamed(fields) => fields
455 .unnamed
456 .iter()
457 .enumerate()
458 .map(|(field_index, field)| {
459 let field_name = syn::Index::from(field_index);
460 let field_name_str = Literal::string(&field_index.to_string());
461 let field_ty = &field.ty;
462 let field_offset = quote_structlike_field_offset(
463 crate_path,
464 ty_name,
465 ty_generics,
466 &field_name,
467 field_ty,
468 );
469
470 quote_spanned! { field.span() =>
471 #crate_path::Field {
472 name: #field_name_str,
473 offset: { #field_offset },
474 ty: ::core::any::type_name::<#field_ty>(),
475 }
476 }
477 })
478 .collect(),
479 syn::Fields::Unit => vec![],
480 }
481}
482
483fn quote_structlike_field_offset(
484 crate_path: &syn::Path,
485 ty_name: &syn::Ident,
486 ty_generics: &syn::TypeGenerics,
487 field_name: &impl quote::ToTokens,
488 field_ty: &syn::Type,
489) -> proc_macro2::TokenStream {
490 quote! {
491 #crate_path::MaybeUninhabited::new::<#field_ty>(
492 ::core::mem::offset_of!(#ty_name #ty_generics, #field_name)
493 )
494 }
495}
496
497fn quote_enum_variants(
498 crate_path: &syn::Path,
499 ty_name: &syn::Ident,
500 ty_generics: &syn::TypeGenerics,
501 r#enum: &syn::DataEnum,
502) -> Vec<proc_macro2::TokenStream> {
503 let mut last_discriminant = syn::Expr::Lit(syn::ExprLit {
504 attrs: vec![],
505 lit: syn::Lit::Int(syn::LitInt::from(proc_macro2::Literal::usize_unsuffixed(0))),
506 });
507 let mut last_discriminant_offset = 0;
508
509 r#enum
510 .variants
511 .iter()
512 .map(|variant| {
513 let variant_name = &variant.ident;
514 let variant_name_str = Literal::string(&variant_name.to_string());
515
516 let fields = quote_variant_fields(
517 crate_path,
518 ty_name,
519 ty_generics,
520 variant_name,
521 &variant.fields,
522 );
523
524 let discriminant = match variant.discriminant.as_ref() {
525 None => {
526 let discriminant = syn::Expr::Binary(syn::ExprBinary {
527 attrs: vec![],
528 left: Box::new(last_discriminant.clone()),
529 op: syn::parse_quote!(+),
530 right: Box::new(syn::Expr::Lit(syn::ExprLit {
531 attrs: vec![],
532 lit: syn::Lit::Int(syn::LitInt::from(
533 proc_macro2::Literal::usize_unsuffixed(last_discriminant_offset),
534 )),
535 })),
536 });
537 last_discriminant_offset += 1;
538 discriminant
539 },
540 Some((_, discriminant)) => {
541 last_discriminant = discriminant.clone();
542 last_discriminant_offset = 0;
543 discriminant.clone()
544 },
545 };
546
547 let variant_inhabited = match &variant.fields {
549 syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
550 | syn::Fields::Unnamed(syn::FieldsUnnamed {
551 unnamed: fields, ..
552 }) => {
553 let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
554
555 quote! { #crate_path::inhabited::all![#(#field_tys),*] }
556 },
557 syn::Fields::Unit => quote! { #crate_path::inhabited::Inhabited },
558 };
559
560 let discriminant = quote! {
561 #crate_path::MaybeUninhabited::new::<#variant_inhabited>(
562 #crate_path::Discriminant::new::<Self>(#discriminant)
563 )
564 };
565
566 quote! {
567 #crate_path::Variant {
568 name: #variant_name_str,
569 discriminant: #discriminant,
570 fields: &[#(#fields),*],
571 }
572 }
573 })
574 .collect::<Vec<_>>()
575}
576
577fn quote_variant_fields(
578 crate_path: &syn::Path,
579 ty_name: &syn::Ident,
580 ty_generics: &syn::TypeGenerics,
581 variant_name: &syn::Ident,
582 variant_fields: &syn::Fields,
583) -> Vec<proc_macro2::TokenStream> {
584 match variant_fields {
585 syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) => fields
586 .iter()
587 .map(|field| {
588 let field_name_str = Literal::string(&field.ident.as_ref().unwrap().to_string());
589 let field_name = &field.ident;
590 let field_ty = &field.ty;
591
592 let offset = quote_structlike_variant_field_offset(
593 crate_path,
594 ty_name,
595 ty_generics,
596 variant_name,
597 field_name,
598 field_ty,
599 );
600
601 quote_spanned! { field.span() =>
602 #crate_path::Field {
603 name: #field_name_str,
604 offset: #offset,
605 ty: ::core::any::type_name::<#field_ty>(),
606 }
607 }
608 })
609 .collect(),
610 syn::Fields::Unnamed(syn::FieldsUnnamed {
611 unnamed: fields, ..
612 }) => fields
613 .iter()
614 .enumerate()
615 .map(|(field_index, field)| {
616 let field_name_str = Literal::string(&field_index.to_string());
617 let field_index = syn::Index::from(field_index);
618 let field_ty = &field.ty;
619
620 let offset = quote_structlike_variant_field_offset(
621 crate_path,
622 ty_name,
623 ty_generics,
624 variant_name,
625 &field_index,
626 field_ty,
627 );
628
629 quote_spanned! { field.span() =>
630 #crate_path::Field {
631 name: #field_name_str,
632 offset: #offset,
633 ty: ::core::any::type_name::<#field_ty>(),
634 }
635 }
636 })
637 .collect(),
638 syn::Fields::Unit => vec![],
639 }
640}
641
642fn quote_structlike_variant_field_offset(
643 crate_path: &syn::Path,
644 ty_name: &syn::Ident,
645 ty_generics: &syn::TypeGenerics,
646 variant_name: &syn::Ident,
647 field_name: &impl quote::ToTokens,
648 field_ty: &syn::Type,
649) -> proc_macro2::TokenStream {
650 quote! {
651 #crate_path::MaybeUninhabited::new::<#field_ty>(
652 ::core::mem::offset_of!(#ty_name #ty_generics, #variant_name.#field_name)
653 )
654 }
655}
656
657fn inhabited_for_type(crate_path: &syn::Path, data: &syn::Data) -> proc_macro2::TokenStream {
658 match data {
659 syn::Data::Struct(data) => {
660 let fields = match &data.fields {
662 syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
663 | syn::Fields::Unnamed(syn::FieldsUnnamed {
664 unnamed: fields, ..
665 }) => fields,
666 syn::Fields::Unit => return quote! { #crate_path::inhabited::Inhabited },
667 };
668
669 let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
670
671 quote! { #crate_path::inhabited::all![#(#field_tys),*] }
672 },
673 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
674 let variants_inhabited = variants.iter().map(|syn::Variant { fields, .. }| {
675 let fields = match fields {
677 syn::Fields::Named(syn::FieldsNamed { named: fields, .. })
678 | syn::Fields::Unnamed(syn::FieldsUnnamed {
679 unnamed: fields, ..
680 }) => fields,
681 syn::Fields::Unit => return quote! { #crate_path::inhabited::Inhabited },
682 };
683
684 let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
685
686 quote! { #crate_path::inhabited::all![#(#field_tys),*] }
687 });
688
689 quote! {
691 #crate_path::inhabited::any![#(#variants_inhabited),*]
692 }
693 },
694 syn::Data::Union(syn::DataUnion {
695 fields: syn::FieldsNamed { named: fields, .. },
696 ..
697 }) => {
698 let field_tys = fields.iter().map(|syn::Field { ty, .. }| ty);
700
701 quote! { #crate_path::inhabited::any![#(#field_tys),*] }
702 },
703 }
704}