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