From cfd52bba1f77515017bdd1cd8d18ea2747990c2c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2025 14:46:40 -0800 Subject: [PATCH 01/11] rustc: Stop passing `--allow-undefined` on wasm targets This commit updates how the linker is invoked on WebAssembly targets (all of them) to avoid passing the `--allow-undefined` flag to the linker. Historically, if I remember this correctly, when `wasm-ld` was first integrated this was practically required because at the time it was otherwise impossible to import a function from the host into a wasm binary. Or, at least, I'm pretty sure that was why this was added. At the time, as the documentation around this option indicates, it was known that this was going to be a hazard. This doesn't match behavior on native, for example, and can easily paper over what should be a linker error with some sort of other obscure runtime error. An example is that this program currently compiles and links, it just prints null: unsafe extern "C" { static nonexistent: u8; } fn main() { println!("{:?}", &raw const nonexistent); } This can easily lead to mistakes like rust-lang/libc/4880 and defer what should be a compile-time link error to weird or unusual behavior at link time. Additionally, in the intervening time since `wasm-ld` was first introduced here, lots has changed and notably this program works as expected: #[link(wasm_import_module = "host")] unsafe extern "C" { fn foo(); } fn main() { unsafe { foo(); } } Notably this continues to compile without error and the final wasm binary indeed has an imported function from the host. What this change means, however, is that this program: unsafe extern "C" { fn foo(); } fn main() { unsafe { foo(); } } this currently compiles successfully and emits an import from the `env` module. After this change, however, this will fail to compile with a link error stating that the `foo` symbol is not defined. --- compiler/rustc_target/src/spec/base/wasm.rs | 10 ---------- tests/run-make/wasm-stringify-ints-small/foo.rs | 2 ++ tests/run-make/wasm-unexpected-features/foo.rs | 5 +++++ tests/ui/linking/executable-no-mangle-strip.rs | 1 + 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 7ede45766ea59..1f0f564a77908 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -28,16 +28,6 @@ pub(crate) fn options() -> TargetOptions { // stack overflow will be guaranteed to trap as it underflows instead of // corrupting static data. concat!($prefix, "--stack-first"), - // FIXME we probably shouldn't pass this but instead pass an explicit list - // of symbols we'll allow to be undefined. We don't currently have a - // mechanism of knowing, however, which symbols are intended to be imported - // from the environment and which are intended to be imported from other - // objects linked elsewhere. This is a coarse approximation but is sure to - // hide some bugs and frustrate someone at some point, so we should ideally - // work towards a world where we can explicitly list symbols that are - // supposed to be imported and have all other symbols generate errors if - // they remain undefined. - concat!($prefix, "--allow-undefined"), // LLD only implements C++-like demangling, which doesn't match our own // mangling scheme. Tell LLD to not demangle anything and leave it up to // us to demangle these symbols later. Currently rustc does not perform diff --git a/tests/run-make/wasm-stringify-ints-small/foo.rs b/tests/run-make/wasm-stringify-ints-small/foo.rs index 7a947f013ad48..50ced86f3bc24 100644 --- a/tests/run-make/wasm-stringify-ints-small/foo.rs +++ b/tests/run-make/wasm-stringify-ints-small/foo.rs @@ -1,5 +1,6 @@ #![crate_type = "cdylib"] +#[link(wasm_import_module = "the-world")] extern "C" { fn observe(ptr: *const u8, len: usize); } @@ -7,6 +8,7 @@ extern "C" { macro_rules! s { ( $( $f:ident -> $t:ty );* $(;)* ) => { $( + #[link(wasm_import_module = "the-world")] extern "C" { fn $f() -> $t; } diff --git a/tests/run-make/wasm-unexpected-features/foo.rs b/tests/run-make/wasm-unexpected-features/foo.rs index 5c7aa2d619046..37062cb6f9d33 100644 --- a/tests/run-make/wasm-unexpected-features/foo.rs +++ b/tests/run-make/wasm-unexpected-features/foo.rs @@ -14,6 +14,11 @@ unsafe extern "Rust" { fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } +#[rustc_std_internal_symbol] +pub unsafe fn __rdl_alloc_error_handler(_size: usize, _align: usize) -> ! { + loop {} +} + #[used] static mut BUF: [u8; 1024] = [0; 1024]; diff --git a/tests/ui/linking/executable-no-mangle-strip.rs b/tests/ui/linking/executable-no-mangle-strip.rs index cc283dc53ee3a..240c16af67bdb 100644 --- a/tests/ui/linking/executable-no-mangle-strip.rs +++ b/tests/ui/linking/executable-no-mangle-strip.rs @@ -1,5 +1,6 @@ //@ run-pass //@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu +//@ ignore-wasm: wasm, for better or worse, exports all #[no_mangle] // Regression test for . // Functions in the binary marked with no_mangle should be GC-ed if they From 08abc44fa5eec6639cc10d60efa6807038d107ad Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Thu, 19 Feb 2026 13:21:07 +0100 Subject: [PATCH 02/11] Add test for SGX delayed host lookup via ToSocketAddr --- library/std/src/sys/net/connection/sgx.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 8c9c17d3f1714..1b3027c0cfb2d 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -512,3 +512,16 @@ pub fn lookup_host(host: &str, port: u16) -> io::Result { NonIpSockAddr { host: format!("{host}:{port}") }, )) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unparseable_sockaddr() { + let addr = "local"; + let error = addr.to_socket_addrs().unwrap_err(); + let non_ip_addr = error.downcast::().unwrap(); + assert_eq!(addr, non_ip_addr.host); + } +} From e2b308726ae6336c837688664953097ce2cab83c Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Tue, 24 Feb 2026 12:00:05 -0800 Subject: [PATCH 03/11] Fix SGX delayed host lookup via ToSocketAddr --- library/std/src/net/mod.rs | 4 +++ library/std/src/net/socket_addr.rs | 34 +++++++++++-------- library/std/src/sys/net/connection/motor.rs | 2 ++ library/std/src/sys/net/connection/sgx.rs | 9 ++--- .../std/src/sys/net/connection/socket/mod.rs | 2 ++ .../std/src/sys/net/connection/uefi/mod.rs | 2 ++ .../std/src/sys/net/connection/unsupported.rs | 2 ++ library/std/src/sys/net/connection/wasip1.rs | 2 ++ .../std/src/sys/net/connection/xous/mod.rs | 2 ++ 9 files changed, 41 insertions(+), 18 deletions(-) diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 3e4447eb33f2a..5af3dc32c95d1 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -70,3 +70,7 @@ pub enum Shutdown { #[stable(feature = "rust1", since = "1.0.0")] Both, } + +// allow(unused_imports): This function is only used on some targets +#[allow(unused_imports)] +pub(crate) use socket_addr::lookup_host_string; diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 8214ad381f1f7..fac570d72bccd 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -189,9 +189,23 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -fn lookup_host(host: &str, port: u16) -> io::Result> { - let addrs = crate::sys::net::lookup_host(host, port)?; - Ok(Vec::from_iter(addrs).into_iter()) +// Default implementation, this shouldn't be called directly by the function's +// path in this module. Instead, use the platform-specific implementation +// (which may be this one). +// +// allow(dead_code): This function is only used on some targets +#[allow(dead_code)] +pub(crate) fn lookup_host_string(addr: &str) -> io::Result> { + // Split the string by ':' and convert the second part to u16... + let Some((host, port_str)) = addr.rsplit_once(':') else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); + }; + let Ok(port) = port_str.parse::() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); + }; + + // ... and make the system look up the host. + crate::sys::net::lookup_host(host, port) } #[stable(feature = "rust1", since = "1.0.0")] @@ -207,7 +221,7 @@ impl ToSocketAddrs for (&str, u16) { } // Otherwise, make the system look it up. - lookup_host(host, port) + crate::sys::net::lookup_host(host, port).map(|addrs| Vec::from_iter(addrs).into_iter()) } } @@ -229,16 +243,8 @@ impl ToSocketAddrs for str { return Ok(vec![addr].into_iter()); } - // Otherwise, split the string by ':' and convert the second part to u16... - let Some((host, port_str)) = self.rsplit_once(':') else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); - }; - let Ok(port) = port_str.parse::() else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); - }; - - // ... and make the system look up the host. - lookup_host(host, port) + // Otherwise, make the system look it up. + crate::sys::net::lookup_host_string(self).map(|addrs| Vec::from_iter(addrs).into_iter()) } } diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index 79a528792106c..8555291adc3c4 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -395,6 +395,8 @@ pub struct LookupHost { addresses: alloc::collections::VecDeque, } +pub(crate) use crate::net::lookup_host_string; + pub fn lookup_host(host: &str, port: u16) -> io::Result { let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; Ok(LookupHost { addresses }) diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 1b3027c0cfb2d..6ecb0a69a027b 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -506,11 +506,12 @@ impl Iterator for LookupHost { } } +pub fn lookup_host_string(addr: impl Into) -> io::Result { + Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host: addr.into() })) +} + pub fn lookup_host(host: &str, port: u16) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Uncategorized, - NonIpSockAddr { host: format!("{host}:{port}") }, - )) + lookup_host_string(format!("{host}:{port}")) } #[cfg(test)] diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 256b99dfa9874..54595b5a895e0 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -340,6 +340,8 @@ impl Drop for LookupHost { } } +pub(crate) use crate::net::lookup_host_string; + pub fn lookup_host(host: &str, port: u16) -> io::Result { init(); run_with_cstr(host.as_bytes(), &|c_host| { diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 107a3e23733de..299460572a87d 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -345,6 +345,8 @@ impl Iterator for LookupHost { } } +pub(crate) use crate::net::lookup_host_string; + pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fb18e8dec557c..7089e17a7beda 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -311,6 +311,8 @@ impl Iterator for LookupHost { } } +pub(crate) use crate::net::lookup_host_string; + pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 95a4ab2fbf0fd..1ef12e5ec4cf1 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -483,6 +483,8 @@ impl Iterator for LookupHost { } } +pub(crate) use crate::net::lookup_host_string; + pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index 0f77be5c3fac8..f3fd83a10f2c6 100644 --- a/library/std/src/sys/net/connection/xous/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -46,3 +46,5 @@ pub struct GetAddress { } pub use dns::lookup_host; + +pub(crate) use crate::net::lookup_host_string; From fadd60ba47e21cdbacec7c081cdc5f80fc5aa2dd Mon Sep 17 00:00:00 2001 From: Sinan Nalkaya Date: Fri, 13 Mar 2026 11:42:49 +0100 Subject: [PATCH 04/11] Move default implementation of lookup_host_string to sys/net/connection. --- library/std/src/net/mod.rs | 4 ---- library/std/src/net/socket_addr.rs | 19 ------------------- library/std/src/sys/net/connection/mod.rs | 19 +++++++++++++++++++ library/std/src/sys/net/connection/motor.rs | 2 -- library/std/src/sys/net/connection/sgx.rs | 2 +- .../std/src/sys/net/connection/socket/mod.rs | 2 -- .../std/src/sys/net/connection/uefi/mod.rs | 2 -- .../std/src/sys/net/connection/unsupported.rs | 2 -- library/std/src/sys/net/connection/wasip1.rs | 2 -- .../std/src/sys/net/connection/xous/mod.rs | 2 -- 10 files changed, 20 insertions(+), 36 deletions(-) diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 5af3dc32c95d1..3e4447eb33f2a 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -70,7 +70,3 @@ pub enum Shutdown { #[stable(feature = "rust1", since = "1.0.0")] Both, } - -// allow(unused_imports): This function is only used on some targets -#[allow(unused_imports)] -pub(crate) use socket_addr::lookup_host_string; diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index fac570d72bccd..cae14e34e73e7 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -189,25 +189,6 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -// Default implementation, this shouldn't be called directly by the function's -// path in this module. Instead, use the platform-specific implementation -// (which may be this one). -// -// allow(dead_code): This function is only used on some targets -#[allow(dead_code)] -pub(crate) fn lookup_host_string(addr: &str) -> io::Result> { - // Split the string by ':' and convert the second part to u16... - let Some((host, port_str)) = addr.rsplit_once(':') else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); - }; - let Ok(port) = port_str.parse::() else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); - }; - - // ... and make the system look up the host. - crate::sys::net::lookup_host(host, port) -} - #[stable(feature = "rust1", since = "1.0.0")] impl ToSocketAddrs for (&str, u16) { type Iter = vec::IntoIter; diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs index 2f064914a8317..84b53fd375c93 100644 --- a/library/std/src/sys/net/connection/mod.rs +++ b/library/std/src/sys/net/connection/mod.rs @@ -59,3 +59,22 @@ where None => Err(Error::NO_ADDRESSES), } } + +// Default implementation, may be overridden by platform-specific implementations. +#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))] +pub(crate) fn lookup_host_string( + addr: &str, +) -> crate::io::Result> { + use crate::io; + + // Split the string by ':' and convert the second part to u16... + let Some((host, port_str)) = addr.rsplit_once(':') else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); + }; + let Ok(port) = port_str.parse::() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); + }; + + // ... and make the system look up the host. + crate::sys::net::lookup_host(host, port) +} diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index 8555291adc3c4..79a528792106c 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -395,8 +395,6 @@ pub struct LookupHost { addresses: alloc::collections::VecDeque, } -pub(crate) use crate::net::lookup_host_string; - pub fn lookup_host(host: &str, port: u16) -> io::Result { let (_port, addresses) = moto_rt::net::lookup_host(host, port).map_err(map_motor_error)?; Ok(LookupHost { addresses }) diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 6ecb0a69a027b..a637a83663fc3 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -506,7 +506,7 @@ impl Iterator for LookupHost { } } -pub fn lookup_host_string(addr: impl Into) -> io::Result { +pub(crate) fn lookup_host_string(addr: impl Into) -> io::Result { Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host: addr.into() })) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 54595b5a895e0..256b99dfa9874 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -340,8 +340,6 @@ impl Drop for LookupHost { } } -pub(crate) use crate::net::lookup_host_string; - pub fn lookup_host(host: &str, port: u16) -> io::Result { init(); run_with_cstr(host.as_bytes(), &|c_host| { diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 299460572a87d..107a3e23733de 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -345,8 +345,6 @@ impl Iterator for LookupHost { } } -pub(crate) use crate::net::lookup_host_string; - pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index 7089e17a7beda..fb18e8dec557c 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -311,8 +311,6 @@ impl Iterator for LookupHost { } } -pub(crate) use crate::net::lookup_host_string; - pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 1ef12e5ec4cf1..95a4ab2fbf0fd 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -483,8 +483,6 @@ impl Iterator for LookupHost { } } -pub(crate) use crate::net::lookup_host_string; - pub fn lookup_host(_host: &str, _port: u16) -> io::Result { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index f3fd83a10f2c6..0f77be5c3fac8 100644 --- a/library/std/src/sys/net/connection/xous/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -46,5 +46,3 @@ pub struct GetAddress { } pub use dns::lookup_host; - -pub(crate) use crate::net::lookup_host_string; From 83a7a4ad74ad6801706bbbc6c4e4d27b2e8dcfd6 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Sun, 8 Mar 2026 00:37:26 -0500 Subject: [PATCH 05/11] Clarified docs in std::sync::RwLock and added a test that checks if we can reach max reader --- library/std/src/sync/poison/rwlock.rs | 2 +- library/std/tests/sync/rwlock.rs | 65 ++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index c01ee17eecda3..4cfd9d19df74a 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -16,7 +16,7 @@ use crate::sys::sync as sys; /// /// In comparison, a [`Mutex`] does not distinguish between readers or writers /// that acquire the lock, therefore blocking any threads waiting for the lock to -/// become available. An `RwLock` will allow any number of readers to acquire the +/// become available. An `RwLock` will allow multiple readers to acquire the /// lock as long as a writer is not holding the lock. /// /// The priority policy of the lock is dependent on the underlying operating diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index 392c45c8ba05d..d6287305481ea 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -7,7 +7,7 @@ use std::sync::{ Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError, }; -use std::{hint, mem, thread}; +use std::{hint, mem, thread, u32}; use rand::Rng; @@ -883,3 +883,66 @@ fn test_rwlock_with_mut() { assert_eq!(*rwlock.read(), 5); assert_eq!(result, 10); } + +// To note: there are (currently) four different implementations of Rwlock: +// - On Windows (but not Win 7), Linux, Android, FreeBSD, OpenBSD, DragonFly, +// Fuchsia, WASM, Hermit, and Motor OSs, it relies on rwlock/futex.rs, which has +// a max reader of 1 << 30 - 2 (or 1073741822). A "too many active reader" error +// is displayed after it exceeds the max number of readers. +// - On Unix, Win 7, Fortranix (target env of sgx), Xous, and TeeOS, it leans +// on rwlock/queue.rs, which uses a linked list under the hood stored on the stack +// to hold a queue of waiters. Assuming no stack overflow, the max number of readers +// that can be queued up at one time is limited to usize::MAX - (1 << 4) as the first +// four bits are reserved for LOCKED, QUEUED, QUEUE_LOCKED, and DOWNGRADED flags. Any +// pending readers after that max count, parks the thread and tries to acquire a read lock +// again when the thread wakes up. +// - On SolidASP3, it leans on rwlock/solid.rs, which utilizes rwl_loc_rdl, so the max +// number of readers depends on the internal implementation of rwl_loc_rdl. +// - Every other platforms utilizes rwlock/no_threads.rs, which has a max reader of +// isize::MAX. An arithmetic overflow error occurs if it exceeds the max reader count. +#[test] +fn test_rwlock_max_readers() { + let mut read_lock_ctr: u32 = 0; + let rwlock: RwLock = RwLock::new(0); + + const MAX_READERS: u32 = cfg_select! { + miri => 100, + any( + all(target_os = "windows", not(target_vendor = "win7")), + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "fuchsia", + all(target_family = "wasm", target_feature = "atomics"), + target_os = "hermit", + target_os = "motor", + ) => { + (1 << 30) - 2 + }, + any( + target_family = "unix", + all(target_os = "windows", target_vendor = "win7"), + all(target_vendor = "fortanix", target_env = "sgx"), + target_os = "xous", + target_os = "teeos", + ) => { + u32::MAX + }, + target_os = "solid_asp3" => { + (1 << 30) + }, + _ => { + u32::MAX + } + }; + + while read_lock_ctr < MAX_READERS { + let lock = rwlock.read(); + mem::forget(lock); + read_lock_ctr += 1; + } + + assert_eq!(read_lock_ctr, MAX_READERS); +} From 4b0caec04bb1dd73373b0660841c14b72507bede Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Sat, 28 Mar 2026 19:19:10 -0400 Subject: [PATCH 06/11] Panic/return false on overflow in no_threads read/try_read impl --- library/std/src/sys/sync/rwlock/no_threads.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/std/src/sys/sync/rwlock/no_threads.rs b/library/std/src/sys/sync/rwlock/no_threads.rs index 6b919bde80bb5..81202ba8a3ae8 100644 --- a/library/std/src/sys/sync/rwlock/no_threads.rs +++ b/library/std/src/sys/sync/rwlock/no_threads.rs @@ -17,6 +17,10 @@ impl RwLock { #[inline] pub fn read(&self) { let m = self.mode.get(); + + // Check for overflow. + assert!(m == isize::MAX, "too many active read locks on RwLock"); + if m >= 0 { self.mode.set(m + 1); } else { @@ -28,6 +32,9 @@ impl RwLock { pub fn try_read(&self) -> bool { let m = self.mode.get(); if m >= 0 { + if m == isize::MAX { + return false; + } self.mode.set(m + 1); true } else { From 1d7d435d08c59f917b3fc266a2844fe002cfa7d7 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Sun, 29 Mar 2026 20:06:25 -0400 Subject: [PATCH 07/11] Panic on unlocking a non-read locked RwLock + provided better error msgs --- library/std/src/sys/sync/rwlock/no_threads.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/sync/rwlock/no_threads.rs b/library/std/src/sys/sync/rwlock/no_threads.rs index 81202ba8a3ae8..6de5577504c43 100644 --- a/library/std/src/sys/sync/rwlock/no_threads.rs +++ b/library/std/src/sys/sync/rwlock/no_threads.rs @@ -17,12 +17,8 @@ impl RwLock { #[inline] pub fn read(&self) { let m = self.mode.get(); - - // Check for overflow. - assert!(m == isize::MAX, "too many active read locks on RwLock"); - if m >= 0 { - self.mode.set(m + 1); + self.mode.set(m.checked_add(1).expect("rwlock overflowed read locks")); } else { rtabort!("rwlock locked for writing"); } @@ -63,16 +59,19 @@ impl RwLock { #[inline] pub unsafe fn read_unlock(&self) { - self.mode.set(self.mode.get() - 1); + assert!( + self.mode.replace(self.mode.get() - 1) > 0, + "rwlock has not been locked for reading" + ); } #[inline] pub unsafe fn write_unlock(&self) { - assert_eq!(self.mode.replace(0), -1); + assert_eq!(self.mode.replace(0), -1, "rwlock has not been locked for writing"); } #[inline] pub unsafe fn downgrade(&self) { - assert_eq!(self.mode.replace(1), -1); + assert_eq!(self.mode.replace(1), -1, "rwlock has not been locked for writing"); } } From a374727025868805e215680dc7ce83cf929bdebe Mon Sep 17 00:00:00 2001 From: lms0806 Date: Fri, 27 Mar 2026 19:09:25 +0900 Subject: [PATCH 08/11] resolve: issue 154452 solve --- library/alloc/src/boxed.rs | 9 +++++ .../alloc/src/collections/binary_heap/mod.rs | 8 ++-- library/alloc/src/collections/btree/map.rs | 6 +-- library/alloc/src/collections/btree/set.rs | 3 +- library/alloc/src/collections/linked_list.rs | 2 +- .../alloc/src/collections/vec_deque/mod.rs | 12 ++++-- library/alloc/src/rc.rs | 1 + library/alloc/src/vec/mod.rs | 3 +- library/std/src/collections/hash/map.rs | 33 +++++++++++++++- library/std/src/collections/hash/set.rs | 39 ++++++++++++++++++- 10 files changed, 97 insertions(+), 19 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ae16a8401552d..2c87d47246498 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -695,6 +695,15 @@ impl Box { /// does the same as [Box::into_pin]\([Box::new_in]\(x, alloc)). Consider using /// [`into_pin`](Box::into_pin) if you already have a `Box`, or if you want to /// construct a (pinned) `Box` in a different way than with [`Box::new_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::alloc::System; + /// + /// let x = Box::pin_in(1, System); + /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index d46b1972b5b00..34de563fe4f33 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -549,8 +549,8 @@ impl BinaryHeap { /// /// use std::alloc::System; /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new_in(System); - /// heap.push(4); + /// + /// let heap : BinaryHeap = BinaryHeap::new_in(System); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] @@ -573,8 +573,8 @@ impl BinaryHeap { /// /// use std::alloc::System; /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity_in(10, System); - /// heap.push(4); + /// + /// let heap: BinaryHeap = BinaryHeap::with_capacity_in(10, System); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e7a10d2220b05..efddd4ecde0f6 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -684,13 +684,11 @@ impl BTreeMap { /// ``` /// # #![feature(allocator_api)] /// # #![feature(btreemap_alloc)] + /// /// use std::collections::BTreeMap; /// use std::alloc::Global; /// - /// let mut map = BTreeMap::new_in(Global); - /// - /// // entries can now be inserted into the empty map - /// map.insert(1, "a"); + /// let map: BTreeMap = BTreeMap::new_in(Global); /// ``` #[unstable(feature = "btreemap_alloc", issue = "32838")] #[must_use] diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index af6f5c7d70177..b696e2323e577 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -355,10 +355,11 @@ impl BTreeSet { /// # #![allow(unused_mut)] /// # #![feature(allocator_api)] /// # #![feature(btreemap_alloc)] + /// /// use std::collections::BTreeSet; /// use std::alloc::Global; /// - /// let mut set: BTreeSet = BTreeSet::new_in(Global); + /// let set: BTreeSet = BTreeSet::new_in(Global); /// ``` #[unstable(feature = "btreemap_alloc", issue = "32838")] #[must_use] diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 674828b8e7ded..ade0f1a62c475 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -509,7 +509,7 @@ impl LinkedList { /// use std::alloc::System; /// use std::collections::LinkedList; /// - /// let list: LinkedList = LinkedList::new_in(System); + /// let list: LinkedList = LinkedList::new_in(System); /// ``` #[inline] #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 3185fd56d8c08..43a6df7ddf753 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -790,7 +790,7 @@ impl VecDeque { /// ``` /// use std::collections::VecDeque; /// - /// let deque: VecDeque = VecDeque::with_capacity(10); + /// let deque: VecDeque = VecDeque::with_capacity(10); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] @@ -830,9 +830,12 @@ impl VecDeque { /// # Examples /// /// ``` + /// # #![feature(allocator_api)] + /// /// use std::collections::VecDeque; + /// use std::alloc::Global; /// - /// let deque: VecDeque = VecDeque::new(); + /// let deque: VecDeque = VecDeque::new_in(Global); /// ``` #[inline] #[unstable(feature = "allocator_api", issue = "32838")] @@ -845,9 +848,12 @@ impl VecDeque { /// # Examples /// /// ``` + /// # #![feature(allocator_api)] + /// /// use std::collections::VecDeque; + /// use std::alloc::Global; /// - /// let deque: VecDeque = VecDeque::with_capacity(10); + /// let deque: VecDeque = VecDeque::with_capacity_in(10, Global); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque { diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index fdcb004a21683..43d4d349a427e 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -737,6 +737,7 @@ impl Rc { /// /// ``` /// #![feature(allocator_api)] + /// /// use std::rc::Rc; /// use std::alloc::System; /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7796f3b8b2fb3..4c76a886c5df3 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1054,8 +1054,7 @@ impl Vec { /// /// use std::alloc::System; /// - /// # #[allow(unused_mut)] - /// let mut vec: Vec = Vec::new_in(System); + /// let vec: Vec = Vec::new_in(System); /// ``` #[inline] #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index fe49660325e65..4192254f6c824 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -301,8 +301,11 @@ impl HashMap { /// # Examples /// /// ``` + /// # #![feature(allocator_api)] /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::new(); + /// use std::alloc::Global; + /// + /// let map: HashMap = HashMap::new_in(Global); /// ``` #[inline] #[must_use] @@ -321,8 +324,11 @@ impl HashMap { /// # Examples /// /// ``` + /// # #![feature(allocator_api)] /// use std::collections::HashMap; - /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); + /// use std::alloc::Global; + /// + /// let map: HashMap = HashMap::with_capacity_in(10, Global); /// ``` #[inline] #[must_use] @@ -410,6 +416,18 @@ impl HashMap { /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashMap` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::alloc::Global; + /// use std::collections::HashMap; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let map: HashMap = HashMap::with_hasher_in(s, Global); + /// ``` #[inline] #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] @@ -432,6 +450,17 @@ impl HashMap { /// The `hasher` passed should implement the [`BuildHasher`] trait for /// the `HashMap` to be useful, see its documentation for details. /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::alloc::Global; + /// use std::collections::HashMap; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let map: HashMap = HashMap::with_capacity_and_hasher_in(10, s, Global); + /// ``` #[inline] #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 0fe26848fe7bb..fadd62652c62d 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -176,6 +176,15 @@ impl HashSet { /// /// The hash set is initially created with a capacity of 0, so it will not allocate until it /// is first inserted into. + /// # Examples + /// + /// ``` + /// # #![feature(allocator_api)] + /// use std::alloc::Global; + /// use std::collections::HashSet; + /// + /// let set: HashSet = HashSet::new_in(Global); + /// ``` #[inline] #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] @@ -192,9 +201,11 @@ impl HashSet { /// # Examples /// /// ``` + /// # #![feature(allocator_api)] /// use std::collections::HashSet; - /// let set: HashSet = HashSet::with_capacity(10); - /// assert!(set.capacity() >= 10); + /// use std::alloc::Global; + /// + /// let set: HashSet = HashSet::with_capacity_in(10, Global); /// ``` #[inline] #[must_use] @@ -282,6 +293,18 @@ impl HashSet { /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// # #![feature(allocator_api)] + /// use std::alloc::Global; + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let set: HashSet = HashSet::with_hasher_in(s, Global); + /// ``` #[inline] #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] @@ -303,6 +326,18 @@ impl HashSet { /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// # #![feature(allocator_api)] + /// use std::alloc::Global; + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let set: HashSet = HashSet::with_capacity_and_hasher_in(10, s, Global); + /// ``` #[inline] #[must_use] #[unstable(feature = "allocator_api", issue = "32838")] From 6c467763047de340e4c40ed661df03a9fd4455c8 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Sun, 29 Mar 2026 22:20:45 +0300 Subject: [PATCH 09/11] Debug for vec::ExtractIf --- library/alloc/src/vec/extract_if.rs | 35 ++++++++++++++++------------- library/alloctests/tests/vec.rs | 18 +++++++++------ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index 35eb7549738df..a4c4c19682195 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -130,20 +130,25 @@ where A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let peek = if self.idx < self.end { - // This has to use pointer arithmetic as `self.vec[self.idx]` or - // `self.vec.get_unchecked(self.idx)` wouldn't work since we - // temporarily set the length of `self.vec` to zero. - // - // SAFETY: - // Since `self.idx` is smaller than `self.end` and `self.end` is - // smaller than `self.old_len`, `idx` is valid for indexing the - // buffer. Also, per the invariant of `self.idx`, this element - // has not been inspected/moved out yet. - Some(unsafe { &*self.vec.as_ptr().add(self.idx) }) - } else { - None - }; - f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive() + // We have to use pointer arithmetics here, + // because the length of `self.vec` is temporarily set to 0. + let start = self.vec.as_ptr(); + + // SAFETY: we always keep first `self.idx - self.del` elements valid. + let retained = unsafe { slice::from_raw_parts(start, self.idx - self.del) }; + + // SAFETY: we have not yet touched elements starting at `self.idx`. + let valid_tail = + unsafe { slice::from_raw_parts(start.add(self.idx), self.old_len - self.idx) }; + + // SAFETY: `end - idx <= old_len - idx`, because `end <= old_len`. Also `idx <= end` by invariant. + let (remainder, skipped_tail) = + unsafe { valid_tail.split_at_unchecked(self.end - self.idx) }; + + f.debug_struct("ExtractIf") + .field("retained", &retained) + .field("remainder", &remainder) + .field("skipped_tail", &skipped_tail) + .finish_non_exhaustive() } } diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index db7da0db47d74..d85d2e44cd2ba 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -1651,13 +1651,17 @@ fn extract_if_unconsumed() { #[test] fn extract_if_debug() { - let mut vec = vec![1, 2]; - let mut drain = vec.extract_if(.., |&mut x| x % 2 != 0); - assert!(format!("{drain:?}").contains("Some(1)")); - drain.next(); - assert!(format!("{drain:?}").contains("Some(2)")); - drain.next(); - assert!(format!("{drain:?}").contains("None")); + let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8]; + let mut drain = vec.extract_if(1..5, |&mut x| x % 2 != 0); + assert_eq!( + format!("{drain:?}"), + "ExtractIf { retained: [1], remainder: [2, 3, 4, 5], skipped_tail: [6, 7, 8], .. }" + ); + drain.next().unwrap(); + assert_eq!( + format!("{drain:?}"), + "ExtractIf { retained: [1, 2], remainder: [4, 5], skipped_tail: [6, 7, 8], .. }" + ); } #[test] From f1d240c040e4df7db2504938141e150e2c92a7d9 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 3 Apr 2026 21:28:18 -0700 Subject: [PATCH 10/11] rustdoc-search: match path components on words Since the length of a path is treated as sorting criteria, and every path that contains the query without exactly matching it must be longer, exact matches will always sort first if they exist. --- src/librustdoc/html/static/js/search.js | 53 +++++++++----- tests/rustdoc-js-std/path-maxeditdistance.js | 15 +++- tests/rustdoc-js/path-substring.js | 75 ++++++++++++++++++++ tests/rustdoc-js/path-substring.rs | 7 ++ 4 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 tests/rustdoc-js/path-substring.js create mode 100644 tests/rustdoc-js/path-substring.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 85a76054a12dd..e8343b2e21c81 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -2799,6 +2799,8 @@ class DocSearch { result_list.sort((aaa, bbb) => { const aai = aaa.item; const bbi = bbb.item; + const ap = aai.modulePath !== undefined ? aai.modulePath : ""; + const bp = bbi.modulePath !== undefined ? bbi.modulePath : ""; /** @type {number} */ let a; /** @type {number} */ @@ -2829,14 +2831,25 @@ class DocSearch { if (a !== b) { return a - b; } - } - // Sort by distance in the path part, if specified - // (less changes required to match means higher rankings) - a = Number(aaa.path_dist); - b = Number(bbb.path_dist); - if (a !== b) { - return a - b; + if (parsedQuery.elems[0] && + parsedQuery.elems[0].pathWithoutLast.length !== 0 + ) { + // Sort by distance in the path part, if specified + // (less changes required to match means higher rankings) + a = Number(aaa.path_dist); + b = Number(bbb.path_dist); + if (a !== b) { + return a - b; + } + + // sort by path (longer goes later) + a = ap.length + (aai.parent ? aai.parent.name.length + 2 : 0); + b = bp.length + (bbi.parent ? bbi.parent.name.length + 2 : 0); + if (a !== b) { + return a - b; + } + } } // (later literal occurrence, if any, goes later) @@ -2890,8 +2903,8 @@ class DocSearch { } // sort by item name (lexicographically larger goes later) - let aw = aai.normalizedName; - let bw = bbi.normalizedName; + const aw = aai.normalizedName; + const bw = bbi.normalizedName; if (aw !== bw) { return (aw > bw ? +1 : -1); } @@ -2914,12 +2927,8 @@ class DocSearch { } // sort by path (lexicographically larger goes later) - const ap = aai.modulePath; - const bp = bbi.modulePath; - aw = ap === undefined ? "" : ap; - bw = bp === undefined ? "" : bp; - if (aw !== bw) { - return (aw > bw ? +1 : -1); + if (ap !== bp) { + return (ap > bp ? +1 : -1); } // que sera, sera @@ -3848,13 +3857,19 @@ class DocSearch { let dist_total = 0; for (let x = 0; x < clength; ++x) { const [p, c] = [path[i + x], contains[x]]; + const indexOf = p.indexOf(c); if (parsedQuery.literalSearch && p !== c) { continue pathiter; - } else if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && - p.indexOf(c) !== -1 - ) { + } else if (indexOf !== -1) { // discount distance on substring match - dist_total += Math.floor((p.length - c.length) / 3); + // if component is surrounded by underscores or edges, + // count the distance as zero + if ( + (indexOf !== 0 && p[indexOf - 1] !== "_") || + (indexOf + c.length !== p.length && p[indexOf + c.length] !== "_") + ) { + dist_total += Math.floor((p.length - c.length) / 3); + } } else { const dist = editDistance(p, c, maxPathEditDistance); if (dist > maxPathEditDistance) { diff --git a/tests/rustdoc-js-std/path-maxeditdistance.js b/tests/rustdoc-js-std/path-maxeditdistance.js index b22a506eee5fa..96d58d239865c 100644 --- a/tests/rustdoc-js-std/path-maxeditdistance.js +++ b/tests/rustdoc-js-std/path-maxeditdistance.js @@ -10,15 +10,24 @@ const EXPECTED = [ query: 'vec::iter', others: [ // std::net::ToSocketAttrs::iter should not show up here + { 'path': 'std::collections::vec_deque', 'name': 'Iter' }, { 'path': 'std::collections::VecDeque', 'name': 'iter' }, + { 'path': 'std::collections::vec_deque', 'name': 'IterMut' }, { 'path': 'std::collections::VecDeque', 'name': 'iter_mut' }, - { 'path': 'std::vec::Vec', 'name': 'from_iter' }, { 'path': 'std::vec', 'name': 'IntoIter' }, + { 'path': 'std::vec::Vec', 'name': 'from_iter' }, { 'path': 'std::vec::Vec', 'name': 'into_iter' }, - { 'path': 'std::vec::ExtractIf', 'name': 'into_iter' }, { 'path': 'std::vec::Drain', 'name': 'into_iter' }, - { 'path': 'std::vec::IntoIter', 'name': 'into_iter' }, { 'path': 'std::vec::Splice', 'name': 'into_iter' }, + { 'path': 'std::vec::IntoIter', 'name': 'into_iter' }, + { 'path': 'std::vec::ExtractIf', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque', 'name': 'IntoIter' }, + { 'path': 'std::collections::vec_deque::Iter', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque::Drain', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque::Splice', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque::IterMut', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque::IntoIter', 'name': 'into_iter' }, + { 'path': 'std::collections::vec_deque::ExtractIf', 'name': 'into_iter' }, { 'path': 'std::collections::VecDeque', 'name': 'from_iter' }, { 'path': 'std::collections::VecDeque', 'name': 'into_iter' }, ], diff --git a/tests/rustdoc-js/path-substring.js b/tests/rustdoc-js/path-substring.js new file mode 100644 index 0000000000000..86529e4f100c9 --- /dev/null +++ b/tests/rustdoc-js/path-substring.js @@ -0,0 +1,75 @@ +// exact-check +// ignore-tidy-linelength +const EXPECTED = [ + // should match (substring) + { + 'query': 'struct:now::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + { + 'query': 'struct:is::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + { + 'query': 'struct:is_the::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + { + 'query': 'struct:the::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + { + 'query': 'struct:their::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + // should not match + { + 'query': 'struct:ood::Country', + 'others': [], + }, + { + 'query': 'struct:goo::Country', + 'others': [], + }, + { + 'query': 'struct:he::Country', + 'others': [], + }, + { + 'query': 'struct:heir::Country', + 'others': [], + }, + { + 'query': 'struct:hei::Country', + 'others': [], + }, + { + 'query': 'struct:no::Country', + 'others': [], + }, + // should match (edit distance) + { + 'query': 'struct:nowisthetimeforallgoodmentocometotheaidoftheir::Country', + 'others': [ + { 'path': 'x::nowisthetimeforallgoodmentocometotheaidoftheir', 'name': 'Country' }, + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + ], + }, + { + 'query': 'struct:now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their::Country', + 'others': [ + { 'path': 'x::now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their', 'name': 'Country' }, + { 'path': 'x::nowisthetimeforallgoodmentocometotheaidoftheir', 'name': 'Country' }, + ], + }, +]; diff --git a/tests/rustdoc-js/path-substring.rs b/tests/rustdoc-js/path-substring.rs new file mode 100644 index 0000000000000..0d093d84c1c97 --- /dev/null +++ b/tests/rustdoc-js/path-substring.rs @@ -0,0 +1,7 @@ +#![crate_name = "x"] +pub mod now_is_the_time_for_all_good_men_to_come_to_the_aid_of_their { + pub struct Country; +} +pub mod nowisthetimeforallgoodmentocometotheaidoftheir { + pub struct Country; +} From a1feab16381bc476b9e3592a4ed9af5a5ac39b1d Mon Sep 17 00:00:00 2001 From: malezjaa Date: Wed, 18 Mar 2026 18:46:55 +0100 Subject: [PATCH 11/11] use libm for acosh and asinh --- library/coretests/tests/num/floats.rs | 30 +++++++++++++++++++++++++-- library/std/src/num/f128.rs | 10 ++------- library/std/src/num/f16.rs | 10 ++------- library/std/src/num/f32.rs | 10 ++------- library/std/src/num/f64.rs | 10 ++------- library/std/src/sys/cmath.rs | 16 ++++++++++++++ src/tools/miri/src/shims/math.rs | 8 +++++++ src/tools/miri/tests/pass/float.rs | 4 ++-- 8 files changed, 62 insertions(+), 36 deletions(-) diff --git a/library/coretests/tests/num/floats.rs b/library/coretests/tests/num/floats.rs index d87f850944503..a388e75469c8c 100644 --- a/library/coretests/tests/num/floats.rs +++ b/library/coretests/tests/num/floats.rs @@ -2,6 +2,9 @@ use std::hint::black_box; use std::num::FpCategory as Fp; use std::ops::{Add, Div, Mul, Rem, Sub}; +/// i586 has issues with floating point precision. +const I586: bool = cfg!(target_arch = "x86") && cfg!(not(target_feature = "sse2")); + pub(crate) trait TestableFloat: Sized { const BITS: u32; /// Unsigned int with the same size, for converting to/from bits. @@ -59,6 +62,7 @@ pub(crate) trait TestableFloat: Sized { const NEG_MUL_ADD_RESULT: Self; /// Reciprocal of the maximum val const MAX_RECIP: Self; + const ASINH_ACOSH_MAX: Self; } impl TestableFloat for f16 { @@ -103,6 +107,7 @@ impl TestableFloat for f16 { const MUL_ADD_RESULT: Self = 62.031; const NEG_MUL_ADD_RESULT: Self = 48.625; const MAX_RECIP: Self = 1.526624e-5; + const ASINH_ACOSH_MAX: Self = 11.781; } impl TestableFloat for f32 { @@ -120,8 +125,20 @@ impl TestableFloat for f32 { const LOG_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; const LOG2_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; const LOG10_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; - const ASINH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; - const ACOSH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; + const ASINH_APPROX: Self = if cfg!(miri) { + 1e-3 + } else if I586 { + 1e-5 + } else { + Self::APPROX + }; + const ACOSH_APPROX: Self = if cfg!(miri) { + 1e-3 + } else if I586 { + 1e-5 + } else { + Self::APPROX + }; const ATANH_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; const GAMMA_APPROX: Self = if cfg!(miri) { 1e-3 } else { Self::APPROX }; const GAMMA_APPROX_LOOSE: Self = if cfg!(miri) { 1e-2 } else { 1e-4 }; @@ -149,6 +166,7 @@ impl TestableFloat for f32 { const MUL_ADD_RESULT: Self = 62.05; const NEG_MUL_ADD_RESULT: Self = 48.65; const MAX_RECIP: Self = 2.938736e-39; + const ASINH_ACOSH_MAX: Self = 89.4159851; } impl TestableFloat for f64 { @@ -180,6 +198,7 @@ impl TestableFloat for f64 { const MUL_ADD_RESULT: Self = 62.050000000000004; const NEG_MUL_ADD_RESULT: Self = 48.650000000000006; const MAX_RECIP: Self = 5.562684646268003e-309; + const ASINH_ACOSH_MAX: Self = 710.47586007394398; } impl TestableFloat for f128 { @@ -221,6 +240,7 @@ impl TestableFloat for f128 { const MUL_ADD_RESULT: Self = 62.0500000000000000000000000000000037; const NEG_MUL_ADD_RESULT: Self = 48.6500000000000000000000000000000049; const MAX_RECIP: Self = 8.40525785778023376565669454330438228902076605e-4933; + const ASINH_ACOSH_MAX: Self = 11357.216553474703894801348310092223; } /// Determine the tolerance for values of the argument type. @@ -1705,6 +1725,9 @@ float_test! { assert_approx_eq!(flt(-200.0).asinh(), -5.991470797049389, Float::ASINH_APPROX); + // issue 153878: large values were rounding to infinity + assert_approx_eq!(Float::MAX.asinh(), Float::ASINH_ACOSH_MAX, Float::ASINH_APPROX); + #[allow(overflowing_literals)] if Float::MAX > flt(66000.0) { // regression test for the catastrophic cancellation fixed in 72486 @@ -1733,6 +1756,9 @@ float_test! { assert_approx_eq!(flt(2.0).acosh(), 1.31695789692481670862504634730796844, Float::ACOSH_APPROX); assert_approx_eq!(flt(3.0).acosh(), 1.76274717403908605046521864995958461, Float::ACOSH_APPROX); + // issue 153878: large values were rounding to infinity + assert_approx_eq!(Float::MAX.acosh(), Float::ASINH_ACOSH_MAX, Float::ACOSH_APPROX); + #[allow(overflowing_literals)] if Float::MAX > flt(66000.0) { // test for low accuracy from issue 104548 diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index 2c8898a6aa86a..d7c7a82674bf0 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -867,9 +867,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn asinh(self) -> f128 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + cmath::asinhf128(self) } /// Inverse hyperbolic cosine function. @@ -900,11 +898,7 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn acosh(self) -> f128 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } + cmath::acoshf128(self) } /// Inverse hyperbolic tangent function. diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 7ca266c8a5f60..ef610eacb05d7 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -832,9 +832,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn asinh(self) -> f16 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + cmath::asinhf(self as f32) as f16 } /// Inverse hyperbolic cosine function. @@ -865,11 +863,7 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn acosh(self) -> f16 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } + cmath::acoshf(self as f32) as f16 } /// Inverse hyperbolic tangent function. diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 77e6824784605..771a0cae6dcd8 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -1091,9 +1091,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f32 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + cmath::asinhf(self) } /// Inverse hyperbolic cosine function. @@ -1119,11 +1117,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f32 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } + cmath::acoshf(self) } /// Inverse hyperbolic tangent function. diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index e0b9948a924db..59ef39a382b27 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -1091,9 +1091,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn asinh(self) -> f64 { - let ax = self.abs(); - let ix = 1.0 / ax; - (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + cmath::asinh(self) } /// Inverse hyperbolic cosine function. @@ -1119,11 +1117,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn acosh(self) -> f64 { - if self < 1.0 { - Self::NAN - } else { - (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() - } + cmath::acosh(self) } /// Inverse hyperbolic tangent function. diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 1592218ead8b0..2104087141d1a 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -4,7 +4,9 @@ // or by `compiler-builtins` on unsupported platforms. unsafe extern "C" { pub safe fn acos(n: f64) -> f64; + pub safe fn acosh(n: f64) -> f64; pub safe fn asin(n: f64) -> f64; + pub safe fn asinh(n: f64) -> f64; pub safe fn atan(n: f64) -> f64; pub safe fn atan2(a: f64, b: f64) -> f64; pub safe fn cosh(n: f64) -> f64; @@ -30,7 +32,9 @@ unsafe extern "C" { pub safe fn erfcf(n: f32) -> f32; pub safe fn acosf128(n: f128) -> f128; + pub safe fn acoshf128(n: f128) -> f128; pub safe fn asinf128(n: f128) -> f128; + pub safe fn asinhf128(n: f128) -> f128; pub safe fn atanf128(n: f128) -> f128; pub safe fn atan2f128(a: f128, b: f128) -> f128; pub safe fn cbrtf128(n: f128) -> f128; @@ -57,6 +61,16 @@ cfg_select! { f64::acos(n as f64) as f32 } + #[inline] + pub fn acoshf(n: f32) -> f32 { + f64::acosh(n as f64) as f32 + } + + #[inline] + pub fn asinhf(n: f32) -> f32 { + f64::asinh(n as f64) as f32 + } + #[inline] pub fn asinf(n: f32) -> f32 { f64::asin(n as f64) as f32 @@ -95,7 +109,9 @@ cfg_select! { _ => { unsafe extern "C" { pub safe fn acosf(n: f32) -> f32; + pub safe fn acoshf(n: f32) -> f32; pub safe fn asinf(n: f32) -> f32; + pub safe fn asinhf(n: f32) -> f32; pub safe fn atan2f(a: f32, b: f32) -> f32; pub safe fn atanf(n: f32) -> f32; pub safe fn coshf(n: f32) -> f32; diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs index ef185aa2a3e9f..593e4883cc08a 100644 --- a/src/tools/miri/src/shims/math.rs +++ b/src/tools/miri/src/shims/math.rs @@ -30,6 +30,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "acosf" | "asinf" | "atanf" + | "acoshf" + | "asinhf" | "log1pf" | "expm1f" | "tgammaf" @@ -52,6 +54,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "acosf" => f_host.acos(), "asinf" => f_host.asin(), "atanf" => f_host.atan(), + "acoshf" => f_host.acosh(), + "asinhf" => f_host.asinh(), "log1pf" => f_host.ln_1p(), "expm1f" => f_host.exp_m1(), "tgammaf" => f_host.gamma(), @@ -113,6 +117,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "acos" | "asin" | "atan" + | "acosh" + | "asinh" | "log1p" | "expm1" | "tgamma" @@ -135,6 +141,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "acos" => f_host.acos(), "asin" => f_host.asin(), "atan" => f_host.atan(), + "acosh" => f_host.acosh(), + "asinh" => f_host.asinh(), "log1p" => f_host.ln_1p(), "expm1" => f_host.exp_m1(), "tgamma" => f_host.gamma(), diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 0b5a71a45e2b8..93f2bbe35a257 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -1607,9 +1607,9 @@ fn test_non_determinism() { check_nondet(|| 1.0f32.sinh()); check_nondet(|| 1.0f32.cosh()); check_nondet(|| 1.0f32.tanh()); + check_nondet(|| 1.0f32.asinh()); + check_nondet(|| 2.0f32.acosh()); } - check_nondet(|| 1.0f32.asinh()); - check_nondet(|| 2.0f32.acosh()); check_nondet(|| 0.5f32.atanh()); check_nondet(|| 5.0f32.gamma()); check_nondet(|| 5.0f32.ln_gamma());