From 921b6c02aa01b40b9dfea318f4ae7ec1a9811499 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Sat, 31 May 2025 21:51:33 +0000 Subject: If HOME is empty, use the fallback instead --- library/std/src/env.rs | 2 +- library/std/src/sys/pal/unix/os.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/env.rs b/library/std/src/env.rs index ce2dc795220..9b0c295ae3a 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -617,7 +617,7 @@ impl Error for JoinPathsError { /// # Unix /// /// - Returns the value of the 'HOME' environment variable if it is set -/// (including to an empty string). +/// (and not an empty string). /// - Otherwise, it tries to determine the home directory by invoking the `getpwuid_r` function /// using the UID of the current user. An empty home directory field returned from the /// `getpwuid_r` function is considered to be a valid value. diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 48609030aed..633cb299736 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -633,7 +633,10 @@ pub fn temp_dir() -> PathBuf { } pub fn home_dir() -> Option { - return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); + return crate::env::var_os("HOME") + .filter(|s| !s.is_empty()) + .or_else(|| unsafe { fallback() }) + .map(PathBuf::from); #[cfg(any( target_os = "android", -- cgit 1.4.1-3-g733a5 From 978e11baa2eb7bb0ca72379179f7cb3dba1772ff Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 1 Jul 2025 03:23:07 -0500 Subject: Upgrade the `fortanix-sgx-abi` dependency 0.6.1 removes the `compiler-builtins` dependency, part of RUST-142265. The breaking change from 0.5 to 0.6 is for an update to the `insecure_time` API [1]. I validated that `./x c library --target x86_64-fortanix-unknown-sgx` completes successfully with this change. Link: https://github.com/fortanix/rust-sgx/commit/a34e9767f37d6585c18bdbd31cddcadc56670d57 [1] --- library/Cargo.lock | 5 ++--- library/std/Cargo.toml | 2 +- library/std/src/sys/pal/sgx/abi/usercalls/mod.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'library/std/src') diff --git a/library/Cargo.lock b/library/Cargo.lock index c681c5935df..3d176803b61 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -90,11 +90,10 @@ dependencies = [ [[package]] name = "fortanix-sgx-abi" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" +checksum = "5efc85edd5b83e8394f4371dd0da6859dff63dd387dab8568fece6af4cde6f84" dependencies = [ - "compiler_builtins", "rustc-std-workspace-core", ] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7c2a43ef207..10a3db78e27 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -68,7 +68,7 @@ rand_xorshift = "0.4.0" dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.5.0", features = [ +fortanix-sgx-abi = { version = "0.6.1", features = [ 'rustc-dep-of-std', ], public = true } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index cbdaf439b28..d859249f66b 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -267,7 +267,7 @@ pub fn send(event_set: u64, tcs: Option) -> IoResult<()> { /// Usercall `insecure_time`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn insecure_time() -> Duration { - let t = unsafe { raw::insecure_time() }; + let t = unsafe { raw::insecure_time().0 }; Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _) } -- cgit 1.4.1-3-g733a5 From 69b9bae57ded527f5fd7f036b6cc1cca0b462ac4 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sun, 13 Jul 2025 00:58:12 +0200 Subject: Guarantee 8 bytes of alignment in Thread::into_raw --- library/std/src/thread/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'library/std/src') diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 26b2fb44724..2552d6316fa 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1380,6 +1380,11 @@ where } /// The internal representation of a `Thread` handle +/// +/// We explicitly set the alignment for our guarantee in Thread::into_raw. This +/// allows applications to stuff extra metadata bits into the alignment, which +/// can be rather useful when working with atomics. +#[repr(align(8))] struct Inner { name: Option, id: ThreadId, @@ -1563,7 +1568,8 @@ impl Thread { /// Consumes the `Thread`, returning a raw pointer. /// /// To avoid a memory leak the pointer must be converted - /// back into a `Thread` using [`Thread::from_raw`]. + /// back into a `Thread` using [`Thread::from_raw`]. The pointer is + /// guaranteed to be aligned to at least 8 bytes. /// /// # Examples /// -- cgit 1.4.1-3-g733a5 From 6f4d0bdde85f9e2a0e61bb3e16d407540ddc09ba Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sun, 13 Jul 2025 01:20:29 +0200 Subject: Tidy --- library/std/src/thread/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'library/std/src') diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 2552d6316fa..73c09257092 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1380,7 +1380,7 @@ where } /// The internal representation of a `Thread` handle -/// +/// /// We explicitly set the alignment for our guarantee in Thread::into_raw. This /// allows applications to stuff extra metadata bits into the alignment, which /// can be rather useful when working with atomics. -- cgit 1.4.1-3-g733a5 From 58537fb869a8e08d5146e8feab982f17b3fd990a Mon Sep 17 00:00:00 2001 From: roblabla Date: Mon, 21 Jul 2025 01:32:26 +0200 Subject: Fix broken TLS destructors on 32-bit win7 On the 32-bit win7 target, we use OS TLS instead of native TLS, due to issues with how the OS handles alignment. Unfortunately, this caused issues due to the TLS destructors not running, causing memory leaks among other problems. On Windows, to support OS TLS, the TlsAlloc family of function is used by Rust. This function does not support TLS destructors at all. However, rust has some code to emulate those destructors, by leveraging the TLS support functionality found in the MSVC CRT (specifically, in tlssup.c of the CRT). Specifically, the CRT provides the ability to register callbacks that are called (among other things) on thread destruction. By registering our own callback, we can run through a list of registered destructors functions to execute. To use this functionality, the user must do two things: 1. They must put the address to their callback in a section between `.CRT$XLB` and `.CRT$XLY`. 2. They must add a reference to `_tls_used` (or `__tls_used` on x86) to make sure the TLS support code in tlssup.c isn't garbage collected by the linker. Prior to this commit, this second bit wasn't being done properly by the Rust TLS support code. Instead of adding a reference to _tls_used, it instead had a reference to its own callback to prevent it from getting GC'd by the linker. While this is _also_ necessary, not having a reference on _tls_used made the entire support non-functional. This commit reworks the code to: 1. Add an unconditional `#[used]` attribute on the CALLBACK, which should be enough to prevent it from getting GC'd by the linker. 2. Add a reference to `_tls_used`, which should pull the TLS support code into the Rust programs and not let it be GC'd by the linker. --- library/std/src/sys/thread_local/guard/windows.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index b15a0d7c0bd..f747129465d 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -58,7 +58,7 @@ //! We don't actually use the `/INCLUDE` linker flag here like the article //! mentions because the Rust compiler doesn't propagate linker flags, but //! instead we use a shim function which performs a volatile 1-byte load from -//! the address of the symbol to ensure it sticks around. +//! the address of the _tls_used symbol to ensure it sticks around. //! //! [1]: https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way //! [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base/threading/thread_local_storage_win.cc#L42 @@ -68,9 +68,20 @@ use core::ffi::c_void; use crate::ptr; use crate::sys::c; +unsafe extern "C" { + #[link_name = "_tls_used"] + static TLS_USED: u8; +} pub fn enable() { - // When destructors are used, we don't want LLVM eliminating CALLBACK for any - // reason. Once the symbol makes it to the linker, it will do the rest. + // When destructors are used, we need to add a reference to the _tls_used + // symbol provided by the CRT, otherwise the TLS support code will get + // GC'd by the linker and our callback won't be called. + unsafe { ptr::from_ref(&TLS_USED).read_volatile() }; + // We also need to reference CALLBACK to make sure it does not get GC'd + // by the compiler/LLVM. The callback will end up inside the TLS + // callback array pointed to by _TLS_USED through linker shenanigans, + // but as far as the compiler is concerned, it looks like the data is + // unused, so we need this hack to prevent it from disappearing. unsafe { ptr::from_ref(&CALLBACK).read_volatile() }; } -- cgit 1.4.1-3-g733a5 From eb9c65476c9dbcf90d67e938218731f58e6c699a Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Sat, 12 Jul 2025 18:47:19 +0530 Subject: std: net: uefi: Add support to query connection data - Use EFI_TCP4_GET_MODE_DATA to be able to query for ttl, nodelay, peer_addr and socket_addr. - peer_addr is needed for implementation of `accept`. Signed-off-by: Ayush Singh --- library/std/src/sys/net/connection/uefi/mod.rs | 32 ++++++++++--------- library/std/src/sys/net/connection/uefi/tcp.rs | 42 +++++++++++++++++++++++++ library/std/src/sys/net/connection/uefi/tcp4.rs | 18 +++++++++++ library/std/src/sys/pal/uefi/helpers.rs | 4 +++ 4 files changed, 81 insertions(+), 15 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 884cbd4ac1d..16e3487a174 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -86,11 +86,11 @@ impl TcpStream { } pub fn peer_addr(&self) -> io::Result { - unsupported() + self.inner.peer_addr() } pub fn socket_addr(&self) -> io::Result { - unsupported() + self.inner.socket_addr() } pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { @@ -114,7 +114,7 @@ impl TcpStream { } pub fn nodelay(&self) -> io::Result { - unsupported() + self.inner.nodelay() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { @@ -122,7 +122,7 @@ impl TcpStream { } pub fn ttl(&self) -> io::Result { - unsupported() + self.inner.ttl() } pub fn take_error(&self) -> io::Result> { @@ -140,7 +140,9 @@ impl fmt::Debug for TcpStream { } } -pub struct TcpListener(!); +pub struct TcpListener { + inner: tcp::Tcp, +} impl TcpListener { pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { @@ -148,45 +150,45 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result { - self.0 + unsupported() } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 + unsupported() } pub fn duplicate(&self) -> io::Result { - self.0 + unsupported() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 + unsupported() } pub fn ttl(&self) -> io::Result { - self.0 + self.inner.ttl() } pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } pub fn only_v6(&self) -> io::Result { - self.0 + unsupported() } pub fn take_error(&self) -> io::Result> { - self.0 + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 + unsupported() } } impl fmt::Debug for TcpListener { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + todo!() } } diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs index 1152f69446e..aac97007bbf 100644 --- a/library/std/src/sys/net/connection/uefi/tcp.rs +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -1,6 +1,8 @@ use super::tcp4; use crate::io; use crate::net::SocketAddr; +use crate::ptr::NonNull; +use crate::sys::{helpers, unsupported}; use crate::time::Duration; pub(crate) enum Tcp { @@ -31,4 +33,44 @@ impl Tcp { Self::V4(client) => client.read(buf, timeout), } } + + pub(crate) fn ttl(&self) -> io::Result { + match self { + Self::V4(client) => client.get_mode_data().map(|x| x.time_to_live.into()), + } + } + + pub(crate) fn nodelay(&self) -> io::Result { + match self { + Self::V4(client) => { + let temp = client.get_mode_data()?; + match NonNull::new(temp.control_option) { + Some(x) => unsafe { Ok(x.as_ref().enable_nagle.into()) }, + None => unsupported(), + } + } + } + } + + pub fn peer_addr(&self) -> io::Result { + match self { + Self::V4(client) => client.get_mode_data().map(|x| { + SocketAddr::new( + helpers::ipv4_from_r_efi(x.access_point.remote_address).into(), + x.access_point.remote_port, + ) + }), + } + } + + pub fn socket_addr(&self) -> io::Result { + match self { + Self::V4(client) => client.get_mode_data().map(|x| { + SocketAddr::new( + helpers::ipv4_from_r_efi(x.access_point.station_address).into(), + x.access_point.station_port, + ) + }), + } + } } diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs index 6342718929a..75862ff247b 100644 --- a/library/std/src/sys/net/connection/uefi/tcp4.rs +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -67,6 +67,24 @@ impl Tcp4 { if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + pub(crate) fn get_mode_data(&self) -> io::Result { + let mut config_data = tcp4::ConfigData::default(); + let protocol = self.protocol.as_ptr(); + + let r = unsafe { + ((*protocol).get_mode_data)( + protocol, + crate::ptr::null_mut(), + &mut config_data, + crate::ptr::null_mut(), + crate::ptr::null_mut(), + crate::ptr::null_mut(), + ) + }; + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(config_data) } + } + pub(crate) fn connect(&self, timeout: Option) -> io::Result<()> { let evt = unsafe { self.create_evt() }?; let completion_token = diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 420481648a7..271dc4d11de 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -761,3 +761,7 @@ impl Drop for OwnedEvent { pub(crate) const fn ipv4_to_r_efi(addr: crate::net::Ipv4Addr) -> efi::Ipv4Address { efi::Ipv4Address { addr: addr.octets() } } + +pub(crate) const fn ipv4_from_r_efi(ip: efi::Ipv4Address) -> crate::net::Ipv4Addr { + crate::net::Ipv4Addr::new(ip.addr[0], ip.addr[1], ip.addr[2], ip.addr[3]) +} -- cgit 1.4.1-3-g733a5 From 173926da2bd94bde24740a4b9e6b1bac1bfcb910 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 1 Jul 2025 09:33:35 -0700 Subject: Remove `[T]::array_chunks(_mut)` --- ...27-sysroot_tests-128bit-atomic-operations.patch | 2 +- library/alloc/src/lib.rs | 1 - library/alloc/src/slice.rs | 4 - library/alloc/src/string.rs | 24 +- library/core/src/slice/iter.rs | 249 --------------------- library/core/src/slice/mod.rs | 76 ------- library/core/src/str/iter.rs | 2 +- library/coretests/tests/lib.rs | 1 - library/coretests/tests/slice.rs | 184 --------------- library/std/src/lib.rs | 1 - library/std/src/sys/random/sgx.rs | 14 +- library/std/src/sys/random/uefi.rs | 5 +- 12 files changed, 23 insertions(+), 540 deletions(-) (limited to 'library/std/src') diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index f6e6bbc2387..f3d1d5c43ea 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -19,7 +19,7 @@ index 1e336bf..35e6f54 100644 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] - #![feature(array_chunks)] + #![feature(array_ptr_get)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs index b735957..ea728b6 100644 --- a/coretests/tests/atomic.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 6b6e4df4cba..c091e496c50 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -94,7 +94,6 @@ // tidy-alphabetical-start #![feature(alloc_layout_extra)] #![feature(allocator_api)] -#![feature(array_chunks)] #![feature(array_into_iter_constructors)] #![feature(array_windows)] #![feature(ascii_char)] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index b4da56578c8..ce9f967cc38 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -16,10 +16,6 @@ use core::cmp::Ordering::{self, Less}; use core::mem::MaybeUninit; #[cfg(not(no_global_oom_handling))] use core::ptr; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use core::slice::ArrayChunks; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use core::slice::ArrayChunksMut; #[unstable(feature = "array_windows", issue = "75027")] pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index a189c00a6b6..d58240f3051 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -787,12 +787,12 @@ impl String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "str_from_utf16_endian", issue = "116258")] pub fn from_utf16le(v: &[u8]) -> Result { - if v.len() % 2 != 0 { + let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error(())); - } + }; match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), - _ => char::decode_utf16(v.array_chunks::<2>().copied().map(u16::from_le_bytes)) + _ => char::decode_utf16(chunks.iter().copied().map(u16::from_le_bytes)) .collect::>() .map_err(|_| FromUtf16Error(())), } @@ -830,11 +830,11 @@ impl String { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", _ => { - let mut iter = v.array_chunks::<2>(); - let string = char::decode_utf16(iter.by_ref().copied().map(u16::from_le_bytes)) + let (chunks, remainder) = v.as_chunks::<2>(); + let string = char::decode_utf16(chunks.iter().copied().map(u16::from_le_bytes)) .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) .collect(); - if iter.remainder().is_empty() { string } else { string + "\u{FFFD}" } + if remainder.is_empty() { string } else { string + "\u{FFFD}" } } } } @@ -862,12 +862,12 @@ impl String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "str_from_utf16_endian", issue = "116258")] pub fn from_utf16be(v: &[u8]) -> Result { - if v.len() % 2 != 0 { + let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error(())); - } + }; match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), - _ => char::decode_utf16(v.array_chunks::<2>().copied().map(u16::from_be_bytes)) + _ => char::decode_utf16(chunks.iter().copied().map(u16::from_be_bytes)) .collect::>() .map_err(|_| FromUtf16Error(())), } @@ -905,11 +905,11 @@ impl String { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", _ => { - let mut iter = v.array_chunks::<2>(); - let string = char::decode_utf16(iter.by_ref().copied().map(u16::from_be_bytes)) + let (chunks, remainder) = v.as_chunks::<2>(); + let string = char::decode_utf16(chunks.iter().copied().map(u16::from_be_bytes)) .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) .collect(); - if iter.remainder().is_empty() { string } else { string + "\u{FFFD}" } + if remainder.is_empty() { string } else { string + "\u{FFFD}" } } } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 33132dcc714..ae910e05252 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2301,255 +2301,6 @@ impl ExactSizeIterator for ArrayWindows<'_, T, N> { } } -/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a -/// time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `N-1` elements will be omitted but can be retrieved from -/// the [`remainder`] function from the iterator. -/// -/// This struct is created by the [`array_chunks`] method on [slices]. -/// -/// # Example -/// -/// ``` -/// #![feature(array_chunks)] -/// -/// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let mut iter = slice.array_chunks::<2>(); -/// assert_eq!(iter.next(), Some(&['l', 'o'])); -/// assert_eq!(iter.next(), Some(&['r', 'e'])); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// [`array_chunks`]: slice::array_chunks -/// [`remainder`]: ArrayChunks::remainder -/// [slices]: slice -#[derive(Debug)] -#[unstable(feature = "array_chunks", issue = "74985")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct ArrayChunks<'a, T: 'a, const N: usize> { - iter: Iter<'a, [T; N]>, - rem: &'a [T], -} - -impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - pub(super) const fn new(slice: &'a [T]) -> Self { - let (array_slice, rem) = slice.as_chunks(); - Self { iter: array_slice.iter(), rem } - } - - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `N-1` - /// elements. - #[must_use] - #[unstable(feature = "array_chunks", issue = "74985")] - pub fn remainder(&self) -> &'a [T] { - self.rem - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "array_chunks", issue = "74985")] -impl Clone for ArrayChunks<'_, T, N> { - fn clone(&self) -> Self { - ArrayChunks { iter: self.iter.clone(), rem: self.rem } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> { - type Item = &'a [T; N]; - - #[inline] - fn next(&mut self) -> Option<&'a [T; N]> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a [T; N] { - // SAFETY: The safety guarantees of `__iterator_get_unchecked` are - // transferred to the caller. - unsafe { self.iter.__iterator_get_unchecked(i) } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T; N]> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.iter.nth_back(n) - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl ExactSizeIterator for ArrayChunks<'_, T, N> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ArrayChunks<'_, T, N> {} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl FusedIterator for ArrayChunks<'_, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunks<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements -/// at a time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `N-1` elements will be omitted but can be retrieved from -/// the [`into_remainder`] function from the iterator. -/// -/// This struct is created by the [`array_chunks_mut`] method on [slices]. -/// -/// # Example -/// -/// ``` -/// #![feature(array_chunks)] -/// -/// let mut slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.array_chunks_mut::<2>(); -/// ``` -/// -/// [`array_chunks_mut`]: slice::array_chunks_mut -/// [`into_remainder`]: ../../std/slice/struct.ArrayChunksMut.html#method.into_remainder -/// [slices]: slice -#[derive(Debug)] -#[unstable(feature = "array_chunks", issue = "74985")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct ArrayChunksMut<'a, T: 'a, const N: usize> { - iter: IterMut<'a, [T; N]>, - rem: &'a mut [T], -} - -impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> { - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - pub(super) const fn new(slice: &'a mut [T]) -> Self { - let (array_slice, rem) = slice.as_chunks_mut(); - Self { iter: array_slice.iter_mut(), rem } - } - - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `N-1` - /// elements. - #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "array_chunks", issue = "74985")] - pub fn into_remainder(self) -> &'a mut [T] { - self.rem - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> Iterator for ArrayChunksMut<'a, T, N> { - type Item = &'a mut [T; N]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T; N]> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a mut [T; N] { - // SAFETY: The safety guarantees of `__iterator_get_unchecked` are transferred to - // the caller. - unsafe { self.iter.__iterator_get_unchecked(i) } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunksMut<'a, T, N> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T; N]> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.iter.nth_back(n) - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl ExactSizeIterator for ArrayChunksMut<'_, T, N> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ArrayChunksMut<'_, T, N> {} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl FusedIterator for ArrayChunksMut<'_, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunksMut<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; -} - /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. /// diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fe5affc48b..1f34a7931d2 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -52,8 +52,6 @@ pub use index::SliceIndex; pub use index::{range, try_range}; #[unstable(feature = "array_windows", issue = "75027")] pub use iter::ArrayWindows; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use iter::{ArrayChunks, ArrayChunksMut}; #[stable(feature = "slice_group_by", since = "1.77.0")] pub use iter::{ChunkBy, ChunkByMut}; #[stable(feature = "rust1", since = "1.0.0")] @@ -1448,42 +1446,6 @@ impl [T] { (remainder, array_slice) } - /// Returns an iterator over `N` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are array references and do not overlap. If `N` does not divide the - /// length of the slice, then the last up to `N-1` elements will be omitted and can be - /// retrieved from the `remainder` function of the iterator. - /// - /// This method is the const generic equivalent of [`chunks_exact`]. - /// - /// # Panics - /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. - /// - /// # Examples - /// - /// ``` - /// #![feature(array_chunks)] - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.array_chunks(); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert!(iter.next().is_none()); - /// assert_eq!(iter.remainder(), &['m']); - /// ``` - /// - /// [`chunks_exact`]: slice::chunks_exact - #[unstable(feature = "array_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - #[track_caller] - pub const fn array_chunks(&self) -> ArrayChunks<'_, T, N> { - assert!(N != 0, "chunk size must be non-zero"); - ArrayChunks::new(self) - } - /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// @@ -1646,44 +1608,6 @@ impl [T] { (remainder, array_slice) } - /// Returns an iterator over `N` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are mutable array references and do not overlap. If `N` does not divide - /// the length of the slice, then the last up to `N-1` elements will be omitted and - /// can be retrieved from the `into_remainder` function of the iterator. - /// - /// This method is the const generic equivalent of [`chunks_exact_mut`]. - /// - /// # Panics - /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. - /// - /// # Examples - /// - /// ``` - /// #![feature(array_chunks)] - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.array_chunks_mut() { - /// *chunk = [count; 2]; - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 0]); - /// ``` - /// - /// [`chunks_exact_mut`]: slice::chunks_exact_mut - #[unstable(feature = "array_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - #[track_caller] - pub const fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { - assert!(N != 0, "chunk size must be non-zero"); - ArrayChunksMut::new(self) - } - /// Returns an iterator over overlapping windows of `N` elements of a slice, /// starting at the beginning of the slice. /// diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index bcf886484ad..d2985d8a186 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -52,7 +52,7 @@ impl<'a> Iterator for Chars<'a> { const CHUNK_SIZE: usize = 32; if remainder >= CHUNK_SIZE { - let mut chunks = self.iter.as_slice().array_chunks::(); + let mut chunks = self.iter.as_slice().as_chunks::().0.iter(); let mut bytes_skipped: usize = 0; while remainder > CHUNK_SIZE diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c5bfd1574e2..57a89365fa9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -2,7 +2,6 @@ #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] -#![feature(array_chunks)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] #![feature(array_windows)] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index d17e681480c..992f24cb18f 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -611,190 +611,6 @@ fn test_chunks_exact_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 4]); } -#[test] -fn test_array_chunks_infer() { - let v: &[i32] = &[0, 1, 2, 3, 4, -4]; - let c = v.array_chunks(); - for &[a, b, c] in c { - assert_eq!(a + b + c, 3); - } - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let total = v2.array_chunks().map(|&[a, b]| a * b).sum::(); - assert_eq!(total, 2 * 3 + 4 * 5); -} - -#[test] -fn test_array_chunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<3>(); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.array_chunks::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.array_chunks::<2>(); - assert_eq!(c.remainder(), &[4]); -} - -#[test] -fn test_array_chunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .array_chunks::<2>() - .zip(v2.array_chunks::<2>()) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22]); -} - -#[test] -fn test_array_chunks_mut_infer() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - for a in v.array_chunks_mut() { - let sum = a.iter().sum::(); - *a = [sum; 3]; - } - assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b)); - assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]); -} - -#[test] -fn test_array_chunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<3>(); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.array_chunks_mut::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks_mut::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.into_remainder(), &[4]); -} - -#[test] -fn test_array_chunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 4]); -} - #[test] fn test_array_windows_infer() { let v: &[i32] = &[0, 1, 0, 1]; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 323742a75b0..433e013b40f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -324,7 +324,6 @@ // // Library features (core): // tidy-alphabetical-start -#![feature(array_chunks)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(char_internals)] diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs index c3647a8df22..462b19003fa 100644 --- a/library/std/src/sys/random/sgx.rs +++ b/library/std/src/sys/random/sgx.rs @@ -46,22 +46,22 @@ fn rdrand16() -> u16 { } pub fn fill_bytes(bytes: &mut [u8]) { - let mut chunks = bytes.array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = bytes.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand64().to_ne_bytes(); } - let mut chunks = chunks.into_remainder().array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = remainder.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand32().to_ne_bytes(); } - let mut chunks = chunks.into_remainder().array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = remainder.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand16().to_ne_bytes(); } - if let [byte] = chunks.into_remainder() { + if let [byte] = remainder { *byte = rdrand16() as u8; } } diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs index 5f001f0f532..4a71d32fffe 100644 --- a/library/std/src/sys/random/uefi.rs +++ b/library/std/src/sys/random/uefi.rs @@ -138,12 +138,11 @@ mod rdrand { } unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> { - let mut chunks = dest.array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, tail) = dest.as_chunks_mut(); + for chunk in chunks { *chunk = unsafe { rdrand() }?.to_ne_bytes(); } - let tail = chunks.into_remainder(); let n = tail.len(); if n > 0 { let src = unsafe { rdrand() }?.to_ne_bytes(); -- cgit 1.4.1-3-g733a5 From 68f08c5dd9f5bee706a221df13660cc9b3a71c93 Mon Sep 17 00:00:00 2001 From: Yosh Date: Mon, 21 Jul 2025 03:04:17 +0200 Subject: Add `core::mem::DropGuard` Fix CI for drop_guard fix CI fix all tidy lints fix tidy link add first batch of feedback from review Add second batch of feedback from review add third batch of feedback from review fix failing test Update library/core/src/mem/drop_guard.rs Co-authored-by: Ruby Lazuli fix doctests Implement changes from T-Libs-API review And start tracking based on the tracking issue. fix tidy lint --- library/core/src/mem/drop_guard.rs | 155 +++++++++++++++++++++++++++++++++++++ library/core/src/mem/mod.rs | 4 + library/coretests/tests/lib.rs | 1 + library/coretests/tests/mem.rs | 46 +++++++++++ library/std/src/lib.rs | 1 + 5 files changed, 207 insertions(+) create mode 100644 library/core/src/mem/drop_guard.rs (limited to 'library/std/src') diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs new file mode 100644 index 00000000000..47ccb69acc8 --- /dev/null +++ b/library/core/src/mem/drop_guard.rs @@ -0,0 +1,155 @@ +use crate::fmt::{self, Debug}; +use crate::mem::ManuallyDrop; +use crate::ops::{Deref, DerefMut}; + +/// Wrap a value and run a closure when dropped. +/// +/// This is useful for quickly creating desructors inline. +/// +/// # Examples +/// +/// ```rust +/// # #![allow(unused)] +/// #![feature(drop_guard)] +/// +/// use std::mem::DropGuard; +/// +/// { +/// // Create a new guard around a string that will +/// // print its value when dropped. +/// let s = String::from("Chashu likes tuna"); +/// let mut s = DropGuard::new(s, |s| println!("{s}")); +/// +/// // Modify the string contained in the guard. +/// s.push_str("!!!"); +/// +/// // The guard will be dropped here, printing: +/// // "Chashu likes tuna!!!" +/// } +/// ``` +#[unstable(feature = "drop_guard", issue = "144426")] +#[doc(alias = "ScopeGuard")] +#[doc(alias = "defer")] +pub struct DropGuard +where + F: FnOnce(T), +{ + inner: ManuallyDrop, + f: ManuallyDrop, +} + +impl DropGuard +where + F: FnOnce(T), +{ + /// Create a new instance of `DropGuard`. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(drop_guard)] + /// + /// use std::mem::DropGuard; + /// + /// let value = String::from("Chashu likes tuna"); + /// let guard = DropGuard::new(value, |s| println!("{s}")); + /// ``` + #[unstable(feature = "drop_guard", issue = "144426")] + #[must_use] + pub const fn new(inner: T, f: F) -> Self { + Self { inner: ManuallyDrop::new(inner), f: ManuallyDrop::new(f) } + } + + /// Consumes the `DropGuard`, returning the wrapped value. + /// + /// This will not execute the closure. This is implemented as an associated + /// function to prevent any potential conflicts with any other methods called + /// `into_inner` from the `Deref` and `DerefMut` impls. + /// + /// It is typically preferred to call this function instead of `mem::forget` + /// because it will return the stored value and drop variables captured + /// by the closure instead of leaking their owned resources. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(drop_guard)] + /// + /// use std::mem::DropGuard; + /// + /// let value = String::from("Nori likes chicken"); + /// let guard = DropGuard::new(value, |s| println!("{s}")); + /// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken"); + /// ``` + #[unstable(feature = "drop_guard", issue = "144426")] + #[inline] + pub fn into_inner(guard: Self) -> T { + // First we ensure that dropping the guard will not trigger + // its destructor + let mut guard = ManuallyDrop::new(guard); + + // Next we manually read the stored value from the guard. + // + // SAFETY: this is safe because we've taken ownership of the guard. + let value = unsafe { ManuallyDrop::take(&mut guard.inner) }; + + // Finally we drop the stored closure. We do this *after* having read + // the value, so that even if the closure's `drop` function panics, + // unwinding still tries to drop the value. + // + // SAFETY: this is safe because we've taken ownership of the guard. + unsafe { ManuallyDrop::drop(&mut guard.f) }; + value + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Deref for DropGuard +where + F: FnOnce(T), +{ + type Target = T; + + fn deref(&self) -> &T { + &*self.inner + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl DerefMut for DropGuard +where + F: FnOnce(T), +{ + fn deref_mut(&mut self) -> &mut T { + &mut *self.inner + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Drop for DropGuard +where + F: FnOnce(T), +{ + fn drop(&mut self) { + // SAFETY: `DropGuard` is in the process of being dropped. + let inner = unsafe { ManuallyDrop::take(&mut self.inner) }; + + // SAFETY: `DropGuard` is in the process of being dropped. + let f = unsafe { ManuallyDrop::take(&mut self.f) }; + + f(inner); + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Debug for DropGuard +where + T: Debug, + F: FnOnce(T), +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2198d098c4b..fcb9f25ede2 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,6 +21,10 @@ mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, TransmuteFrom}; +mod drop_guard; +#[unstable(feature = "drop_guard", issue = "144426")] +pub use drop_guard::DropGuard; + // This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do // the special magic "types have equal size" check at the call site. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4cfac9ecc2a..c8f516f0a95 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -30,6 +30,7 @@ #![feature(core_private_diy_float)] #![feature(cstr_display)] #![feature(dec2flt)] +#![feature(drop_guard)] #![feature(duration_constants)] #![feature(duration_constructors)] #![feature(duration_constructors_lite)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 9c15be4a8c4..e896c61ef48 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,5 +1,6 @@ use core::mem::*; use core::{array, ptr}; +use std::cell::Cell; #[cfg(panic = "unwind")] use std::rc::Rc; @@ -795,3 +796,48 @@ fn const_maybe_uninit_zeroed() { assert_eq!(unsafe { (*UNINIT.0.cast::<[[u8; SIZE]; 1]>())[0] }, [0u8; SIZE]); } + +#[test] +fn drop_guards_only_dropped_by_closure_when_run() { + let value_drops = Cell::new(0); + let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get())); + let closure_drops = Cell::new(0); + let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get())); + assert_eq!(value_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(closure_drops.get(), 1); +} + +#[test] +fn drop_guard_into_inner() { + let dropped = Cell::new(false); + let value = DropGuard::new(42, |_| dropped.set(true)); + let guard = DropGuard::new(value, |_| dropped.set(true)); + let inner = DropGuard::into_inner(guard); + assert_eq!(dropped.get(), false); + assert_eq!(*inner, 42); +} + +#[test] +#[cfg(panic = "unwind")] +fn drop_guard_always_drops_value_if_closure_drop_unwinds() { + // Create a value with a destructor, which we will validate ran successfully. + let mut value_was_dropped = false; + let value_with_tracked_destruction = DropGuard::new((), |_| value_was_dropped = true); + + // Create a closure that will begin unwinding when dropped. + let drop_bomb = DropGuard::new((), |_| panic!()); + let closure_that_panics_on_drop = move |_| { + let _drop_bomb = drop_bomb; + }; + + // This will run the closure, which will panic when dropped. This should + // run the destructor of the value we passed, which we validate. + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop); + DropGuard::into_inner(guard); + })); + assert!(value_was_dropped); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 323742a75b0..1b1e85cfc2f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -331,6 +331,7 @@ #![feature(clone_to_uninit)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] +#![feature(drop_guard)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_iter)] -- cgit 1.4.1-3-g733a5 From 73751a04910d08052bf6b825f8829cacba86c648 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 26 Jul 2025 14:29:00 +0200 Subject: thread name in stack overflow message --- library/std/src/sys/pal/hermit/thread.rs | 6 +++++- library/std/src/sys/pal/itron/thread.rs | 6 +++++- library/std/src/sys/pal/sgx/thread.rs | 6 +++++- library/std/src/sys/pal/teeos/thread.rs | 6 +++++- library/std/src/sys/pal/uefi/thread.rs | 6 +++++- library/std/src/sys/pal/unix/stack_overflow.rs | 23 +++++++++++++---------- library/std/src/sys/pal/unix/thread.rs | 24 +++++++++++++++++------- library/std/src/sys/pal/unsupported/thread.rs | 6 +++++- library/std/src/sys/pal/wasi/thread.rs | 4 ++-- library/std/src/sys/pal/wasm/atomics/thread.rs | 6 +++++- library/std/src/sys/pal/windows/thread.rs | 6 +++++- library/std/src/sys/pal/xous/thread.rs | 6 +++++- library/std/src/thread/mod.rs | 2 +- tests/ui/runtime/out-of-stack.rs | 12 +++++++++--- 14 files changed, 87 insertions(+), 32 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 9bc5a16b800..95fe4f902d3 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -58,7 +58,11 @@ impl Thread { } } - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { unsafe { Thread::new_with_coreid(stack, p, -1 /* = no specific core */) } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 813e1cbcd58..0d28051fcc4 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -86,7 +86,11 @@ impl Thread { /// # Safety /// /// See `thread::Builder::spawn_unchecked` for safety requirements. - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let inner = Box::new(ThreadInner { start: UnsafeCell::new(ManuallyDrop::new(p)), lifecycle: AtomicUsize::new(LIFECYCLE_INIT), diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 85f6dcd96b4..a236c362706 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -96,7 +96,11 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b9cdc7a2a58..a91d95626e7 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -22,7 +22,11 @@ unsafe extern "C" { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index e4776ec42fb..75c364362b2 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -11,7 +11,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index a3be2cdf738..d89100e6919 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -8,8 +8,8 @@ pub struct Handler { } impl Handler { - pub unsafe fn new() -> Handler { - make_handler(false) + pub unsafe fn new(thread_name: Option>) -> Handler { + make_handler(false, thread_name) } fn null() -> Handler { @@ -72,7 +72,6 @@ mod imp { use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::thread::with_current_name; use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -158,13 +157,12 @@ mod imp { if !NEED_ALTSTACK.load(Ordering::Relaxed) { // haven't set up our sigaltstack yet NEED_ALTSTACK.store(true, Ordering::Release); - let handler = unsafe { make_handler(true) }; + let handler = unsafe { make_handler(true, None) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); if let Some(guard_page_range) = guard_page_range.take() { - let thread_name = with_current_name(|name| name.map(Box::from)); - set_current_info(guard_page_range, thread_name); + set_current_info(guard_page_range, Some(Box::from("main"))); } } @@ -230,14 +228,13 @@ mod imp { /// # Safety /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn make_handler(main_thread: bool) -> Handler { + pub unsafe fn make_handler(main_thread: bool, thread_name: Option>) -> Handler { if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } if !main_thread { if let Some(guard_page_range) = unsafe { current_guard() } { - let thread_name = with_current_name(|name| name.map(Box::from)); set_current_info(guard_page_range, thread_name); } } @@ -634,7 +631,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + _main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { super::Handler::null() } @@ -717,7 +717,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { if !main_thread { reserve_stack(); } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e4f5520d8a3..7f6440152d4 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -22,6 +22,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; #[cfg(any(target_os = "espidf", target_os = "nuttx"))] pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used +struct ThreadData { + name: Option>, + f: Box, +} + pub struct Thread { id: libc::pthread_t, } @@ -34,8 +39,12 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new( + stack: usize, + name: Option<&str>, + f: Box, + ) -> io::Result { + let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f })); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); @@ -73,7 +82,7 @@ impl Thread { }; } - let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, p as *mut _); + let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _); // Note: if the thread creation fails and this assert fails, then p will // be leaked. However, an alternative design could cause double-free // which is clearly worse. @@ -82,19 +91,20 @@ impl Thread { return if ret != 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + drop(Box::from_raw(data)); Err(io::Error::from_raw_os_error(ret)) } else { Ok(Thread { id: native }) }; - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { unsafe { + let data = Box::from_raw(data as *mut ThreadData); // Next, set up our stack overflow handler which may get triggered if we run // out of stack. - let _handler = stack_overflow::Handler::new(); + let _handler = stack_overflow::Handler::new(data.name); // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); + (data.f)(); } ptr::null_mut() } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index 8a3119fa292..5a1e3fde986 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 5f21a553673..a46c74630c9 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -73,7 +73,7 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; @@ -120,7 +120,7 @@ impl Thread { } } } else { - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box) -> io::Result { crate::sys::unsupported() } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 44ce3eab109..ebfabaafc79 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 14785171755..b45f76fb546 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -20,7 +20,11 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); // CreateThread rounds up values for the stack size to the nearest page size (at least 4kb). diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 1b344e984dc..f2404a62abf 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -20,7 +20,11 @@ pub const GUARD_PAGE_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 6075173db47..62ecacccd2e 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -595,7 +595,7 @@ impl Builder { // Similarly, the `sys` implementation must guarantee that no references to the closure // exist after the thread has terminated, which is signaled by `Thread::join` // returning. - native: unsafe { imp::Thread::new(stack_size, main)? }, + native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, thread: my_thread, packet: my_packet, }) diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 6be34afb560..913d3637c8f 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -19,7 +19,7 @@ extern crate libc; use std::env; use std::hint::black_box; use std::process::Command; -use std::thread; +use std::thread::Builder; fn silent_recurse() { let buf = [0u8; 1000]; @@ -56,9 +56,9 @@ fn main() { } else if args.len() > 1 && args[1] == "loud" { loud_recurse(); } else if args.len() > 1 && args[1] == "silent-thread" { - thread::spawn(silent_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(silent_recurse).unwrap().join(); } else if args.len() > 1 && args[1] == "loud-thread" { - thread::spawn(loud_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(loud_recurse).unwrap().join(); } else { let mut modes = vec![ "silent-thread", @@ -82,6 +82,12 @@ fn main() { let error = String::from_utf8_lossy(&silent.stderr); assert!(error.contains("has overflowed its stack"), "missing overflow message: {}", error); + + if mode.contains("thread") { + assert!(error.contains("ferris"), "missing thread name: {}", error); + } else { + assert!(error.contains("main"), "missing thread name: {}", error); + } } } } -- cgit 1.4.1-3-g733a5 From ec4dc1c5f87e3de735cefb21ac51522568b1b391 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 13:34:04 +0200 Subject: clean up existing poison files --- library/std/src/sync/poison.rs | 6 +++--- library/std/src/sync/poison/condvar.rs | 2 +- library/std/src/sync/poison/mutex.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'library/std/src') diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 0c05f152ef8..b901a5701a4 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -13,7 +13,9 @@ //! depend on the primitive. See [#Overview] below. //! //! For the alternative implementations that do not employ poisoning, -//! see `std::sync::nonpoisoning`. +//! see [`std::sync::nonpoison`]. +//! +//! [`std::sync::nonpoison`]: crate::sync::nonpoison //! //! # Overview //! @@ -56,8 +58,6 @@ //! while it is locked exclusively (write mode). If a panic occurs in any reader, //! then the lock will not be poisoned. -// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. - #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::{Condvar, WaitTimeoutResult}; #[unstable(feature = "mapped_lock_guards", issue = "117108")] diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index 7f0f3f652bc..0e9d4233c65 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -13,7 +13,7 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); -// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. +// FIXME(nonpoison_condvar): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. // Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 30325be685c..64744f18c74 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -650,7 +650,7 @@ impl fmt::Debug for Mutex { d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { - d.field("data", &format_args!("")); + d.field("data", &""); } } d.field("poisoned", &self.poison.get()); -- cgit 1.4.1-3-g733a5 From 3bdc228c103d72a61d9639d6fd0dd5c1566ec37f Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 13:47:49 +0200 Subject: add `nonpoison::mutex` implementation Adds the equivalent `nonpoison` types to the `poison::mutex` module. These types and implementations are gated under the `nonpoison_mutex` feature gate. Also blesses the ui tests that now have a name conflicts (because these types no longer have unique names). The full path distinguishes the different types. Co-authored-by: Aandreba Co-authored-by: Trevor Gross --- library/std/src/sync/mod.rs | 2 + library/std/src/sync/nonpoison.rs | 37 ++ library/std/src/sync/nonpoison/mutex.rs | 611 +++++++++++++++++++++ .../issue-64130-non-send-future-diags.stderr | 4 +- tests/ui/async-await/issue-71137.stderr | 4 +- tests/ui/async-await/issues/issue-67893.rs | 2 +- tests/ui/async-await/issues/issue-67893.stderr | 6 +- tests/ui/lint/must_not_suspend/mutex.rs | 2 +- tests/ui/lint/must_not_suspend/mutex.stderr | 2 +- .../privacy/private-field-access-in-mutex-54062.rs | 2 +- .../private-field-access-in-mutex-54062.stderr | 2 +- tests/ui/suggestions/inner_type.fixed | 2 +- tests/ui/suggestions/inner_type.rs | 2 +- tests/ui/suggestions/inner_type.stderr | 4 +- tests/ui/sync/mutexguard-sync.stderr | 2 +- .../const-traits/span-bug-issue-121418.stderr | 6 +- tests/ui/typeck/assign-non-lval-derefmut.fixed | 4 +- tests/ui/typeck/assign-non-lval-derefmut.rs | 4 +- tests/ui/typeck/assign-non-lval-derefmut.stderr | 14 +- tests/ui/typeck/deref-multi.stderr | 2 +- 20 files changed, 682 insertions(+), 32 deletions(-) create mode 100644 library/std/src/sync/nonpoison.rs create mode 100644 library/std/src/sync/nonpoison/mutex.rs (limited to 'library/std/src') diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index e67b4f6f22f..6ef3bf25cf6 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -225,6 +225,8 @@ pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWrit pub mod mpmc; pub mod mpsc; +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub mod nonpoison; #[unstable(feature = "sync_poison_mod", issue = "134646")] pub mod poison; diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs new file mode 100644 index 00000000000..2bbf226dc2c --- /dev/null +++ b/library/std/src/sync/nonpoison.rs @@ -0,0 +1,37 @@ +//! Non-poisoning synchronous locks. +//! +//! The difference from the locks in the [`poison`] module is that the locks in this module will not +//! become poisoned when a thread panics while holding a guard. +//! +//! [`poison`]: super::poison + +use crate::fmt; + +/// A type alias for the result of a nonblocking locking method. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub type TryLockResult = Result; + +/// A lock could not be acquired at this time because the operation would otherwise block. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub struct WouldBlock; + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Debug for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "WouldBlock".fmt(f) + } +} + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Display for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "try_lock failed because the operation would block".fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +pub use self::mutex::{Mutex, MutexGuard}; + +mod mutex; diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs new file mode 100644 index 00000000000..b6861c78f00 --- /dev/null +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -0,0 +1,611 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{Deref, DerefMut}; +use crate::ptr::NonNull; +use crate::sync::nonpoison::{TryLockResult, WouldBlock}; +use crate::sys::sync as sys; + +/// A mutual exclusion primitive useful for protecting shared data that does not keep track of +/// lock poisoning. +/// +/// For more information about mutexes, check out the documentation for the poisoning variant of +/// this lock at [`poison::Mutex`]. +/// +/// [`poison::Mutex`]: crate::sync::poison::Mutex +/// +/// # Examples +/// +/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via +/// poisoning. If you need this functionality, see [`poison::Mutex`]. +/// +/// ``` +/// #![feature(nonpoison_mutex)] +/// +/// use std::thread; +/// use std::sync::{Arc, nonpoison::Mutex}; +/// +/// let mutex = Arc::new(Mutex::new(0u32)); +/// let mut handles = Vec::new(); +/// +/// for n in 0..10 { +/// let m = Arc::clone(&mutex); +/// let handle = thread::spawn(move || { +/// let mut guard = m.lock(); +/// *guard += 1; +/// panic!("panic from thread {n} {guard}") +/// }); +/// handles.push(handle); +/// } +/// +/// for h in handles { +/// let _ = h.join(); +/// } +/// +/// println!("Finished, locked {} times", mutex.lock()); +/// ``` +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")] +pub struct Mutex { + inner: sys::Mutex, + data: UnsafeCell, +} + +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for Mutex {} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`lock`] and [`try_lock`] methods on +/// [`Mutex`]. +/// +/// [`lock`]: Mutex::lock +/// [`try_lock`]: Mutex::try_lock +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")] +pub struct MutexGuard<'a, T: ?Sized + 'a> { + lock: &'a Mutex, +} + +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MutexGuard<'_, T> {} + +// FIXME(nonpoison_condvar): Use this link instead: [`Condvar`]: crate::sync::nonpoison::Condvar +/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a +/// subfield of the protected data. When this structure is dropped (falls out +/// of scope), the lock will be unlocked. +/// +/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the +/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the +/// locked object is modified by another thread while the `Mutex` is unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`map`] and [`filter_map`] methods on +/// [`MutexGuard`]. +/// +/// [`map`]: MutexGuard::map +/// [`filter_map`]: MutexGuard::filter_map +/// [`Condvar`]: crate::sync::Condvar +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MappedMutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +pub struct MappedMutexGuard<'a, T: ?Sized + 'a> { + // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a + // `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. + // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field + // below for the correct variance over `T` (invariance). + data: NonNull, + inner: &'a sys::Mutex, + _variance: PhantomData<&'a mut T>, +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MappedMutexGuard<'_, T> {} +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MappedMutexGuard<'_, T> {} + +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + #[inline] + pub const fn new(t: T) -> Mutex { + Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) } + } + + /// Returns the contained value by cloning it. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_cloned(&self) -> T + where + T: Clone, + { + self.lock().clone() + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// mutex.set(11); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn set(&self, value: T) { + if mem::needs_drop::() { + // If the contained value has a non-trivial destructor, we + // call that destructor after the lock has been released. + drop(self.replace(value)) + } else { + *self.lock() = value; + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.replace(11), 7); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn replace(&self, value: T) -> T { + let mut guard = self.lock(); + mem::replace(&mut *guard, value) + } +} + +impl Mutex { + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the lock + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// The exact behavior on locking a mutex in the thread which already holds + /// the lock is left unspecified. However, this function will not return on + /// the second call (it might panic or deadlock, for example). + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by + /// the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::{Arc, nonpoison::Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// *c_mutex.lock() = 10; + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn lock(&self) -> MutexGuard<'_, T> { + unsafe { + self.inner.lock(); + MutexGuard::new(self) + } + } + + /// Attempts to acquire this lock. + /// + /// This function does not block. If the lock could not be acquired at this time, then + /// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned. + /// + /// The lock will be unlocked when the guard is dropped. + /// + /// # Errors + /// + /// If the mutex could not be acquired because it is already locked, then this call will return + /// the [`WouldBlock`] error. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// let mut lock = c_mutex.try_lock(); + /// if let Ok(ref mut mutex) = lock { + /// **mutex = 10; + /// } else { + /// println!("try_lock failed"); + /// } + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn try_lock(&self) -> TryLockResult> { + unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } } + } + + /// Consumes this mutex, returning the underlying data. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// assert_eq!(mutex.into_inner(), 0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn into_inner(self) -> T + where + T: Sized, + { + self.data.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `Mutex` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(0); + /// *mutex.get_mut() = 10; + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_mut(&mut self) -> &mut T { + self.data.get_mut() + } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the mutex is dropped. + #[unstable(feature = "mutex_data_ptr", issue = "140368")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl From for Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// This is equivalent to [`Mutex::new`]. + fn from(t: T) -> Self { + Mutex::new(t) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Default for Mutex { + /// Creates a `Mutex`, with the `Default` value for T. + fn default() -> Mutex { + Mutex::new(Default::default()) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("Mutex"); + match self.try_lock() { + Ok(guard) => { + d.field("data", &&*guard); + } + Err(WouldBlock) => { + d.field("data", &""); + } + } + d.finish_non_exhaustive() + } +} + +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { + unsafe fn new(lock: &'mutex Mutex) -> MutexGuard<'mutex, T> { + return MutexGuard { lock }; + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Drop for MutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.lock.inner.unlock(); + } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Display for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Deref for MappedMutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl DerefMut for MappedMutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { self.data.as_mut() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Drop for MappedMutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.inner.unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Debug for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Display for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_mut() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(mut orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} diff --git a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr index d28807e223b..beaf8e9c96d 100644 --- a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr +++ b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | is_send(foo()); | ^^^^^ future returned by `foo` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, u32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, u32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-non-send-future-diags.rs:17:11 | LL | let g = x.lock().unwrap(); - | - has type `MutexGuard<'_, u32>` which is not `Send` + | - has type `std::sync::MutexGuard<'_, u32>` which is not `Send` LL | baz().await; | ^^^^^ await occurs here, with `g` maybe used later note: required by a bound in `is_send` diff --git a/tests/ui/async-await/issue-71137.stderr b/tests/ui/async-await/issue-71137.stderr index 8739c22a310..d567e3f2063 100644 --- a/tests/ui/async-await/issue-71137.stderr +++ b/tests/ui/async-await/issue-71137.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | fake_spawn(wrong_mutex()); | ^^^^^^^^^^^^^ future returned by `wrong_mutex` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, i32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, i32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-71137.rs:14:26 | LL | let mut guard = m.lock().unwrap(); - | --------- has type `MutexGuard<'_, i32>` which is not `Send` + | --------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` LL | (async { "right"; }).await; | ^^^^^ await occurs here, with `mut guard` maybe used later note: required by a bound in `fake_spawn` diff --git a/tests/ui/async-await/issues/issue-67893.rs b/tests/ui/async-await/issues/issue-67893.rs index 73cce38c94a..2020abe7a5a 100644 --- a/tests/ui/async-await/issues/issue-67893.rs +++ b/tests/ui/async-await/issues/issue-67893.rs @@ -7,5 +7,5 @@ fn g(_: impl Send) {} fn main() { g(issue_67893::run()) - //~^ ERROR `MutexGuard<'_, ()>` cannot be sent between threads safely + //~^ ERROR `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely } diff --git a/tests/ui/async-await/issues/issue-67893.stderr b/tests/ui/async-await/issues/issue-67893.stderr index c01237255b8..34f28dd53c7 100644 --- a/tests/ui/async-await/issues/issue-67893.stderr +++ b/tests/ui/async-await/issues/issue-67893.stderr @@ -1,8 +1,8 @@ -error[E0277]: `MutexGuard<'_, ()>` cannot be sent between threads safely +error[E0277]: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely --> $DIR/issue-67893.rs:9:7 | LL | g(issue_67893::run()) - | - ^^^^^^^^^^^^^^^^^^ `MutexGuard<'_, ()>` cannot be sent between threads safely + | - ^^^^^^^^^^^^^^^^^^ `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely | | | required by a bound introduced by this call | @@ -11,7 +11,7 @@ LL | g(issue_67893::run()) LL | pub async fn run() { | ------------------ within this `impl Future` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, ()>` note: required because it's used within this `async` fn body --> $DIR/auxiliary/issue_67893.rs:9:20 | diff --git a/tests/ui/lint/must_not_suspend/mutex.rs b/tests/ui/lint/must_not_suspend/mutex.rs index d14f7130b4c..8dd4cc17615 100644 --- a/tests/ui/lint/must_not_suspend/mutex.rs +++ b/tests/ui/lint/must_not_suspend/mutex.rs @@ -5,7 +5,7 @@ async fn other() {} pub async fn uhoh(m: std::sync::Mutex<()>) { - let _guard = m.lock().unwrap(); //~ ERROR `MutexGuard` held across + let _guard = m.lock().unwrap(); //~ ERROR `std::sync::MutexGuard` held across other().await; } diff --git a/tests/ui/lint/must_not_suspend/mutex.stderr b/tests/ui/lint/must_not_suspend/mutex.stderr index ca53a753150..0db1f2575b1 100644 --- a/tests/ui/lint/must_not_suspend/mutex.stderr +++ b/tests/ui/lint/must_not_suspend/mutex.stderr @@ -1,4 +1,4 @@ -error: `MutexGuard` held across a suspend point, but should not be +error: `std::sync::MutexGuard` held across a suspend point, but should not be --> $DIR/mutex.rs:8:9 | LL | let _guard = m.lock().unwrap(); diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.rs b/tests/ui/privacy/private-field-access-in-mutex-54062.rs index f145f9c3e52..c957e0bc7e8 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.rs +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.rs @@ -8,7 +8,7 @@ fn main() {} fn testing(test: Test) { let _ = test.comps.inner.try_lock(); - //~^ ERROR: field `inner` of struct `Mutex` is private + //~^ ERROR: field `inner` of struct `std::sync::Mutex` is private } // https://github.com/rust-lang/rust/issues/54062 diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr index 14244107597..f7f84640648 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `inner` of struct `Mutex` is private +error[E0616]: field `inner` of struct `std::sync::Mutex` is private --> $DIR/private-field-access-in-mutex-54062.rs:10:24 | LL | let _ = test.comps.inner.try_lock(); diff --git a/tests/ui/suggestions/inner_type.fixed b/tests/ui/suggestions/inner_type.fixed index 3dc939d6b5c..175a2a02acd 100644 --- a/tests/ui/suggestions/inner_type.fixed +++ b/tests/ui/suggestions/inner_type.fixed @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.lock().unwrap().method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.rs b/tests/ui/suggestions/inner_type.rs index 81a05c25311..ab021414f56 100644 --- a/tests/ui/suggestions/inner_type.rs +++ b/tests/ui/suggestions/inner_type.rs @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.stderr b/tests/ui/suggestions/inner_type.stderr index 5ac3d04f104..67ebb5789b7 100644 --- a/tests/ui/suggestions/inner_type.stderr +++ b/tests/ui/suggestions/inner_type.stderr @@ -30,11 +30,11 @@ help: use `.borrow_mut()` to mutably borrow the `Struct`, panicking if any LL | other_item.borrow_mut().some_mutable_method(); | +++++++++++++ -error[E0599]: no method named `method` found for struct `Mutex` in the current scope +error[E0599]: no method named `method` found for struct `std::sync::Mutex` in the current scope --> $DIR/inner_type.rs:27:18 | LL | another_item.method(); - | ^^^^^^ method not found in `Mutex>` + | ^^^^^^ method not found in `std::sync::Mutex>` | note: the method `method` exists on the type `Struct` --> $DIR/inner_type.rs:9:5 diff --git a/tests/ui/sync/mutexguard-sync.stderr b/tests/ui/sync/mutexguard-sync.stderr index 1501a793d5e..ab9983c1f2c 100644 --- a/tests/ui/sync/mutexguard-sync.stderr +++ b/tests/ui/sync/mutexguard-sync.stderr @@ -8,7 +8,7 @@ LL | test_sync(guard); | = help: the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead - = note: required for `MutexGuard<'_, Cell>` to implement `Sync` + = note: required for `std::sync::MutexGuard<'_, Cell>` to implement `Sync` note: required by a bound in `test_sync` --> $DIR/mutexguard-sync.rs:5:17 | diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr index 92cfecd0540..f31129d9cb7 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr @@ -14,8 +14,8 @@ error[E0277]: the size for values of type `(dyn T + 'static)` cannot be known at LL | pub const fn new() -> std::sync::Mutex {} | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` -note: required because it appears within the type `Mutex<(dyn T + 'static)>` + = help: within `std::sync::Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` +note: required because it appears within the type `std::sync::Mutex<(dyn T + 'static)>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL = note: the return type of a function must have a statically known size @@ -27,7 +27,7 @@ LL | pub const fn new() -> std::sync::Mutex {} | | | implicitly returns `()` as its body has no tail or `return` expression | - = note: expected struct `Mutex<(dyn T + 'static)>` + = note: expected struct `std::sync::Mutex<(dyn T + 'static)>` found unit type `()` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/assign-non-lval-derefmut.fixed b/tests/ui/typeck/assign-non-lval-derefmut.fixed index 6ecec574f2e..e6f97a9e86c 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.fixed +++ b/tests/ui/typeck/assign-non-lval-derefmut.fixed @@ -5,11 +5,11 @@ fn main() { *x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment *x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); *y = 2; //~^ ERROR mismatched types *y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.rs b/tests/ui/typeck/assign-non-lval-derefmut.rs index ac1be913e2a..a53a52c7e4d 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.rs +++ b/tests/ui/typeck/assign-non-lval-derefmut.rs @@ -5,11 +5,11 @@ fn main() { x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); y = 2; //~^ ERROR mismatched types y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr index 16fb1e9c5c3..f57b5abe2ee 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.stderr +++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr @@ -11,15 +11,15 @@ help: consider dereferencing here to assign to the mutably borrowed value LL | *x.lock().unwrap() = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:7:5 | LL | x.lock().unwrap() += 1; | -----------------^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` @@ -36,22 +36,22 @@ LL | let mut y = x.lock().unwrap(); LL | y = 2; | ^ expected `MutexGuard<'_, usize>`, found integer | - = note: expected struct `MutexGuard<'_, usize>` + = note: expected struct `std::sync::MutexGuard<'_, usize>` found type `{integer}` help: consider dereferencing here to assign to the mutably borrowed value | LL | *y = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:13:5 | LL | y += 1; | -^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` diff --git a/tests/ui/typeck/deref-multi.stderr b/tests/ui/typeck/deref-multi.stderr index 02513853c48..c4fa49e43ef 100644 --- a/tests/ui/typeck/deref-multi.stderr +++ b/tests/ui/typeck/deref-multi.stderr @@ -63,7 +63,7 @@ LL | x.lock().unwrap() | ^^^^^^^^^^^^^^^^^ expected `i32`, found `MutexGuard<'_, &i32>` | = note: expected type `i32` - found struct `MutexGuard<'_, &i32>` + found struct `std::sync::MutexGuard<'_, &i32>` help: consider dereferencing the type | LL | **x.lock().unwrap() -- cgit 1.4.1-3-g733a5