rustc_index_macros/
newtype.rs1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::parse::*;
4use syn::*;
5
6struct 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 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 if body.is_empty() {
81 break;
82 }
83
84 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 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 #vis const MAX_AS_U32: u32 = #max;
214
215 #vis const MAX: Self = Self::from_u32(#max);
217
218 #vis const ZERO: Self = Self::from_u32(0);
220
221 #[inline]
227 #vis const fn from_usize(value: usize) -> Self {
228 assert!(value <= (#max as usize));
229 unsafe {
231 Self::from_u32_unchecked(value as u32)
232 }
233 }
234
235 #[inline]
241 #vis const fn from_u32(value: u32) -> Self {
242 assert!(value <= #max);
243 unsafe {
245 Self::from_u32_unchecked(value)
246 }
247 }
248
249 #[inline]
255 #vis const fn from_u16(value: u16) -> Self {
256 let value = value as u32;
257 assert!(value <= #max);
258 unsafe {
260 Self::from_u32_unchecked(value)
261 }
262 }
263
264 #[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 #[inline]
279 #vis const fn index(self) -> usize {
280 self.as_usize()
281 }
282
283 #[inline]
285 #vis const fn as_u32(self) -> u32 {
286 unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
287 }
288
289 #[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}