From what I gathered in discussion with @arielb1 on IRC, it seems to be a common opinion that the following program should be UB because the reference is not properly aligned:
fn main() {
let x = 2usize as *const u32;
let y : &u32 = unsafe { &*x };
}
Notice that the function contains an unsafe block, so discussion is still open whether the usual aliasing rules have to apply to references here. But it is my impression that alignment and non-NULLness are considered stronger guarantees than aliasing and being “not dangling”. That is somewhat surprising to me; we are now talking about some form of “two-staged validity” where some guarantees have to always hold, even in unsafe code, while others may be temporarily violated in unsafe code.
One piece of evidence for this (maybe?) is that somewhat recently (?), ZST pointers in Rust were changed from being 1 as *const T to being align as *const T. Was there discussion somewhere about why this is necessary? After all, such pointers will never be dereferenced, so why should alignment matter?
Furthermore, @arielb1 mentioned that there is an exception to the rule that alignment must be satisfied for the case where the reference is immediately casted to a pointer, like in &*x as *const _.
This makes me wonder, what if we change the bad program above like so:
fn main() {
let x = 2usize as *const u32;
let y : &u32 = unsafe { &*x };
let y = y as *const _;
}
Is this UB or not? On the one hand, if the first program above is UB, this one must be – or else, being UB would depend on actions in the future, which can’t be right. On the other hand, the program pretty much does “immediately cast the reference to a pointer”, and indeed, in MIR, these two cases cannot be distinguished.