Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
54ee0ee
Force copy `rustc-dev` artifacts for rustfmt/clippy under download-rustc
jieyouxu May 13, 2026
9e3e1ca
Rename a confusing local
jieyouxu May 24, 2026
71487e8
Explicitly add rustc libs to path for `RustcPrivate` tools
jieyouxu May 24, 2026
fed6279
library: use strict provenance lints consistently
hanna-kruppe May 22, 2026
ecdf683
update TargetFeature::Forbidden docs
RalfJung May 28, 2026
375c51c
move target feature list explanation to module-level doc comment
RalfJung May 28, 2026
80a8b21
Fix CI free-disk-space-linux script
ehuss May 30, 2026
732998a
allow target-dependent int2ptr cast for pthread_t
hanna-kruppe May 31, 2026
9c25035
Mention how to fill a buffer with random bytes
joshtriplett May 31, 2026
f65c565
Add doc aliases for `DefaultRandomSource`, for people looking for ran…
joshtriplett May 31, 2026
3467b6f
Clean resolved signature for delegated functions in rustdoc
Dnreikronos May 31, 2026
5d3c4fb
Rollup merge of #156832 - hanna-kruppe:consistent-strict-provenance-l…
jhpratt Jun 1, 2026
229b0d3
Rollup merge of #157223 - Dnreikronos:rustdoc-async-delegation-157040…
jhpratt Jun 1, 2026
6cf4baf
Rollup merge of #156528 - jieyouxu:jieyouxu/fix/rustfmt-download-rust…
jhpratt Jun 1, 2026
3a02bed
Rollup merge of #157059 - RalfJung:forbidden-target-features, r=worki…
jhpratt Jun 1, 2026
9e6dd14
Rollup merge of #157159 - ehuss:fix-free-disk-space, r=Mark-Simulacrum
jhpratt Jun 1, 2026
e5b9884
Rollup merge of #157221 - joshtriplett:random-source-doc-alias, r=jhp…
jhpratt Jun 1, 2026
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
96 changes: 49 additions & 47 deletions compiler/rustc_target/src/target_features.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
//! Declares Rust's target feature names for each target.
//! Note that these are similar to but not always identical to LLVM's feature names,
//! and Rust adds some features that do not correspond to LLVM features at all.
//!
//! The target features listed here can be used in `#[target_feature]` and `#[cfg(target_feature)]`.
//! They also do not trigger any warnings when used with `-Ctarget-feature`.
//!
//! Note that even unstable (and even entirely unlisted) features can be used with `-Ctarget-feature`
//! on stable. Using a feature not on the list of Rust target features only emits a warning.
//! Only `cfg(target_feature)` and `#[target_feature]` actually do any stability gating.
//! `cfg(target_feature)` for unstable features just works on nightly without any feature gate.
//! `#[target_feature]` requires a feature gate.
//!
//! When adding features to the below lists
//! check whether they're named already elsewhere in rust
//! e.g. in stdarch and whether the given name matches LLVM's
//! if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted.
//! Additionally, if the feature is not available in older version of LLVM supported by the current
//! rust, the same function must be updated to filter out these features to avoid triggering
//! warnings.
//!
//! Also note that all target features listed here must be purely additive: for target_feature 1.1 to
//! be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a
//! per-function level, since we would then allow safe calls from functions with `+soft-float` to
//! functions without that feature!
//!
//! It is important for soundness to consider the interaction of target features and the function
//! call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as
//! arguments, so letting people toggle that feature would be unsound. To this end, the
//! [`Target::abi_required_features`] function computes which target features must and must not be
//! enabled for any given target, and individual features can also be marked as [`Forbidden`]. See
//! <https://github.com/rust-lang/rust/issues/116344> for some more context.
//!
//! The one exception to features that change the ABI is features that enable larger vector
//! registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store
//! information about which target feature is ABI-required for which vector size; this is used to
//! ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For
//! the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.)
//! Also see <https://github.com/rust-lang/rust/issues/116558>.
//!
//! Stabilizing a target feature requires t-lang approval.
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_macros::StableHash;
use rustc_span::{Symbol, sym};
Expand Down Expand Up @@ -32,9 +70,12 @@ pub enum Stability {
Symbol,
),
/// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be
/// set in the target spec. It is never set in `cfg(target_feature)`. Used in
/// particular for features are actually ABI configuration flags (not all targets are as nice as
/// RISC-V and have an explicit way to set the ABI separate from target features).
/// set in the target spec. It is never set in `cfg(target_feature)`. Used in particular for
/// features are actually ABI configuration flags (such as "soft-float" on many targets).
/// However, "forbidden" target features can still sometimes be enabled via `-Ctarget-cpu` or
/// target feature implications (on the Rust/LLVM level). To prevent that, ABI-relevant target
/// features are ideally pinned down (required or forbidden) in
/// [`Target::abi_required_features`].
Forbidden {
reason: &'static str,
/// True if this is always an error, false if this can be reported as a warning when set via
Expand Down Expand Up @@ -102,50 +143,11 @@ impl Stability {
}
}

// Here we list target features that rustc "understands": they can be used in `#[target_feature]`
// and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with
// `-Ctarget-feature`.
//
// Note that even unstable (and even entirely unlisted) features can be used with `-Ctarget-feature`
// on stable. Using a feature not on the list of Rust target features only emits a warning.
// Only `cfg(target_feature)` and `#[target_feature]` actually do any stability gating.
// `cfg(target_feature)` for unstable features just works on nightly without any feature gate.
// `#[target_feature]` requires a feature gate.
//
// When adding features to the below lists
// check whether they're named already elsewhere in rust
// e.g. in stdarch and whether the given name matches LLVM's
// if it doesn't, to_llvm_feature in llvm_util in rustc_codegen_llvm needs to be adapted.
// Additionally, if the feature is not available in older version of LLVM supported by the current
// rust, the same function must be updated to filter out these features to avoid triggering
// warnings.
//
// Also note that all target features listed here must be purely additive: for target_feature 1.1 to
// be sound, we can never allow features like `+soft-float` (on x86) to be controlled on a
// per-function level, since we would then allow safe calls from functions with `+soft-float` to
// functions without that feature!
//
// It is important for soundness to consider the interaction of targets features and the function
// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as
// arguments, so letting people toggle that feature would be unsound. To this end, the
// `abi_required_features` function computes which target features must and must not be enabled for
// any given target, and individual features can also be marked as `Forbidden`.
// See https://github.com/rust-lang/rust/issues/116344 for some more context.
//
// The one exception to features that change the ABI is features that enable larger vector
// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store
// information about which target feature is ABI-required for which vector size; this is used to
// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For
// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.)
// Also see https://github.com/rust-lang/rust/issues/116558.
//
// Stabilizing a target feature requires t-lang approval.

// If feature A "implies" feature B, then:
// - when A gets enabled (via `-Ctarget-feature` or `#[target_feature]`), we also enable B
// - when B gets disabled (via `-Ctarget-feature`), we also disable A
//
// Both of these are also applied transitively.
/// If feature A "implies" feature B, then:
/// - when A gets enabled (via `-Ctarget-feature` or `#[target_feature]`), we also enable B
/// - when B gets disabled (via `-Ctarget-feature`), we also disable A
///
/// Both of these are also applied transitively.
type ImpliedFeatures = &'static [&'static str];

static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
Expand Down
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
// Lints:
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
#![warn(deprecated_in_future)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/benches/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![feature(strict_provenance_lints)]
#![feature(test)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]

extern crate test;

Expand Down
4 changes: 2 additions & 2 deletions library/alloctests/tests/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ fn box_clone_from_ptr_stability() {
for size in (0..8).map(|i| 2usize.pow(i)) {
let control = vec![Dummy { _data: 42 }; size].into_boxed_slice();
let mut copy = vec![Dummy { _data: 84 }; size].into_boxed_slice();
let copy_raw = copy.as_ptr() as usize;
let copy_raw = copy.as_ptr();
copy.clone_from(&control);
assert_eq!(copy.as_ptr() as usize, copy_raw);
assert_eq!(copy.as_ptr(), copy_raw);
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/alloctests/tests/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn check_overalign_requests<T: Allocator>(allocator: T) {
.collect();
for &ptr in &pointers {
assert_eq!(
(ptr.as_non_null_ptr().as_ptr() as usize) % align,
ptr.as_non_null_ptr().as_ptr().addr() % align,
0,
"Got a pointer less aligned than requested"
)
Expand Down
1 change: 1 addition & 0 deletions library/alloctests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#![feature(ptr_cast_slice)]
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]

extern crate alloc;
Expand Down
2 changes: 1 addition & 1 deletion library/alloctests/tests/sort/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ fn self_cmp<T: Ord + Clone + Debug, S: Sort>(
pattern_fn(len).into_iter().map(|val| type_into_fn(val)).collect::<Vec<_>>();

let comparison_fn = |a: &T, b: &T| {
assert_ne!(a as *const T as usize, b as *const T as usize);
assert_ne!(a as *const T, b as *const T);
a.cmp(b)
};

Expand Down
8 changes: 4 additions & 4 deletions library/alloctests/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,7 @@ fn test_into_iter_zst() {
struct AlignedZstWithDrop([u64; 0]);
impl Drop for AlignedZstWithDrop {
fn drop(&mut self) {
let addr = self as *mut _ as usize;
let addr = (self as *mut Self).addr();
assert!(hint::black_box(addr) % align_of::<u64>() == 0);
}
}
Expand Down Expand Up @@ -1356,10 +1356,10 @@ fn overaligned_allocations() {
for i in 0..0x1000 {
v.reserve_exact(i);
assert!(v[0].0 == 273);
assert!(v.as_ptr() as usize & 0xff == 0);
assert!(v.as_ptr().addr() & 0xff == 0);
v.shrink_to_fit();
assert!(v[0].0 == 273);
assert!(v.as_ptr() as usize & 0xff == 0);
assert!(v.as_ptr().addr() & 0xff == 0);
}
}

Expand Down Expand Up @@ -2574,7 +2574,7 @@ fn test_box_zero_allocator() {

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() == 0 {
let addr = ptr.as_ptr() as usize;
let addr = ptr.as_ptr().addr();
let mut state = self.state.borrow_mut();
std::println!("freeing {addr}");
assert!(state.0.remove(&addr), "ZST free that wasn't allocated");
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#![deny(rust_2021_incompatible_or_patterns)]
#![deny(unsafe_op_in_unsafe_fn)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
#![warn(deprecated_in_future)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ impl<T: PointeeSized> *const T {
/// [`with_exposed_provenance`]: with_exposed_provenance
#[inline(always)]
#[stable(feature = "exposed_provenance", since = "1.84.0")]
#[expect(lossy_provenance_casts, reason = "this *is* the replacement")]
pub fn expose_provenance(self) -> usize {
self.cast::<()>() as usize
}
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2618,21 +2618,21 @@ impl<F: FnPtr> Ord for F {
#[stable(feature = "fnptr_impls", since = "1.4.0")]
impl<F: FnPtr> hash::Hash for F {
fn hash<HH: hash::Hasher>(&self, state: &mut HH) {
state.write_usize(self.addr() as _)
state.write_usize(self.addr().addr())
}
}

#[stable(feature = "fnptr_impls", since = "1.4.0")]
impl<F: FnPtr> fmt::Pointer for F {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::pointer_fmt_inner(self.addr() as _, f)
fmt::pointer_fmt_inner(self.addr().addr(), f)
}
}

#[stable(feature = "fnptr_impls", since = "1.4.0")]
impl<F: FnPtr> fmt::Debug for F {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::pointer_fmt_inner(self.addr() as _, f)
fmt::pointer_fmt_inner(self.addr().addr(), f)
}
}

Expand Down
1 change: 1 addition & 0 deletions library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl<T: PointeeSized> *mut T {
/// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut
#[inline(always)]
#[stable(feature = "exposed_provenance", since = "1.84.0")]
#[expect(lossy_provenance_casts, reason = "this *is* the replacement")]
pub fn expose_provenance(self) -> usize {
self.cast::<()>() as usize
}
Expand Down
4 changes: 2 additions & 2 deletions library/coretests/tests/char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ fn test_encode_utf8() {
let mut buf = [0; char::MAX_LEN_UTF8];
let ptr = buf.as_ptr();
let s = input.encode_utf8(&mut buf);
assert_eq!(s.as_ptr() as usize, ptr as usize);
assert_eq!(s.as_ptr(), ptr);
assert!(str::from_utf8(s.as_bytes()).is_ok());
assert_eq!(s.as_bytes(), expect);
}
Expand All @@ -335,7 +335,7 @@ fn test_encode_utf16() {
let mut buf = [0; 2];
let ptr = buf.as_mut_ptr();
let b = input.encode_utf16(&mut buf);
assert_eq!(b.as_mut_ptr() as usize, ptr as usize);
assert_eq!(b.as_mut_ptr(), ptr);
assert_eq!(b, expect);
}

Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
// tidy-alphabetical-end
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]

/// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality.
Expand Down
2 changes: 1 addition & 1 deletion library/coretests/tests/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ fn align_offset_stride_one() {
#[test]
fn align_offset_various_strides() {
unsafe fn test_stride<T>(ptr: *const T, align: usize) -> bool {
let numptr = ptr as usize;
let numptr = ptr.addr();
let mut expected = usize::MAX;
// Naive but definitely correct way to find the *first* aligned element of stride::<T>.
for el in 0..align {
Expand Down
2 changes: 1 addition & 1 deletion library/coretests/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,7 @@ fn test_align_to_empty_mid() {
type Chunk = u32;
for offset in 0..4 {
let (_, mid, _) = unsafe { bytes[offset..offset + 1].align_to::<Chunk>() };
assert_eq!(mid.as_ptr() as usize % align_of::<Chunk>(), 0);
assert_eq!(mid.as_ptr().addr() % align_of::<Chunk>(), 0);
}
}

Expand Down
6 changes: 3 additions & 3 deletions library/coretests/tests/waker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ use std::task::{RawWaker, RawWakerVTable, Waker};
fn test_waker_getters() {
let raw_waker = RawWaker::new(ptr::without_provenance_mut(42usize), &WAKER_VTABLE);
let waker = unsafe { Waker::from_raw(raw_waker) };
assert_eq!(waker.data() as usize, 42);
assert_eq!(waker.data().addr(), 42);
assert!(ptr::eq(waker.vtable(), &WAKER_VTABLE));

let waker2 = waker.clone();
assert_eq!(waker2.data() as usize, 43);
assert_eq!(waker2.data().addr(), 43);
assert!(ptr::eq(waker2.vtable(), &WAKER_VTABLE));
}

static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
|data| RawWaker::new(ptr::without_provenance_mut(data as usize + 1), &WAKER_VTABLE),
|data| RawWaker::new(ptr::without_provenance_mut(data.addr() + 1), &WAKER_VTABLE),
|_| {},
|_| {},
|_| {},
Expand Down
9 changes: 8 additions & 1 deletion library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@
#![allow(unused_lifetimes)]
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(lossy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]
#![allow(rustdoc::redundant_explicit_links)]
#![warn(rustdoc::unescaped_backticks)]
Expand Down Expand Up @@ -723,7 +724,13 @@ pub mod alloc;
mod panicking;

#[path = "../../backtrace/src/lib.rs"]
#[allow(dead_code, unused_attributes, fuzzy_provenance_casts, unsafe_op_in_unsafe_fn)]
#[allow(
dead_code,
unused_attributes,
fuzzy_provenance_casts,
lossy_provenance_casts,
unsafe_op_in_unsafe_fn
)]
mod backtrace_rs;

#[stable(feature = "cfg_select", since = "1.95.0")]
Expand Down
5 changes: 5 additions & 0 deletions library/std/src/os/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ pub trait JoinHandleExt {

#[stable(feature = "thread_extensions", since = "1.9.0")]
impl<T> JoinHandleExt for JoinHandle<T> {
// This is an int2ptr cast on some platforms (e.g., *-musl) where RawPthread
// is an integer but libc::pthread_t is a pointer. Exposed provenance is the
// safe choice here, but `as` also works when it's int2int or ptr2ptr.
#[allow(lossy_provenance_casts)]
fn as_pthread_t(&self) -> RawPthread {
self.as_inner().id() as RawPthread
}

#[allow(lossy_provenance_casts)] // see above for why
fn into_pthread_t(self) -> RawPthread {
self.into_inner().into_id() as RawPthread
}
Expand Down
3 changes: 3 additions & 0 deletions library/std/src/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use crate::sys::random as sys;
/// security is not a concern, consider using an alternative random number
/// generator (potentially seeded from this one).
///
/// If you need to fill a buffer with random bytes, use `DefaultRandomSource.fill_bytes(&mut buf)`.
///
/// # Underlying sources
///
/// Platform | Source
Expand Down Expand Up @@ -54,6 +56,7 @@ use crate::sys::random as sys;
///
/// [`getrandom`]: https://www.man7.org/linux/man-pages/man2/getrandom.2.html
/// [`/dev/urandom`]: https://www.man7.org/linux/man-pages/man4/random.4.html
#[doc(alias = "getrandom", alias = "getentropy", alias = "arc4random")]
#[derive(Default, Debug, Clone, Copy)]
#[unstable(feature = "random", issue = "130703")]
pub struct DefaultRandomSource;
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sync/mpmc/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Operation {
/// and is alive for the entire duration of a blocking operation.
#[inline]
pub fn hook<T>(r: &mut T) -> Operation {
let val = r as *mut T as usize;
let val = (r as *mut T).addr();
// Make sure that the pointer address doesn't equal the numerical representation of
// `Selected::{Waiting, Aborted, Disconnected}`.
assert!(val > 2);
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sync/mpmc/zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl Default for ZeroToken {

impl fmt::Debug for ZeroToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&(self.0 as usize), f)
fmt::Debug::fmt(&self.0.addr(), f)
}
}

Expand Down
3 changes: 2 additions & 1 deletion library/std/src/sys/args/sgx.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers
// FIXME: this module systematically confuses pointers and integers
#![allow(fuzzy_provenance_casts, lossy_provenance_casts)]

use crate::ffi::OsString;
use crate::num::NonZero;
Expand Down
Loading
Loading