resource_bound_derive/
lib.rs1
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#[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 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 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 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();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 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 if lifetime_attr.lt.len() > 0 && !heap_permission{
118 let (unsolved_type,unsolved_lt)=lifetime_attr.lt.iter().next().unwrap();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); return Err(e); }
130
131 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 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}