Skip to main content

prgpu_macro/
lib.rs

1use proc_macro::TokenStream;
2
3mod diagnostics;
4mod generate;
5mod layout;
6mod parse;
7mod types;
8
9use types::GpuType;
10
11#[proc_macro_attribute]
12pub fn gpu_struct(attr: TokenStream, item: TokenStream) -> TokenStream {
13    let attr_tokens: proc_macro2::TokenStream = attr.into();
14    let item_tokens: proc_macro2::TokenStream = item.into();
15
16    let config = match parse::parse_config(&attr_tokens) {
17        Ok(c) => c,
18        Err(e) => return e.to_compile_error().into(),
19    };
20
21    let mut item_struct: syn::ItemStruct = match syn::parse2(item_tokens.clone()) {
22        Ok(s) => s,
23        Err(e) => return e.to_compile_error().into(),
24    };
25
26    // Reject generic structs
27    if !item_struct.generics.params.is_empty() {
28        return syn::Error::new(
29            item_struct.ident.span(),
30            "#[gpu_struct] does not support generic structs; \
31             remove generic parameters or use a concrete type",
32        )
33        .to_compile_error()
34        .into();
35    }
36
37    // Validate existing repr attributes
38    if let Err(e) = diagnostics::validate_repr(&item_struct, &config) {
39        return e.to_compile_error().into();
40    }
41
42    // Extract named fields
43    let fields = match &item_struct.fields {
44        syn::Fields::Named(f) => &f.named,
45        syn::Fields::Unit => {
46            return syn::Error::new(
47                item_struct.ident.span(),
48                "#[gpu_struct] cannot be applied to unit structs; add at least one field",
49            )
50            .to_compile_error()
51            .into();
52        }
53        _ => {
54            return syn::Error::new(
55                item_struct.ident.span(),
56                "#[gpu_struct] only supports structs with named fields",
57            )
58            .to_compile_error()
59            .into();
60        }
61    };
62
63    // Resolve field types
64    let mut resolved_fields: Vec<(syn::Ident, GpuType, proc_macro2::Span)> = Vec::new();
65    for field in fields {
66        let field_name = field.ident.clone().unwrap();
67        let field_span = syn::spanned::Spanned::span(&field.ty);
68
69        // Check for #[gpu_nested] attribute on this field
70        let is_gpu_nested = field.attrs.iter().any(|attr| {
71            attr.path().segments.len() == 1 && attr.path().segments[0].ident == "gpu_nested"
72        });
73
74        match types::resolve_type(&field.ty, &config, is_gpu_nested) {
75            Ok(gpu_type) => resolved_fields.push((field_name, gpu_type, field_span)),
76            Err(e) => return e.to_compile_error().into(),
77        }
78    }
79
80    // Compute layout
81    let field_layouts: Vec<_> = resolved_fields
82        .iter()
83        .map(|(name, gpu_type, _)| (name.clone(), gpu_type.clone()))
84        .collect();
85
86    let struct_layout = layout::compute_layout(&field_layouts, config.align, &config.targets);
87
88    // Generate output
89    let output = generate::generate(&mut item_struct, &config, &struct_layout, &resolved_fields);
90
91    output.into()
92}