typic_derive/
lib.rs

1#![allow(warnings)]
2extern crate proc_macro;
3
4use if_chain::*;
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use quote::*;
8use std::cmp::Ord;
9use std::cmp::{max, min};
10use syn;
11use syn::visit::Visit;
12use syn::{parse_macro_input, parse_quote};
13use syn::{Attribute, Lit, Meta, NestedMeta, Visibility};
14
15#[proc_macro_derive(StableABI)]
16pub fn stable_abi(input: TokenStream) -> TokenStream {
17    use syn::DeriveInput;
18    let DeriveInput {
19      ident,
20      generics,
21      ..
22    } = parse_macro_input!(input as DeriveInput);
23
24    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
25    (quote! {
26        unsafe impl #impl_generics typic::stability::TransmutableFrom
27        for #ident #ty_generics #where_clause
28        {
29            type Type = Self;
30        }
31
32        unsafe impl #impl_generics typic::stability::TransmutableInto
33        for #ident #ty_generics #where_clause
34        {
35            type Type = Self;
36        }
37    
38    }).into()
39}
40
41#[proc_macro_attribute]
42pub fn typicrepr(_args: TokenStream, input: TokenStream) -> TokenStream {
43    repr(_args, input)
44}
45
46fn impl_struct(definition: syn::ItemStruct) -> TokenStream {
47    let name = &definition.ident;
48    let attrs = &definition.attrs;
49    let generics = &definition.generics;
50
51    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
52
53    let all_public = definition.fields.iter().all(|field| {
54        if let Visibility::Public(_) = field.vis {
55            true
56        } else {
57            false
58        }
59    });
60
61    let mut repr = Repr::default();
62    attrs
63        .into_iter()
64        .for_each(|attr| repr.visit_attribute(attr));
65
66    if let Some(Method::Transparent) = repr.method {
67        return (quote! {
68          #definition
69
70          impl #impl_generics typic::internal::Type
71          for #name #ty_generics #where_clause
72          {
73            #[doc(hidden)]
74            type ReprAlign =
75                <#name #ty_generics as typic::internal::Type>::ReprAlign;
76
77            #[doc(hidden)]
78            type ReprPacked =
79                <#name #ty_generics as typic::internal::Type>::ReprPacked;
80
81            #[doc(hidden)]
82            type HighLevel =
83                <#name #ty_generics as typic::internal::Type>::HighLevel;
84          }
85        })
86        .into();
87    }
88
89    let repr_align = repr
90        .align
91        .map(|n| format_ident!("U{}", n))
92        .unwrap_or(format_ident!("MinAlign"));
93
94    let repr_packed = repr
95        .packed
96        .map(|n| format_ident!("U{}", n))
97        .unwrap_or(format_ident!("MaxAlign"));
98
99    // no repr
100    if let None = repr.method {
101        return (quote! {
102          #definition
103
104          impl #impl_generics typic::internal::Type
105          for #name #ty_generics #where_clause
106          {
107            #[doc(hidden)] type ReprAlign = typic::internal::#repr_align;
108            #[doc(hidden)] type ReprPacked = typic::internal::#repr_packed;
109            #[doc(hidden)] type HighLevel = Self;
110          }
111        })
112        .into();
113    }
114
115    // otherwise, it's a C repr
116    assert_eq!(repr.method, Some(Method::C));
117
118    let fields = definition
119        .fields
120        .iter()
121        .rfold(
122            quote! {typic::internal::PNil},
123            |rest, field| {
124              let vis = if let Visibility::Public(_) = field.vis {
125                format_ident!("Public")
126              } else {
127                format_ident!("Private")
128              };
129              let field = field.ty.clone();
130              quote! {
131                typic::internal::PCons<
132                  typic::internal::Field<
133                    typic::internal::field::#vis ,
134                    #field>,
135                  #rest>
136              }
137            },
138        );
139
140    (quote! {
141      #definition
142
143      impl #impl_generics typic::internal::Type
144      for #name #ty_generics #where_clause
145      {
146        #[doc(hidden)] type ReprAlign = typic::internal::#repr_align;
147        #[doc(hidden)] type ReprPacked = typic::internal::#repr_packed;
148        #[doc(hidden)] type HighLevel = #fields;
149      }
150    })
151    .into()
152}
153
154#[proc_macro_attribute]
155pub fn repr(args: TokenStream, input: TokenStream) -> TokenStream {
156    let args: TokenStream2 = args.into();
157    let input: TokenStream2 = input.into();
158    let definition: syn::Item = parse_quote!(#[repr(#args)] #input);
159
160    match definition {
161        syn::Item::Struct(definition) => impl_struct(definition),
162        _ => unimplemented!(),
163    }
164}
165
166#[derive(Debug, Eq, PartialEq, Clone, Copy)]
167enum Method {
168    C,
169    Packed,
170    Transparent,
171}
172
173#[derive(Debug, Eq, PartialEq, Clone, Copy)]
174enum Size {
175    I8,
176    I16,
177    I32,
178    I64,
179    I128,
180    ISize,
181    U8,
182    U16,
183    U32,
184    U64,
185    U128,
186    USize,
187}
188
189#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
190struct Repr {
191    method: Option<Method>,
192    align: Option<u32>,
193    packed: Option<u32>,
194    size: Option<Size>,
195}
196
197impl<'ast> Visit<'ast> for Repr {
198    fn visit_attribute(&mut self, attr: &'ast Attribute) {
199        if_chain! {
200            if let Ok(Meta::List(meta_list)) = attr.parse_meta();
201            if let Some(ident) = meta_list.path.get_ident();
202            if ident.to_string() == "repr";
203            then {
204                for meta in meta_list.nested {
205                    match meta {
206                        NestedMeta::Meta(Meta::Path(path)) => {
207                            let ident = (if let Some(ident) = path.get_ident() {
208                                ident.to_string()
209                            } else {
210                                continue;
211                            });
212
213                            match &ident[..] {
214                                "C" =>
215                                    self.method = Some(Method::C),
216                                "transparent" =>
217                                    self.method = Some(Method::Transparent),
218
219                                "packed" =>
220                                    self.packed = self.packed.min(Some(1)),
221
222                                "i8"    => self.size = Some(Size::I8),
223                                "i16"   => self.size = Some(Size::I16),
224                                "i32"   => self.size = Some(Size::I32),
225                                "i64"   => self.size = Some(Size::I64),
226                                "i132"  => self.size = Some(Size::I128),
227                                "isize" => self.size = Some(Size::ISize),
228                                "u8"    => self.size = Some(Size::U8),
229                                "u16"   => self.size = Some(Size::U16),
230                                "u32"   => self.size = Some(Size::U32),
231                                "u64"   => self.size = Some(Size::U64),
232                                "u132"  => self.size = Some(Size::U128),
233                                "usize" => self.size = Some(Size::USize),
234                                _ => {},
235                            }
236                        },
237                        NestedMeta::Meta(Meta::List(meta_list)) => {
238                            if_chain! {
239                                if let Some(ident) = meta_list.path.get_ident();
240                                if meta_list.nested.len() == 1;
241                                if let Some(n) =  meta_list.nested.first();
242                                if let NestedMeta::Lit(Lit::Int(n)) = n;
243                                let ident = ident.to_string();
244                                if let Ok(n) = n.base10_parse::<u32>();
245                                then {
246                                    match &ident[..] {
247                                        "align" => {
248                                            self.align = self.align.max(Some(n));
249                                        },
250                                        "packed" => {
251                                             self.packed = self.packed.min(Some(n));
252                                        }
253                                        _ => {}
254                                    }
255                                }
256                            }
257                        }
258                        _ => {},
259                    }
260                }
261            }
262        }
263    }
264}