Skip to main content

rustc_index_macros/
newtype.rs

1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::parse::*;
4use syn::*;
5
6// We parse the input and emit the output in a single step.
7// This field stores the final macro output
8struct Newtype(TokenStream);
9
10impl Parse for Newtype {
11    fn parse(input: ParseStream<'_>) -> Result<Self> {
12        let mut attrs = input.call(Attribute::parse_outer)?;
13        let vis: Visibility = input.parse()?;
14        input.parse::<Token![struct]>()?;
15        let name: Ident = input.parse()?;
16
17        let body;
18        braced!(body in input);
19
20        // Any additional `#[derive]` macro paths to apply
21        let mut debug_format: Option<Lit> = None;
22        let mut max = None;
23        let mut consts = Vec::new();
24        let mut encodable = false;
25        let mut ord = false;
26        let mut stable_hash = false;
27        let mut gate_rustc_only = quote! {};
28        let mut gate_rustc_only_cfg = quote! { all() };
29
30        attrs.retain(|attr| match attr.path().get_ident() {
31            Some(ident) => match &*ident.to_string() {
32                "gate_rustc_only" => {
33                    gate_rustc_only = quote! { #[cfg(feature = "nightly")] };
34                    gate_rustc_only_cfg = quote! { feature = "nightly" };
35                    false
36                }
37                "encodable" => {
38                    encodable = true;
39                    false
40                }
41                "orderable" => {
42                    ord = true;
43                    false
44                }
45                "stable_hash" => {
46                    stable_hash = true;
47                    false
48                }
49                "max" => {
50                    let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. }) = &attr.meta
51                    else {
52                        panic!("#[max = NUMBER] attribute requires max value");
53                    };
54
55                    if let Some(old) = max.replace(lit.lit.clone()) {
56                        panic!("Specified multiple max: {old:?}");
57                    }
58
59                    false
60                }
61                "debug_format" => {
62                    let Meta::NameValue(MetaNameValue { value: Expr::Lit(lit), .. }) = &attr.meta
63                    else {
64                        panic!("#[debug_format = FMT] attribute requires a format");
65                    };
66
67                    if let Some(old) = debug_format.replace(lit.lit.clone()) {
68                        panic!("Specified multiple debug format options: {old:?}");
69                    }
70
71                    false
72                }
73                _ => true,
74            },
75            _ => true,
76        });
77
78        loop {
79            // We've parsed everything that the user provided, so we're done
80            if body.is_empty() {
81                break;
82            }
83
84            // Otherwise, we are parsing a user-defined constant
85            let const_attrs = body.call(Attribute::parse_outer)?;
86            body.parse::<Token![const]>()?;
87            let const_name: Ident = body.parse()?;
88            body.parse::<Token![=]>()?;
89            let const_val: Expr = body.parse()?;
90            body.parse::<Token![;]>()?;
91            consts.push(quote! { #(#const_attrs)* #vis const #const_name: #name = #name::from_u32(#const_val); });
92        }
93
94        let debug_format =
95            debug_format.unwrap_or_else(|| Lit::Str(LitStr::new("{}", Span::call_site())));
96
97        // shave off 256 indices at the end to allow space for packing these indices into enums
98        let max = max.unwrap_or_else(|| Lit::Int(LitInt::new("0xFFFF_FF00", Span::call_site())));
99
100        let encodable_impls = if encodable {
101            quote! {
102                #gate_rustc_only
103                impl<D: ::rustc_serialize::Decoder> ::rustc_serialize::Decodable<D> for #name {
104                    fn decode(d: &mut D) -> Self {
105                        Self::from_u32(d.read_u32())
106                    }
107                }
108                #gate_rustc_only
109                impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name {
110                    fn encode(&self, e: &mut E) {
111                        e.emit_u32(self.as_u32());
112                    }
113                }
114            }
115        } else {
116            quote! {}
117        };
118        let step = if ord {
119            quote! {
120                #gate_rustc_only
121                impl ::std::iter::Step for #name {
122                    #[inline]
123                    fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
124                        <usize as ::std::iter::Step>::steps_between(
125                            &Self::index(*start),
126                            &Self::index(*end),
127                        )
128                    }
129
130                    #[inline]
131                    fn forward_checked(start: Self, u: usize) -> Option<Self> {
132                        Self::index(start).checked_add(u).map(Self::from_usize)
133                    }
134
135                    #[inline]
136                    fn backward_checked(start: Self, u: usize) -> Option<Self> {
137                        Self::index(start).checked_sub(u).map(Self::from_usize)
138                    }
139
140                    #[inline]
141                    #[cfg(not(bootstrap))]
142                    fn forward_overflowing(start: Self, u: usize) -> (Self, bool) {
143                        let (s, o) = Self::index(start).overflowing_add(u);
144                        (Self::from_usize(s), o)
145                    }
146
147                    #[inline]
148                    #[cfg(not(bootstrap))]
149                    fn backward_overflowing(start: Self, u: usize) -> (Self, bool) {
150                        let (s, o) = Self::index(start).overflowing_sub(u);
151                        (Self::from_usize(s), o)
152                    }
153                }
154                impl ::std::cmp::Ord for #name {
155                    #[inline]
156                    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
157                        self.as_u32().cmp(&other.as_u32())
158                    }
159                }
160                impl ::std::cmp::PartialOrd for #name {
161                    #[inline]
162                    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
163                        Some(self.cmp(other))
164                    }
165                }
166            }
167        } else {
168            quote! {}
169        };
170
171        let stable_hash_impl = if stable_hash {
172            quote! {
173                #gate_rustc_only
174                impl ::rustc_data_structures::stable_hash::StableHash for #name {
175                    fn stable_hash<
176                        __Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt
177                    >(
178                        &self,
179                        hcx: &mut __Hcx,
180                        hasher: &mut ::rustc_data_structures::stable_hash::StableHasher
181                    ) {
182                        self.as_u32().stable_hash(hcx, hasher)
183                    }
184                }
185            }
186        } else {
187            quote! {}
188        };
189
190        let debug_impl = quote! {
191            impl ::std::fmt::Debug for #name {
192                fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
193                    write!(fmt, #debug_format, self.as_u32())
194                }
195            }
196        };
197
198        Ok(Self(quote! {
199            #(#attrs)*
200            #[derive(Clone, Copy)]
201            #[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)]
202            #vis struct #name {
203                #[cfg(not(#gate_rustc_only_cfg))]
204                private_use_as_methods_instead: u32,
205                #[cfg(#gate_rustc_only_cfg)]
206                private_use_as_methods_instead: pattern_type!(u32 is 0..=#max),
207            }
208
209            #(#consts)*
210
211            impl #name {
212                /// Maximum value the index can take, as a `u32`.
213                #vis const MAX_AS_U32: u32  = #max;
214
215                /// Maximum value the index can take.
216                #vis const MAX: Self = Self::from_u32(#max);
217
218                /// Zero value of the index.
219                #vis const ZERO: Self = Self::from_u32(0);
220
221                /// Creates a new index from a given `usize`.
222                ///
223                /// # Panics
224                ///
225                /// Will panic if `value` exceeds `MAX`.
226                #[inline]
227                #vis const fn from_usize(value: usize) -> Self {
228                    assert!(value <= (#max as usize));
229                    // SAFETY: We just checked that `value <= max`.
230                    unsafe {
231                        Self::from_u32_unchecked(value as u32)
232                    }
233                }
234
235                /// Creates a new index from a given `u32`.
236                ///
237                /// # Panics
238                ///
239                /// Will panic if `value` exceeds `MAX`.
240                #[inline]
241                #vis const fn from_u32(value: u32) -> Self {
242                    assert!(value <= #max);
243                    // SAFETY: We just checked that `value <= max`.
244                    unsafe {
245                        Self::from_u32_unchecked(value)
246                    }
247                }
248
249                /// Creates a new index from a given `u16`.
250                ///
251                /// # Panics
252                ///
253                /// Will panic if `value` exceeds `MAX`.
254                #[inline]
255                #vis const fn from_u16(value: u16) -> Self {
256                    let value = value as u32;
257                    assert!(value <= #max);
258                    // SAFETY: We just checked that `value <= max`.
259                    unsafe {
260                        Self::from_u32_unchecked(value)
261                    }
262                }
263
264                /// Creates a new index from a given `u32`.
265                ///
266                /// # Safety
267                ///
268                /// The provided value must be less than or equal to the maximum value for the newtype.
269                /// Providing a value outside this range is undefined due to layout restrictions.
270                ///
271                /// Prefer using `from_u32`.
272                #[inline]
273                #vis const unsafe fn from_u32_unchecked(value: u32) -> Self {
274                    Self { private_use_as_methods_instead: unsafe { std::mem::transmute(value) } }
275                }
276
277                /// Extracts the value of this index as a `usize`.
278                #[inline]
279                #vis const fn index(self) -> usize {
280                    self.as_usize()
281                }
282
283                /// Extracts the value of this index as a `u32`.
284                #[inline]
285                #vis const fn as_u32(self) -> u32 {
286                    unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
287                }
288
289                /// Extracts the value of this index as a `usize`.
290                #[inline]
291                #vis const fn as_usize(self) -> usize {
292                    self.as_u32() as usize
293                }
294            }
295
296            impl std::ops::Add<usize> for #name {
297                type Output = Self;
298
299                #[inline]
300                fn add(self, other: usize) -> Self {
301                    Self::from_usize(self.index() + other)
302                }
303            }
304
305            impl std::ops::AddAssign<usize> for #name {
306                #[inline]
307                fn add_assign(&mut self, other: usize) {
308                    *self = *self + other;
309                }
310            }
311
312            impl rustc_index::Idx for #name {
313                #[inline]
314                fn new(value: usize) -> Self {
315                    Self::from_usize(value)
316                }
317
318                #[inline]
319                fn index(self) -> usize {
320                    self.as_usize()
321                }
322            }
323
324            #step
325
326            #stable_hash_impl
327
328            impl From<#name> for u32 {
329                #[inline]
330                fn from(v: #name) -> u32 {
331                    v.as_u32()
332                }
333            }
334
335            impl From<#name> for usize {
336                #[inline]
337                fn from(v: #name) -> usize {
338                    v.as_usize()
339                }
340            }
341
342            impl From<usize> for #name {
343                #[inline]
344                fn from(value: usize) -> Self {
345                    Self::from_usize(value)
346                }
347            }
348
349            impl From<u32> for #name {
350                #[inline]
351                fn from(value: u32) -> Self {
352                    Self::from_u32(value)
353                }
354            }
355
356            impl ::std::cmp::Eq for #name {}
357
358            impl ::std::cmp::PartialEq for #name {
359                fn eq(&self, other: &Self) -> bool {
360                    self.as_u32().eq(&other.as_u32())
361                }
362            }
363
364            #gate_rustc_only
365            impl ::std::marker::StructuralPartialEq for #name {}
366
367            impl ::std::hash::Hash for #name {
368                fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
369                    self.as_u32().hash(state)
370                }
371            }
372
373            #encodable_impls
374            #debug_impl
375        }))
376    }
377}
378
379pub(crate) fn newtype(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
380    let input = parse_macro_input!(input as Newtype);
381    input.0.into()
382}