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 | |