Skip to content

Commit

Permalink
Rollup merge of #97321 - RalfJung:int-to-fnptr, r=Dylan-DPC
Browse files Browse the repository at this point in the history
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 rust-lang/miri#2151, Miri will start complaining about direct int-to-fnptr transmutes (in the sense that it is UB to call the resulting pointer).
  • Loading branch information
Dylan-DPC committed May 24, 2022
2 parents af15e45 + 5137d15 commit 4bd4018
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
3 changes: 3 additions & 0 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
26 changes: 26 additions & 0 deletions library/std/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 4bd4018

Please sign in to comment.