about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-05-24 15:58:26 +0200
committerGitHub <noreply@github.com>2022-05-24 15:58:26 +0200
commit4bd40186dbb498f8a3952a24f343b468ad5ef064 (patch)
tree27999aee32cdff30d1658addd4eac9ffd9f935e0
parentaf15e45e283640ec72642949511abf113a7a6c57 (diff)
parent5137d15f91d5778a0d037e5bc4f1f70d9b013aa7 (diff)
downloadrust-4bd40186dbb498f8a3952a24f343b468ad5ef064.tar.gz
rust-4bd40186dbb498f8a3952a24f343b468ad5ef064.zip
Rollup merge of #97321 - RalfJung:int-to-fnptr, r=Dylan-DPC
explain how to turn integers into fn ptrs

(with an intermediate raw ptr, not a direct transmute)
Direct int2ptr transmute, under the semantics I am imagining, will produce a ptr with "invalid" provenance that is invalid to deref or call. We cannot give it the same semantics as int2ptr casts since those do [something complicated](https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html).

To my great surprise, that is already what the example in the `transmute` docs does. :)  I still added a comment to say that that part is important, and I added a section explicitly talking about this to the `fn()` type docs.

With https://github.com/rust-lang/miri/pull/2151, Miri will start complaining about direct int-to-fnptr transmutes (in the sense that it is UB to call the resulting pointer).
-rw-r--r--library/core/src/intrinsics.rs3
-rw-r--r--library/core/src/primitive_docs.rs26
-rw-r--r--library/std/src/primitive_docs.rs26
3 files changed, 55 insertions, 0 deletions
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 88e4262922d..6ba359f6edc 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -930,6 +930,9 @@ extern "rust-intrinsic" {
     /// fn foo() -> i32 {
     ///     0
     /// }
+    /// // Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
+    /// // This avoids an integer-to-pointer `transmute`, which can be problematic.
+    /// // Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
     /// let pointer = foo as *const ();
     /// let function = unsafe {
     ///     std::mem::transmute::<*const (), fn() -> i32>(pointer)
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 631cc313fa0..688ab63bf13 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1351,6 +1351,32 @@ mod prim_ref {}
 /// is a reference to the function-specific ZST. `&bar` is basically never what you
 /// want when `bar` is a function.
 ///
+/// ### Casting to and from integers
+///
+/// You cast function pointers directly to integers:
+///
+/// ```rust
+/// let fnptr: fn(i32) -> i32 = |x| x+2;
+/// let fnptr_addr = fnptr as usize;
+/// ```
+///
+/// However, a direct cast back is not possible. You need to use `transmute`:
+///
+/// ```rust
+/// # let fnptr: fn(i32) -> i32 = |x| x+2;
+/// # let fnptr_addr = fnptr as usize;
+/// let fnptr = fnptr_addr as *const ();
+/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) };
+/// assert_eq!(fnptr(40), 42);
+/// ```
+///
+/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
+/// This avoids an integer-to-pointer `transmute`, which can be problematic.
+/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
+///
+/// Note that all of this is not portable to platforms where function pointers and data pointers
+/// have different sizes.
+///
 /// ### Traits
 ///
 /// Function pointers implement the following traits:
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 631cc313fa0..688ab63bf13 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -1351,6 +1351,32 @@ mod prim_ref {}
 /// is a reference to the function-specific ZST. `&bar` is basically never what you
 /// want when `bar` is a function.
 ///
+/// ### Casting to and from integers
+///
+/// You cast function pointers directly to integers:
+///
+/// ```rust
+/// let fnptr: fn(i32) -> i32 = |x| x+2;
+/// let fnptr_addr = fnptr as usize;
+/// ```
+///
+/// However, a direct cast back is not possible. You need to use `transmute`:
+///
+/// ```rust
+/// # let fnptr: fn(i32) -> i32 = |x| x+2;
+/// # let fnptr_addr = fnptr as usize;
+/// let fnptr = fnptr_addr as *const ();
+/// let fnptr: fn(i32) -> i32 = unsafe { std::mem::transmute(fnptr) };
+/// assert_eq!(fnptr(40), 42);
+/// ```
+///
+/// Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer.
+/// This avoids an integer-to-pointer `transmute`, which can be problematic.
+/// Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine.
+///
+/// Note that all of this is not portable to platforms where function pointers and data pointers
+/// have different sizes.
+///
 /// ### Traits
 ///
 /// Function pointers implement the following traits: