abi_stable_derive/stable_abi/
repr_attrs.rs

1#[allow(unused_imports)]
2use core_extensions::{matches, SelfOps};
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::parse::ParseBuffer;
6
7use as_derive_utils::{
8    parse_utils::ParseBufferExt, return_syn_err, syn_err, to_token_fn::ToTokenFnMut,
9};
10
11use crate::{ignored_wrapper::Ignored, literals_constructors::rslice_tokenizer};
12
13use super::common_tokens::CommonTokens;
14
15/// Used to parse ReprAttr from attributes.
16#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17pub struct UncheckedReprAttr {
18    is_aligned: Option<u32>,
19    is_packed: Option<u32>,
20    repr_kind: Option<UncheckedReprKind>,
21    repr_span: Ignored<Span>,
22    discriminant_repr: Option<DiscriminantRepr>,
23}
24
25impl Default for UncheckedReprAttr {
26    fn default() -> Self {
27        Self {
28            is_aligned: None,
29            is_packed: None,
30            repr_kind: None,
31            repr_span: Ignored::new(Span::call_site()),
32            discriminant_repr: None,
33        }
34    }
35}
36
37#[derive(Debug, Copy, Clone, PartialEq, Eq)]
38pub enum UncheckedReprKind {
39    C,
40    Transparent,
41    /// Means that only `repr(IntegerType)` was used.
42    Int,
43}
44
45/// How the discriminant of an enum is represented.
46#[repr(u8)]
47#[derive(Debug, Copy, Clone, PartialEq, Eq)]
48pub enum DiscriminantRepr {
49    U8,
50    I8,
51    U16,
52    I16,
53    U32,
54    I32,
55    U64,
56    I64,
57    Usize,
58    /// This is the default discriminant type for `repr(C)`.
59    Isize,
60}
61
62/// The representation attribute of the type.
63///
64/// This doesn't include `#[repr(align())]` since the alignment is
65/// stored as part of TypeLayout anyway.
66#[derive(Debug, Copy, Clone, PartialEq, Eq)]
67pub enum Repr {
68    C(Option<DiscriminantRepr>),
69    Transparent,
70    /// Means that only `repr(IntegerType)` was used.
71    Int(DiscriminantRepr),
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq)]
75pub struct ReprAttr {
76    pub span: Ignored<Span>,
77    pub is_aligned: Option<u32>,
78    pub is_packed: Option<u32>,
79    pub variant: Repr,
80}
81
82pub(crate) static REPR_ERROR_MSG: &str = "\n\
83    the #[repr(..)] attribute must be one of the supported attributes:\n\
84    \t- #[repr(C)]\n\
85    \t- #[repr(transparent)]\n\
86    \t- #[repr(integer_type_up_to_64_bits)]:enums only\n\
87    \t- #[repr(usize)]:enums only\n\
88    \t- #[repr(isize)]:enums only\n\
89    \t- #[repr(align(<some_integer>))]\n\
90";
91
92impl UncheckedReprAttr {
93    pub fn set_aligned(&mut self, alignment: u32) -> Result<(), syn::Error> {
94        self.is_aligned = Some(alignment);
95        Ok(())
96    }
97    pub fn set_packed(&mut self, packing: Option<u32>) -> Result<(), syn::Error> {
98        self.is_packed = packing.or(Some(1));
99        Ok(())
100    }
101    pub fn set_repr_kind(
102        &mut self,
103        repr_kind: UncheckedReprKind,
104        repr_span: proc_macro2::Span,
105    ) -> Result<(), syn::Error> {
106        if let Some(from) = self.discriminant_repr {
107            return_syn_err!(
108                repr_span,
109                "Attempting to override {:?} representation with {:?}.",
110                from,
111                repr_kind
112            );
113        }
114        self.repr_kind = Some(repr_kind);
115        self.repr_span.value = repr_span;
116        Ok(())
117    }
118    pub fn set_discriminant_repr(
119        &mut self,
120        discriminant_repr: DiscriminantRepr,
121        repr_span: proc_macro2::Span,
122    ) -> Result<(), syn::Error> {
123        if let Some(x) = self.discriminant_repr {
124            return_syn_err!(
125                repr_span,
126                "Attempting to override {:?} representation with {:?}.",
127                x,
128                discriminant_repr
129            );
130        }
131        self.repr_kind = self.repr_kind.or(Some(UncheckedReprKind::Int));
132        self.repr_span.value = repr_span;
133
134        self.discriminant_repr = Some(discriminant_repr);
135        Ok(())
136    }
137}
138
139mod kw {
140    syn::custom_keyword! {u8}
141    syn::custom_keyword! {i8}
142    syn::custom_keyword! {u16}
143    syn::custom_keyword! {i16}
144    syn::custom_keyword! {u32}
145    syn::custom_keyword! {i32}
146    syn::custom_keyword! {u64}
147    syn::custom_keyword! {i64}
148    syn::custom_keyword! {usize}
149    syn::custom_keyword! {isize}
150}
151
152impl DiscriminantRepr {
153    pub fn from_parser(input: &'_ ParseBuffer<'_>) -> Option<Self> {
154        if input.peek_parse(kw::u8).ok()?.is_some() {
155            Some(DiscriminantRepr::U8)
156        } else if input.peek_parse(kw::i8).ok()?.is_some() {
157            Some(DiscriminantRepr::I8)
158        } else if input.peek_parse(kw::u16).ok()?.is_some() {
159            Some(DiscriminantRepr::U16)
160        } else if input.peek_parse(kw::i16).ok()?.is_some() {
161            Some(DiscriminantRepr::I16)
162        } else if input.peek_parse(kw::u32).ok()?.is_some() {
163            Some(DiscriminantRepr::U32)
164        } else if input.peek_parse(kw::i32).ok()?.is_some() {
165            Some(DiscriminantRepr::I32)
166        } else if input.peek_parse(kw::u64).ok()?.is_some() {
167            Some(DiscriminantRepr::U64)
168        } else if input.peek_parse(kw::i64).ok()?.is_some() {
169            Some(DiscriminantRepr::I64)
170        } else if input.peek_parse(kw::usize).ok()?.is_some() {
171            Some(DiscriminantRepr::Usize)
172        } else if input.peek_parse(kw::isize).ok()?.is_some() {
173            Some(DiscriminantRepr::Isize)
174        } else {
175            None
176        }
177    }
178}
179
180impl ReprAttr {
181    pub fn new(unchecked: UncheckedReprAttr) -> Result<Self, syn::Error> {
182        let span = unchecked.repr_span;
183        let is_aligned = unchecked.is_aligned;
184        let is_packed = unchecked.is_packed;
185        let ura: UncheckedReprKind = unchecked
186            .repr_kind
187            .ok_or_else(|| syn_err!(*span, "{}", REPR_ERROR_MSG))?;
188        let dr: Option<DiscriminantRepr> = unchecked.discriminant_repr;
189        let variant = match (ura, dr) {
190            (UncheckedReprKind::C, x) => Repr::C(x),
191            (UncheckedReprKind::Transparent, None) => Repr::Transparent,
192            (UncheckedReprKind::Transparent, Some(_)) => {
193                return_syn_err!(
194                    *span,
195                    "repr(transparent) cannot be combined with repr(IntegerType)",
196                )
197            }
198            (UncheckedReprKind::Int, None) => panic!("Bug:(UncheckedReprKind::Int,None)"),
199            (UncheckedReprKind::Int, Some(x)) => Repr::Int(x),
200        };
201        Ok(Self {
202            span,
203            variant,
204            is_aligned,
205            is_packed,
206        })
207    }
208
209    /// Gets the type of the discriminant determined by this representation attribute.
210    /// Returns None if the representation is `#[repr(transparent)]`.
211    pub fn type_ident(&self) -> Option<syn::Ident> {
212        let int_repr = match self.variant {
213            Repr::C(None) => DiscriminantRepr::Isize,
214            Repr::C(Some(int_repr)) | Repr::Int(int_repr) => int_repr,
215            Repr::Transparent => return None,
216        };
217
218        let ty_lit = match int_repr {
219            DiscriminantRepr::U8 => "u8",
220            DiscriminantRepr::U16 => "u16",
221            DiscriminantRepr::U32 => "u32",
222            DiscriminantRepr::U64 => "u64",
223            DiscriminantRepr::I8 => "i8",
224            DiscriminantRepr::I16 => "i16",
225            DiscriminantRepr::I32 => "i32",
226            DiscriminantRepr::I64 => "i64",
227            DiscriminantRepr::Usize => "usize",
228            DiscriminantRepr::Isize => "isize",
229        };
230
231        Some(syn::Ident::new(ty_lit, Span::call_site()))
232    }
233
234    /// Returns a type which outputs a `DiscriminantRepr` with
235    /// a slice of the items in the iterator,
236    /// where each Option is unwrapped by replacing `None`s
237    /// with the value of the last `Some()` incremented by the distance to the current element.
238    pub(crate) fn tokenize_discriminant_exprs<'a, I>(
239        self,
240        exprs: I,
241        ctokens: &'a CommonTokens,
242    ) -> impl ToTokens + 'a
243    where
244        I: IntoIterator<Item = Option<&'a syn::Expr>> + 'a,
245    {
246        let mut exprs = exprs.into_iter();
247
248        ToTokenFnMut::new(move |ts| {
249            let int_repr = match self.variant {
250                Repr::C(x) => x,
251                Repr::Int(x) => Some(x),
252                Repr::Transparent => unreachable!(),
253            };
254
255            match int_repr.unwrap_or(DiscriminantRepr::Isize) {
256                DiscriminantRepr::U8 => quote!(__TLDiscriminants::from_u8_slice),
257                DiscriminantRepr::U16 => quote!(__TLDiscriminants::from_u16_slice),
258                DiscriminantRepr::U32 => quote!(__TLDiscriminants::from_u32_slice),
259                DiscriminantRepr::U64 => quote!(__TLDiscriminants::from_u64_slice),
260                DiscriminantRepr::I8 => quote!(__TLDiscriminants::from_i8_slice),
261                DiscriminantRepr::I16 => quote!(__TLDiscriminants::from_i16_slice),
262                DiscriminantRepr::I32 => quote!(__TLDiscriminants::from_i32_slice),
263                DiscriminantRepr::I64 => quote!(__TLDiscriminants::from_i64_slice),
264                DiscriminantRepr::Usize => quote!(__TLDiscriminants::from_usize_slice),
265                DiscriminantRepr::Isize => quote!(__TLDiscriminants::from_isize_slice),
266            }
267            .to_tokens(ts);
268
269            ctokens.paren.surround(ts, |ts| {
270                tokenize_discriminant_exprs_inner(&mut exprs, SliceType::RSlice, ctokens, ts);
271            });
272        })
273    }
274
275    /// Returns a type which outputs a slice with the items in the iterator,
276    /// where each Option is unwrapped by replacing `None`s
277    /// with the value of the last `Some()` incremented by the distance to the current element.
278    pub(crate) fn tokenize_discriminant_slice<'a, I>(
279        self,
280        exprs: I,
281        ctokens: &'a CommonTokens,
282    ) -> impl ToTokens + 'a
283    where
284        I: IntoIterator<Item = Option<&'a syn::Expr>> + 'a,
285    {
286        let mut exprs = exprs.into_iter();
287
288        ToTokenFnMut::new(move |ts| {
289            tokenize_discriminant_exprs_inner(&mut exprs, SliceType::StdSlice, ctokens, ts);
290        })
291    }
292}
293
294#[allow(dead_code)]
295impl ReprAttr {
296    pub fn span(self) -> Span {
297        *self.span
298    }
299
300    pub fn is_repr_transparent(self) -> bool {
301        matches!(self.variant, Repr::Transparent { .. })
302    }
303
304    pub fn is_repr_c(self) -> bool {
305        matches!(self.variant, Repr::C { .. })
306    }
307
308    pub fn is_repr_int(self) -> bool {
309        matches!(self.variant, Repr::Int { .. })
310    }
311}
312
313#[derive(Copy, Clone)]
314enum SliceType {
315    StdSlice,
316    RSlice,
317}
318
319/// Outputs the items in the iterator separated by commas,
320/// where each Option is unwrapped by replacing `None`s
321/// with the value of the last `Some()` incremented by the distance to the current element.
322fn tokenize_discriminant_exprs_inner<'a, I>(
323    exprs: I,
324    type_: SliceType,
325    ctokens: &'a CommonTokens,
326    ts: &mut TokenStream,
327) where
328    I: Iterator<Item = Option<&'a syn::Expr>>,
329{
330    let zero_expr = crate::utils::expr_from_int(0);
331    let mut last_explicit_discr = &zero_expr;
332    let mut since_last_expr = 0;
333
334    let iter = exprs.map(|expr| match expr {
335        Some(discr) => {
336            let ts = quote!(#discr);
337            last_explicit_discr = discr;
338            since_last_expr = 1;
339            ts
340        }
341        None => {
342            let offset = crate::utils::uint_lit(since_last_expr);
343            let ts = quote!( (#last_explicit_discr)+#offset );
344            since_last_expr += 1;
345            ts
346        }
347    });
348    match type_ {
349        SliceType::StdSlice => {
350            ctokens.and_.to_tokens(ts);
351            ctokens.bracket.surround(ts, |ts| {
352                for elem in iter {
353                    elem.to_tokens(ts);
354                    ctokens.comma.to_tokens(ts)
355                }
356            });
357        }
358        SliceType::RSlice => {
359            rslice_tokenizer(iter).to_tokens(ts);
360        }
361    }
362}
363
364impl ToTokens for ReprAttr {
365    fn to_tokens(&self, ts: &mut TokenStream) {
366        match self.variant {
367            Repr::C(None) => {
368                quote!(__ReprAttr::C)
369            }
370            Repr::C(Some(int_repr)) => {
371                let int_repr = discr_repr_tokenizer(int_repr);
372                quote!(__ReprAttr::CAndInt(#int_repr))
373            }
374            Repr::Transparent => {
375                quote!(__ReprAttr::Transparent)
376            }
377            Repr::Int(int_repr) => {
378                let int_repr = discr_repr_tokenizer(int_repr);
379                quote!(__ReprAttr::Int(#int_repr))
380            }
381        }
382        .to_tokens(ts);
383    }
384}
385
386fn discr_repr_tokenizer(repr: DiscriminantRepr) -> impl ToTokens {
387    ToTokenFnMut::new(move |ts| {
388        match repr {
389            DiscriminantRepr::U8 => quote!(__DiscriminantRepr::U8),
390            DiscriminantRepr::I8 => quote!(__DiscriminantRepr::I8),
391            DiscriminantRepr::U16 => quote!(__DiscriminantRepr::U16),
392            DiscriminantRepr::I16 => quote!(__DiscriminantRepr::I16),
393            DiscriminantRepr::U32 => quote!(__DiscriminantRepr::U32),
394            DiscriminantRepr::I32 => quote!(__DiscriminantRepr::I32),
395            DiscriminantRepr::U64 => quote!(__DiscriminantRepr::U64),
396            DiscriminantRepr::I64 => quote!(__DiscriminantRepr::I64),
397            DiscriminantRepr::Usize => quote!(__DiscriminantRepr::Usize),
398            DiscriminantRepr::Isize => quote!(__DiscriminantRepr::Isize),
399        }
400        .to_tokens(ts);
401    })
402}