# Valid uses of addr_of! The documentation for `addr_of!` says this: > Note, however, that the `expr` in `addr_of!(expr)` is still subject to all the usual rules. In particular, `addr_of!(*ptr::null())` is Undefined Behavior because it dereferences a null pointer. This wording is quite unclear about what "the usual rules" are. The community wants this use of `addr_of!` to be valid, as a technique to turn an unaligned pointer to a struct into an unaligned pointer to one of its fields: (Ignore the fact that the struct isn't at some funky alignment so this code isn't quite a perfect example) ```rust struct Misalignment { a: u32, } fn main() { let items: [Misalignment; 2] = [Misalignment { a: 0 }, Misalignment { a: 1 }]; unsafe { let ptr: *const Misalignment = items.as_ptr().cast::<u8>().add(1).cast::<Misalignment>(); let _ptr = core::ptr::addr_of!((*ptr).a); } } ``` The same "usual rules" argument that says that disallowing dereference of a null pointer can reasonably lead a user to conclude that `addr_of!` use like above is not valid because it dereferences a misaligned pointer. But if the above code is not allowed, `addr_of!` is not very useful. ## Other possibly-valid calls Is this well-defined? ```rust= #[repr(packed)] struct Misaligner { _head: u8, tail: u32, } fn main() { let oops = Misaligner { _head: 0u8, tail: 0u32, }; let ptr: *const Misaligner = &oops as *const Misaligner; unsafe { let _ptr: *const u32 = core::ptr::addr_of!((*ptr).tail); } } ``` ## Deref coercion Some users want `addr_of!` to not go through Deref coercion. The ambigiuty of combining "the usual rules" along with probably permitting the unaligned dereference above makes it unclear if `addr_of!` is somehow special. Whatever is resolved here should make it clear whether or not Deref coercion applies inside `addr_of!`. ## Operations in order * Deref: Value -> Place * Checks: Inbounds, alignment * Offset: Place -> Place * Checks: Off-topic for this meeting, discussed in previous meeting * `addr_of!`: Place -> Value * Checks: Nothing ## Examples for discussion ```rust= #[repr(align(8))] struct Overaligned(u32); let o = [0_u32; 2]; // Assume allocated at alignment 4, consider if size 1 let p = addr_of!(o) as *const Overaligned; let _x = (*p).0; // UB under Ralf's alignment computation, but could be allowed ``` If people expect the first example to be allowed (alignment violation), then wouldn't they also expect this to be allowed? Should we allow this? ```rust= #[repr(C)] struct Misalignment { x: u32, a: u32, } fn main() { unsafe { let ptr: *const Misalignment = 1usize as *const Misalignment; let _ptr = core::ptr::addr_of!((*ptr).a); } // many people imagine this happens unsafe { let ptr: *const Misalignment = 1usize as *const Misalignment; let offset: usize = offset_of!(Misalignment.a); let out = ptr.wrapping_bytes_add(offset); } // ... but it's actually `bytes_add`, no `wrapping`! } ``` ```rust struct S { a: (), b: u32, } let r: &S = ...; foo(); // deallocs memory backing `r` let _x = (*r).a; ``` ## We should write down the rules and PR to `addr_of`/`addr_of_mut` \- Connor We should come up with some set of rules for place projections/pointer derefence, and PR them to the docs. They can be conservative or lax, probably more conservative for now since they can be relaxed later. Should we document relaxation rules: - Alignment probably yes? - Dereferenceable?