• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Gilad Naaman, Ralf Jung
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in all
11 // copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 // SOFTWARE.
20 
21 /// `addr_of!`, or just ref-then-cast when that is not available.
22 #[cfg(raw_ref_macros)]
23 #[macro_export]
24 #[doc(hidden)]
25 macro_rules! _memoffset__addr_of {
26     ($path:expr) => {{
27         $crate::__priv::ptr::addr_of!($path)
28     }};
29 }
30 #[cfg(not(raw_ref_macros))]
31 #[macro_export]
32 #[doc(hidden)]
33 macro_rules! _memoffset__addr_of {
34     ($path:expr) => {{
35         // This is UB because we create an intermediate reference to uninitialized memory.
36         // Nothing we can do about that without `addr_of!` though.
37         &$path as *const _
38     }};
39 }
40 
41 /// Deref-coercion protection macro.
42 ///
43 /// Prevents complilation if the specified field name is not a part of the
44 /// struct definition.
45 ///
46 /// ```compile_fail
47 /// use memoffset::_memoffset__field_check;
48 ///
49 /// struct Foo {
50 ///     foo: i32,
51 /// }
52 ///
53 /// type BoxedFoo = Box<Foo>;
54 ///
55 /// _memoffset__field_check!(BoxedFoo, foo);
56 /// ```
57 #[cfg(allow_clippy)]
58 #[macro_export]
59 #[doc(hidden)]
60 macro_rules! _memoffset__field_check {
61     ($type:path, $field:tt) => {
62         // Make sure the field actually exists. This line ensures that a
63         // compile-time error is generated if $field is accessed through a
64         // Deref impl.
65         #[allow(clippy::unneeded_field_pattern)]
66         let $type { $field: _, .. };
67     };
68 }
69 #[cfg(not(allow_clippy))]
70 #[macro_export]
71 #[doc(hidden)]
72 macro_rules! _memoffset__field_check {
73     ($type:path, $field:tt) => {
74         // Make sure the field actually exists. This line ensures that a
75         // compile-time error is generated if $field is accessed through a
76         // Deref impl.
77         let $type { $field: _, .. };
78     };
79 }
80 
81 /// Deref-coercion protection macro.
82 ///
83 /// Prevents complilation if the specified type is not a tuple.
84 ///
85 /// ```compile_fail
86 /// use memoffset::_memoffset__field_check_tuple;
87 ///
88 /// _memoffset__field_check_tuple!(i32, 0);
89 /// ```
90 #[cfg(allow_clippy)]
91 #[macro_export]
92 #[doc(hidden)]
93 macro_rules! _memoffset__field_check_tuple {
94     ($type:ty, $field:tt) => {
95         // Make sure the type argument is a tuple
96         #[allow(clippy::unneeded_wildcard_pattern)]
97         let (_, ..): $type;
98     };
99 }
100 #[cfg(not(allow_clippy))]
101 #[macro_export]
102 #[doc(hidden)]
103 macro_rules! _memoffset__field_check_tuple {
104     ($type:ty, $field:tt) => {
105         // Make sure the type argument is a tuple
106         let (_, ..): $type;
107     };
108 }
109 
110 /// Deref-coercion protection macro for unions.
111 /// Unfortunately accepts single-field structs as well, which is not ideal,
112 /// but ultimately pretty harmless.
113 ///
114 /// ```compile_fail
115 /// use memoffset::_memoffset__field_check_union;
116 ///
117 /// union Foo {
118 ///     variant_a: i32,
119 /// }
120 ///
121 /// type BoxedFoo = Box<Foo>;
122 ///
123 /// _memoffset__field_check_union!(BoxedFoo, variant_a);
124 /// ```
125 #[cfg(allow_clippy)]
126 #[macro_export]
127 #[doc(hidden)]
128 macro_rules! _memoffset__field_check_union {
129     ($type:path, $field:tt) => {
130         // Make sure the field actually exists. This line ensures that a
131         // compile-time error is generated if $field is accessed through a
132         // Deref impl.
133         #[allow(clippy::unneeded_wildcard_pattern)]
134         // rustc1.19 requires unsafe here for the pattern; not needed in newer versions
135         #[allow(unused_unsafe)]
136         unsafe {
137             let $type { $field: _ };
138         }
139     };
140 }
141 #[cfg(not(allow_clippy))]
142 #[macro_export]
143 #[doc(hidden)]
144 macro_rules! _memoffset__field_check_union {
145     ($type:path, $field:tt) => {
146         // Make sure the field actually exists. This line ensures that a
147         // compile-time error is generated if $field is accessed through a
148         // Deref impl.
149         // rustc1.19 requires unsafe here for the pattern; not needed in newer versions
150         #[allow(unused_unsafe)]
151         unsafe {
152             let $type { $field: _ };
153         }
154     };
155 }
156 
157 /// Computes a const raw pointer to the given field of the given base pointer
158 /// to the given parent type.
159 ///
160 /// The `base` pointer *must not* be dangling, but it *may* point to
161 /// uninitialized memory.
162 #[macro_export(local_inner_macros)]
163 macro_rules! raw_field {
164     ($base:expr, $parent:path, $field:tt) => {{
165         _memoffset__field_check!($parent, $field);
166         let base = $base; // evaluate $base outside the `unsafe` block
167 
168         // Get the field address.
169         // Crucially, we know that this will not trigger a deref coercion because
170         // of the field check we did above.
171         #[allow(unused_unsafe)] // for when the macro is used in an unsafe block
172         unsafe {
173             _memoffset__addr_of!((*(base as *const $parent)).$field)
174         }
175     }};
176 }
177 
178 /// Computes a const raw pointer to the given field of the given base pointer
179 /// to the given parent tuple typle.
180 ///
181 /// The `base` pointer *must not* be dangling, but it *may* point to
182 /// uninitialized memory.
183 #[cfg(tuple_ty)]
184 #[macro_export(local_inner_macros)]
185 macro_rules! raw_field_tuple {
186     ($base:expr, $parent:ty, $field:tt) => {{
187         _memoffset__field_check_tuple!($parent, $field);
188         let base = $base; // evaluate $base outside the `unsafe` block
189 
190         // Get the field address.
191         // Crucially, we know that this will not trigger a deref coercion because
192         // of the field check we did above.
193         #[allow(unused_unsafe)] // for when the macro is used in an unsafe block
194         unsafe {
195             _memoffset__addr_of!((*(base as *const $parent)).$field)
196         }
197     }};
198 }
199 
200 /// Computes a const raw pointer to the given field of the given base pointer
201 /// to the given parent tuple typle.
202 ///
203 /// The `base` pointer *must not* be dangling, but it *may* point to
204 /// uninitialized memory.
205 ///
206 /// ## Note
207 /// This macro is the same as `raw_field`, except for a different Deref-coercion check that
208 /// supports unions.
209 /// Due to macro_rules limitations, this check will accept structs with a single field as well as unions.
210 /// This is not a stable guarantee, and future versions of this crate might fail
211 /// on any use of this macro with a struct, without a semver bump.
212 #[macro_export(local_inner_macros)]
213 macro_rules! raw_field_union {
214     ($base:expr, $parent:path, $field:tt) => {{
215         _memoffset__field_check_union!($parent, $field);
216         let base = $base; // evaluate $base outside the `unsafe` block
217 
218         // Get the field address.
219         // Crucially, we know that this will not trigger a deref coercion because
220         // of the field check we did above.
221         #[allow(unused_unsafe)] // for when the macro is used in an unsafe block
222         unsafe {
223             _memoffset__addr_of!((*(base as *const $parent)).$field)
224         }
225     }};
226 }
227