From 57eacbba4017142aefddbe6aac246942fc195891 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 9 Oct 2024 10:19:34 -0700 Subject: [PATCH 1/3] Expand `ptr::fn_addr_eq()` documentation. * Describe more clearly what is (not) guaranteed, and de-emphasize the implementation details. * Explain what you *can* reliably use it for. --- library/core/src/ptr/mod.rs | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 67f1b0cd16de4..a207f7e5b9aa7 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2164,13 +2164,36 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// Compares the *addresses* of the two function pointers for equality. /// -/// Function pointers comparisons can have surprising results since -/// they are never guaranteed to be unique and could vary between different -/// code generation units. Furthermore, different functions could have the -/// same address after being merged together. +/// This is the same as `f == g`, but using this function makes clear that the potentially +/// surprising semantics of function pointer comparison are involved. +/// There are very few guarantees about how functions are compiled and they have no intrinsic +/// “identity”; in particular, this comparison: +/// +/// * May return `true` unexpectedly, in cases where functions are equivalent. +/// For example, the following program is likely (but not guaranteed) to print `(true, true)` +/// when compiled with optimization: +/// +/// ``` +/// # #![feature(ptr_fn_addr_eq)] +/// let f: fn(i32) -> i32 = |x| x; +/// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body +/// let h: fn(u32) -> u32 = |x| x + 0; // different signature too +/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); +/// ``` +/// +/// * May return `false` in any case. +/// This is particularly likely with generic functions but may happen with any function. +/// (From an implementation perspective, this is possible because functions may sometimes be +/// processed more than once by the compiler, resulting in duplicate machine code.) +/// +/// 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 the same as `f == g` but using this function makes clear -/// that you are aware of these potentially surprising semantics. /// /// # Examples /// @@ -2182,6 +2205,9 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// fn b() { println!("b"); } /// assert!(!ptr::fn_addr_eq(a as fn(), b as fn())); /// ``` +/// +/// [subtype]: https://doc.rust-lang.org/reference/subtyping.html + #[unstable(feature = "ptr_fn_addr_eq", issue = "129322")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] From 8e2ac498f4e8240c21f1e4ed9ed8c2370a5913f7 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 9 Oct 2024 12:42:01 -0700 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Urgau <3616612+Urgau@users.noreply.github.com> --- library/core/src/ptr/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index a207f7e5b9aa7..5ee2419591dcf 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2166,10 +2166,12 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// /// This is the same as `f == g`, but using this function makes clear that the potentially /// surprising semantics of function pointer comparison are involved. -/// There are very few guarantees about how functions are compiled and they have no intrinsic +/// +/// There are **very few guarantees** about how functions are compiled and they have no intrinsic /// “identity”; in particular, this comparison: /// /// * May return `true` unexpectedly, in cases where functions are equivalent. +/// /// For example, the following program is likely (but not guaranteed) to print `(true, true)` /// when compiled with optimization: /// @@ -2182,6 +2184,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// * May return `false` in any case. +/// /// This is particularly likely with generic functions but may happen with any function. /// (From an implementation perspective, this is possible because functions may sometimes be /// processed more than once by the compiler, resulting in duplicate machine code.) @@ -2207,7 +2210,6 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// [subtype]: https://doc.rust-lang.org/reference/subtyping.html - #[unstable(feature = "ptr_fn_addr_eq", issue = "129322")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] From 5280f152b0dad9faf33478a06c6a1cdf97b71ae4 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Wed, 9 Oct 2024 12:53:03 -0700 Subject: [PATCH 3/3] Add "not guaranteed to be equal" Co-authored-by: Urgau <3616612+Urgau@users.noreply.github.com> --- library/core/src/ptr/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 5ee2419591dcf..dfe28aa7756eb 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2180,7 +2180,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// let f: fn(i32) -> i32 = |x| x; /// let g: fn(i32) -> i32 = |x| x + 0; // different closure, different body /// let h: fn(u32) -> u32 = |x| x + 0; // different signature too -/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); +/// dbg!(std::ptr::fn_addr_eq(f, g), std::ptr::fn_addr_eq(f, h)); // not guaranteed to be equal /// ``` /// /// * May return `false` in any case.