From e95751a9ff2af6a23427eee42e71258c11d89d7b Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 1 Jun 2025 14:56:45 +0800 Subject: [PATCH 01/25] Explain TOCTOU on the top of `std::fs`, and ref it in functions Signed-off-by: xizheyin Signed-off-by: xizheyin --- library/std/src/fs.rs | 50 ++++++++++++++++++++++++++++++----------- library/std/src/path.rs | 6 +++-- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 711efc7d011c1..9e45f1fbfbd2b 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -4,6 +4,27 @@ //! filesystem. All methods in this module represent cross-platform filesystem //! operations. Extra platform-specific functionality can be found in the //! extension traits of `std::os::$platform`. +//! +//! # Time of Check to Time of Use (TOCTOU) +//! +//! Many filesystem operations are subject to a race condition known as "Time of Check to Time of Use" +//! (TOCTOU). This occurs when a program checks a condition (like file existence or permissions) +//! and then uses the result of that check to make a decision, but the condition may have changed +//! between the check and the use. +//! +//! For example, checking if a file exists and then creating it if it doesn't is vulnerable to +//! TOCTOU - another process could create the file between your check and creation attempt. +//! +//! Another example is with symbolic links: when removing a directory, if another process replaces +//! the directory with a symbolic link between the check and the removal operation, the removal +//! might affect the wrong location. This is why operations like [`remove_dir_all`] need to use +//! atomic operations to prevent such race conditions. +//! +//! To avoid TOCTOU issues: +//! - Be aware that metadata operations (like [`metadata`] or [`symlink_metadata`]) may be affected by +//! changes made by other processes. +//! - Use atomic operations when possible (like [`File::create_new`] instead of checking existence then creating). +//! - Keep file open for the duration of operations. #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] @@ -549,13 +570,14 @@ impl File { /// non-exhaustive list of likely errors. /// /// This option is useful because it is atomic. Otherwise between checking whether a file - /// exists and creating a new one, the file may have been created by another process (a TOCTOU + /// exists and creating a new one, the file may have been created by another process (a [TOCTOU] /// race condition / attack). /// /// This can also be written using /// `File::options().read(true).write(true).create_new(true).open(...)`. /// /// [`AlreadyExists`]: crate::io::ErrorKind::AlreadyExists + /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou /// /// # Examples /// @@ -1580,7 +1602,7 @@ impl OpenOptions { /// /// This option is useful because it is atomic. Otherwise between checking /// whether a file exists and creating a new one, the file may have been - /// created by another process (a TOCTOU race condition / attack). + /// created by another process (a [TOCTOU] race condition / attack). /// /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are /// ignored. @@ -1591,6 +1613,7 @@ impl OpenOptions { /// [`.create()`]: OpenOptions::create /// [`.truncate()`]: OpenOptions::truncate /// [`AlreadyExists`]: io::ErrorKind::AlreadyExists + /// [TOCTOU]: self#time-of-check-to-time-of-use-toctou /// /// # Examples /// @@ -2924,17 +2947,17 @@ pub fn remove_dir>(path: P) -> io::Result<()> { /// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile`. /// /// ## Time-of-check to time-of-use (TOCTOU) race conditions -/// On a few platforms there is no way to remove a directory's contents without following symlinks -/// unless you perform a check and then operate on paths based on that directory. -/// This allows concurrently-running code to replace the directory with a symlink after the check, -/// causing a removal to instead operate on a path based on the symlink. This is a TOCTOU race. -/// By default, `fs::remove_dir_all` protects against a symlink TOCTOU race on all platforms -/// except the following. It should not be used in security-sensitive contexts on these platforms: -/// - Miri: Even when emulating targets where the underlying implementation will protect against -/// TOCTOU races, Miri will not do so. -/// - Redox OS: This function does not protect against TOCTOU races, as Redox does not implement -/// the required platform support to do so. +/// See the [module-level TOCTOU explanation](self#time-of-check-to-time-of-use-toctou). +/// +/// On most platforms, `fs::remove_dir_all` protects against symlink TOCTOU races by default. +/// However, on the following platforms, this protection is not provided and the function should +/// not be used in security-sensitive contexts: +/// - **Miri**: Even when emulating targets where the underlying implementation will protect against +/// TOCTOU races, Miri will not do so. +/// - **Redox OS**: This function does not protect against TOCTOU races, as Redox does not implement +/// the required platform support to do so. /// +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou /// [changes]: io#platform-specific-behavior /// /// # Errors @@ -3208,7 +3231,7 @@ impl AsInnerMut for DirBuilder { /// permission is denied on one of the parent directories. /// /// Note that while this avoids some pitfalls of the `exists()` method, it still can not -/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios +/// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios /// where those bugs are not an issue. /// /// # Examples @@ -3221,6 +3244,7 @@ impl AsInnerMut for DirBuilder { /// ``` /// /// [`Path::exists`]: crate::path::Path::exists +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou #[stable(feature = "fs_try_exists", since = "1.81.0")] #[inline] pub fn exists>(path: P) -> io::Result { diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 050c617f5649c..ab8351ff9682b 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3090,7 +3090,7 @@ impl Path { /// Returns `true` if the path points at an existing entity. /// /// Warning: this method may be error-prone, consider using [`try_exists()`] instead! - /// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs. + /// It also has a risk of introducing time-of-check to time-of-use ([TOCTOU]) bugs. /// /// This function will traverse symbolic links to query information about the /// destination file. @@ -3111,6 +3111,7 @@ impl Path { /// check errors, call [`Path::try_exists`]. /// /// [`try_exists()`]: Self::try_exists + /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou #[stable(feature = "path_ext", since = "1.5.0")] #[must_use] #[inline] @@ -3130,7 +3131,7 @@ impl Path { /// permission is denied on one of the parent directories. /// /// Note that while this avoids some pitfalls of the `exists()` method, it still can not - /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios + /// prevent time-of-check to time-of-use ([TOCTOU]) bugs. You should only use it in scenarios /// where those bugs are not an issue. /// /// This is an alias for [`std::fs::exists`](crate::fs::exists). @@ -3143,6 +3144,7 @@ impl Path { /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); /// ``` /// + /// [TOCTOU]: fs#time-of-check-to-time-of-use-toctou /// [`exists()`]: Self::exists #[stable(feature = "path_try_exists", since = "1.63.0")] #[inline] From 230b55dcf821a25d945e8d0ddec2cea460a46935 Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Fri, 6 Jun 2025 22:14:30 -0500 Subject: [PATCH 02/25] Add `into_chunks` --- library/alloc/src/vec/mod.rs | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index ce7321544b6b9..71fd5b409ac80 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2999,6 +2999,58 @@ impl Vec { (initialized, spare, &mut self.len) } } + + /// Groups every `N` elements in the `Vec` into chunks to produce a `Vec<[T; N]>`, dropping + /// elements in the remainder. `N` must be greater than zero. + /// + /// If the capacity is not a multiple of the chunk size, the buffer will shrink down to the + /// nearest multiple with a reallocation or deallocation. + /// + /// This function can be used to reverse [`Vec::into_flattened`]. + /// + /// # Examples + /// + /// ``` + /// let vec = vec![0, 1, 2, 3, 4, 5, 6, 7]; + /// assert_eq!(vec.into_chunks::<3>(), [[0, 1, 2], [3, 4, 5]]); + /// + /// let vec = vec![0, 1, 2, 3]; + /// let chunks: Vec<[u8; 10]> = vec.into_chunks(); + /// assert!(chunks.is_empty()); + /// + /// let flat = vec![0; 8 * 8 * 8]; + /// let reshaped: Vec<[[[u8; 8]; 8]; 8]> = flat.into_chunks().into_chunks().into_chunks(); + /// assert_eq!(reshaped.len(), 1); + /// ``` + #[unstable(feature = "vec_into_chunks", issue = "142137")] + pub fn into_chunks(mut self) -> Vec<[T; N], A> { + const { + assert!(N != 0, "chunk size should be greater than zero"); + } + + let (len, cap) = (self.len(), self.capacity()); + + let len_remainder = len % N; + if len_remainder != 0 { + self.truncate(len - len_remainder); + } + + let cap_remainder = cap % N; + if !T::IS_ZST && cap_remainder != 0 { + self.buf.shrink_to_fit(cap - cap_remainder); + } + + let (ptr, _, _, alloc) = self.into_raw_parts_with_alloc(); + + // SAFETY: + // - `ptr` and `alloc` were just returned from `self.into_raw_parts_with_alloc()` + // - `[T; N]` has the same alignment as `T` + // - `size_of::<[T; N]>() * cap / N == size_of::() * cap` + // - `len / N <= cap / N` because `len <= cap` + // - the allocated memory consists of `len / N` valid values of type `[T; N]` + // - `cap / N` fits the size of the allocated memory after shrinking + unsafe { Vec::from_raw_parts_in(ptr.cast(), len / N, cap / N, alloc) } + } } impl Vec { From e4e77737f9eebf1ed6a382fa4f4b8403554dad7c Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Fri, 6 Jun 2025 23:06:23 -0500 Subject: [PATCH 03/25] put feature attribute in example --- library/alloc/src/vec/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 71fd5b409ac80..8bd8db3b7e299 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3011,6 +3011,8 @@ impl Vec { /// # Examples /// /// ``` + /// #![feature(vec_into_chunks)] + /// /// let vec = vec![0, 1, 2, 3, 4, 5, 6, 7]; /// assert_eq!(vec.into_chunks::<3>(), [[0, 1, 2], [3, 4, 5]]); /// From 343db27aa6256ebbde88b1147eb42d41d4e77e64 Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Sat, 7 Jun 2025 01:14:24 -0500 Subject: [PATCH 04/25] cfg for no no_global_oom_handling --- library/alloc/src/vec/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 8bd8db3b7e299..bdac38e912021 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3024,6 +3024,7 @@ impl Vec { /// let reshaped: Vec<[[[u8; 8]; 8]; 8]> = flat.into_chunks().into_chunks().into_chunks(); /// assert_eq!(reshaped.len(), 1); /// ``` + #[cfg(not(no_global_oom_handling))] #[unstable(feature = "vec_into_chunks", issue = "142137")] pub fn into_chunks(mut self) -> Vec<[T; N], A> { const { From c1d32d85339a5c06f95da49c1731ac9a3d9c2ea3 Mon Sep 17 00:00:00 2001 From: Amogh Shivaram Date: Sat, 7 Jun 2025 08:18:44 -0500 Subject: [PATCH 05/25] fix wording in assert --- library/alloc/src/vec/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index bdac38e912021..bd4273d23aa2b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3028,7 +3028,7 @@ impl Vec { #[unstable(feature = "vec_into_chunks", issue = "142137")] pub fn into_chunks(mut self) -> Vec<[T; N], A> { const { - assert!(N != 0, "chunk size should be greater than zero"); + assert!(N != 0, "chunk size must be greater than zero"); } let (len, cap) = (self.len(), self.capacity()); From cfa6731c495192a490492e8aae91425a9825223b Mon Sep 17 00:00:00 2001 From: ostylk Date: Mon, 16 Jun 2025 22:17:09 +0200 Subject: [PATCH 06/25] expose abi information on ppc64 targets --- .../rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs | 1 + .../src/spec/targets/powerpc64_unknown_linux_gnu.rs | 1 + .../src/spec/targets/powerpc64_unknown_linux_musl.rs | 1 + .../rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs | 1 + compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs | 1 + .../src/spec/targets/powerpc64le_unknown_freebsd.rs | 1 + .../src/spec/targets/powerpc64le_unknown_linux_gnu.rs | 1 + .../src/spec/targets/powerpc64le_unknown_linux_musl.rs | 1 + tests/ui/check-cfg/well-known-values.stderr | 2 +- 9 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index 66733d5d4b89c..b01ca989282ef 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index ecf68ddff8c2f..bc7e445f3f8ae 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv1".into(); base.llvm_abiname = "elfv1".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index e205aef82856e..4dc76f0936c92 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index bcb328020eea8..9dc44aa05cc0a 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 37c888ba51443..10072f8092deb 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv1".into(); base.llvm_abiname = "elfv1".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index 3096c4d14ad87..a7f4e0eabb0a2 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs index 9e406af53b5b5..af5704b51ba33 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index f145c5b8c1451..26ee6a68c6a8b 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; + base.abi = "elfv2".into(); base.llvm_abiname = "elfv2".into(); Target { diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 532c1ab13d118..2484974cdc27c 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` From 45538e0beb220c86610ee8f650ee1ed8c6fc6b52 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 22 Jun 2025 20:47:21 +0200 Subject: [PATCH 07/25] ci: aarch64-gnu: Stop skipping `panic_abort_doc_tests` The skipped test passes since nightly-2024-11-29. Let's stop skipping it to increase the chance of detecing a regression. --- src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile index d5027589e0bd6..e6133fce83e28 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu/Dockerfile @@ -26,6 +26,5 @@ ENV RUST_CONFIGURE_ARGS \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs -# FIXME: Skipping cargo panic_abort_doc_tests due to https://github.com/rust-lang/rust/issues/123733 ENV SCRIPT python3 ../x.py --stage 2 test && \ - python3 ../x.py --stage 2 test src/tools/cargo --test-args \"--skip panic_abort_doc_tests\" + python3 ../x.py --stage 2 test src/tools/cargo From 4238e5e2c68c1c01c1db651e75c9a293e86f98f9 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sat, 31 May 2025 15:33:46 +0200 Subject: [PATCH 08/25] sleep_until: add platform specific implementations Except for unix they are all copies of the existing non platform specific implementation. This is the only way since tidy does not allow `#[cfg(target_os = ` in src/thread/mod.rs. Once this is merged more specializations will follow. --- library/std/src/sys/pal/hermit/thread.rs | 10 +++- library/std/src/sys/pal/itron/thread.rs | 10 +++- library/std/src/sys/pal/sgx/thread.rs | 10 +++- library/std/src/sys/pal/teeos/thread.rs | 10 +++- library/std/src/sys/pal/uefi/thread.rs | 10 +++- library/std/src/sys/pal/unix/thread.rs | 59 ++++++++++++++++++- library/std/src/sys/pal/unix/time.rs | 7 +++ library/std/src/sys/pal/unsupported/thread.rs | 4 ++ library/std/src/sys/pal/wasi/thread.rs | 10 +++- .../std/src/sys/pal/wasm/atomics/thread.rs | 10 +++- library/std/src/sys/pal/windows/thread.rs | 10 +++- library/std/src/sys/pal/xous/thread.rs | 10 +++- library/std/src/thread/mod.rs | 39 +++++++++--- library/std/src/time.rs | 7 +++ library/std/tests/thread.rs | 14 ++++- 15 files changed, 200 insertions(+), 20 deletions(-) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index bb68a824fc313..9bc5a16b80023 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -4,7 +4,7 @@ use super::hermit_abi; use crate::ffi::CStr; use crate::mem::ManuallyDrop; use crate::num::NonZero; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{io, ptr}; pub type Tid = hermit_abi::Tid; @@ -86,6 +86,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { unsafe { let _ = hermit_abi::join(self.tid); diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index a974f4f17ae67..813e1cbcd58fb 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -10,7 +10,7 @@ use crate::mem::ManuallyDrop; use crate::num::NonZero; use crate::ptr::NonNull; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{hint, io}; pub struct Thread { @@ -205,6 +205,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { // Safety: `ThreadInner` is alive at this point let inner = unsafe { self.p_inner.as_ref() }; diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 219ef1b7a9897..85f6dcd96b4a5 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -5,7 +5,7 @@ use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::num::NonZero; -use crate::time::Duration; +use crate::time::{Duration, Instant}; pub struct Thread(task_queue::JoinHandle); @@ -132,6 +132,14 @@ impl Thread { usercalls::wait_timeout(0, dur, || true); } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { self.0.wait(); } diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index e3b4908f85863..b9cdc7a2a58bb 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -2,7 +2,7 @@ use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; use crate::sys::os; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{cmp, io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; @@ -109,6 +109,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + /// must join, because no pthread_detach supported pub fn join(self) { let id = self.into_id(); diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index 7d4006ff4b2f7..e4776ec42fbba 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -3,7 +3,7 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::ptr::NonNull; -use crate::time::Duration; +use crate::time::{Duration, Instant}; pub struct Thread(!); @@ -39,6 +39,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { self.0 } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index d8b189413f4a3..7ee7241f1000b 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -6,7 +6,7 @@ use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{cmp, io, ptr}; #[cfg(not(any( target_os = "l4re", @@ -296,6 +296,63 @@ impl Thread { } } + // Any unix that has clock_nanosleep + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + ))] + pub fn sleep_until(deadline: Instant) { + let mut ts = deadline + .into_inner() + .into_timespec() + .to_timespec() + .expect("Timespec is narrower then libc::timespec thus conversion can't fail"); + let ts_ptr = &mut ts as *mut _; + + // If we're awoken with a signal and the return value is -1 + // clock_nanosleep needs to be called again. + unsafe { + while libc::clock_nanosleep(libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, ts_ptr, ts_ptr) + == -1 + { + assert_eq!( + os::errno(), + libc::EINTR, + "clock nanosleep should only return an error if interrupted" + ); + } + } + } + + // Any unix that does not have clock_nanosleep + #[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + )))] + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { let id = self.into_id(); let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 0074d7674741b..d58e1c8b564f5 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -291,6 +291,13 @@ impl Instant { pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(Instant { t: self.t.checked_sub_duration(other)? }) } + + // reason for allow(unused): this is needed by the `sleep_until` implementation + // for some unix platforms not all. + #[allow(unused)] + pub(crate) fn into_timespec(self) -> Timespec { + self.t + } } impl fmt::Debug for Instant { diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index 89f8bad7026ee..9e52d5c94bc68 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -26,6 +26,10 @@ impl Thread { panic!("can't sleep"); } + pub fn sleep_until(_deadline: Instant) { + panic!("can't sleep"); + } + pub fn join(self) { self.0 } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index cc569bb3daf68..5f21a553673a3 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -2,7 +2,7 @@ use crate::ffi::CStr; use crate::num::NonZero; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{io, mem}; cfg_if::cfg_if! { @@ -171,6 +171,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index dd5aff391fd8b..44ce3eab109f4 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -2,7 +2,7 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::sys::unsupported; -use crate::time::Duration; +use crate::time::{Duration, Instant}; pub struct Thread(!); @@ -41,6 +41,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) {} } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 45e52cf4d047f..147851717553a 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -8,7 +8,7 @@ use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -106,6 +106,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn handle(&self) -> &Handle { &self.handle } diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 0ebb46dc19faa..1b344e984dc36 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -8,7 +8,7 @@ use crate::os::xous::ffi::{ map_memory, update_memory_flags, }; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; pub struct Thread { tid: ThreadId, @@ -128,6 +128,14 @@ impl Thread { } } + pub fn sleep_until(deadline: Instant) { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + } + pub fn join(self) { join_thread(self.tid).unwrap(); } diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 26b2fb4472436..7ee6990be8bb7 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -897,8 +897,33 @@ pub fn sleep(dur: Duration) { /// /// # Platform-specific behavior /// -/// This function uses [`sleep`] internally, see its platform-specific behavior. -/// +/// In most cases this function will call an OS specific function. Where that +/// is not supported [`sleep`] is used. Those platforms are referred to as other +/// in the table below. +/// +/// # Underlying System calls +/// +/// The following system calls are [currently] being used: +/// +/// | Platform | System call | +/// |-----------|----------------------------------------------------------------------| +/// | Linux | [clock_nanosleep] (Monotonic clock) | +/// | BSD except OpenBSD | [clock_nanosleep] (Monotonic Clock)] | +/// | Android | [clock_nanosleep] (Monotonic Clock)] | +/// | Solaris | [clock_nanosleep] (Monotonic Clock)] | +/// | Illumos | [clock_nanosleep] (Monotonic Clock)] | +/// | Dragonfly | [clock_nanosleep] (Monotonic Clock)] | +/// | Hurd | [clock_nanosleep] (Monotonic Clock)] | +/// | Fuchsia | [clock_nanosleep] (Monotonic Clock)] | +/// | Vxworks | [clock_nanosleep] (Monotonic Clock)] | +/// | Other | `sleep_until` uses [`sleep`] and does not issue a syscall itself | +/// +/// [currently]: crate::io#platform-specific-behavior +/// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep +/// [subscription_clock]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-subscription_clock-record +/// [mach_wait_until]: https://developer.apple.com/library/archive/technotes/tn2169/_index.html +/// +/// **Disclaimer:** These system calls might change over time. /// /// # Examples /// @@ -923,9 +948,9 @@ pub fn sleep(dur: Duration) { /// } /// ``` /// -/// A slow api we must not call too fast and which takes a few +/// A slow API we must not call too fast and which takes a few /// tries before succeeding. By using `sleep_until` the time the -/// api call takes does not influence when we retry or when we give up +/// API call takes does not influence when we retry or when we give up /// /// ```no_run /// #![feature(thread_sleep_until)] @@ -960,11 +985,7 @@ pub fn sleep(dur: Duration) { /// ``` #[unstable(feature = "thread_sleep_until", issue = "113752")] pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - sleep(delay); - } + imp::Thread::sleep_until(deadline) } /// Used to ensure that `park` and `park_timeout` do not unwind, as that can diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 03af35e809c91..08d137dc14ac2 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -407,6 +407,13 @@ impl Instant { pub fn checked_sub(&self, duration: Duration) -> Option { self.0.checked_sub_duration(&duration).map(Instant) } + + // used by platform specific `sleep_until` implementations. + // reason for #[allow(unused)]: not every platform has a specific `sleep_until`. + #[allow(unused)] + pub(crate) fn into_inner(self) -> time::Instant { + self.0 + } } #[stable(feature = "time2", since = "1.8.0")] diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 1bb17d149fa10..32561dd6ab6a3 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -1,7 +1,8 @@ +#![feature(thread_sleep_until)] use std::cell::{Cell, RefCell}; use std::sync::{Arc, Mutex}; use std::thread; -use std::time::Duration; +use std::time::{Duration, Instant}; #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads @@ -17,6 +18,17 @@ fn sleep_very_long() { assert_eq!(*finished.lock().unwrap(), false); } +#[test] +fn sleep_until() { + let now = Instant::now(); + let period = Duration::from_millis(100); + let deadline = now + period; + thread::sleep_until(deadline); + + let elapsed = now.elapsed(); + assert!(elapsed >= period); +} + #[test] fn thread_local_containing_const_statements() { // This exercises the `const $init:block` cases of the thread_local macro. From 395aaf8302b822127dfcb77e02fe25880ef60d21 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 3 Jun 2025 08:13:26 +0200 Subject: [PATCH 09/25] sleep_until: add reason to allow(unused) Co-authored-by: Josh Stone --- library/std/src/sys/pal/unix/time.rs | 4 +--- library/std/src/time.rs | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index d58e1c8b564f5..afce740af9f80 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -292,9 +292,7 @@ impl Instant { Some(Instant { t: self.t.checked_sub_duration(other)? }) } - // reason for allow(unused): this is needed by the `sleep_until` implementation - // for some unix platforms not all. - #[allow(unused)] + #[allow(unused, reason = "needed by the `sleep_until` on some unix platforms")] pub(crate) fn into_timespec(self) -> Timespec { self.t } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 08d137dc14ac2..b4d62b5688a22 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -409,8 +409,7 @@ impl Instant { } // used by platform specific `sleep_until` implementations. - // reason for #[allow(unused)]: not every platform has a specific `sleep_until`. - #[allow(unused)] + #[allow(unused, reason = "not every platform has a specific `sleep_until`")] pub(crate) fn into_inner(self) -> time::Instant { self.0 } From 168a1a174acc9e7a28d2b77d602ad100254ca229 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Tue, 3 Jun 2025 08:51:10 +0200 Subject: [PATCH 10/25] sleep_until: handle 32bit time_t targets --- library/std/src/sys/pal/unix/thread.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7ee7241f1000b..42441dd43b370 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -310,11 +310,17 @@ impl Thread { target_os = "vxworks", ))] pub fn sleep_until(deadline: Instant) { - let mut ts = deadline - .into_inner() - .into_timespec() - .to_timespec() - .expect("Timespec is narrower then libc::timespec thus conversion can't fail"); + let Some(mut ts) = deadline.into_inner().into_timespec().to_timespec() else { + // The deadline is further in the future then can be passed to + // clock_nanosleep. We have to use Self::sleep intead. This might + // happen on 32 bit platforms, especially closer to 2038. + let now = Instant::now(); + if let Some(delay) = deadline.checked_duration_since(now) { + Self::sleep(delay); + } + return; + }; + let ts_ptr = &mut ts as *mut _; // If we're awoken with a signal and the return value is -1 @@ -347,7 +353,6 @@ impl Thread { )))] pub fn sleep_until(deadline: Instant) { let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { Self::sleep(delay); } From 1626d55069636fd06a266868c5b24ebca33ffc84 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Thu, 5 Jun 2025 18:13:19 +0200 Subject: [PATCH 11/25] sleep_until: improve maintainability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonas Böttiger - use the internal platform specific Instant::CLOCK_ID - skip allow(unused) on on platform that uses it such that it can not become dead code - sleep_until: remove leftover links --- library/std/src/sys/pal/unix/thread.rs | 8 ++++++-- library/std/src/sys/pal/unix/time.rs | 10 +++++----- library/std/src/thread/mod.rs | 2 -- library/std/src/time.rs | 7 +++++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 42441dd43b370..c1bc497c0f674 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -326,8 +326,12 @@ impl Thread { // If we're awoken with a signal and the return value is -1 // clock_nanosleep needs to be called again. unsafe { - while libc::clock_nanosleep(libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, ts_ptr, ts_ptr) - == -1 + while libc::clock_nanosleep( + super::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + ts_ptr, + ts_ptr, + ) == -1 { assert_eq!( os::errno(), diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index afce740af9f80..f34e6d1dcc968 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -261,6 +261,10 @@ pub struct Instant { } impl Instant { + #[cfg(target_vendor = "apple")] + pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW; + #[cfg(not(target_vendor = "apple"))] + pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC; pub fn now() -> Instant { // https://www.manpagez.com/man/3/clock_gettime/ // @@ -273,11 +277,7 @@ impl Instant { // // Instant on macos was historically implemented using mach_absolute_time; // we preserve this value domain out of an abundance of caution. - #[cfg(target_vendor = "apple")] - const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW; - #[cfg(not(target_vendor = "apple"))] - const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC; - Instant { t: Timespec::now(clock_id) } + Instant { t: Timespec::now(Self::CLOCK_ID) } } pub fn checked_sub_instant(&self, other: &Instant) -> Option { diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 7ee6990be8bb7..6075173db47f4 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -920,8 +920,6 @@ pub fn sleep(dur: Duration) { /// /// [currently]: crate::io#platform-specific-behavior /// [clock_nanosleep]: https://linux.die.net/man/3/clock_nanosleep -/// [subscription_clock]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#-subscription_clock-record -/// [mach_wait_until]: https://developer.apple.com/library/archive/technotes/tn2169/_index.html /// /// **Disclaimer:** These system calls might change over time. /// diff --git a/library/std/src/time.rs b/library/std/src/time.rs index b4d62b5688a22..979f51ad3a39e 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -408,8 +408,11 @@ impl Instant { self.0.checked_sub_duration(&duration).map(Instant) } - // used by platform specific `sleep_until` implementations. - #[allow(unused, reason = "not every platform has a specific `sleep_until`")] + // Used by platform specific `sleep_until` implementations such as the one used on Linux. + #[cfg_attr( + not(target_os = "linux"), + allow(unused, reason = "not every platform has a specific `sleep_until`") + )] pub(crate) fn into_inner(self) -> time::Instant { self.0 } From f1f39d104c71c00df092e7726376d95a102dbf86 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Thu, 12 Jun 2025 01:07:28 +0200 Subject: [PATCH 12/25] sleep_until: Fix error handling + improve maintainablity - In contradiction to nanosleep clock_nanosleep returns the error directly and does not require a call to `os::errno()`. - The last argument to clock_nanosleep can be NULL removing the need for mutating the timespec. - Missed an `allow(unused)` that could be made conditional. --- library/std/src/sys/pal/unix/thread.rs | 35 ++++++++++++++------------ library/std/src/sys/pal/unix/time.rs | 5 +++- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index c1bc497c0f674..6276f46c5fe52 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -310,7 +310,7 @@ impl Thread { target_os = "vxworks", ))] pub fn sleep_until(deadline: Instant) { - let Some(mut ts) = deadline.into_inner().into_timespec().to_timespec() else { + let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { // The deadline is further in the future then can be passed to // clock_nanosleep. We have to use Self::sleep intead. This might // happen on 32 bit platforms, especially closer to 2038. @@ -321,23 +321,26 @@ impl Thread { return; }; - let ts_ptr = &mut ts as *mut _; - - // If we're awoken with a signal and the return value is -1 - // clock_nanosleep needs to be called again. unsafe { - while libc::clock_nanosleep( - super::time::Instant::CLOCK_ID, - libc::TIMER_ABSTIME, - ts_ptr, - ts_ptr, - ) == -1 - { - assert_eq!( - os::errno(), - libc::EINTR, - "clock nanosleep should only return an error if interrupted" + // When we get interrupted (res = EINTR) call clock_nanosleep again + loop { + let res = libc::clock_nanosleep( + super::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + &ts, + core::ptr::null_mut(), // not required with TIMER_ABSTIME ); + + if res == 0 { + break; + } else { + assert_eq!( + res, + libc::EINTR, + "timespec is in range, + clockid is valid and kernel should support it" + ); + } } } } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index f34e6d1dcc968..bd7f74fea6a9c 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -292,7 +292,10 @@ impl Instant { Some(Instant { t: self.t.checked_sub_duration(other)? }) } - #[allow(unused, reason = "needed by the `sleep_until` on some unix platforms")] + #[cfg_attr( + not(target_os = "linux"), + allow(unused, reason = "needed by the `sleep_until` on some unix platforms") + )] pub(crate) fn into_timespec(self) -> Timespec { self.t } From e86fddc6d33c1d7a6cd5827e8b0500388de89c73 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 29 Jun 2025 15:29:43 +0200 Subject: [PATCH 13/25] fix bitcast of single-element SIMD vectors --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 10 +--- tests/codegen/transmute-scalar.rs | 55 ++++++++++++++++---- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index db5ac6a514fb4..e1d8b7546cf42 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1123,7 +1123,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // While optimizations will remove no-op transmutes, they might still be // there in debug or things that aren't no-op in MIR because they change // the Rust type but not the underlying layout/niche. - if from_scalar == to_scalar { + if from_scalar == to_scalar && from_backend_ty == to_backend_ty { return imm; } @@ -1142,13 +1142,7 @@ pub(super) fn transmute_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( assume_scalar_range(bx, imm, from_scalar, from_backend_ty); imm = match (from_scalar.primitive(), to_scalar.primitive()) { - (Int(..) | Float(_), Int(..) | Float(_)) => { - if from_backend_ty == to_backend_ty { - imm - } else { - bx.bitcast(imm, to_backend_ty) - } - } + (Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty), (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty), (Int(..), Pointer(..)) => bx.ptradd(bx.const_null(bx.type_ptr()), imm), (Pointer(..), Int(..)) => { diff --git a/tests/codegen/transmute-scalar.rs b/tests/codegen/transmute-scalar.rs index c57ade58c30e4..3ac6ba3beb1ee 100644 --- a/tests/codegen/transmute-scalar.rs +++ b/tests/codegen/transmute-scalar.rs @@ -1,6 +1,12 @@ +//@ add-core-stubs //@ compile-flags: -C opt-level=0 -C no-prepopulate-passes #![crate_type = "lib"] +#![feature(no_core, repr_simd, arm_target_feature, mips_target_feature, s390x_target_feature)] +#![no_core] +extern crate minicore; + +use minicore::*; // With opaque ptrs in LLVM, `transmute` can load/store any `alloca` as any type, // without needing to pointercast, and SRoA will turn that into a `bitcast`. @@ -14,7 +20,7 @@ // CHECK-NEXT: ret i32 %_0 #[no_mangle] pub fn f32_to_bits(x: f32) -> u32 { - unsafe { std::mem::transmute(x) } + unsafe { mem::transmute(x) } } // CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 zeroext %b) @@ -22,7 +28,7 @@ pub fn f32_to_bits(x: f32) -> u32 { // CHECK-NEXT: ret i8 %_0 #[no_mangle] pub fn bool_to_byte(b: bool) -> u8 { - unsafe { std::mem::transmute(b) } + unsafe { mem::transmute(b) } } // CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8{{.*}} %byte) @@ -30,14 +36,14 @@ pub fn bool_to_byte(b: bool) -> u8 { // CHECK-NEXT: ret i1 %_0 #[no_mangle] pub unsafe fn byte_to_bool(byte: u8) -> bool { - std::mem::transmute(byte) + mem::transmute(byte) } // CHECK-LABEL: define{{.*}}ptr @ptr_to_ptr(ptr %p) // CHECK: ret ptr %p #[no_mangle] pub fn ptr_to_ptr(p: *mut u16) -> *mut u8 { - unsafe { std::mem::transmute(p) } + unsafe { mem::transmute(p) } } // CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int(ptr %p) @@ -45,7 +51,7 @@ pub fn ptr_to_ptr(p: *mut u16) -> *mut u8 { // CHECK-NEXT: ret [[USIZE]] %_0 #[no_mangle] pub fn ptr_to_int(p: *mut u16) -> usize { - unsafe { std::mem::transmute(p) } + unsafe { mem::transmute(p) } } // CHECK: define{{.*}}ptr @int_to_ptr([[USIZE]] %i) @@ -53,7 +59,7 @@ pub fn ptr_to_int(p: *mut u16) -> usize { // CHECK-NEXT: ret ptr %_0 #[no_mangle] pub fn int_to_ptr(i: usize) -> *mut u16 { - unsafe { std::mem::transmute(i) } + unsafe { mem::transmute(i) } } // This is the one case where signedness matters to transmuting: @@ -70,7 +76,7 @@ pub enum FakeBoolSigned { // CHECK-NEXT: ret i8 %_0 #[no_mangle] pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned { - unsafe { std::mem::transmute(b) } + unsafe { mem::transmute(b) } } // CHECK-LABEL: define{{.*}}i1 @fake_bool_signed_to_bool(i8 %b) @@ -78,7 +84,7 @@ pub fn bool_to_fake_bool_signed(b: bool) -> FakeBoolSigned { // CHECK-NEXT: ret i1 %_0 #[no_mangle] pub fn fake_bool_signed_to_bool(b: FakeBoolSigned) -> bool { - unsafe { std::mem::transmute(b) } + unsafe { mem::transmute(b) } } #[repr(u8)] @@ -91,12 +97,41 @@ pub enum FakeBoolUnsigned { // CHECK: ret i1 %b #[no_mangle] pub fn bool_to_fake_bool_unsigned(b: bool) -> FakeBoolUnsigned { - unsafe { std::mem::transmute(b) } + unsafe { mem::transmute(b) } } // CHECK-LABEL: define{{.*}}i1 @fake_bool_unsigned_to_bool(i1 zeroext %b) // CHECK: ret i1 %b #[no_mangle] pub fn fake_bool_unsigned_to_bool(b: FakeBoolUnsigned) -> bool { - unsafe { std::mem::transmute(b) } + unsafe { mem::transmute(b) } +} + +#[repr(simd)] +struct S([i64; 1]); + +// CHECK-LABEL: define{{.*}}i64 @single_element_simd_to_scalar(<1 x i64> %b) +// CHECK: bitcast <1 x i64> %b to i64 +// CHECK: ret i64 +#[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] +#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] +#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] +pub extern "C" fn single_element_simd_to_scalar(b: S) -> i64 { + unsafe { mem::transmute(b) } +} + +// CHECK-LABEL: define{{.*}}<1 x i64> @scalar_to_single_element_simd(i64 %b) +// CHECK: bitcast i64 %b to <1 x i64> +// CHECK: ret <1 x i64> +#[no_mangle] +#[cfg_attr(target_family = "wasm", target_feature(enable = "simd128"))] +#[cfg_attr(target_arch = "arm", target_feature(enable = "neon"))] +#[cfg_attr(target_arch = "x86", target_feature(enable = "sse"))] +#[cfg_attr(target_arch = "mips", target_feature(enable = "msa"))] +#[cfg_attr(target_arch = "s390x", target_feature(enable = "vector"))] +pub extern "C" fn scalar_to_single_element_simd(b: i64) -> S { + unsafe { mem::transmute(b) } } From 08278eb1d54408ac4c4cc08b741076ce745661f6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Jun 2025 21:21:09 +0000 Subject: [PATCH 14/25] Don't recompute DisambiguatorState for every RPITIT in trait definition --- .../src/hir_ty_lowering/mod.rs | 28 ++++++--- compiler/rustc_middle/src/query/mod.rs | 7 --- compiler/rustc_ty_utils/src/assoc.rs | 58 +++++++++---------- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 74739355e1fcb..ed1e8cc92a2a1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2460,13 +2460,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // type a projection. let in_trait = match opaque_ty.origin { hir::OpaqueTyOrigin::FnReturn { + parent, in_trait_or_impl: Some(hir::RpitContext::Trait), .. } | hir::OpaqueTyOrigin::AsyncFn { + parent, in_trait_or_impl: Some(hir::RpitContext::Trait), .. - } => true, + } => Some(parent), hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), .. @@ -2475,7 +2477,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { in_trait_or_impl: None | Some(hir::RpitContext::TraitImpl), .. } - | hir::OpaqueTyOrigin::TyAlias { .. } => false, + | hir::OpaqueTyOrigin::TyAlias { .. } => None, }; self.lower_opaque_ty(opaque_ty.def_id, in_trait) @@ -2595,17 +2597,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. #[instrument(level = "debug", skip(self), ret)] - fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: bool) -> Ty<'tcx> { + fn lower_opaque_ty(&self, def_id: LocalDefId, in_trait: Option) -> Ty<'tcx> { let tcx = self.tcx(); let lifetimes = tcx.opaque_captured_lifetimes(def_id); debug!(?lifetimes); - // If this is an RPITIT and we are using the new RPITIT lowering scheme, we - // generate the def_id of an associated type for the trait and return as - // type a projection. - let def_id = if in_trait { - tcx.associated_type_for_impl_trait_in_trait(def_id).to_def_id() + // If this is an RPITIT and we are using the new RPITIT lowering scheme, + // do a linear search to map this to the synthetic associated type that + // it will be lowered to. + let def_id = if let Some(parent_def_id) = in_trait { + *tcx.associated_types_for_impl_traits_in_associated_fn(parent_def_id) + .iter() + .find(|rpitit| match tcx.opt_rpitit_info(**rpitit) { + Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => { + opaque_def_id.expect_local() == def_id + } + _ => unreachable!(), + }) + .unwrap() } else { def_id.to_def_id() }; @@ -2628,7 +2638,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); debug!(?args); - if in_trait { + if in_trait.is_some() { Ty::new_projection_from_args(tcx, def_id, args) } else { Ty::new_opaque(tcx, def_id, args) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8a3d26e1b0314..39ff667982491 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1092,13 +1092,6 @@ rustc_queries! { separate_provide_extern } - /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding - /// associated item. - query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId { - desc { |tcx| "creating the associated item corresponding to the opaque type `{}`", tcx.def_path_str(opaque_ty_def_id.to_def_id()) } - cache_on_disk_if { true } - } - /// Given an `impl_id`, return the trait it implements along with some header information. /// Return `None` if this is an inherent impl. query impl_trait_header(impl_id: DefId) -> Option> { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index f14a45aa1e3b9..a65f9b347dc6d 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,9 +1,8 @@ -use rustc_data_structures::fx::FxIndexSet; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -14,7 +13,6 @@ pub(crate) fn provide(providers: &mut Providers) { associated_item_def_ids, associated_items, associated_types_for_impl_traits_in_associated_fn, - associated_type_for_impl_trait_in_trait, impl_item_implementor_ids, ..*providers }; @@ -160,20 +158,22 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A container: ty::AssocItemContainer::Impl, } } -struct RPITVisitor { - rpits: FxIndexSet, +struct RPITVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + synthetics: Vec, + data: DefPathData, + disambiguator: DisambiguatorState, } -impl<'tcx> Visitor<'tcx> for RPITVisitor { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { - if let hir::TyKind::OpaqueDef(opaq) = ty.kind - && self.rpits.insert(opaq.def_id) - { - for bound in opaq.bounds { - intravisit::walk_param_bound(self, bound); - } - } - intravisit::walk_ty(self, ty) +impl<'tcx> Visitor<'tcx> for RPITVisitor<'tcx> { + fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) -> Self::Result { + self.synthetics.push(associated_type_for_impl_trait_in_trait( + self.tcx, + opaque.def_id, + self.data, + &mut self.disambiguator, + )); + intravisit::walk_opaque_ty(self, opaque) } } @@ -194,14 +194,18 @@ fn associated_types_for_impl_traits_in_associated_fn( match tcx.def_kind(parent_def_id) { DefKind::Trait => { - let mut visitor = RPITVisitor { rpits: FxIndexSet::default() }; - if let Some(output) = tcx.hir_get_fn_output(fn_def_id) { + let data = DefPathData::AnonAssocTy(tcx.item_name(fn_def_id.to_def_id())); + let mut visitor = RPITVisitor { + tcx, + synthetics: vec![], + data, + disambiguator: DisambiguatorState::with(parent_def_id, data, 0), + }; visitor.visit_fn_ret_ty(output); - - tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| { - tcx.associated_type_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id() - })) + tcx.arena.alloc_from_iter( + visitor.synthetics.into_iter().map(|def_id| def_id.to_def_id()), + ) } else { &[] } @@ -211,7 +215,6 @@ fn associated_types_for_impl_traits_in_associated_fn( let Some(trait_fn_def_id) = tcx.associated_item(fn_def_id).trait_item_def_id else { return &[]; }; - tcx.arena.alloc_from_iter( tcx.associated_types_for_impl_traits_in_associated_fn(trait_fn_def_id).iter().map( move |&trait_assoc_def_id| { @@ -236,6 +239,8 @@ fn associated_types_for_impl_traits_in_associated_fn( fn associated_type_for_impl_trait_in_trait( tcx: TyCtxt<'_>, opaque_ty_def_id: LocalDefId, + data: DefPathData, + disambiguator: &mut DisambiguatorState, ) -> LocalDefId { let (hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. }) = @@ -246,22 +251,15 @@ fn associated_type_for_impl_trait_in_trait( let trait_def_id = tcx.local_parent(fn_def_id); assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait); - // Collect all opaque types in return position for the method and use - // the index as the disambiguator to make an unique def path. - let mut visitor = RPITVisitor { rpits: FxIndexSet::default() }; - visitor.visit_fn_ret_ty(tcx.hir_get_fn_output(fn_def_id).unwrap()); - let disambiguator = visitor.rpits.get_index_of(&opaque_ty_def_id).unwrap().try_into().unwrap(); - let span = tcx.def_span(opaque_ty_def_id); // Also use the method name to create an unique def path. - let data = DefPathData::AnonAssocTy(tcx.item_name(fn_def_id.to_def_id())); let trait_assoc_ty = tcx.at(span).create_def( trait_def_id, // No name because this is an anonymous associated type. None, DefKind::AssocTy, Some(data), - &mut DisambiguatorState::with(trait_def_id, data, disambiguator), + disambiguator, ); let local_def_id = trait_assoc_ty.def_id(); From c07328613f2b0230f867da6c47034223786da5d0 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 30 Jun 2025 14:51:44 -0700 Subject: [PATCH 15/25] Use the correct export kind for __rust_alloc_error_handler_should_panic --- .../rustc_codegen_ssa/src/back/symbol_export.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 75f7a46355652..483f69e4c0e4d 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -219,13 +219,18 @@ fn exported_symbols_provider_local<'tcx>( // Mark allocator shim symbols as exported only if they were generated. if allocator_kind_for_codegen(tcx).is_some() { - for symbol_name in ALLOCATOR_METHODS + for (symbol_name, export_kind) in ALLOCATOR_METHODS .iter() - .map(|method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) + .map(|method| { + ( + mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()), + SymbolExportKind::Text, + ) + }) .chain([ - mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - mangle_internal_symbol(tcx, OomStrategy::SYMBOL), - mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + (mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), SymbolExportKind::Text), + (mangle_internal_symbol(tcx, OomStrategy::SYMBOL), SymbolExportKind::Data), + (mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), SymbolExportKind::Text), ]) { let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); @@ -234,7 +239,7 @@ fn exported_symbols_provider_local<'tcx>( exported_symbol, SymbolExportInfo { level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, + kind: export_kind, used: false, rustc_std_internal_symbol: true, }, From 3bf650f980a2ff5dcc521a7138c21f1ed67f0b8b Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Mon, 30 Jun 2025 18:03:39 -0400 Subject: [PATCH 16/25] add failing test, bless incorrect output --- tests/ui/typeck/auxiliary/private-dep.rs | 3 ++ tests/ui/typeck/auxiliary/public-dep.rs | 11 +++++ .../dont-suggest-private-dependencies.rs | 30 +++++++++++++ .../dont-suggest-private-dependencies.stderr | 43 +++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 tests/ui/typeck/auxiliary/private-dep.rs create mode 100644 tests/ui/typeck/auxiliary/public-dep.rs create mode 100644 tests/ui/typeck/dont-suggest-private-dependencies.rs create mode 100644 tests/ui/typeck/dont-suggest-private-dependencies.stderr diff --git a/tests/ui/typeck/auxiliary/private-dep.rs b/tests/ui/typeck/auxiliary/private-dep.rs new file mode 100644 index 0000000000000..472b40ef6229d --- /dev/null +++ b/tests/ui/typeck/auxiliary/private-dep.rs @@ -0,0 +1,3 @@ +pub trait A { + fn foo() {} +} diff --git a/tests/ui/typeck/auxiliary/public-dep.rs b/tests/ui/typeck/auxiliary/public-dep.rs new file mode 100644 index 0000000000000..438692a1caa39 --- /dev/null +++ b/tests/ui/typeck/auxiliary/public-dep.rs @@ -0,0 +1,11 @@ +//@ aux-crate:priv:private_dep=private-dep.rs +//@ compile-flags: -Zunstable-options + +extern crate private_dep; +use private_dep::A; + +pub struct B; + +impl A for B { + fn foo() {} +} diff --git a/tests/ui/typeck/dont-suggest-private-dependencies.rs b/tests/ui/typeck/dont-suggest-private-dependencies.rs new file mode 100644 index 0000000000000..afac00c521310 --- /dev/null +++ b/tests/ui/typeck/dont-suggest-private-dependencies.rs @@ -0,0 +1,30 @@ +// Don't suggest importing a function from a private dependency. +// Issues: #138191, #142676 + +// Avoid suggesting traits from std-private deps +//@ forbid-output: compiler_builtins +//@ forbid-output: object + +// Check a custom trait to withstand changes in above crates +//@ aux-crate:public_dep=public-dep.rs +//@ compile-flags: -Zunstable-options +//@ forbid-output: private_dep + +struct VecReader(Vec); + +impl std::io::Read for VecReader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.0.read(buf) + //~^ ERROR no method named `read` found for struct `Vec` + } +} + +extern crate public_dep; +use public_dep::B; + +fn main() { + let _ = u8::cast_from_lossy(9); + //~^ ERROR no function or associated item named `cast_from_lossy` found for type `u8` + let _ = B::foo(); + //~^ ERROR no function or associated item named `foo` found for struct `B` +} diff --git a/tests/ui/typeck/dont-suggest-private-dependencies.stderr b/tests/ui/typeck/dont-suggest-private-dependencies.stderr new file mode 100644 index 0000000000000..18848a2c08d31 --- /dev/null +++ b/tests/ui/typeck/dont-suggest-private-dependencies.stderr @@ -0,0 +1,43 @@ +error[E0599]: no method named `read` found for struct `Vec` in the current scope + --> $DIR/dont-suggest-private-dependencies.rs:17:16 + | +LL | self.0.read(buf) + | ^^^^ + | + = help: items from traits can only be used if the trait is in scope +help: trait `ReadRef` which provides `read` is implemented but not in scope; perhaps you want to import it + | +LL + use object::read::read_ref::ReadRef; + | +help: there is a method `read_at` with a similar name + | +LL | self.0.read_at(buf) + | +++ + +error[E0599]: no function or associated item named `cast_from_lossy` found for type `u8` in the current scope + --> $DIR/dont-suggest-private-dependencies.rs:26:17 + | +LL | let _ = u8::cast_from_lossy(9); + | ^^^^^^^^^^^^^^^ function or associated item not found in `u8` + | + = help: items from traits can only be used if the trait is in scope +help: trait `CastFrom` which provides `cast_from_lossy` is implemented but not in scope; perhaps you want to import it + | +LL + use compiler_builtins::math::libm_math::support::int_traits::CastFrom; + | + +error[E0599]: no function or associated item named `foo` found for struct `B` in the current scope + --> $DIR/dont-suggest-private-dependencies.rs:28:16 + | +LL | let _ = B::foo(); + | ^^^ function or associated item not found in `B` + | + = help: items from traits can only be used if the trait is in scope +help: trait `A` which provides `foo` is implemented but not in scope; perhaps you want to import it + | +LL + use private_dep::A; + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0599`. From 1e6e4bb95ab7879283e4274bd80c3e04b1ea3f7c Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Mon, 30 Jun 2025 18:04:46 -0400 Subject: [PATCH 17/25] make compiler_builtins a private dependency --- library/rustc-std-workspace-core/Cargo.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml index bd318fc2f9e9c..1ddc112380f16 100644 --- a/library/rustc-std-workspace-core/Cargo.toml +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["public-dependency"] + [package] name = "rustc-std-workspace-core" version = "1.99.0" @@ -11,5 +13,7 @@ edition = "2024" path = "lib.rs" [dependencies] -core = { path = "../core" } -compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features = ["compiler-builtins"] } +core = { path = "../core", public = true } +compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features = [ + "compiler-builtins", +] } From 51aa76fc67263b527eaa143221dd637875b9080e Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Mon, 30 Jun 2025 20:27:33 -0400 Subject: [PATCH 18/25] avoid suggesting traits from private dependencies --- .../src/hir_ty_lowering/mod.rs | 2 +- compiler/rustc_hir_typeck/src/method/suggest.rs | 7 ++++--- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_smir/src/rustc_smir/context.rs | 6 +++++- .../traits/fulfillment_errors.rs | 2 +- src/librustdoc/clean/blanket_impl.rs | 2 +- src/librustdoc/core.rs | 2 +- .../dont-suggest-private-dependencies.stderr | 17 ----------------- 8 files changed, 14 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index baf3b9b5bc9e0..51cd1b7704d83 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1588,7 +1588,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &infcx_ }; - tcx.all_traits() + tcx.all_traits_including_private() .filter(|trait_def_id| { // Consider only traits with the associated type tcx.associated_items(*trait_def_id) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b35aef13c5255..f446bc468e488 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1725,7 +1725,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if unsatisfied_predicates.is_empty() // ...or if we already suggested that name because of `rustc_confusable` annotation && Some(similar_candidate.name()) != confusable_suggested - // and if the we aren't in an expansion. + // and if we aren't in an expansion. && !span.from_expansion() { self.find_likely_intended_associated_item( @@ -3477,9 +3477,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diag<'_>, item_name: Ident, - valid_out_of_scope_traits: Vec, + mut valid_out_of_scope_traits: Vec, explain: bool, ) -> bool { + valid_out_of_scope_traits.retain(|id| self.tcx.is_user_visible_dep(id.krate)); if !valid_out_of_scope_traits.is_empty() { let mut candidates = valid_out_of_scope_traits; candidates.sort_by_key(|id| self.tcx.def_path_str(id)); @@ -4384,7 +4385,7 @@ pub(crate) struct TraitInfo { /// Retrieves all traits in this crate and any dependent crates, /// and wraps them into `TraitInfo` for custom sorting. pub(crate) fn all_traits(tcx: TyCtxt<'_>) -> Vec { - tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect() + tcx.all_traits_including_private().map(|def_id| TraitInfo { def_id }).collect() } fn print_disambiguation_help<'tcx>( diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index aa5355551ce06..f8b6a30682bc7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2318,7 +2318,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// All traits in the crate graph, including those not visible to the user. - pub fn all_traits(self) -> impl Iterator { + pub fn all_traits_including_private(self) -> impl Iterator { iter::once(LOCAL_CRATE) .chain(self.crates(()).iter().copied()) .flat_map(move |cnum| self.traits(cnum).iter().copied()) diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index baa4c0681e806..3fa83cfc6a08c 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -130,7 +130,11 @@ impl<'tcx> SmirCtxt<'tcx> { pub fn all_trait_decls(&self) -> stable_mir::TraitDecls { let mut tables = self.0.borrow_mut(); - tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect() + tables + .tcx + .all_traits_including_private() + .map(|trait_def_id| tables.trait_def(trait_def_id)) + .collect() } pub fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 0c88bd3dcbc54..db2517a837948 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1850,7 +1850,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let trait_def_id = trait_pred.def_id(); let trait_name = self.tcx.item_name(trait_def_id); let crate_name = self.tcx.crate_name(trait_def_id.krate); - if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| { + if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| { trait_name == self.tcx.item_name(trait_def_id) && trait_def_id.krate != def_id.krate && crate_name == self.tcx.crate_name(def_id.krate) diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index c889f52b789a4..11d5b472d738b 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -23,7 +23,7 @@ pub(crate) fn synthesize_blanket_impls( let ty = tcx.type_of(item_def_id); let mut blanket_impls = Vec::new(); - for trait_def_id in tcx.all_traits() { + for trait_def_id in tcx.visible_traits() { if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) || cx.generated_synthetics.contains(&(ty.skip_binder(), trait_def_id)) { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index cf3c4ac97af63..bd57bb21e639b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -353,7 +353,7 @@ pub(crate) fn run_global_ctxt( rustc_passes::stability::check_unused_or_stable_features(tcx); let auto_traits = - tcx.all_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect(); + tcx.visible_traits().filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)).collect(); let mut ctxt = DocContext { tcx, diff --git a/tests/ui/typeck/dont-suggest-private-dependencies.stderr b/tests/ui/typeck/dont-suggest-private-dependencies.stderr index 18848a2c08d31..0fb1e69c28a21 100644 --- a/tests/ui/typeck/dont-suggest-private-dependencies.stderr +++ b/tests/ui/typeck/dont-suggest-private-dependencies.stderr @@ -4,11 +4,6 @@ error[E0599]: no method named `read` found for struct `Vec` in the current s LL | self.0.read(buf) | ^^^^ | - = help: items from traits can only be used if the trait is in scope -help: trait `ReadRef` which provides `read` is implemented but not in scope; perhaps you want to import it - | -LL + use object::read::read_ref::ReadRef; - | help: there is a method `read_at` with a similar name | LL | self.0.read_at(buf) @@ -19,24 +14,12 @@ error[E0599]: no function or associated item named `cast_from_lossy` found for t | LL | let _ = u8::cast_from_lossy(9); | ^^^^^^^^^^^^^^^ function or associated item not found in `u8` - | - = help: items from traits can only be used if the trait is in scope -help: trait `CastFrom` which provides `cast_from_lossy` is implemented but not in scope; perhaps you want to import it - | -LL + use compiler_builtins::math::libm_math::support::int_traits::CastFrom; - | error[E0599]: no function or associated item named `foo` found for struct `B` in the current scope --> $DIR/dont-suggest-private-dependencies.rs:28:16 | LL | let _ = B::foo(); | ^^^ function or associated item not found in `B` - | - = help: items from traits can only be used if the trait is in scope -help: trait `A` which provides `foo` is implemented but not in scope; perhaps you want to import it - | -LL + use private_dep::A; - | error: aborting due to 3 previous errors From 311a99cac46b5366fc9a26a4fa4cb66a3ea5fac6 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Tue, 1 Jul 2025 11:25:10 +0200 Subject: [PATCH 19/25] ci: support optional jobs --- src/ci/citool/src/jobs.rs | 7 +++++-- src/ci/citool/src/jobs/tests.rs | 26 ++++++++++++++++++++++---- src/ci/citool/tests/test-jobs.yml | 5 +++++ src/ci/github-actions/jobs.yml | 11 +++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 81e002edb1565..31c761d16703f 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -66,6 +66,8 @@ pub struct JobDatabase { pub try_jobs: Vec, #[serde(rename = "auto")] pub auto_jobs: Vec, + #[serde(rename = "optional")] + pub optional_jobs: Vec, /// Shared environments for the individual run types. envs: JobEnvironments, @@ -75,9 +77,10 @@ impl JobDatabase { /// Find `auto` jobs that correspond to the passed `pattern`. /// Patterns are matched using the glob syntax. /// For example `dist-*` matches all jobs starting with `dist-`. - fn find_auto_jobs_by_pattern(&self, pattern: &str) -> Vec { + fn find_auto_or_optional_jobs_by_pattern(&self, pattern: &str) -> Vec { self.auto_jobs .iter() + .chain(self.optional_jobs.iter()) .filter(|j| glob_match::glob_match(pattern, &j.name)) .cloned() .collect() @@ -181,7 +184,7 @@ fn calculate_jobs( let mut jobs: Vec = vec![]; let mut unknown_patterns = vec![]; for pattern in patterns { - let matched_jobs = db.find_auto_jobs_by_pattern(pattern); + let matched_jobs = db.find_auto_or_optional_jobs_by_pattern(pattern); if matched_jobs.is_empty() { unknown_patterns.push(pattern.clone()); } else { diff --git a/src/ci/citool/src/jobs/tests.rs b/src/ci/citool/src/jobs/tests.rs index ed5444d4333d3..63ac508b632eb 100644 --- a/src/ci/citool/src/jobs/tests.rs +++ b/src/ci/citool/src/jobs/tests.rs @@ -46,6 +46,13 @@ auto: - name: test-msvc-i686-2 os: ubuntu env: {} +optional: + - name: optional-job-1 + os: ubuntu + env: {} + - name: optional-dist-x86_64 + os: ubuntu + env: {} "#, ) .unwrap(); @@ -57,12 +64,18 @@ auto: "*i686*", &["test-i686", "dist-i686", "test-msvc-i686-1", "test-msvc-i686-2"], ); + // Test that optional jobs are found + check_pattern(&db, "optional-*", &["optional-job-1", "optional-dist-x86_64"]); + check_pattern(&db, "*optional*", &["optional-job-1", "optional-dist-x86_64"]); } #[track_caller] fn check_pattern(db: &JobDatabase, pattern: &str, expected: &[&str]) { - let jobs = - db.find_auto_jobs_by_pattern(pattern).into_iter().map(|j| j.name).collect::>(); + let jobs = db + .find_auto_or_optional_jobs_by_pattern(pattern) + .into_iter() + .map(|j| j.name) + .collect::>(); assert_eq!(jobs, expected); } @@ -116,8 +129,13 @@ fn validate_jobs() { load_job_db(&db_str).expect("Failed to load job database") }; - let all_jobs = - db.pr_jobs.iter().chain(db.try_jobs.iter()).chain(db.auto_jobs.iter()).collect::>(); + let all_jobs = db + .pr_jobs + .iter() + .chain(db.try_jobs.iter()) + .chain(db.auto_jobs.iter()) + .chain(db.optional_jobs.iter()) + .collect::>(); let errors: Vec = all_jobs.into_iter().filter_map(|job| validate_codebuild_image(job).err()).collect(); diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index d262da1110255..7b349ceba3f5b 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -139,3 +139,8 @@ auto: DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows + +# Jobs that only run when explicitly invoked via `@bors try`. +optional: + - name: test-optional-job + <<: *job-linux-4c diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 3aa435003d3be..1e253131c1afe 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -160,6 +160,17 @@ pr: try: - <<: *job-dist-x86_64-linux +# Jobs that only run when explicitly invoked in one of the following ways: +# - comment `@bors2 try jobs=` +# - `try-job: ` in the PR description and comment `@bors try` or `@bors2 try`. +optional: + # This job is used just to test optional jobs. + # It will be replaced by tier 2 and tier 3 jobs in the future. + - name: optional-mingw-check-1 + env: + IMAGE: mingw-check-1 + <<: *job-linux-4c + # Main CI jobs that have to be green to merge a commit into master # These jobs automatically inherit envs.auto, to avoid repeating # it in each job definition. From 308ef8dc9107faa5f13ef9a856f5251de6d1b2de Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 29 Jun 2025 12:50:21 -0400 Subject: [PATCH 20/25] Remove `repr(align)` code --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 10 +------ compiler/rustc_passes/messages.ftl | 4 +++ compiler/rustc_passes/src/check_attr.rs | 5 +--- compiler/rustc_passes/src/errors.rs | 9 +++++++ tests/ui/attributes/malformed-fn-align.rs | 9 +++++++ tests/ui/attributes/malformed-fn-align.stderr | 26 ++++++++++++++++++- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 6e2143858de8c..999d733e3a7c9 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,7 +4,7 @@ use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ - AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, ReprAttr, UsedBy, find_attr, + AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; @@ -109,14 +109,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let hir::Attribute::Parsed(p) = attr { match p { - AttributeKind::Repr(reprs) => { - codegen_fn_attrs.alignment = reprs - .iter() - .filter_map( - |(r, _)| if let ReprAttr::ReprAlign(x) = r { Some(*x) } else { None }, - ) - .max(); - } AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => { codegen_fn_attrs.export_name = Some(*name); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 9a60c1510349a..74a9611d426ce 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -13,6 +13,10 @@ passes_abi_ne = passes_abi_of = fn_abi_of({$fn_name}) = {$fn_abi} +passes_align_attr_application = + `#[align(...)]` should be applied to a function item + .label = not a function item + passes_align_should_be_repr_align = `#[align(...)]` is not supported on {$item} items .suggestion = use `#[repr(align(...))]` instead diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4bc48cb6aca6f..6ee42de1b4398 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1896,10 +1896,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }); } _ => { - self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: repr_span, - span, - }); + self.dcx().emit_err(errors::AlignAttrApplication { hint_span: repr_span, span }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d498827707332..76608c2795dce 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1838,3 +1838,12 @@ pub(crate) struct AlignShouldBeReprAlign { pub item: &'static str, pub align_bytes: u64, } + +#[derive(Diagnostic)] +#[diag(passes_align_attr_application)] +pub(crate) struct AlignAttrApplication { + #[primary_span] + pub hint_span: Span, + #[label] + pub span: Span, +} diff --git a/tests/ui/attributes/malformed-fn-align.rs b/tests/ui/attributes/malformed-fn-align.rs index f5ab9555e561f..870bc34d45484 100644 --- a/tests/ui/attributes/malformed-fn-align.rs +++ b/tests/ui/attributes/malformed-fn-align.rs @@ -23,3 +23,12 @@ fn f4() {} #[align(16)] //~ ERROR `#[align(...)]` is not supported on struct items struct S1; + +#[align(32)] //~ ERROR `#[align(...)]` should be applied to a function item +const FOO: i32 = 42; + +#[align(32)] //~ ERROR `#[align(...)]` should be applied to a function item +mod test {} + +#[align(32)] //~ ERROR `#[align(...)]` should be applied to a function item +use ::std::iter; diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr index b769d0b457ddd..e1fe5353a4100 100644 --- a/tests/ui/attributes/malformed-fn-align.stderr +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -61,7 +61,31 @@ LL - #[align(16)] LL + #[repr(align(16))] | -error: aborting due to 7 previous errors +error: `#[align(...)]` should be applied to a function item + --> $DIR/malformed-fn-align.rs:27:1 + | +LL | #[align(32)] + | ^^^^^^^^^^^^ +LL | const FOO: i32 = 42; + | -------------------- not a function item + +error: `#[align(...)]` should be applied to a function item + --> $DIR/malformed-fn-align.rs:30:1 + | +LL | #[align(32)] + | ^^^^^^^^^^^^ +LL | mod test {} + | ----------- not a function item + +error: `#[align(...)]` should be applied to a function item + --> $DIR/malformed-fn-align.rs:33:1 + | +LL | #[align(32)] + | ^^^^^^^^^^^^ +LL | use ::std::iter; + | ---------------- not a function item + +error: aborting due to 10 previous errors Some errors have detailed explanations: E0539, E0589, E0805. For more information about an error, try `rustc --explain E0539`. From 278f4f4b78ae7a8a52742934fd76c69d235df5f2 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 29 Jun 2025 13:01:41 -0400 Subject: [PATCH 21/25] =?UTF-8?q?Support=20`#[align(=E2=80=A6)]`=20on=20fn?= =?UTF-8?q?s=20in=20`extern`=20blocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/rustc_passes/src/check_attr.rs | 2 +- tests/codegen/align-fn.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6ee42de1b4398..b2c6f21cd8845 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1887,7 +1887,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks if the `#[align]` attributes on `item` are valid. fn check_align(&self, span: Span, target: Target, align: Align, repr_span: Span) { match target { - Target::Fn | Target::Method(_) => {} + Target::Fn | Target::Method(_) | Target::ForeignFn => {} Target::Struct | Target::Union | Target::Enum => { self.dcx().emit_err(errors::AlignShouldBeReprAlign { span: repr_span, diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs index 90073ff308143..4a9fc49d2113d 100644 --- a/tests/codegen/align-fn.rs +++ b/tests/codegen/align-fn.rs @@ -116,3 +116,17 @@ pub fn align_specified_twice_2() {} #[align(32)] #[align(256)] pub fn align_specified_twice_3() {} + +const _: () = { + // CHECK-LABEL: align_unmangled + // CHECK-SAME: align 256 + #[unsafe(no_mangle)] + #[align(32)] + #[align(256)] + extern "C" fn align_unmangled() {} +}; + +unsafe extern "C" { + #[align(256)] + fn align_unmangled(); +} From 55d021031186eb17e028fc0e2d12683e7cd371bc Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 29 Jun 2025 14:23:02 -0400 Subject: [PATCH 22/25] Test `async fn` --- tests/codegen/align-fn.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs index 4a9fc49d2113d..6f26ebe66f226 100644 --- a/tests/codegen/align-fn.rs +++ b/tests/codegen/align-fn.rs @@ -1,4 +1,5 @@ //@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 -Clink-dead-code +//@ edition: 2024 #![crate_type = "lib"] #![feature(fn_align)] @@ -130,3 +131,9 @@ unsafe extern "C" { #[align(256)] fn align_unmangled(); } + +// CHECK-LABEL: async_align +// CHECK-SAME: align 64 +#[unsafe(no_mangle)] +#[align(64)] +pub async fn async_align() {} From 0382357638789469c820a33fb1767e273c3e3c8b Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 29 Jun 2025 14:33:51 -0400 Subject: [PATCH 23/25] Add test for `dyn` alignment --- tests/ui/attributes/fn-align-dyn.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/ui/attributes/fn-align-dyn.rs diff --git a/tests/ui/attributes/fn-align-dyn.rs b/tests/ui/attributes/fn-align-dyn.rs new file mode 100644 index 0000000000000..d67de05e7a9fd --- /dev/null +++ b/tests/ui/attributes/fn-align-dyn.rs @@ -0,0 +1,15 @@ +//@ run-pass +#![feature(fn_align)] + +trait Test { + #[align(4096)] + fn foo(&self); + + #[align(4096)] + fn foo1(&self); +} + +fn main() { + assert_eq!((::foo as fn(_) as usize & !1) % 4096, 0); + assert_eq!((::foo1 as fn(_) as usize & !1) % 4096, 0); +} From 233e2eff72e39c4b140aaf982313289d38dab9b6 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 1 Jul 2025 15:58:23 -0400 Subject: [PATCH 24/25] Add FIXME for gen et al --- tests/codegen/align-fn.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs index 6f26ebe66f226..c871d1259507f 100644 --- a/tests/codegen/align-fn.rs +++ b/tests/codegen/align-fn.rs @@ -132,6 +132,7 @@ unsafe extern "C" { fn align_unmangled(); } +// FIXME also check `gen` et al // CHECK-LABEL: async_align // CHECK-SAME: align 64 #[unsafe(no_mangle)] From 440bf29d68e9f8045576460c3836462ab1dccc35 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 1 Jul 2025 16:13:37 -0400 Subject: [PATCH 25/25] Add more tests for invalid alignments --- tests/ui/attributes/malformed-fn-align.rs | 15 +++++++ tests/ui/attributes/malformed-fn-align.stderr | 42 ++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/tests/ui/attributes/malformed-fn-align.rs b/tests/ui/attributes/malformed-fn-align.rs index 870bc34d45484..e06e611684242 100644 --- a/tests/ui/attributes/malformed-fn-align.rs +++ b/tests/ui/attributes/malformed-fn-align.rs @@ -21,6 +21,21 @@ fn f3() {} #[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on function items fn f4() {} +#[align(-1)] //~ ERROR expected unsuffixed literal, found `-` +fn f5() {} + +#[align(3)] //~ ERROR invalid alignment value: not a power of two +fn f6() {} + +#[align(4usize)] //~ ERROR invalid alignment value: not an unsuffixed integer [E0589] +//~^ ERROR suffixed literals are not allowed in attributes +fn f7() {} + +#[align(16)] +#[align(3)] //~ ERROR invalid alignment value: not a power of two +#[align(16)] +fn f8() {} + #[align(16)] //~ ERROR `#[align(...)]` is not supported on struct items struct S1; diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr index e1fe5353a4100..af3625b1f3b9e 100644 --- a/tests/ui/attributes/malformed-fn-align.stderr +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -1,3 +1,17 @@ +error: expected unsuffixed literal, found `-` + --> $DIR/malformed-fn-align.rs:24:9 + | +LL | #[align(-1)] + | ^ + +error: suffixed literals are not allowed in attributes + --> $DIR/malformed-fn-align.rs:30:9 + | +LL | #[align(4usize)] + | ^^^^^^ + | + = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) + error[E0539]: malformed `align` attribute input --> $DIR/malformed-fn-align.rs:5:5 | @@ -37,6 +51,24 @@ error[E0589]: invalid alignment value: not a power of two LL | #[align(0)] | ^ +error[E0589]: invalid alignment value: not a power of two + --> $DIR/malformed-fn-align.rs:27:9 + | +LL | #[align(3)] + | ^ + +error[E0589]: invalid alignment value: not an unsuffixed integer + --> $DIR/malformed-fn-align.rs:30:9 + | +LL | #[align(4usize)] + | ^^^^^^ + +error[E0589]: invalid alignment value: not a power of two + --> $DIR/malformed-fn-align.rs:35:9 + | +LL | #[align(3)] + | ^ + error: `#[repr(align(...))]` is not supported on function items --> $DIR/malformed-fn-align.rs:21:8 | @@ -50,7 +82,7 @@ LL | #[repr(align(16))] | ^^^^^^^^^ error: `#[align(...)]` is not supported on struct items - --> $DIR/malformed-fn-align.rs:24:1 + --> $DIR/malformed-fn-align.rs:39:1 | LL | #[align(16)] | ^^^^^^^^^^^^ @@ -62,7 +94,7 @@ LL + #[repr(align(16))] | error: `#[align(...)]` should be applied to a function item - --> $DIR/malformed-fn-align.rs:27:1 + --> $DIR/malformed-fn-align.rs:42:1 | LL | #[align(32)] | ^^^^^^^^^^^^ @@ -70,7 +102,7 @@ LL | const FOO: i32 = 42; | -------------------- not a function item error: `#[align(...)]` should be applied to a function item - --> $DIR/malformed-fn-align.rs:30:1 + --> $DIR/malformed-fn-align.rs:45:1 | LL | #[align(32)] | ^^^^^^^^^^^^ @@ -78,14 +110,14 @@ LL | mod test {} | ----------- not a function item error: `#[align(...)]` should be applied to a function item - --> $DIR/malformed-fn-align.rs:33:1 + --> $DIR/malformed-fn-align.rs:48:1 | LL | #[align(32)] | ^^^^^^^^^^^^ LL | use ::std::iter; | ---------------- not a function item -error: aborting due to 10 previous errors +error: aborting due to 15 previous errors Some errors have detailed explanations: E0539, E0589, E0805. For more information about an error, try `rustc --explain E0539`.