What the hell is going on here
Today, Mara Bos posted a intriguing quizz on twitter
#[repr(transparent)]
struct W(i32);
let w = W(123);
let a: &dyn Sync = &w;
let b: &dyn Sync = &w.0;
println!("{:?}", std::ptr::eq(a, b));
Which gets even funnier if you actually display the value for those pointers:
println!("{:p} {:p} {:?}", a, b, std::ptr::eq(a, b));
And the result is:
!in debug mode: 0x7fff6b5c986c 0x7fff6b5c986c false!<
but even more interestingly,
!in release mode: 0x7fff4f6a99b4 0x7fff4f6a99b4 true!<
I know almost nothing in assembly, but I decided to try and see what the compiler explorer would say about it to understand what's going on.
You can see it by yourself here
In short, in release, the code does:
lea rax, [rip + .L__unnamed_1]
lea rcx, [rip + .L__unnamed_2]
cmp rcx, rax
.L__unnamed_2:
.quad core::ptr::drop_in_place<i32>
.asciz "\\004\\000\\000\\000\\000\\000\\000\\000\\004\\000\\000\\000\\000\\000\\000"
.L__unnamed_1:
.quad core::ptr::drop_in_place<i32>
.asciz "\\004\\000\\000\\000\\000\\000\\000\\000\\004\\000\\000\\000\\000\\000\\000"
where in debug it does:
lea rdi, [rsp + 20]
lea rdx, [rsp + 20]
lea rsi, [rip + .L__unnamed_3]
lea rcx, [rip + .L__unnamed_4]
cmp rdi, rdx
sete al
cmp rsi, rcx
sete cl
and al, cl
.L__unnamed_3:
.quad core::ptr::drop_in_place<example::main::W>
.asciz "\\004\\000\\000\\000\\000\\000\\000\\000\\004\\000\\000\\000\\000\\000\\000"
.L__unnamed_4:
.quad core::ptr::drop_in_place<i32>
.asciz "\\004\\000\\000\\000\\000\\000\\000\\000\\004\\000\\000\\000\\000\\000\\000"
So in very short, in release mode, it compares L__unnamed_1 and L__unnamed_2 (which are the same, why is LLVM even emitting two different ones?), whereas in debug it compares L__unnamed_3 and L__unnamed_4 which are different because of the generic parameter in drop_in_place.
But now that I've seen that, I still don't really understand what's going on.
Why is a ptr comparison comparing things beyond the address themselves (I feel this has to do with this I guess), and then why is it OK if the behavior aren't consistent before and after optimization?
Edit: and I also ran it in Miri to see if it wasn't some soundness bug in the compiler triggering UB, but Miri think this code is OK (and the answer is false indeed)