8181/// E::A,
8282/// }
8383/// ```
84+ #[ cfg( bootstrap) ]
8485#[ macro_export]
8586macro_rules! impl_tag {
8687 (
@@ -93,12 +94,20 @@ macro_rules! impl_tag {
9394 // `bits_for_tags` is called on the same `${index()}`-es as
9495 // `into_usize` returns, thus `BITS` constant is correct.
9596 unsafe impl $crate:: tagged_ptr:: Tag for $Self {
97+ #[ cfg( bootstrap) ]
9698 const BITS : u32 = $crate:: tagged_ptr:: bits_for_tags( & [
9799 $(
98100 ${ index( ) } ,
99101 $( ${ ignore( path) } ) *
100102 ) *
101103 ] ) ;
104+ #[ cfg( not( bootstrap) ) ]
105+ const BITS : u32 = $crate:: tagged_ptr:: bits_for_tags( & [
106+ $(
107+ ${ index( ) } ,
108+ $( ${ ignore( $path) } ) *
109+ ) *
110+ ] ) ;
102111
103112 #[ inline]
104113 fn into_usize( self ) -> usize {
@@ -140,5 +149,149 @@ macro_rules! impl_tag {
140149 } ;
141150}
142151
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+
143296#[ cfg( test) ]
144297mod tests;
0 commit comments