resource_bound_derive/
lib.rs

1
2mod outer_attr;
3
4use std::collections::HashMap;
5
6use proc_macro::{TokenStream};
7use quote::quote;
8use syn::{DeriveInput, Fields, Type,spanned::Spanned};
9
10use outer_attr::OuterAttributes;
11
12
13/// Enforces compile-time resource constraints on structs.
14///
15/// See the crate README for details and examples.
16
17#[proc_macro_derive(ResourceBound, attributes(size_limit, allow_heap))]
18pub fn resource_derive(input: TokenStream) -> TokenStream {
19    let input = syn::parse_macro_input!(input as DeriveInput);
20
21    match expand_resource(&input) {
22        Ok(ts) => ts,
23        Err(e) => e.to_compile_error().into(),
24    }
25}
26
27fn expand_resource(input: &DeriveInput) -> syn::Result<TokenStream> {
28    let name = &input.ident;
29    let fields;
30    // 1️⃣ Validate target
31    match &input.data {
32        syn::Data::Struct(ds) => {fields=&ds.fields}
33        syn::Data::Enum(_) => {
34            return Err(syn::Error::new_spanned(
35                input,
36                "ResourceBound cannot be derived for enums",
37            ));
38        }
39        syn::Data::Union(_) => {
40            return Err(syn::Error::new_spanned(
41                input,
42                "ResourceBound cannot be derived for unions",
43            ));
44        }
45    }
46
47  
48
49   
50    // 2️⃣ Parse outer attributes
51    let mut outer_attrs = OuterAttributes::new();
52    for attr in &input.attrs {
53        outer_attr::parse_struct_attrs(&mut outer_attrs, attr)?;
54    }
55
56
57    //extract data
58    let mut lifetime_attr=LifetimeAttr{  lt:HashMap::new()   };
59    let size_limit=match outer_attrs.get_max_size() {
60        Some(s)=>s,
61        None=>0    
62    };
63    let heap_permission=outer_attrs.get_heap_allo();//false by default if not explicitly defined
64    if !heap_permission{
65        map_fields(&fields,&mut outer_attrs,&mut lifetime_attr)?;
66    }
67    let mut field_ctr=0;
68    let field_types=outer_attrs.get_types();
69    let field_assert=field_types.iter().map(move |ty|{
70        field_ctr+=1;
71        let ctr_name=String::from("FIELD")+&field_ctr.to_string().to_ascii_uppercase()+&name.to_string().to_ascii_uppercase()[..];
72        let ctr_name=syn::Ident::new(&ctr_name,ty.span());
73        let assert_fn_name=String::from("assert_fn")+&field_ctr.to_string()[..]+&name.to_string().to_ascii_lowercase()[..];
74        let assert_fn_name=syn::Ident::new(&assert_fn_name[..], ty.span());
75        quote! {
76            
77                const #ctr_name: () ={
78                    const fn #assert_fn_name<T:StackOnly>(){};
79                    #assert_fn_name::<#ty>();
80                    ()
81                };
82        }
83    });
84
85    let heap_assert_code=if !heap_permission {
86        quote! {
87           #(#field_assert)* 
88        }
89    }else{
90        quote! {
91            
92        }
93    };
94    
95    
96    
97
98    //Lifetimes
99      //lifetimes
100    let lifetimes=input.generics.lifetimes();
101    let mut lifetimes_names=vec![];
102    lifetimes.for_each(|lt|{
103        lifetimes_names.push(lt.lifetime.clone());
104    });
105
106    let lifetimes_code=quote! {
107        <#(#lifetimes_names),*>
108    };
109
110    //let c=lifetimes_use.to_string();                //dev only
111    //let e=syn::Error::new(c.span(), c);          //dev only
112    //return Err(e);                               //dev only
113    
114    //rules check
115
116    //rule1: &T must be annotated with allow_heap=true
117    if lifetime_attr.lt.len() > 0 && !heap_permission{
118        let (unsolved_type,unsolved_lt)=lifetime_attr.lt.iter().next().unwrap();//safe unwrap due to prev condition
119        let unsolved_lt=unsolved_lt.to_string();
120        let error_msg=format!("if you are borrowing '&T'
121{} then allocating mechanism is unknown even if `T` implements `StackOnly`
122 , so struct {} must be annotated with  `allow_heap=true`.
123 
124 consider feature :
125 #[allow_heap=true]",unsolved_lt,name);
126        let e=syn::Error::new(unsolved_type.span(),error_msg);          //dev only
127        return Err(e);                               //dev only
128        
129    }     
130
131    // Expansion - code
132
133    let expanded_limit=quote! {
134        
135        #heap_assert_code
136        impl #lifetimes_code #name #lifetimes_code {
137            #[doc(hidden)]
138            const __RESOURCE_BOUND_ASSERTION_FAILED_BECAUSE_THE_STRUCT_SIZE_IS_MORE_THAN_THE_EXPLICITLY_DEFINED_VALUE: [(); 0] =
139                [(); (core::mem::size_of::<Self>() <= #size_limit) as usize - 1];
140        }
141
142    };
143    
144    // 3️⃣ Generate code
145    Ok(expanded_limit
146    .into())
147}
148
149
150fn map_fields(fields:&Fields,mutable:&mut OuterAttributes,field_attr:&mut LifetimeAttr)->syn::Result<()>{
151    
152    for field in fields{
153        let ty=&field.ty;
154        mutable.push_type(ty.clone());
155        match ty{
156            Type::Reference(rf)=>{
157                match &rf.lifetime{
158                    Some(lt)=>{
159                        field_attr.lt.insert(ty.clone(), lt.clone());
160                    },
161                    None=>{
162
163                    },
164                }
165                
166            },
167            _=>{}
168        }
169    }
170
171
172    Ok(())
173}
174
175struct LifetimeAttr{
176    lt:HashMap<syn::Type,syn::Lifetime>,
177}