extern crate proc_macro;
use proc_macro::{TokenStream};
use std::convert::TryInto;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Expr, FieldValue, Member};
use syn::parse::Parser;
fn inner_place_expr(member: proc_macro2::TokenStream, val: &Expr, nesting: u32) -> proc_macro2::TokenStream {
match val {
Expr::Array(arr) => {
let construction = arr
.elems
.iter()
.enumerate()
.map(|(i, item)| {
inner_place_expr(quote! { #member[#i] }, item, nesting + 1)
});
quote! {
#(#construction)*
}
},
Expr::Repeat(rep) => {
let v = &rep.expr;
let len = &rep.len;
let loop_var = format_ident!("i_{}", nesting);
let initializer = inner_place_expr(quote! { #member[#loop_var] }, &v, nesting + 1);
quote! {
for #loop_var in 0..#len {
#initializer
}
}
},
expr => {
quote! {
(&raw mut #member).write(#expr);
}
}
}
}
struct PlaceBoxedInput {
expr: syn::Expr,
ty: Option<syn::Type>,
}
impl syn::parse::Parse for PlaceBoxedInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let expr = input.parse::<syn::Expr>()?;
let ty = if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
Some(input.parse::<syn::Type>()?)
} else {
None
};
Ok(Self { expr, ty })
}
}
#[proc_macro]
pub fn place_boxed(input: TokenStream) -> TokenStream {
let inp = parse_macro_input!(input as PlaceBoxedInput);
let struct_expr = match inp.expr {
syn::Expr::Struct(s) => s,
e => {
let Some(ty) = inp.ty else {
return syn::Error::new_spanned(e, "Expected type argument for constructing non-structure type")
.to_compile_error()
.into();
};
let generated = inner_place_expr(quote!{ (*ptr) }, &e, 0);
return quote! {
unsafe {
let _ensure_correct = || { #e };
let mut res = std::boxed::Box::<#ty>::new_uninit();
let ptr = res.as_mut_ptr();
#generated
res.assume_init()
}
}.into()
}
};
if let Some(rest) = &struct_expr.rest {
return syn::Error::new_spanned(rest, "place_boxed limitation: Cannot use \
..Rest expressions in struct initializers")
.to_compile_error()
.into();
}
let path = &struct_expr.path;
let mut generated = Vec::new();
for field in &struct_expr.fields {
let name = &field.member;
generated.push(
inner_place_expr(
quote!{
(*ptr).#name
},
&field.expr, 0
)
)
}
quote! {
unsafe {
let _ensure_struct_correct = || {
#struct_expr
};
let mut res = std::boxed::Box::<#path>::new_uninit();
let ptr = res.as_mut_ptr();
#(#generated)*
res.assume_init()
}
}.into()
}