]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_data_structures/src/tagged_ptr/copy.rs
Rollup merge of #106716 - c410-f3r:rfc-2397-1, r=davidtwco
[rust.git] / compiler / rustc_data_structures / src / tagged_ptr / copy.rs
1 use super::{Pointer, Tag};
2 use crate::stable_hasher::{HashStable, StableHasher};
3 use std::fmt;
4 use std::marker::PhantomData;
5 use std::num::NonZeroUsize;
6
7 /// A `Copy` TaggedPtr.
8 ///
9 /// You should use this instead of the `TaggedPtr` type in all cases where
10 /// `P: Copy`.
11 ///
12 /// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without
13 /// unpacking. Otherwise we don't implement PartialEq/Eq/Hash; if you want that,
14 /// wrap the TaggedPtr.
15 pub struct CopyTaggedPtr<P, T, const COMPARE_PACKED: bool>
16 where
17     P: Pointer,
18     T: Tag,
19 {
20     packed: NonZeroUsize,
21     data: PhantomData<(P, T)>,
22 }
23
24 impl<P, T, const COMPARE_PACKED: bool> Copy for CopyTaggedPtr<P, T, COMPARE_PACKED>
25 where
26     P: Pointer,
27     T: Tag,
28     P: Copy,
29 {
30 }
31
32 impl<P, T, const COMPARE_PACKED: bool> Clone for CopyTaggedPtr<P, T, COMPARE_PACKED>
33 where
34     P: Pointer,
35     T: Tag,
36     P: Copy,
37 {
38     fn clone(&self) -> Self {
39         *self
40     }
41 }
42
43 // We pack the tag into the *upper* bits of the pointer to ease retrieval of the
44 // value; a left shift is a multiplication and those are embeddable in
45 // instruction encoding.
46 impl<P, T, const COMPARE_PACKED: bool> CopyTaggedPtr<P, T, COMPARE_PACKED>
47 where
48     P: Pointer,
49     T: Tag,
50 {
51     const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
52     const ASSERTION: () = {
53         assert!(T::BITS <= P::BITS);
54         // Used for the transmute_copy's below
55         assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::<usize>());
56     };
57
58     pub fn new(pointer: P, tag: T) -> Self {
59         // Trigger assert!
60         let () = Self::ASSERTION;
61         let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT;
62
63         Self {
64             // SAFETY: We know that the pointer is non-null, as it must be
65             // dereferenceable per `Pointer` safety contract.
66             packed: unsafe {
67                 NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag)
68             },
69             data: PhantomData,
70         }
71     }
72
73     pub(super) fn pointer_raw(&self) -> usize {
74         self.packed.get() << T::BITS
75     }
76     pub fn pointer(self) -> P
77     where
78         P: Copy,
79     {
80         // SAFETY: pointer_raw returns the original pointer
81         //
82         // Note that this isn't going to double-drop or anything because we have
83         // P: Copy
84         unsafe { P::from_usize(self.pointer_raw()) }
85     }
86     pub fn pointer_ref(&self) -> &P::Target {
87         // SAFETY: pointer_raw returns the original pointer
88         unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
89     }
90     pub fn pointer_mut(&mut self) -> &mut P::Target
91     where
92         P: std::ops::DerefMut,
93     {
94         // SAFETY: pointer_raw returns the original pointer
95         unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
96     }
97     #[inline]
98     pub fn tag(&self) -> T {
99         unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) }
100     }
101     #[inline]
102     pub fn set_tag(&mut self, tag: T) {
103         let mut packed = self.packed.get();
104         let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT;
105         let tag_mask = (1 << T::BITS) - 1;
106         packed &= !(tag_mask << Self::TAG_BIT_SHIFT);
107         packed |= new_tag;
108         self.packed = unsafe { NonZeroUsize::new_unchecked(packed) };
109     }
110 }
111
112 impl<P, T, const COMPARE_PACKED: bool> std::ops::Deref for CopyTaggedPtr<P, T, COMPARE_PACKED>
113 where
114     P: Pointer,
115     T: Tag,
116 {
117     type Target = P::Target;
118     fn deref(&self) -> &Self::Target {
119         self.pointer_ref()
120     }
121 }
122
123 impl<P, T, const COMPARE_PACKED: bool> std::ops::DerefMut for CopyTaggedPtr<P, T, COMPARE_PACKED>
124 where
125     P: Pointer + std::ops::DerefMut,
126     T: Tag,
127 {
128     fn deref_mut(&mut self) -> &mut Self::Target {
129         self.pointer_mut()
130     }
131 }
132
133 impl<P, T, const COMPARE_PACKED: bool> fmt::Debug for CopyTaggedPtr<P, T, COMPARE_PACKED>
134 where
135     P: Pointer,
136     P::Target: fmt::Debug,
137     T: Tag + fmt::Debug,
138 {
139     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140         f.debug_struct("CopyTaggedPtr")
141             .field("pointer", &self.pointer_ref())
142             .field("tag", &self.tag())
143             .finish()
144     }
145 }
146
147 impl<P, T> PartialEq for CopyTaggedPtr<P, T, true>
148 where
149     P: Pointer,
150     T: Tag,
151 {
152     fn eq(&self, other: &Self) -> bool {
153         self.packed == other.packed
154     }
155 }
156
157 impl<P, T> Eq for CopyTaggedPtr<P, T, true>
158 where
159     P: Pointer,
160     T: Tag,
161 {
162 }
163
164 impl<P, T> std::hash::Hash for CopyTaggedPtr<P, T, true>
165 where
166     P: Pointer,
167     T: Tag,
168 {
169     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
170         self.packed.hash(state);
171     }
172 }
173
174 impl<P, T, HCX, const COMPARE_PACKED: bool> HashStable<HCX> for CopyTaggedPtr<P, T, COMPARE_PACKED>
175 where
176     P: Pointer + HashStable<HCX>,
177     T: Tag + HashStable<HCX>,
178 {
179     fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
180         unsafe {
181             Pointer::with_ref(self.pointer_raw(), |p: &P| p.hash_stable(hcx, hasher));
182         }
183         self.tag().hash_stable(hcx, hasher);
184     }
185 }