1use std::str::FromStr;
5
6use litrs::{BoolLit, Literal};
7use proc_macro2::{Span, TokenStream};
8use quote::{quote, quote_spanned, ToTokens};
9use syn::{
10 parse_quote, parse_quote_spanned,
11 spanned::Spanned,
12 visit_mut::{self, VisitMut},
13 Expr, ExprCall, ExprPath, ExprStruct, Token,
14};
15
16use crate::expr_helpers::ExprCallExt;
17
18extern crate proc_macro;
19
20mod expr_helpers;
21
22#[proc_macro]
34pub fn boxify(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
35 let value_to_box = syn::parse_macro_input!(item as Expr);
36
37 boxify_impl(value_to_box).into()
38}
39
40fn boxify_impl(value_to_box: Expr) -> TokenStream {
41 if let Some(zero_alloc) = zero_alloc_special_handling(&value_to_box) {
42 return zero_alloc;
43 }
44
45 let validate_fields = validate_fields(value_to_box.clone());
46
47 let final_value_ptr = parse_quote! {
48 __boxify_final_value_ptr
49 };
50 let instantiation_code = match &value_to_box {
51 Expr::Struct(_) | Expr::Repeat(_) | Expr::Tuple(_) | Expr::Call(_) | Expr::Path(_) => {
54 fill_ptr(&final_value_ptr, &value_to_box)
55 }
56 _ => unimplemented!("Unsupported input type"),
57 };
58
59 quote! {{
60 let mut __boxify_final_value = ::boxify::new_box_uninit_typed(#validate_fields);
61 let __boxify_final_value_ptr = __boxify_final_value.as_mut_ptr();
62
63 #instantiation_code
64
65 unsafe { ::boxify::assume_init(__boxify_final_value) }
67 }}
68}
69
70fn zero_alloc_special_handling(value_to_box: &Expr) -> Option<TokenStream> {
72 if let Expr::Repeat(array) = value_to_box {
73 let value_expr = &*array.expr;
74 if let Ok(literal) = Literal::parse(value_expr.to_token_stream().to_string()) {
76 let is_zero = match &literal {
77 Literal::Integer(i) if i.value::<u128>() == Some(0) => true,
78 Literal::Bool(BoolLit::False) => true,
79 Literal::Float(f) if f64::from_str(f.number_part()) == Ok(0f64) => true,
80 Literal::Char(c) if c.value() == '\0' => true,
81 Literal::Byte(b) if b.value() == 0 => true,
82 _ => false,
83 };
84
85 if is_zero {
87 return Some(quote! {
88 unsafe {
90 ::boxify::new_box_zeroed_typed(::boxify::TypeInferer::new(|| {
92 #array
93 }))
94 }
95 });
96 }
97 }
98 }
99 None
100}
101
102struct CloneType;
105
106impl VisitMut for CloneType {
107 fn visit_field_value_mut(&mut self, v: &mut syn::FieldValue) {
108 if v.colon_token.is_none() {
109 v.colon_token = Some(Token));
111 }
112
113 let expr = &v.expr;
114 v.expr = parse_quote! {
116 unsafe { ::boxify::clone(&#expr) }
119 };
120 visit_mut::visit_field_value_mut(self, v);
121 }
122}
123
124fn validate_fields(mut expr: Expr) -> proc_macro2::TokenStream {
126 if let Expr::Struct(ExprStruct {
127 dot2_token: Some(dotdot),
128 ..
129 }) = &mut expr
130 {
131 return syn::Error::new(dotdot.span(), "Struct update syntax is not supported")
132 .into_compile_error();
133 }
134
135 CloneType.visit_expr_mut(&mut expr);
136
137 quote! {
139 ::boxify::TypeInferer::new(||
140 {
141 #expr
142 })
143 }
144}
145
146fn validate_not_fn(expr: &ExprCall) -> TokenStream {
151 let mut clone = expr.clone();
153 CloneType.visit_expr_call_mut(&mut clone);
154
155 let mut match_expr = expr.clone();
160 match_expr.replace_params(parse_quote! { _ });
161
162 quote_spanned! {expr.span()=> {
163 ::boxify::TypeInferer::new(||
164 {
165 match #clone {
167 #match_expr => {}
168 }
169 });
170 }}
171}
172
173fn fill_ptr(ptr: &Expr, value: &Expr) -> proc_macro2::TokenStream {
178 match value {
179 Expr::Repeat(array) => fill_array(ptr, array),
183 Expr::Struct(strct) => fill_struct_fields(ptr, strct),
184 Expr::Tuple(tuple) => fill_tuple(ptr, tuple.span(), &tuple.elems),
185 Expr::Call(call) => {
186 if let Expr::Path(ExprPath { path, .. }) = &*call.func {
187 let ident = path.segments.last().expect("empty path not supported");
188 let first_char = ident
189 .ident
190 .to_string()
191 .chars()
192 .next()
193 .expect("empty ident not supported");
194 if first_char.is_uppercase() {
195 let validate_not_fn_call = validate_not_fn(call);
198 let fill_code = fill_tuple(ptr, call.span(), &call.args);
199
200 quote! {{
201 #validate_not_fn_call
202 #fill_code
203 }}
204 } else {
205 quote! {
210 unsafe { #ptr.write_unaligned(#value); }
212 }
213 }
214 } else {
215 unimplemented!("Function calls are not supported")
216 }
217 }
218 e => {
219 quote! {
222 unsafe { #ptr.write_unaligned(#e); }
224 }
225 }
226 }
227}
228
229fn fill_struct_fields(strct_ptr: &Expr, strct: &syn::ExprStruct) -> proc_macro2::TokenStream {
231 let instantiation_codes = strct.fields.iter().map(|field| {
232 let ident = &field.member;
233 let expr = &field.expr;
234
235 let field_ptr = parse_quote! {
236 ::core::ptr::addr_of_mut!((*#strct_ptr).#ident)
237 };
238 fill_ptr(&field_ptr, expr)
239 });
240 quote! {
241 #(#instantiation_codes);*
242 }
243}
244
245fn fill_array(ptr: &Expr, array: &syn::ExprRepeat) -> proc_macro2::TokenStream {
246 let value = &*array.expr;
247 quote! {
248 unsafe { ::boxify::fill_array(#ptr, #value); }
250 }
251}
252
253fn fill_tuple(
255 ptr: &Expr,
256 span: Span,
257 elems: &syn::punctuated::Punctuated<Expr, syn::token::Comma>,
258) -> proc_macro2::TokenStream {
259 let instantiation_codes = elems.iter().enumerate().map(|(index, value)| {
260 let index = syn::Index::from(index);
261 let field_ptr = parse_quote_spanned! {value.span()=>
262 ::core::ptr::addr_of_mut!((*#ptr).#index)
263 };
264 fill_ptr(&field_ptr, value)
265 });
266 quote_spanned! {span=>
267 #(#instantiation_codes);*
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use syn::ExprStruct;
274
275 use super::*;
276
277 #[test]
278 fn test_validate_fields() {
279 let expr = parse_quote! {
280 Parent {
281 child: Child {
282 value: 42,
283 grand_child: GrandChild {
284 vec: v,
285 huge_array: [42; 1024 * 1024 * 1024],
286 },
287 },
288 }
289 };
290 let tokens = validate_fields(expr);
291
292 let expected: TokenStream = quote! {
293 ::boxify::TypeInferer::new(|| {
294 Parent {
295 child: unsafe { ::boxify::clone(&Child {
296 value: unsafe { ::boxify::clone(&42) },
297 grand_child: unsafe { ::boxify::clone(&GrandChild {
298 vec: unsafe { ::boxify::clone(&v) },
299 huge_array: unsafe { ::boxify::clone(&[42; 1024 * 1024 * 1024]) },
300 }) },
301 }) },
302 }
303 })
304 };
305 assert_eq!(tokens.to_string(), expected.to_string());
306 }
307
308 #[test]
309 fn boxify_short_form() {
310 let mut a: ExprStruct = parse_quote!(Foo { a });
311 let mut b: ExprStruct = parse_quote!(Foo { a: a });
312
313 let expected: ExprStruct = parse_quote!(Foo {
314 a: unsafe { ::boxify::clone(&a) }
315 });
316
317 CloneType.visit_expr_struct_mut(&mut a);
318 CloneType.visit_expr_struct_mut(&mut b);
319
320 assert_eq!(
321 a.to_token_stream().to_string(),
322 expected.to_token_stream().to_string()
323 );
324 assert_eq!(
325 b.to_token_stream().to_string(),
326 expected.to_token_stream().to_string()
327 );
328 }
329
330 #[test]
331 fn trait_fn_call() {
332 let e: Expr = parse_quote!(La {
333 ma: -42i128,
334 na: E {
335 f: &[42u64; 10],
336 g: -42i32,
337 h: false,
338 i: &[42u64; 10],
339 j: String::from("a string"),
340 __phantom: ::core::marker::PhantomData::<&()>
341 },
342 __phantom: ::core::marker::PhantomData::<&()>
343 });
344
345 boxify_impl(e);
346 }
347}