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)] 25macro_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)] 33macro_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)] 60macro_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)] 72macro_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)] 93macro_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)] 103macro_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)] 128macro_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)] 144macro_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)] 163macro_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)] 185macro_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)] 213macro_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