]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_data_structures / src / tagged_ptr / impl_tag.rs
CommitLineData
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]
85macro_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)]
144mod tests;