The docs for fn_addr_eq say
Despite these false positives and false negatives, this comparison can still be useful. Specifically, if
- T is the same type as U, T is a subtype of U, or U is a subtype of T, and
- ptr::fn_addr_eq(f, g) returns true,
then calling f and calling g will be equivalent.
This is not correct, I think.
Consider this example:
unsafe fn f(x: i32) -> i32 { x }
unsafe fn g(x: i32) -> i32 { std::hint::assert_unchecked(x != 0); x }
fn main() {
let fptr = f as unsafe fn(i32) -> i32;
let gptr = g as unsafe fn(i32) -> i32;
if std::ptr::fn_addr_eq(fptr, gptr) {
// SAFETY: Calling `fptr(0)` here would be allowed, and according to the `fn_addr_eq`
// docs, inside this `if` calling `gptr` is equivalent to calling `fptr`.
unsafe { gptr(0) };
}
}
The compiler may now do the following:
- Realize that
gptr points to g, and use that to inline the call to g.
- Optimize the
then branch to std::hint::assert_unchecked(false), and then replace it by arbitrary code (e.g. a trap).
- Later during compilation, merge
f and g into one function since they compile to the same assembly.
Now if we run this program we hit the trap, i.e., we have exhibited UB.
(See rust-lang/unsafe-code-guidelines#589 for a wider discussion of the problem. When discussing this we were not aware that our libs docs actually make any claims in this regard.)
Cc @rust-lang/opsem @rust-lang/libs-api
The docs for
fn_addr_eqsayThis is not correct, I think.
Consider this example:
The compiler may now do the following:
gptrpoints tog, and use that to inline the call tog.thenbranch tostd::hint::assert_unchecked(false), and then replace it by arbitrary code (e.g. a trap).fandginto one function since they compile to the same assembly.Now if we run this program we hit the trap, i.e., we have exhibited UB.
(See rust-lang/unsafe-code-guidelines#589 for a wider discussion of the problem. When discussing this we were not aware that our libs docs actually make any claims in this regard.)
Cc @rust-lang/opsem @rust-lang/libs-api