81
81
/// E::A,
82
82
/// }
83
83
/// ```
84
+ #[ cfg( bootstrap) ]
84
85
#[ macro_export]
85
86
macro_rules! impl_tag {
86
87
(
@@ -93,12 +94,20 @@ macro_rules! impl_tag {
93
94
// `bits_for_tags` is called on the same `${index()}`-es as
94
95
// `into_usize` returns, thus `BITS` constant is correct.
95
96
unsafe impl $crate:: tagged_ptr:: Tag for $Self {
97
+ #[ cfg( bootstrap) ]
96
98
const BITS : u32 = $crate:: tagged_ptr:: bits_for_tags( & [
97
99
$(
98
100
${ index( ) } ,
99
101
$( ${ ignore( path) } ) *
100
102
) *
101
103
] ) ;
104
+ #[ cfg( not( bootstrap) ) ]
105
+ const BITS : u32 = $crate:: tagged_ptr:: bits_for_tags( & [
106
+ $(
107
+ ${ index( ) } ,
108
+ $( ${ ignore( $path) } ) *
109
+ ) *
110
+ ] ) ;
102
111
103
112
#[ inline]
104
113
fn into_usize( self ) -> usize {
@@ -140,5 +149,149 @@ macro_rules! impl_tag {
140
149
} ;
141
150
}
142
151
152
+ /// Implements [`Tag`] for a given type.
153
+ ///
154
+ /// You can use `impl_tag` on structs and enums.
155
+ /// You need to specify the type and all its possible values,
156
+ /// which can only be paths with optional fields.
157
+ ///
158
+ /// [`Tag`]: crate::tagged_ptr::Tag
159
+ ///
160
+ /// # Examples
161
+ ///
162
+ /// Basic usage:
163
+ ///
164
+ /// ```
165
+ /// #![feature(macro_metavar_expr)]
166
+ /// use rustc_data_structures::{impl_tag, tagged_ptr::Tag};
167
+ ///
168
+ /// #[derive(Copy, Clone, PartialEq, Debug)]
169
+ /// enum SomeTag {
170
+ /// A,
171
+ /// B,
172
+ /// X { v: bool },
173
+ /// Y(bool, bool),
174
+ /// }
175
+ ///
176
+ /// impl_tag! {
177
+ /// // The type for which the `Tag` will be implemented
178
+ /// impl Tag for SomeTag;
179
+ /// // You need to specify all possible tag values:
180
+ /// SomeTag::A, // 0
181
+ /// SomeTag::B, // 1
182
+ /// // For variants with fields, you need to specify the fields:
183
+ /// SomeTag::X { v: true }, // 2
184
+ /// SomeTag::X { v: false }, // 3
185
+ /// // For tuple variants use named syntax:
186
+ /// SomeTag::Y { 0: true, 1: true }, // 4
187
+ /// SomeTag::Y { 0: false, 1: true }, // 5
188
+ /// SomeTag::Y { 0: true, 1: false }, // 6
189
+ /// SomeTag::Y { 0: false, 1: false }, // 7
190
+ /// }
191
+ ///
192
+ /// // Tag values are assigned in order:
193
+ /// assert_eq!(SomeTag::A.into_usize(), 0);
194
+ /// assert_eq!(SomeTag::X { v: false }.into_usize(), 3);
195
+ /// assert_eq!(SomeTag::Y(false, true).into_usize(), 5);
196
+ ///
197
+ /// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B);
198
+ /// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true });
199
+ /// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false));
200
+ /// ```
201
+ ///
202
+ /// Structs are supported:
203
+ ///
204
+ /// ```
205
+ /// #![feature(macro_metavar_expr)]
206
+ /// # use rustc_data_structures::impl_tag;
207
+ /// #[derive(Copy, Clone)]
208
+ /// struct Flags { a: bool, b: bool }
209
+ ///
210
+ /// impl_tag! {
211
+ /// impl Tag for Flags;
212
+ /// Flags { a: true, b: true },
213
+ /// Flags { a: false, b: true },
214
+ /// Flags { a: true, b: false },
215
+ /// Flags { a: false, b: false },
216
+ /// }
217
+ /// ```
218
+ ///
219
+ /// Not specifying all values results in a compile error:
220
+ ///
221
+ /// ```compile_fail,E0004
222
+ /// #![feature(macro_metavar_expr)]
223
+ /// # use rustc_data_structures::impl_tag;
224
+ /// #[derive(Copy, Clone)]
225
+ /// enum E {
226
+ /// A,
227
+ /// B,
228
+ /// }
229
+ ///
230
+ /// impl_tag! {
231
+ /// impl Tag for E;
232
+ /// E::A,
233
+ /// }
234
+ /// ```
235
+ #[ cfg( not( bootstrap) ) ]
236
+ #[ macro_export]
237
+ macro_rules! impl_tag {
238
+ (
239
+ impl Tag for $Self: ty;
240
+ $(
241
+ $( $path: ident) ::* $( { $( $fields: tt ) * } ) ?,
242
+ ) *
243
+ ) => {
244
+ // Safety:
245
+ // `bits_for_tags` is called on the same `${index()}`-es as
246
+ // `into_usize` returns, thus `BITS` constant is correct.
247
+ unsafe impl $crate:: tagged_ptr:: Tag for $Self {
248
+ const BITS : u32 = $crate:: tagged_ptr:: bits_for_tags( & [
249
+ $(
250
+ ${ index( ) } ,
251
+ $( ${ ignore( $path) } ) *
252
+ ) *
253
+ ] ) ;
254
+
255
+ #[ inline]
256
+ fn into_usize( self ) -> usize {
257
+ // This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc)
258
+ // (or at least it should, see <https://github.com/rust-lang/rust/issues/110613>)
259
+ #[ forbid( unreachable_patterns) ]
260
+ match self {
261
+ // `match` is doing heavy lifting here, by requiring exhaustiveness
262
+ $(
263
+ $( $path) ::* $( { $( $fields ) * } ) ? => ${ index( ) } ,
264
+ ) *
265
+ }
266
+ }
267
+
268
+ #[ inline]
269
+ unsafe fn from_usize( tag: usize ) -> Self {
270
+ match tag {
271
+ $(
272
+ ${ index( ) } => $( $path) ::* $( { $( $fields ) * } ) ?,
273
+ ) *
274
+
275
+ // Safety:
276
+ // `into_usize` only returns `${index()}` of the same
277
+ // repetition as we are filtering above, thus if this is
278
+ // reached, the safety contract of this function was
279
+ // already breached.
280
+ _ => unsafe {
281
+ debug_assert!(
282
+ false ,
283
+ "invalid tag: {tag}\
284
+ (this is a bug in the caller of `from_usize`)"
285
+ ) ;
286
+ std:: hint:: unreachable_unchecked( )
287
+ } ,
288
+ }
289
+ }
290
+
291
+ }
292
+ } ;
293
+ }
294
+
295
+
143
296
#[ cfg( test) ]
144
297
mod tests;
0 commit comments