]>
Commit | Line | Data |
---|---|---|
49aad941 FG |
1 | /// Implements [`Tag`] for a given type. |
2 | /// | |
3 | /// You can use `impl_tag` on structs and enums. | |
4 | /// You need to specify the type and all its possible values, | |
5 | /// which can only be paths with optional fields. | |
6 | /// | |
7 | /// [`Tag`]: crate::tagged_ptr::Tag | |
8 | /// | |
9 | /// # Examples | |
10 | /// | |
11 | /// Basic usage: | |
12 | /// | |
13 | /// ``` | |
14 | /// #![feature(macro_metavar_expr)] | |
15 | /// use rustc_data_structures::{impl_tag, tagged_ptr::Tag}; | |
16 | /// | |
17 | /// #[derive(Copy, Clone, PartialEq, Debug)] | |
18 | /// enum SomeTag { | |
19 | /// A, | |
20 | /// B, | |
21 | /// X { v: bool }, | |
22 | /// Y(bool, bool), | |
23 | /// } | |
24 | /// | |
25 | /// impl_tag! { | |
26 | /// // The type for which the `Tag` will be implemented | |
27 | /// impl Tag for SomeTag; | |
28 | /// // You need to specify all possible tag values: | |
29 | /// SomeTag::A, // 0 | |
30 | /// SomeTag::B, // 1 | |
31 | /// // For variants with fields, you need to specify the fields: | |
32 | /// SomeTag::X { v: true }, // 2 | |
33 | /// SomeTag::X { v: false }, // 3 | |
34 | /// // For tuple variants use named syntax: | |
35 | /// SomeTag::Y { 0: true, 1: true }, // 4 | |
36 | /// SomeTag::Y { 0: false, 1: true }, // 5 | |
37 | /// SomeTag::Y { 0: true, 1: false }, // 6 | |
38 | /// SomeTag::Y { 0: false, 1: false }, // 7 | |
39 | /// } | |
40 | /// | |
41 | /// // Tag values are assigned in order: | |
42 | /// assert_eq!(SomeTag::A.into_usize(), 0); | |
43 | /// assert_eq!(SomeTag::X { v: false }.into_usize(), 3); | |
44 | /// assert_eq!(SomeTag::Y(false, true).into_usize(), 5); | |
45 | /// | |
46 | /// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B); | |
47 | /// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true }); | |
48 | /// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false)); | |
49 | /// ``` | |
50 | /// | |
51 | /// Structs are supported: | |
52 | /// | |
53 | /// ``` | |
54 | /// #![feature(macro_metavar_expr)] | |
55 | /// # use rustc_data_structures::impl_tag; | |
56 | /// #[derive(Copy, Clone)] | |
57 | /// struct Flags { a: bool, b: bool } | |
58 | /// | |
59 | /// impl_tag! { | |
60 | /// impl Tag for Flags; | |
61 | /// Flags { a: true, b: true }, | |
62 | /// Flags { a: false, b: true }, | |
63 | /// Flags { a: true, b: false }, | |
64 | /// Flags { a: false, b: false }, | |
65 | /// } | |
66 | /// ``` | |
67 | /// | |
68 | /// Not specifying all values results in a compile error: | |
69 | /// | |
70 | /// ```compile_fail,E0004 | |
71 | /// #![feature(macro_metavar_expr)] | |
72 | /// # use rustc_data_structures::impl_tag; | |
73 | /// #[derive(Copy, Clone)] | |
74 | /// enum E { | |
75 | /// A, | |
76 | /// B, | |
77 | /// } | |
78 | /// | |
79 | /// impl_tag! { | |
80 | /// impl Tag for E; | |
81 | /// E::A, | |
82 | /// } | |
83 | /// ``` | |
84 | #[macro_export] | |
85 | macro_rules! impl_tag { | |
86 | ( | |
87 | impl Tag for $Self:ty; | |
88 | $( | |
89 | $($path:ident)::* $( { $( $fields:tt )* })?, | |
90 | )* | |
91 | ) => { | |
92 | // Safety: | |
93 | // `bits_for_tags` is called on the same `${index()}`-es as | |
94 | // `into_usize` returns, thus `BITS` constant is correct. | |
95 | unsafe impl $crate::tagged_ptr::Tag for $Self { | |
96 | const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[ | |
97 | $( | |
98 | ${index()}, | |
99 | $( ${ignore(path)} )* | |
100 | )* | |
101 | ]); | |
102 | ||
103 | #[inline] | |
104 | fn into_usize(self) -> usize { | |
105 | // This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc) | |
106 | // (or at least it should, see <https://github.com/rust-lang/rust/issues/110613>) | |
107 | #[forbid(unreachable_patterns)] | |
108 | match self { | |
109 | // `match` is doing heavy lifting here, by requiring exhaustiveness | |
110 | $( | |
111 | $($path)::* $( { $( $fields )* } )? => ${index()}, | |
112 | )* | |
113 | } | |
114 | } | |
115 | ||
116 | #[inline] | |
117 | unsafe fn from_usize(tag: usize) -> Self { | |
118 | match tag { | |
119 | $( | |
120 | ${index()} => $($path)::* $( { $( $fields )* } )?, | |
121 | )* | |
122 | ||
123 | // Safety: | |
124 | // `into_usize` only returns `${index()}` of the same | |
125 | // repetition as we are filtering above, thus if this is | |
126 | // reached, the safety contract of this function was | |
127 | // already breached. | |
128 | _ => unsafe { | |
129 | debug_assert!( | |
130 | false, | |
131 | "invalid tag: {tag}\ | |
132 | (this is a bug in the caller of `from_usize`)" | |
133 | ); | |
134 | std::hint::unreachable_unchecked() | |
135 | }, | |
136 | } | |
137 | } | |
138 | ||
139 | } | |
140 | }; | |
141 | } | |
142 | ||
143 | #[cfg(test)] | |
144 | mod tests; |