Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,8 @@ declare_features! (
(unstable, avx10_target_feature, "1.88.0", Some(138843)),
/// Target features on bpf.
(unstable, bpf_target_feature, "1.54.0", Some(150247)),
/// Allows using size_t/ssize_t/uintptr_t/intptr_t/ptrdiff_t.
(unstable, c_size_t, "CURRENT_RUSTC_VERSION", Some(88345)),

@programmerjake programmerjake Jun 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a library feature, afaik it doesn't need to be added here

View changes since the review

/// Allows using C-variadics.
(unstable, c_variadic, "1.34.0", Some(44930)),
/// Allows defining c-variadic functions on targets where this feature has not yet
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ symbols! {
builtin_syntax,
bundle,
c_dash_variadic,
c_size_t,
c_str_literals,
c_unwind,
c_variadic,
Expand Down
6 changes: 4 additions & 2 deletions library/core/src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ mod va_list;
pub use self::va_list::{VaArgSafe, VaList};

mod primitives;
#[unstable(feature = "c_size_t", issue = "88345")]
pub use self::primitives::{
IntPtr, TaggedPointer, c_intptr_t, c_ptrdiff_t, c_size_t, c_ssize_t, c_uintptr_t,
};
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub use self::primitives::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort,
};
#[unstable(feature = "c_size_t", issue = "88345")]
pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t};

// N.B., for LLVM to recognize the void pointer type and by extension
// functions like malloc(), we need to have it represented as i8* in
Expand Down
125 changes: 125 additions & 0 deletions library/core/src/ffi/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! This module is intentionally standalone to facilitate parsing when retrieving
//! core C types.

use crate::mem::{self};

@programmerjake programmerjake Jun 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use crate::mem::{self};
use crate::mem;

View changes since the review


macro_rules! type_alias {
{
$Docfile:tt, $Alias:ident = $Real:ty;
Expand Down Expand Up @@ -174,6 +176,129 @@ pub type c_ptrdiff_t = isize;
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ssize_t = isize;

/// Equivalent to C's `intptr_t` type.
///
/// This type have the same size with a pointer. The C standard technically only
/// requires that this type be a signed integer type just capable of holding a
/// pointer.
#[unstable(feature = "c_size_t", issue = "88345")]
#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
pub struct c_intptr_t(*const ());

@programmerjake programmerjake Jun 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this definition could have an incorrect ABI on some targets since some targets require integers to be sign/zero-extended to fill a register depending on the type's signedness, but if pointers are smaller than a register there is no way for rustc to know if it should sign or zero-extend.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, pointers are not ABI-compatible with any integer type.


/// Equivalent to C's `uintptr_t` type.
///
/// This type have the same size with a pointer. The C standard technically only
/// requires that this type be an unsigned integer type just capable of holding
/// a pointer.
#[unstable(feature = "c_size_t", issue = "88345")]
#[repr(transparent)]
#[derive(Copy, Clone, Debug)]
pub struct c_uintptr_t(*const ());

/// Trait for types that can be used to represent C's `intptr_t` and `uintptr_t` types.
///
/// For handle interchange of rust `pointer` type and C's `intptr_t` and `uintptr_t` types.
#[unstable(feature = "c_size_t", issue = "88345")]
#[rustc_const_unstable(feature = "c_size_t", issue = "88345")]
pub const trait TaggedPointer {

@programmerjake programmerjake Jun 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this trait should probably be sealed

View changes since the review

/// Creates a new instance of IntPtr from a pointer.
#[unstable(feature = "c_size_t", issue = "88345")]
fn new(ptr: *const ()) -> Self;
/// Returns the pointer contained in IntPtr.
#[unstable(feature = "c_size_t", issue = "88345")]
fn ptr(&self) -> *const ();
}

/// Trait for retrieving the integer representation of a TaggedPointer.
///
#[unstable(feature = "c_size_t", issue = "88345")]
pub trait IntPtr: TaggedPointer {
/// The integer type that can hold the tagged pointer value.
type Number;

/// Returns the integer representation of the pointer contained in IntPtr.
#[unstable(feature = "c_size_t", issue = "88345")]
fn integer(&self) -> Self::Number;
}

#[unstable(feature = "c_size_t", issue = "88345")]
#[rustc_const_unstable(feature = "c_size_t", issue = "88345")]
impl const TaggedPointer for c_intptr_t {
fn new(ptr: *const ()) -> Self {
Self(ptr)
}

fn ptr(&self) -> *const () {
self.0
}
}

#[unstable(feature = "c_size_t", issue = "88345")]
impl IntPtr for c_intptr_t {
type Number = c_intptr_definition::c_intptr_t;

fn integer(&self) -> Self::Number {
// A pointer-to-integer transmute currently has exactly the right semantics: it returns the
// integer representing the pointer without exposing the provenance. Note that this is *not*
// a stable guarantee about transmute semantics, it relies on sysroot crates having special status.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
unsafe { mem::transmute(self.0.cast::<()>()) }
}
}

#[unstable(feature = "c_size_t", issue = "88345")]
#[rustc_const_unstable(feature = "c_size_t", issue = "88345")]
impl const TaggedPointer for c_uintptr_t {
fn new(ptr: *const ()) -> Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make a lot of sense to add a usize/isize constructor as well that creates a without_provenance pointer. This would make it more ergonomic to call APIs that use something like "valid pointer" or magic integer (e.g. -1).

Self(ptr)
}

fn ptr(&self) -> *const () {
self.0
}
}

#[unstable(feature = "c_size_t", issue = "88345")]
impl IntPtr for c_uintptr_t {
type Number = c_intptr_definition::c_uintptr_t;

fn integer(&self) -> Self::Number {
// A pointer-to-integer transmute currently has exactly the right semantics: it returns the
// integer representing the pointer without exposing the provenance. Note that this is *not*
// a stable guarantee about transmute semantics, it relies on sysroot crates having special status.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
unsafe { mem::transmute(self.0.cast::<()>()) }
}
}

mod c_intptr_definition {
crate::cfg_select! {
target_pointer_width = "16" => {
pub(super) type c_intptr_t = i16;
pub(super) type c_uintptr_t = u16;
}
target_pointer_width = "32" => {
pub(super) type c_intptr_t = i32;
pub(super) type c_uintptr_t = u32;
}
target_pointer_width = "64" => {
pub(super) type c_intptr_t = i64;
pub(super) type c_uintptr_t = u64;
}
_ => {
/// 128-bit width pointer.
///
/// The C standard only requires that these types be large enough to hold a pointer,
/// so we'll use the i128/u128 integer types in Rust.
pub(super) type c_intptr_t = i128;
pub(super) type c_uintptr_t = u128;
}
}
}

mod c_int_definition {
crate::cfg_select! {
any(target_arch = "avr", target_arch = "msp430") => {
Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/ffi.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod cstr;
mod intptr;
23 changes: 23 additions & 0 deletions library/coretests/tests/ffi/intptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]

use core::ffi::{IntPtr, TaggedPointer, c_intptr_t, c_uintptr_t};

extern "C" fn c_ffi_function_on_uintptr(v: c_uintptr_t) {
assert_eq!((v.integer() as usize) & 0xFF_usize, 16_usize);
}

#[test]
fn test_intptr_unitptr() {
// These types should have the same size as a pointer.
assert_eq!(core::mem::size_of::<c_intptr_t>(), core::mem::size_of::<*const ()>());
assert_eq!(core::mem::size_of::<c_uintptr_t>(), core::mem::size_of::<*const ()>());

let ptr = core::ptr::with_exposed_provenance(16_usize);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let ptr = core::ptr::with_exposed_provenance(16_usize);
let ptr = core::ptr::without_provenance(16_usize);

let ptr_uintptr_t = c_uintptr_t::new(ptr);
let ptr_back = ptr_uintptr_t.ptr();
c_ffi_function_on_uintptr(ptr_uintptr_t);
assert_eq!(ptr_back.addr(), 16_usize);
assert_eq!((ptr_uintptr_t.integer() as usize) & 0xFF_usize, 16_usize);
assert_eq!(ptr, ptr_back);
}
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![feature(bool_to_result)]
#![feature(borrowed_buf_init)]
#![feature(bstr)]
#![feature(c_size_t)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
Expand Down
6 changes: 4 additions & 2 deletions library/std/src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ pub mod c_str;

#[stable(feature = "core_c_void", since = "1.30.0")]
pub use core::ffi::c_void;
#[unstable(feature = "c_size_t", issue = "88345")]
pub use core::ffi::{
IntPtr, TaggedPointer, c_intptr_t, c_ptrdiff_t, c_size_t, c_ssize_t, c_uintptr_t,
};
#[unstable(
feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
Expand All @@ -178,8 +182,6 @@ pub use core::ffi::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort,
};
#[unstable(feature = "c_size_t", issue = "88345")]
pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};

#[doc(inline)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
Expand Down
21 changes: 21 additions & 0 deletions src/doc/unstable-book/src/language-features/c-size-t.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# `c_size_t`

The tracking issue for this feature is: [#88345]

[#88345]: https://github.com/rust-lang/rust/issues/88345
-----

The `c_size_t` feature allows to enable C FFI types `size_t` `ssize_t` `intptr_t` `uintptr_t` `ptrdiff_t`.

## Example

```rust
#![feature(c_size_t)]

use std::ffi::{TaggedPointer, c_uintptr_t};

fn main() {
let ptr = core::ptr::with_exposed_provenance(16_usize);
let _ptr_uintptr_t = c_uintptr_t::new(ptr);
}
```
4 changes: 4 additions & 0 deletions tests/ui/feature-gates/feature-gate-c-size-t.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#![crate_type = "lib"]

use std::ffi::{c_intptr_t};
//~^ ERROR use of unstable library feature `c_size_t`
13 changes: 13 additions & 0 deletions tests/ui/feature-gates/feature-gate-c-size-t.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0658]: use of unstable library feature `c_size_t`
--> $DIR/feature-gate-c-size-t.rs:3:16
|
LL | use std::ffi::{c_intptr_t};
| ^^^^^^^^^^
|
= note: see issue #88345 <https://github.com/rust-lang/rust/issues/88345> for more information
= help: add `#![feature(c_size_t)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0658`.
Loading