diff options
Diffstat (limited to 'library/std')
96 files changed, 1847 insertions, 585 deletions
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index e022c2d14..33c9c6e63 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -18,21 +18,19 @@ panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true } -compiler_builtins = { version = "0.1.95" } +compiler_builtins = { version = "0.1.100" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -addr2line = { version = "0.21.0", optional = true, default-features = false } rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } -miniz_oxide = { version = "0.7.0", optional = true, default-features = false, public = false } -[dependencies.object] -version = "0.32.0" -optional = true -default-features = false -features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] +miniz_oxide = { version = "0.7.0", optional = true, default-features = false } +addr2line = { version = "0.21.0", optional = true, default-features = false } +object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -45,9 +43,9 @@ dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.3.0", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.3.2", features = ['rustc-dep-of-std'], public = true } -[target.wasm32-wasi.dependencies] +[target.'cfg(target_os = "wasi")'.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } [features] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ec774e62d..1eae7fa6a 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -336,7 +336,6 @@ fn default_alloc_error_hook(layout: Layout) { static __rust_alloc_error_handler_should_panic: u8; } - #[allow(unused_unsafe)] if unsafe { __rust_alloc_error_handler_should_panic != 0 } { panic!("memory allocation of {} bytes failed", layout.size()); } else { diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 7543ffadd..e7110aebd 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -89,12 +89,12 @@ mod tests; // a backtrace or actually symbolizing it. use crate::backtrace_rs::{self, BytesOrWideString}; -use crate::cell::UnsafeCell; use crate::env; use crate::ffi::c_void; use crate::fmt; +use crate::panic::UnwindSafe; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use crate::sync::Once; +use crate::sync::LazyLock; use crate::sys_common::backtrace::{lock, output_filename}; use crate::vec::Vec; @@ -133,12 +133,11 @@ pub enum BacktraceStatus { enum Inner { Unsupported, Disabled, - Captured(LazilyResolvedCapture), + Captured(LazyLock<Capture, LazyResolve>), } struct Capture { actual_start: usize, - resolved: bool, frames: Vec<BacktraceFrame>, } @@ -179,7 +178,7 @@ impl fmt::Debug for Backtrace { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("<unsupported>"), Inner::Disabled => return fmt.write_str("<disabled>"), - Inner::Captured(c) => c.force(), + Inner::Captured(c) => &**c, }; let frames = &capture.frames[capture.actual_start..]; @@ -347,11 +346,10 @@ impl Backtrace { let inner = if frames.is_empty() { Inner::Unsupported } else { - Inner::Captured(LazilyResolvedCapture::new(Capture { + Inner::Captured(LazyLock::new(lazy_resolve(Capture { actual_start: actual_start.unwrap_or(0), frames, - resolved: false, - })) + }))) }; Backtrace { inner } @@ -376,7 +374,7 @@ impl<'a> Backtrace { #[must_use] #[unstable(feature = "backtrace_frames", issue = "79676")] pub fn frames(&'a self) -> &'a [BacktraceFrame] { - if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] } + if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] } } } @@ -386,7 +384,7 @@ impl fmt::Display for Backtrace { let capture = match &self.inner { Inner::Unsupported => return fmt.write_str("unsupported backtrace"), Inner::Disabled => return fmt.write_str("disabled backtrace"), - Inner::Captured(c) => c.force(), + Inner::Captured(c) => &**c, }; let full = fmt.alternate(); @@ -430,46 +428,15 @@ impl fmt::Display for Backtrace { } } -struct LazilyResolvedCapture { - sync: Once, - capture: UnsafeCell<Capture>, -} - -impl LazilyResolvedCapture { - fn new(capture: Capture) -> Self { - LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) } - } - - fn force(&self) -> &Capture { - self.sync.call_once(|| { - // SAFETY: This exclusive reference can't overlap with any others - // `Once` guarantees callers will block until this closure returns - // `Once` also guarantees only a single caller will enter this closure - unsafe { &mut *self.capture.get() }.resolve(); - }); - - // SAFETY: This shared reference can't overlap with the exclusive reference above - unsafe { &*self.capture.get() } - } -} - -// SAFETY: Access to the inner value is synchronized using a thread-safe `Once` -// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too -unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {} - -impl Capture { - fn resolve(&mut self) { - // If we're already resolved, nothing to do! - if self.resolved { - return; - } - self.resolved = true; +type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe; +fn lazy_resolve(mut capture: Capture) -> LazyResolve { + move || { // Use the global backtrace lock to synchronize this as it's a // requirement of the `backtrace` crate, and then actually resolve // everything. let _lock = lock(); - for frame in self.frames.iter_mut() { + for frame in capture.frames.iter_mut() { let symbols = &mut frame.symbols; let frame = match &frame.frame { RawFrame::Actual(frame) => frame, @@ -490,6 +457,8 @@ impl Capture { }); } } + + capture } } diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs index 4dfbf88e8..73543a3af 100644 --- a/library/std/src/backtrace/tests.rs +++ b/library/std/src/backtrace/tests.rs @@ -1,4 +1,5 @@ use super::*; +use crate::panic::{RefUnwindSafe, UnwindSafe}; fn generate_fake_frames() -> Vec<BacktraceFrame> { vec![ @@ -43,9 +44,8 @@ fn generate_fake_frames() -> Vec<BacktraceFrame> { #[test] fn test_debug() { let backtrace = Backtrace { - inner: Inner::Captured(LazilyResolvedCapture::new(Capture { + inner: Inner::Captured(LazyLock::preinit(Capture { actual_start: 1, - resolved: true, frames: generate_fake_frames(), })), }; @@ -66,9 +66,8 @@ fn test_debug() { #[test] fn test_frames() { let backtrace = Backtrace { - inner: Inner::Captured(LazilyResolvedCapture::new(Capture { + inner: Inner::Captured(LazyLock::preinit(Capture { actual_start: 1, - resolved: true, frames: generate_fake_frames(), })), }; @@ -93,3 +92,9 @@ fn test_frames() { assert!(iter.all(|(f, e)| format!("{f:#?}") == *e)); } + +#[test] +fn backtrace_unwind_safe() { + fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {} + assert_unwind_safe::<Backtrace>(); +} diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index a083b6560..be173a7ac 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -49,12 +49,14 @@ use crate::sys; /// ``` /// /// In other words, if two keys are equal, their hashes must be equal. +/// Violating this property is a logic error. /// -/// It is a logic error for a key to be modified in such a way that the key's +/// It is also a logic error for a key to be modified in such a way that the key's /// hash, as determined by the [`Hash`] trait, or its equality, as determined by /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will +/// +/// The behavior resulting from either logic error is not specified, but will /// be encapsulated to the `HashMap` that observed the logic error and not /// result in undefined behavior. This could include panics, incorrect results, /// aborts, memory leaks, and non-termination. diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index ec59634df..6d85b26af 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -24,13 +24,14 @@ use super::map::{map_try_reserve_error, RandomState}; /// ``` /// /// In other words, if two keys are equal, their hashes must be equal. +/// Violating this property is a logic error. /// -/// -/// It is a logic error for a key to be modified in such a way that the key's +/// It is also a logic error for a key to be modified in such a way that the key's /// hash, as determined by the [`Hash`] trait, or its equality, as determined by /// the [`Eq`] trait, changes while it is in the map. This is normally only /// possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// The behavior resulting from such a logic error is not specified, but will +/// +/// The behavior resulting from either logic error is not specified, but will /// be encapsulated to the `HashSet` that observed the logic error and not /// result in undefined behavior. This could include panics, incorrect results, /// aborts, memory leaks, and non-termination. @@ -65,8 +66,8 @@ use super::map::{map_try_reserve_error, RandomState}; /// ``` /// /// The easiest way to use `HashSet` with a custom type is to derive -/// [`Eq`] and [`Hash`]. We must also derive [`PartialEq`], this will in the -/// future be implied by [`Eq`]. +/// [`Eq`] and [`Hash`]. We must also derive [`PartialEq`], +/// which is required if [`Eq`] is derived. /// /// ``` /// use std::collections::HashSet; diff --git a/library/std/src/env.rs b/library/std/src/env.rs index d372fa640..f67f6034d 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -178,7 +178,8 @@ impl Iterator for Vars { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Vars { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Vars").finish_non_exhaustive() + let Self { inner: VarsOs { inner } } = self; + f.debug_struct("Vars").field("inner", &inner.str_debug()).finish() } } @@ -196,7 +197,8 @@ impl Iterator for VarsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for VarsOs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("VarOs").finish_non_exhaustive() + let Self { inner } = self; + f.debug_struct("VarsOs").field("inner", inner).finish() } } @@ -829,7 +831,8 @@ impl DoubleEndedIterator for Args { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Args").field("inner", &self.inner.inner).finish() + let Self { inner: ArgsOs { inner } } = self; + f.debug_struct("Args").field("inner", inner).finish() } } @@ -870,7 +873,8 @@ impl DoubleEndedIterator for ArgsOs { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for ArgsOs { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ArgsOs").field("inner", &self.inner).finish() + let Self { inner } = self; + f.debug_struct("ArgsOs").field("inner", inner).finish() } } @@ -890,6 +894,7 @@ pub mod consts { /// - aarch64 /// - loongarch64 /// - m68k + /// - csky /// - mips /// - mips64 /// - powerpc diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs index 94cace03a..558692295 100644 --- a/library/std/src/env/tests.rs +++ b/library/std/src/env/tests.rs @@ -95,8 +95,28 @@ fn args_debug() { format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()), format!("{:?}", args()) ); +} + +#[test] +fn args_os_debug() { assert_eq!( format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()), format!("{:?}", args_os()) ); } + +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()), + format!("{:?}", vars()) + ); +} + +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()), + format!("{:?}", vars_os()) + ); +} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 05f8fd8de..375ff2d24 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -9,6 +9,8 @@ use crate::fmt::{self, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; +#[unstable(feature = "error_generic_member_access", issue = "99301")] +pub use core::error::{request_ref, request_value, Request}; mod private { // This is a hack to prevent `type_id` from being overridden by `Error` @@ -121,7 +123,8 @@ mod private { /// This example produces the following output: /// /// ```console -/// thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here!', src/error.rs:34:40 +/// thread 'main' panicked at src/error.rs:34:40: +/// called `Result::unwrap()` on an `Err` value: SuperError is here!: SuperErrorSideKick is here! /// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace /// ``` /// @@ -370,11 +373,10 @@ impl<E> Report<E> { /// /// ```rust /// #![feature(error_reporter)] - /// #![feature(provide_any)] /// #![feature(error_generic_member_access)] /// # use std::error::Error; /// # use std::fmt; - /// use std::any::Demand; + /// use std::error::Request; /// use std::error::Report; /// use std::backtrace::Backtrace; /// @@ -404,8 +406,8 @@ impl<E> Report<E> { /// } /// /// impl Error for SuperErrorSideKick { - /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { - /// demand.provide_ref::<Backtrace>(&self.backtrace); + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// request.provide_ref::<Backtrace>(&self.backtrace); /// } /// } /// @@ -458,11 +460,11 @@ where fn backtrace(&self) -> Option<&Backtrace> { // have to grab the backtrace on the first error directly since that error may not be // 'static - let backtrace = (&self.error as &dyn Error).request_ref(); + let backtrace = request_ref(&self.error); let backtrace = backtrace.or_else(|| { self.error .source() - .map(|source| source.sources().find_map(|source| source.request_ref())) + .map(|source| source.sources().find_map(|source| request_ref(source))) .flatten() }); backtrace diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs index ee999bd65..ed070a26b 100644 --- a/library/std/src/error/tests.rs +++ b/library/std/src/error/tests.rs @@ -1,6 +1,6 @@ use super::Error; use crate::fmt; -use core::any::Demand; +use core::error::Request; #[derive(Debug, PartialEq)] struct A; @@ -199,7 +199,7 @@ where self.source.as_deref() } - fn provide<'a>(&'a self, req: &mut Demand<'a>) { + fn provide<'a>(&'a self, req: &mut Request<'a>) { self.backtrace.as_ref().map(|bt| req.provide_ref::<Backtrace>(bt)); } } diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index bed90418b..a659b552f 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -61,6 +61,7 @@ impl f32 { /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); /// ``` + #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -135,6 +136,7 @@ impl f32 { /// assert_eq!(g.trunc(), 3.0); /// assert_eq!(h.trunc(), -3.0); /// ``` + #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -321,6 +323,7 @@ impl f32 { /// // limitation due to round-off error /// assert!((-f32::EPSILON).rem_euclid(3.0) != 0.0); /// ``` + #[doc(alias = "modulo", alias = "mod")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -500,10 +503,7 @@ impl f32 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f32 { - #[cfg(target_os = "android")] - return crate::sys::android::log2f32(self); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f32(self) }; + crate::sys::log2f32(self) } /// Returns the base 10 logarithm of the number. @@ -675,6 +675,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -697,6 +698,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arccos")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -718,6 +720,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arctan")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -775,6 +778,7 @@ impl f32 { /// assert!(abs_difference_0 <= f32::EPSILON); /// assert!(abs_difference_1 <= f32::EPSILON); /// ``` + #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -907,6 +911,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arcsinh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -929,6 +934,7 @@ impl f32 { /// /// assert!(abs_difference <= f32::EPSILON); /// ``` + #[doc(alias = "arccosh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -953,6 +959,7 @@ impl f32 { /// /// assert!(abs_difference <= 1e-5); /// ``` + #[doc(alias = "arctanh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -960,4 +967,46 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } + + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f32; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f32 { + unsafe { cmath::tgammaf(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f32; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f32, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs index e949def00..9ca4e8f2f 100644 --- a/library/std/src/f32/tests.rs +++ b/library/std/src/f32/tests.rs @@ -653,6 +653,38 @@ fn test_atanh() { } #[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f32.gamma(), 1.0f32); + assert_approx_eq!(2.0f32.gamma(), 1.0f32); + assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32); + assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f32.gamma(), f32::INFINITY); + assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); + assert!((-1.0f32).gamma().is_nan()); + assert!((-2.0f32).gamma().is_nan()); + assert!(f32::NAN.gamma().is_nan()); + assert!(f32::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); + assert_eq!(171.71f32.gamma(), f32::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); + assert_eq!(1.0f32.ln_gamma().1, 1); + assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); + assert_eq!(2.0f32.ln_gamma().1, 1); + assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); + assert_eq!(3.0f32.ln_gamma().1, 1); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f32).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { use super::consts; diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index e72de05ca..721e1fb75 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -61,6 +61,7 @@ impl f64 { /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); /// ``` + #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -135,6 +136,7 @@ impl f64 { /// assert_eq!(g.trunc(), 3.0); /// assert_eq!(h.trunc(), -3.0); /// ``` + #[doc(alias = "truncate")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -321,6 +323,7 @@ impl f64 { /// // limitation due to round-off error /// assert!((-f64::EPSILON).rem_euclid(3.0) != 0.0); /// ``` + #[doc(alias = "modulo", alias = "mod")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[inline] @@ -456,7 +459,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn ln(self) -> f64 { - self.log_wrapper(|n| unsafe { intrinsics::logf64(n) }) + crate::sys::log_wrapper(self, |n| unsafe { intrinsics::logf64(n) }) } /// Returns the logarithm of the number with respect to an arbitrary base. @@ -500,12 +503,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log2(self) -> f64 { - self.log_wrapper(|n| { - #[cfg(target_os = "android")] - return crate::sys::android::log2f64(n); - #[cfg(not(target_os = "android"))] - return unsafe { intrinsics::log2f64(n) }; - }) + crate::sys::log_wrapper(self, crate::sys::log2f64) } /// Returns the base 10 logarithm of the number. @@ -525,7 +523,7 @@ impl f64 { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn log10(self) -> f64 { - self.log_wrapper(|n| unsafe { intrinsics::log10f64(n) }) + crate::sys::log_wrapper(self, |n| unsafe { intrinsics::log10f64(n) }) } /// The positive difference of two numbers. @@ -677,6 +675,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arcsin")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -699,6 +698,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arccos")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -720,6 +720,7 @@ impl f64 { /// /// assert!(abs_difference < 1e-10); /// ``` + #[doc(alias = "arctan")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -777,6 +778,7 @@ impl f64 { /// assert!(abs_difference_0 < 1e-10); /// assert!(abs_difference_1 < 1e-10); /// ``` + #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -909,6 +911,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arcsinh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -931,6 +934,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arccosh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -955,6 +959,7 @@ impl f64 { /// /// assert!(abs_difference < 1.0e-10); /// ``` + #[doc(alias = "arctanh")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -963,27 +968,45 @@ impl f64 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - // Solaris/Illumos requires a wrapper around log, log2, and log10 functions - // because of their non-standard behavior (e.g., log(-n) returns -Inf instead - // of expected NaN). - #[rustc_allow_incoherent_impl] - fn log_wrapper<F: Fn(f64) -> f64>(self, log_fn: F) -> f64 { - if !cfg!(any(target_os = "solaris", target_os = "illumos")) { - log_fn(self) - } else if self.is_finite() { - if self > 0.0 { - log_fn(self) - } else if self == 0.0 { - Self::NEG_INFINITY // log(0) = -Inf - } else { - Self::NAN // log(-n) = NaN - } - } else if self.is_nan() { - self // log(NaN) = NaN - } else if self > 0.0 { - self // log(Inf) = Inf - } else { - Self::NAN // log(-Inf) = NaN - } + /// Gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 5.0f64; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn gamma(self) -> f64 { + unsafe { cmath::tgamma(self) } + } + + /// Returns the natural logarithm of the gamma function. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_gamma)] + /// let x = 2.0f64; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_gamma", issue = "99842")] + #[inline] + pub fn ln_gamma(self) -> (f64, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgamma_r(self, &mut signgamp) }; + (x, signgamp) } } diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs index 53d351cce..f88d01593 100644 --- a/library/std/src/f64/tests.rs +++ b/library/std/src/f64/tests.rs @@ -636,6 +636,38 @@ fn test_atanh() { } #[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f64.gamma(), 1.0f64); + assert_approx_eq!(2.0f64.gamma(), 1.0f64); + assert_approx_eq!(3.0f64.gamma(), 2.0f64); + assert_approx_eq!(4.0f64.gamma(), 6.0f64); + assert_approx_eq!(5.0f64.gamma(), 24.0f64); + assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f64.gamma(), f64::INFINITY); + assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); + assert!((-1.0f64).gamma().is_nan()); + assert!((-2.0f64).gamma().is_nan()); + assert!(f64::NAN.gamma().is_nan()); + assert!(f64::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); + assert_eq!(171.71f64.gamma(), f64::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); + assert_eq!(1.0f64.ln_gamma().1, 1); + assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); + assert_eq!(2.0f64.ln_gamma().1, 1); + assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); + assert_eq!(3.0f64.ln_gamma().1, 1); + assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f64).ln_gamma().1, -1); +} + +#[test] fn test_real_consts() { use super::consts; let pi: f64 = consts::PI; diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index 3ddb87487..ee9f6ed08 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -156,6 +156,8 @@ #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use alloc::ffi::{CString, FromVecWithNulError, IntoStringError, NulError}; +#[stable(feature = "cstr_from_bytes_until_nul", since = "1.73.0")] +pub use core::ffi::FromBytesUntilNulError; #[stable(feature = "core_c_str", since = "1.64.0")] pub use core::ffi::{CStr, FromBytesWithNulError}; diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index e7bad9d54..43cecb19b 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -110,12 +110,12 @@ impl crate::sealed::Sealed for OsString {} /// [conversions]: super#conversions #[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")] #[stable(feature = "rust1", since = "1.0.0")] -// FIXME: // `OsStr::from_inner` current implementation relies // on `OsStr` being layout-compatible with `Slice`. -// When attribute privacy is implemented, `OsStr` should be annotated as `#[repr(transparent)]`. -// Anyway, `OsStr` representation and layout are considered implementation details, are -// not documented and must not be relied upon. +// However, `OsStr` layout is considered an implementation detail and must not be relied upon. We +// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under +// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. +#[cfg_attr(not(doc), repr(transparent))] pub struct OsStr { inner: Slice, } @@ -141,6 +141,51 @@ impl OsString { OsString { inner: Buf::from_string(String::new()) } } + /// Converts bytes to an `OsString` without checking that the bytes contains + /// valid [`OsStr`]-encoded data. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// See the [module's toplevel documentation about conversions][conversions] for safe, + /// cross-platform [conversions] from/to native representations. + /// + /// # Safety + /// + /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of + /// validated UTF-8 and bytes from [`OsStr::as_os_str_bytes`] from within the same rust version + /// built for the same target platform. For example, reconstructing an `OsString` from bytes sent + /// over the network or stored in a file will likely violate these safety rules. + /// + /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_os_str_bytes`] can be + /// split either immediately before or immediately after any valid non-empty UTF-8 substring. + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_bytes)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("Mary had a little lamb"); + /// let bytes = os_str.as_os_str_bytes(); + /// let words = bytes.split(|b| *b == b' '); + /// let words: Vec<&OsStr> = words.map(|word| { + /// // SAFETY: + /// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes` + /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring + /// unsafe { OsStr::from_os_str_bytes_unchecked(word) } + /// }).collect(); + /// ``` + /// + /// [conversions]: super#conversions + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub unsafe fn from_os_str_bytes_unchecked(bytes: Vec<u8>) -> Self { + OsString { inner: Buf::from_os_str_bytes_unchecked(bytes) } + } + /// Converts to an [`OsStr`] slice. /// /// # Examples @@ -159,6 +204,26 @@ impl OsString { self } + /// Converts the `OsString` into a byte slice. To convert the byte slice back into an + /// `OsString`, use the [`OsStr::from_os_str_bytes_unchecked`] function. + /// + /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. + /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit + /// ASCII. + /// + /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should + /// be treated as opaque and only comparable within the same rust version built for the same + /// target platform. For example, sending the bytes over the network or storing it in a file + /// will likely result in incompatible data. See [`OsString`] for more encoding details + /// and [`std::ffi`] for platform-specific, specified conversions. + /// + /// [`std::ffi`]: crate::ffi + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner.into_os_str_bytes() + } + /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. /// /// On failure, ownership of the original `OsString` is returned. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index c2d82169d..4094e3780 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -16,6 +16,7 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::path::{Path, PathBuf}; use crate::sealed::Sealed; +use crate::sync::Arc; use crate::sys::fs as fs_imp; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; @@ -743,7 +744,7 @@ fn buffer_capacity_required(mut file: &File) -> Option<usize> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Read for File { +impl Read for &File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } @@ -776,7 +777,7 @@ impl Read for File { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for File { +impl Write for &File { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.inner.write(buf) } @@ -790,72 +791,107 @@ impl Write for File { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { self.inner.flush() } } #[stable(feature = "rust1", since = "1.0.0")] -impl Seek for File { +impl Seek for &File { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { self.inner.seek(pos) } } + #[stable(feature = "rust1", since = "1.0.0")] -impl Read for &File { +impl Read for File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - self.inner.read(buf) + (&*self).read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + (&*self).read_vectored(bufs) } - fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.read_buf(cursor) + (&*self).read_buf(cursor) + } + #[inline] + fn is_read_vectored(&self) -> bool { + (&&*self).is_read_vectored() } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + (&*self).read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { + (&*self).read_to_string(buf) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for File { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + (&*self).write(buf) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + (&*self).write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + (&&*self).is_write_vectored() + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&*self).flush() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for File { + fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { + (&*self).seek(pos) + } +} +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Read for Arc<File> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (&**self).read(buf) + } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - self.inner.read_vectored(bufs) + (&**self).read_vectored(bufs) + } + fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (&**self).read_buf(cursor) } - #[inline] fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() + (&**self).is_read_vectored() } - - // Reserves space in the buffer based on the file size when available. fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - let size = buffer_capacity_required(self); - buf.reserve(size.unwrap_or(0)); - io::default_read_to_end(self, buf, size) + (&**self).read_to_end(buf) } - - // Reserves space in the buffer based on the file size when available. fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { - let size = buffer_capacity_required(self); - buf.reserve(size.unwrap_or(0)); - io::default_read_to_string(self, buf, size) + (&**self).read_to_string(buf) } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &File { +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc<File> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.inner.write(buf) + (&**self).write(buf) } - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - self.inner.write_vectored(bufs) + (&**self).write_vectored(bufs) } - #[inline] fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() + (&**self).is_write_vectored() } - + #[inline] fn flush(&mut self) -> io::Result<()> { - self.inner.flush() + (&**self).flush() } } -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for &File { +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Seek for Arc<File> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { - self.inner.seek(pos) + (&**self).seek(pos) } } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9ff01b9c3..d74f0f00e 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -957,6 +957,7 @@ fn readlink_not_symlink() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); @@ -1453,6 +1454,7 @@ fn metadata_access_times() { /// Test creating hard links to symlinks. #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn symlink_hard_link() { let tmpdir = tmpdir(); if !got_symlink_permission(&tmpdir) { diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index ef1f4031e..3322940d2 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,4 +1,8 @@ use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use crate::alloc::Allocator; +use crate::cmp; +use crate::collections::VecDeque; +use crate::io::IoSlice; use crate::mem::MaybeUninit; #[cfg(test)] @@ -86,7 +90,7 @@ where /// Specialization of the read-write loop that reuses the internal /// buffer of a BufReader. If there's no buffer then the writer side -/// should be used intead. +/// should be used instead. trait BufferedReaderSpec { fn buffer_size(&self) -> usize; @@ -104,7 +108,39 @@ where } default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> { - unimplemented!("only called from specializations"); + unreachable!("only called from specializations") + } +} + +impl BufferedReaderSpec for &[u8] { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> { + let len = self.len(); + to.write_all(self)?; + *self = &self[len..]; + Ok(len as u64) + } +} + +impl<A: Allocator> BufferedReaderSpec for VecDeque<u8, A> { + fn buffer_size(&self) -> usize { + // prefer this specialization since the source "buffer" is all we'll ever need, + // even if it's small + usize::MAX + } + + fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> { + let len = self.len(); + let (front, back) = self.as_slices(); + let bufs = &mut [IoSlice::new(front), IoSlice::new(back)]; + to.write_all_vectored(bufs)?; + self.clear(); + Ok(len as u64) } } @@ -218,6 +254,47 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { } } +impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> { + fn buffer_size(&self) -> usize { + cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) + } + + fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> { + let mut bytes = 0; + + // avoid allocating before we have determined that there's anything to read + if self.capacity() == 0 { + bytes = stack_buffer_copy(&mut reader.take(DEFAULT_BUF_SIZE as u64), self)?; + if bytes == 0 { + return Ok(0); + } + } + + loop { + self.reserve(DEFAULT_BUF_SIZE); + let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into(); + match reader.read_buf(buf.unfilled()) { + Ok(()) => {} + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return Err(e), + }; + + let read = buf.filled().len(); + if read == 0 { + break; + } + + // SAFETY: BorrowedBuf guarantees all of its filled bytes are init + // and the number of read bytes can't exceed the spare capacity since + // that's what the buffer is borrowing from. + unsafe { self.set_len(self.len() + read) }; + bytes += read as u64; + } + + Ok(bytes) + } +} + fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 8c816af15..af137eaf8 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -1,4 +1,6 @@ use crate::cmp::{max, min}; +use crate::collections::VecDeque; +use crate::io; use crate::io::*; #[test] @@ -19,7 +21,7 @@ struct ShortReader { impl Read for ShortReader { fn read(&mut self, buf: &mut [u8]) -> Result<usize> { - let bytes = min(self.cap, self.read_size); + let bytes = min(self.cap, self.read_size).min(buf.len()); self.cap -= bytes; self.observed_buffer = max(self.observed_buffer, buf.len()); Ok(bytes) @@ -78,6 +80,40 @@ fn copy_specializes_bufreader() { ); } +#[test] +fn copy_specializes_to_vec() { + let cap = 123456; + let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; + let mut sink = Vec::new(); + assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap()); + assert!( + source.observed_buffer > DEFAULT_BUF_SIZE, + "expected a large buffer to be provided to the reader" + ); +} + +#[test] +fn copy_specializes_from_vecdeque() { + let mut source = VecDeque::with_capacity(100 * 1024); + for _ in 0..20 * 1024 { + source.push_front(0); + } + for _ in 0..20 * 1024 { + source.push_back(0); + } + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); + assert_eq!(20 * 1024, sink.observed_buffer); +} + +#[test] +fn copy_specializes_from_slice() { + let mut source = [1; 60 * 1024].as_slice(); + let mut sink = WriteObserver { observed_buffer: 0 }; + assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); + assert_eq!(60 * 1024, sink.observed_buffer); +} + #[cfg(unix)] mod io_benches { use crate::fs::File; diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index 4800eeda0..034ddd8df 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -99,6 +99,13 @@ impl<'data> BorrowedBuf<'data> { unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) } } + /// Returns a mutable reference to the filled portion of the buffer. + #[inline] + pub fn filled_mut(&mut self) -> &mut [u8] { + // SAFETY: We only slice the filled part of the buffer, which is always valid + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) } + } + /// Returns a cursor over the unfilled part of the buffer. #[inline] pub fn unfilled<'this>(&'this mut self) -> BorrowedCursor<'this> { @@ -303,6 +310,7 @@ impl<'a> Write for BorrowedCursor<'a> { Ok(buf.len()) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index f076ee092..6bc8f181c 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -8,24 +8,41 @@ use crate::io::{ self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, }; -/// A reader which is always at EOF. +/// `Empty` ignores any data written via [`Write`], and will always be empty +/// (returning zero bytes) when read via [`Read`]. /// -/// This struct is generally created by calling [`empty()`]. Please see -/// the documentation of [`empty()`] for more details. +/// This struct is generally created by calling [`empty()`]. Please +/// see the documentation of [`empty()`] for more details. #[stable(feature = "rust1", since = "1.0.0")] #[non_exhaustive] -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Debug, Default)] pub struct Empty; -/// Constructs a new handle to an empty reader. +/// Creates a value that is always at EOF for reads, and ignores all data written. /// -/// All reads from the returned reader will return <code>[Ok]\(0)</code>. +/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] +/// and the contents of the buffer will not be inspected. +/// +/// All calls to [`read`] from the returned reader will return [`Ok(0)`]. +/// +/// [`Ok(buf.len())`]: Ok +/// [`Ok(0)`]: Ok +/// +/// [`write`]: Write::write +/// [`read`]: Read::read /// /// # Examples /// -/// A slightly sad example of not reading anything into a buffer: +/// ```rust +/// use std::io::{self, Write}; /// +/// let buffer = vec![1, 2, 3, 5, 8]; +/// let num_bytes = io::empty().write(&buffer).unwrap(); +/// assert_eq!(num_bytes, 5); /// ``` +/// +/// +/// ```rust /// use std::io::{self, Read}; /// /// let mut buffer = String::new(); @@ -76,13 +93,6 @@ impl Seek for Empty { } } -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Empty { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Empty").finish_non_exhaustive() - } -} - impl SizeHint for Empty { #[inline] fn upper_bound(&self) -> Option<usize> { @@ -90,6 +100,54 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// A reader which yields one byte over and over and over and over and over and... /// /// This struct is generally created by calling [`repeat()`]. Please @@ -182,19 +240,20 @@ impl fmt::Debug for Repeat { /// A writer which will move data into the void. /// -/// This struct is generally created by calling [`sink`]. Please +/// This struct is generally created by calling [`sink()`]. Please /// see the documentation of [`sink()`] for more details. #[stable(feature = "rust1", since = "1.0.0")] #[non_exhaustive] -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Debug, Default)] pub struct Sink; /// Creates an instance of a writer which will successfully consume all data. /// -/// All calls to [`write`] on the returned instance will return `Ok(buf.len())` +/// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// /// [`write`]: Write::write +/// [`Ok(buf.len())`]: Ok /// /// # Examples /// @@ -259,10 +318,3 @@ impl Write for &Sink { Ok(()) } } - -#[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Sink { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sink").finish_non_exhaustive() - } -} diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 1baa94e64..6de91e29c 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -18,7 +18,7 @@ fn empty_reads() { assert_eq!(e.read(&mut []).unwrap(), 0); assert_eq!(e.read(&mut [0]).unwrap(), 0); assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); - assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); + assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0); let buf: &mut [MaybeUninit<_>] = &mut []; let mut buf: BorrowedBuf<'_> = buf.into(); @@ -40,7 +40,7 @@ fn empty_reads() { let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); - e.by_ref().read_buf(buf.unfilled()).unwrap(); + Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); assert_eq!(buf.init_len(), 0); } @@ -66,6 +66,15 @@ fn empty_seeks() { } #[test] +fn empty_sinks() { + let mut e = empty(); + assert_eq!(e.write(&[]).unwrap(), 0); + assert_eq!(e.write(&[0]).unwrap(), 1); + assert_eq!(e.write(&[0; 1024]).unwrap(), 1024); + assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024); +} + +#[test] fn repeat_repeats() { let mut r = repeat(4); let mut b = [0; 1024]; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 72b9ad348..0ccbb16b1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -190,7 +190,7 @@ // To run std tests without x.py without ending up with two copies of std, Miri needs to be // able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>. -// rustc itself never sets the feature, so this line has no affect there. +// rustc itself never sets the feature, so this line has no effect there. #![cfg(any(not(feature = "miri-test-libstd"), test, doctest))] // miri-test-libstd also prefers to make std use the sysroot versions of the dependencies. #![cfg_attr(feature = "miri-test-libstd", feature(rustc_private))] @@ -220,8 +220,10 @@ #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #![deny(rustc::existing_doc_keyword)] #![deny(fuzzy_provenance_casts)] +#![cfg_attr(not(bootstrap), allow(rustdoc::redundant_explicit_links))] // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` #![deny(ffi_unwind_calls)] // std may use features in a platform-specific way @@ -272,6 +274,7 @@ #![feature(staged_api)] #![feature(thread_local)] #![feature(try_blocks)] +#![feature(type_alias_impl_trait)] #![feature(utf8_chunks)] // tidy-alphabetical-end // @@ -286,16 +289,17 @@ #![feature(exact_size_is_empty)] #![feature(exclusive_wrapper)] #![feature(extend_one)] +#![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(int_roundings)] #![feature(ip)] #![feature(ip_in_core)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_write_slice)] +#![feature(offset_of)] #![feature(panic_can_unwind)] #![feature(panic_info_message)] #![feature(panic_internals)] @@ -303,7 +307,6 @@ #![feature(pointer_is_aligned)] #![feature(portable_simd)] #![feature(prelude_2024)] -#![feature(provide_any)] #![feature(ptr_as_uninit)] #![feature(raw_os_nonzero)] #![feature(round_ties_even)] @@ -393,9 +396,15 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; +// FIXME: #94122 this extern crate definition only exist here to stop +// miniz_oxide docs leaking into std docs. Find better way to do it. +// Remove exclusion from tidy platform check when this removed. #[doc(masked)] #[allow(unused_extern_crates)] -#[cfg(feature = "miniz_oxide")] +#[cfg(all( + not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))), + feature = "miniz_oxide" +))] extern crate miniz_oxide; // During testing, this crate is not actually the "real" std library, but rather @@ -542,7 +551,7 @@ pub mod time; // Pull in `std_float` crate into std. The contents of // `std_float` are in a different repository: rust-lang/portable-simd. #[path = "../../portable-simd/crates/std_float/src/lib.rs"] -#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn, unused_unsafe)] +#[allow(missing_debug_implementations, dead_code, unsafe_op_in_unsafe_fn)] #[allow(rustdoc::bare_urls)] #[unstable(feature = "portable_simd", issue = "86656")] mod std_float; @@ -603,7 +612,6 @@ pub mod alloc; // Private support modules mod panicking; -mod personality; #[path = "../../backtrace/src/lib.rs"] #[allow(dead_code, unused_attributes, fuzzy_provenance_casts)] diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 141a18a42..32fd54c8e 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -647,6 +647,7 @@ impl Write for TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -685,6 +686,7 @@ impl Write for &TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/os/android/raw.rs b/library/std/src/os/android/raw.rs index a255d0320..175f8eac9 100644 --- a/library/std/src/os/android/raw.rs +++ b/library/std/src/os/android/raw.rs @@ -21,26 +21,26 @@ pub use self::arch::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, #[cfg(any(target_arch = "arm", target_arch = "x86"))] mod arch { - use crate::os::raw::{c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong}; + use crate::os::raw::{c_long, c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong}; use crate::os::unix::raw::{gid_t, uid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type dev_t = u64; + pub type dev_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; + pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -70,45 +70,47 @@ mod arch { pub st_blksize: u32, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: c_ulonglong, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_ulong, + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_ulong, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_ulong, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ino: c_ulonglong, } } -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] mod arch { - use crate::os::raw::{c_uchar, c_ulong}; + use crate::os::raw::{c_int, c_long, c_uint, c_ulong}; use crate::os::unix::raw::{gid_t, uid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type nlink_t = u64; + pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -117,9 +119,7 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad0: [c_uchar; 4], - #[stable(feature = "raw_ext", since = "1.1.0")] - pub __st_ino: ino_t, + pub st_ino: ino_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mode: mode_t, #[stable(feature = "raw_ext", since = "1.1.0")] @@ -131,27 +131,33 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub __pad3: [c_uchar; 4], + pub __pad1: c_ulong, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_size: off_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blksize: blksize_t, + pub st_blksize: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_int, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_blocks: blkcnt_t, + pub st_blocks: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ino: ino_t, + pub __unused4: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused5: c_uint, } } @@ -163,20 +169,20 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type mode_t = u32; + pub type mode_t = c_uint; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blkcnt_t = u64; + pub type blkcnt_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type blksize_t = u64; + pub type blksize_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type ino_t = u64; + pub type ino_t = c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u32; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type off_t = u64; + pub type off_t = i64; #[stable(feature = "raw_ext", since = "1.1.0")] - pub type time_t = i64; + pub type time_t = c_long; #[repr(C)] #[derive(Clone)] @@ -195,25 +201,30 @@ mod arch { #[stable(feature = "raw_ext", since = "1.1.0")] pub st_gid: gid_t, #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad0: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_size: i64, + pub st_size: off_t, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blksize: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: c_long, + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime: c_ulong, + pub st_atime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_atime_nsec: c_ulong, + pub st_mtime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime: c_ulong, + pub st_mtime_nsec: c_long, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_mtime_nsec: c_ulong, + pub st_ctime: time_t, #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime: c_ulong, + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] - pub st_ctime_nsec: c_ulong, - __unused: [c_long; 3], + pub __pad3: [c_long; 3], } } diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs index b3f7439f8..12c029328 100644 --- a/library/std/src/os/l4re/raw.rs +++ b/library/std/src/os/l4re/raw.rs @@ -27,6 +27,7 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; #[cfg(any( target_arch = "x86", target_arch = "m68k", + target_arch = "csky", target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index c55ca8ba2..a568f9b26 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -27,6 +27,7 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; #[cfg(any( target_arch = "x86", target_arch = "m68k", + target_arch = "csky", target_arch = "powerpc", target_arch = "sparc", target_arch = "arm", @@ -94,7 +95,7 @@ mod arch { } } -#[cfg(target_arch = "mips")] +#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] mod arch { use crate::os::raw::{c_long, c_ulong}; @@ -233,6 +234,7 @@ mod arch { #[cfg(any( target_arch = "loongarch64", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "s390x", target_arch = "sparc64", target_arch = "riscv64", diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs index 19d0ffb2e..5b302e3c2 100644 --- a/library/std/src/os/raw/mod.rs +++ b/library/std/src/os/raw/mod.rs @@ -9,11 +9,6 @@ macro_rules! alias_core_ffi { ($($t:ident)*) => {$( #[stable(feature = "raw_os", since = "1.1.0")] #[doc = include_str!(concat!("../../../../core/src/ffi/", stringify!($t), ".md"))] - // Make this type alias appear cfg-dependent so that Clippy does not suggest - // replacing expressions like `0 as c_char` with `0_i8`/`0_u8`. This #[cfg(all())] can be - // removed after the false positive in https://github.com/rust-lang/rust-clippy/issues/8093 - // is fixed. - #[cfg(all())] #[doc(cfg(all()))] pub type $t = core::ffi::$t; )*} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 1e1c36931..029de8fbf 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -149,7 +149,36 @@ pub trait FileExt { /// Note that similar to [`File::write`], it is not an error to return a /// short write. /// + /// # Bug + /// On some systems, `write_at` utilises [`pwrite64`] to write to files. + /// However, this syscall has a [bug] where files opened with the `O_APPEND` + /// flag fail to respect the offset parameter, always appending to the end + /// of the file instead. + /// + /// It is possible to inadvertantly set this flag, like in the example below. + /// Therefore, it is important to be vigilant while changing options to mitigate + /// unexpected behaviour. + /// + /// ```no_run + /// use std::fs::File; + /// use std::io; + /// use std::os::unix::prelude::FileExt; + /// + /// fn main() -> io::Result<()> { + /// // Open a file with the append option (sets the `O_APPEND` flag) + /// let file = File::options().append(true).open("foo.txt")?; + /// + /// // We attempt to write at offset 10; instead appended to EOF + /// file.write_at(b"sushi", 10)?; + /// + /// // foo.txt is 5 bytes long instead of 15 + /// Ok(()) + /// } + /// ``` + /// /// [`File::write`]: fs::File::write + /// [`pwrite64`]: https://man7.org/linux/man-pages/man2/pwrite.2.html + /// [bug]: https://man7.org/linux/man-pages/man2/pwrite.2.html#BUGS /// /// # Examples /// @@ -159,7 +188,7 @@ pub trait FileExt { /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; + /// let file = File::create("foo.txt")?; /// /// // We now write at the offset 10. /// file.write_at(b"sushi", 10)?; @@ -971,7 +1000,6 @@ impl DirBuilderExt for fs::DirBuilder { /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -979,7 +1007,7 @@ impl DirBuilderExt for fs::DirBuilder { /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "1.73.0")] pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -991,7 +1019,6 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io:: /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1000,7 +1027,7 @@ pub fn chown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io:: /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "1.73.0")] pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -1013,7 +1040,6 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result< /// # Examples /// /// ```no_run -/// #![feature(unix_chown)] /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { @@ -1021,7 +1047,7 @@ pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result< /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "88989")] +#[stable(feature = "unix_chown", since = "1.73.0")] pub fn lchown<P: AsRef<Path>>(dir: P, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index e20170873..41290e001 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -712,6 +712,7 @@ impl<'a> io::Write for &'a UnixStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 39f10c50d..3d4302e66 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -23,6 +23,7 @@ macro_rules! or_panic { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn basic() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -93,6 +94,7 @@ fn pair() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn try_clone() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -119,6 +121,7 @@ fn try_clone() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn iter() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -168,6 +171,7 @@ fn long_path() { #[test] #[cfg(not(target_os = "nto"))] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -195,6 +199,7 @@ fn timeouts() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -214,6 +219,7 @@ fn test_read_timeout() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_with_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -241,6 +247,7 @@ fn test_read_with_timeout() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_timeout_zero_duration() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -260,6 +267,7 @@ fn test_unix_stream_timeout_zero_duration() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -276,6 +284,7 @@ fn test_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unnamed_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -293,6 +302,7 @@ fn test_unnamed_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_connect_to_recv_addr() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -317,6 +327,7 @@ fn test_unix_datagram_connect_to_recv_addr() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_connect_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -343,6 +354,7 @@ fn test_connect_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_recv() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -385,6 +397,7 @@ fn datagram_pair() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_timeout_zero_duration() { let dir = tmpdir(); let path = dir.path().join("sock"); @@ -529,6 +542,7 @@ fn test_abstract_no_pathname_and_not_unnamed() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_peek() { let (txdone, rxdone) = crate::sync::mpsc::channel(); @@ -561,6 +575,7 @@ fn test_unix_stream_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -585,6 +600,7 @@ fn test_unix_datagram_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek_from() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -648,6 +664,7 @@ fn test_send_vectored_fds_unix_stream() { #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_to_unix_datagram() { fn getpid() -> libc::pid_t { unsafe { libc::getpid() } @@ -715,6 +732,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index a6a370409..a0c21f704 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -234,6 +234,7 @@ where *hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info))); } +/// The default panic handler. fn default_hook(info: &PanicInfo<'_>) { // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. @@ -257,7 +258,7 @@ fn default_hook(info: &PanicInfo<'_>) { let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>"); let write = |err: &mut dyn crate::io::Write| { - let _ = writeln!(err, "thread '{name}' panicked at '{msg}', {location}"); + let _ = writeln!(err, "thread '{name}' panicked at {location}:\n{msg}"); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); @@ -272,7 +273,8 @@ fn default_hook(info: &PanicInfo<'_>) { if FIRST_PANIC.swap(false, Ordering::SeqCst) { let _ = writeln!( err, - "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace" + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ + backtrace" ); } } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 28cd3c4e4..5842c096f 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1158,12 +1158,12 @@ impl FusedIterator for Ancestors<'_> {} /// Which method works best depends on what kind of situation you're in. #[cfg_attr(not(test), rustc_diagnostic_item = "PathBuf")] #[stable(feature = "rust1", since = "1.0.0")] -// FIXME: // `PathBuf::as_mut_vec` current implementation relies // on `PathBuf` being layout-compatible with `Vec<u8>`. -// When attribute privacy is implemented, `PathBuf` should be annotated as `#[repr(transparent)]`. -// Anyway, `PathBuf` representation and layout are considered implementation detail, are -// not documented and must not be relied upon. +// However, `PathBuf` layout is considered an implementation detail and must not be relied upon. We +// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under +// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. +#[cfg_attr(not(doc), repr(transparent))] pub struct PathBuf { inner: OsString, } @@ -1983,12 +1983,12 @@ impl AsRef<OsStr> for PathBuf { /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "Path")] #[stable(feature = "rust1", since = "1.0.0")] -// FIXME: // `Path::new` current implementation relies // on `Path` being layout-compatible with `OsStr`. -// When attribute privacy is implemented, `Path` should be annotated as `#[repr(transparent)]`. -// Anyway, `Path` representation and layout are considered implementation detail, are -// not documented and must not be relied upon. +// However, `Path` layout is considered an implementation detail and must not be relied upon. We +// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under +// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. +#[cfg_attr(not(doc), repr(transparent))] pub struct Path { inner: OsStr, } @@ -2608,9 +2608,27 @@ impl Path { } fn _with_extension(&self, extension: &OsStr) -> PathBuf { - let mut buf = self.to_path_buf(); - buf.set_extension(extension); - buf + let self_len = self.as_os_str().len(); + let self_bytes = self.as_os_str().as_os_str_bytes(); + + let (new_capacity, slice_to_copy) = match self.extension() { + None => { + // Enough capacity for the extension and the dot + let capacity = self_len + extension.len() + 1; + let whole_path = self_bytes.iter(); + (capacity, whole_path) + } + Some(previous_extension) => { + let capacity = self_len + extension.len() - previous_extension.len(); + let path_till_dot = self_bytes[..self_len - previous_extension.len()].iter(); + (capacity, path_till_dot) + } + }; + + let mut new_path = PathBuf::with_capacity(new_capacity); + new_path.as_mut_vec().extend(slice_to_copy); + new_path.set_extension(extension); + new_path } /// Produces an iterator over the [`Component`]s of the path. diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index dd307022c..f12ffbf2e 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1183,7 +1183,7 @@ pub fn test_prefix_ext() { #[test] pub fn test_push() { macro_rules! tp ( - ($path:expr, $push:expr, $expected:expr) => ( { + ($path:expr, $push:expr, $expected:expr) => ({ let mut actual = PathBuf::from($path); actual.push($push); assert!(actual.to_str() == Some($expected), @@ -1281,7 +1281,7 @@ pub fn test_push() { #[test] pub fn test_pop() { macro_rules! tp ( - ($path:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $expected:expr, $output:expr) => ({ let mut actual = PathBuf::from($path); let output = actual.pop(); assert!(actual.to_str() == Some($expected) && output == $output, @@ -1335,7 +1335,7 @@ pub fn test_pop() { #[test] pub fn test_set_file_name() { macro_rules! tfn ( - ($path:expr, $file:expr, $expected:expr) => ( { + ($path:expr, $file:expr, $expected:expr) => ({ let mut p = PathBuf::from($path); p.set_file_name($file); assert!(p.to_str() == Some($expected), @@ -1369,7 +1369,7 @@ pub fn test_set_file_name() { #[test] pub fn test_set_extension() { macro_rules! tfe ( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ( { + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ let mut p = PathBuf::from($path); let output = p.set_extension($ext); assert!(p.to_str() == Some($expected) && output == $output, @@ -1395,6 +1395,46 @@ pub fn test_set_extension() { } #[test] +pub fn test_with_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); +} + +#[test] fn test_eq_receivers() { use crate::borrow::Cow; @@ -1669,7 +1709,7 @@ fn into_rc() { #[test] fn test_ord() { macro_rules! ord( - ($ord:ident, $left:expr, $right:expr) => ( { + ($ord:ident, $left:expr, $right:expr) => ({ use core::cmp::Ordering; let left = Path::new($left); diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 8f3201b00..7380b45b0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -280,6 +280,7 @@ impl Write for ChildStdin { io::Write::is_write_vectored(&&*self) } + #[inline] fn flush(&mut self) -> io::Result<()> { (&*self).flush() } @@ -299,6 +300,7 @@ impl Write for &ChildStdin { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -558,6 +560,14 @@ impl Command { /// but this has some implementation limitations on Windows /// (see issue #37519). /// + /// # Platform-specific behavior + /// + /// Note on Windows: For executable files with the .exe extension, + /// it can be omitted when specifying the program for this Command. + /// However, if the file has a different extension, + /// a filename including the extension needs to be provided, + /// otherwise the file won't be found. + /// /// # Examples /// /// Basic usage: @@ -1524,6 +1534,15 @@ impl From<fs::File> for Stdio { #[stable(feature = "process", since = "1.0.0")] pub struct ExitStatus(imp::ExitStatus); +/// The default value is one which indicates successful completion. +#[stable(feature = "process-exitcode-default", since = "1.73.0")] +impl Default for ExitStatus { + fn default() -> Self { + // Ideally this would be done by ExitCode::default().into() but that is complicated. + ExitStatus::from_inner(imp::ExitStatus::default()) + } +} + /// Allows extension traits within `std`. #[unstable(feature = "sealed", issue = "none")] impl crate::sealed::Sealed for ExitStatus {} diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index e39254aa4..ed3c55120 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -130,11 +130,8 @@ impl Barrier { let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - // We need a while loop to guard against spurious wakeups. - // https://en.wikipedia.org/wiki/Spurious_wakeup - while local_gen == lock.generation_id { - lock = self.cvar.wait(lock).unwrap(); - } + let _guard = + self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap(); BarrierWaitResult(false) } else { lock.count = 0; diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 76a1b4a2a..9c4b926b7 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -21,11 +21,11 @@ impl WaitTimeoutResult { /// /// # Examples /// - /// This example spawns a thread which will update the boolean value and - /// then wait 100 milliseconds before notifying the condvar. + /// This example spawns a thread which will sleep 20 milliseconds before + /// updating a boolean value and then notifying the condvar. /// - /// The main thread will wait with a timeout on the condvar and then leave - /// once the boolean has been updated and notified. + /// The main thread will wait with a 10 millisecond timeout on the condvar + /// and will leave the loop upon timeout. /// /// ``` /// use std::sync::{Arc, Condvar, Mutex}; @@ -49,14 +49,12 @@ impl WaitTimeoutResult { /// /// // Wait for the thread to start up. /// let (lock, cvar) = &*pair; - /// let mut started = lock.lock().unwrap(); /// loop { /// // Let's put a timeout on the condvar's wait. - /// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); - /// // 10 milliseconds have passed, or maybe the value changed! - /// started = result.0; - /// if *started == true { - /// // We received the notification and the value has been updated, we can leave. + /// let result = cvar.wait_timeout(lock.lock().unwrap(), Duration::from_millis(10)).unwrap(); + /// // 10 milliseconds have passed. + /// if result.1.timed_out() { + /// // timed out now and we can leave. /// break /// } /// } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index a6bc468b0..3598598cf 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -25,6 +25,8 @@ union Data<T, F> { /// /// # Examples /// +/// Initialize static variables with `LazyLock`. +/// /// ``` /// #![feature(lazy_cell)] /// @@ -54,6 +56,24 @@ union Data<T, F> { /// // Some("Hoyten") /// } /// ``` +/// Initialize fields with `LazyLock`. +/// ``` +/// #![feature(lazy_cell)] +/// +/// use std::sync::LazyLock; +/// +/// #[derive(Debug)] +/// struct UseCellLock { +/// number: LazyLock<u32>, +/// } +/// fn main() { +/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32); +/// +/// let data = UseCellLock { number: lock }; +/// println!("{}", *data.number); +/// } +/// ``` + #[unstable(feature = "lazy_cell", issue = "109736")] pub struct LazyLock<T, F = fn() -> T> { once: Once, @@ -69,6 +89,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } } + /// Creates a new lazy value that is already initialized. + #[inline] + #[cfg(test)] + pub(crate) fn preinit(value: T) -> LazyLock<T, F> { + let once = Once::new(); + once.call_once(|| {}); + LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) } + } + /// Consumes this `LazyLock` returning the stored value. /// /// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise. @@ -193,10 +222,12 @@ impl<T: Default> Default for LazyLock<T> { #[unstable(feature = "lazy_cell", issue = "109736")] impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_tuple("LazyLock"); match self.get() { - Some(v) => f.debug_tuple("LazyLock").field(v).finish(), - None => f.write_str("LazyLock(Uninit)"), - } + Some(v) => d.field(v), + None => d.field(&format_args!("<uninit>")), + }; + d.finish() } } diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index d053d69e2..0cbc61160 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -35,7 +35,9 @@ use crate::ops::{Deref, DerefMut}; any( target_arch = "arm", target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "riscv64", ), repr(align(32)) @@ -59,7 +61,9 @@ use crate::ops::{Deref, DerefMut}; target_arch = "powerpc64", target_arch = "arm", target_arch = "mips", + target_arch = "mips32r6", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "riscv64", target_arch = "s390x", )), diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index c00134c8b..f92bb1a4b 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -303,12 +303,11 @@ pub struct IntoIter<T> { rx: Receiver<T>, } -/// The sending-half of Rust's asynchronous [`channel`] type. This half can only be -/// owned by one thread, but it can be cloned to send to other threads. +/// The sending-half of Rust's asynchronous [`channel`] type. /// /// Messages can be sent through this channel with [`send`]. /// -/// Note: all senders (the original and the clones) need to be dropped for the receiver +/// Note: all senders (the original and its clones) need to be dropped for the receiver /// to stop blocking to receive messages with [`Receiver::recv`]. /// /// [`send`]: Sender::send diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index b8fec6902..b4ae6b7e0 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -490,13 +490,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("<locked>") - } - } - d.field("data", &LockedPlaceholder); + d.field("data", &format_args!("<locked>")); } } d.field("poisoned", &self.poison.get()); diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index e83bc35ee..e2b7b893c 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -365,10 +365,12 @@ impl<T> Default for OnceLock<T> { #[stable(feature = "once_cell", since = "1.70.0")] impl<T: fmt::Debug> fmt::Debug for OnceLock<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_tuple("OnceLock"); match self.get() { - Some(v) => f.debug_tuple("Once").field(v).finish(), - None => f.write_str("Once(Uninit)"), - } + Some(v) => d.field(v), + None => d.field(&format_args!("<uninit>")), + }; + d.finish() } } diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index 7c409cb3e..26aaa2414 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -485,13 +485,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { - struct LockedPlaceholder; - impl fmt::Debug for LockedPlaceholder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("<locked>") - } - } - d.field("data", &LockedPlaceholder); + d.field("data", &format_args!("<locked>")); } } d.field("poisoned", &self.poison.get()); diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index a5fcbdf39..d58aa6c27 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -8,7 +8,9 @@ use crate::ptr; target_arch = "x86", target_arch = "arm", target_arch = "m68k", + target_arch = "csky", target_arch = "mips", + target_arch = "mips32r6", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc", @@ -24,6 +26,7 @@ pub const MIN_ALIGN: usize = 8; target_arch = "aarch64", target_arch = "loongarch64", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "s390x", target_arch = "sparc64", target_arch = "riscv64", diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index bc5da1a18..c0a9619bf 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -87,10 +87,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::<$t>::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs index 5d48ce1e0..7cf291921 100644 --- a/library/std/src/sys/common/thread_local/os_local.rs +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -24,7 +24,6 @@ pub macro thread_local_inner { const fn __init() -> $t { INIT_EXPR } static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = _init { @@ -59,10 +58,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/common/thread_local/static_local.rs b/library/std/src/sys/common/thread_local/static_local.rs index 80322a978..5cb6c541a 100644 --- a/library/std/src/sys/common/thread_local/static_local.rs +++ b/library/std/src/sys/common/thread_local/static_local.rs @@ -43,10 +43,6 @@ pub macro thread_local_inner { static __KEY: $crate::thread::local_impl::Key<$t> = $crate::thread::local_impl::Key::new(); - // FIXME: remove the #[allow(...)] marker when macros don't - // raise warning for missing/extraneous unsafe blocks anymore. - // See https://github.com/rust-lang/rust/issues/74838. - #[allow(unused_unsafe)] unsafe { __KEY.get(move || { if let $crate::option::Option::Some(init) = init { diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 4bb735668..6aa4ea7f5 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -335,6 +335,7 @@ impl File { false } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs index e53dbae61..c79197a9a 100644 --- a/library/std/src/sys/hermit/os.rs +++ b/library/std/src/sys/hermit/os.rs @@ -112,6 +112,34 @@ pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + impl !Send for Env {} impl !Sync for Env {} diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs index 2507f7069..332151e40 100644 --- a/library/std/src/sys/hermit/thread.rs +++ b/library/std/src/sys/hermit/thread.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::mem; @@ -99,7 +98,7 @@ impl Thread { } pub fn available_parallelism() -> io::Result<NonZeroUsize> { - unsupported() + unsafe { Ok(NonZeroUsize::new_unchecked(abi::get_processor_count())) } } pub mod guard { diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs index 5440d85df..7d91460ab 100644 --- a/library/std/src/sys/hermit/time.rs +++ b/library/std/src/sys/hermit/time.rs @@ -40,7 +40,7 @@ impl Timespec { } fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { - let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; + let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. @@ -53,7 +53,7 @@ impl Timespec { } fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { - let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; + let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index c72be1380..beea3f23c 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -23,6 +23,7 @@ #![allow(missing_debug_implementations)] pub mod common; +mod personality; cfg_if::cfg_if! { if #[cfg(unix)] { @@ -60,3 +61,52 @@ cfg_if::cfg_if! { pub const FULL_BACKTRACE_DEFAULT: bool = false; } } + +#[cfg(not(test))] +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + pub use self::android::log2f32; + pub use self::android::log2f64; + } else { + #[inline] + pub fn log2f32(n: f32) -> f32 { + unsafe { crate::intrinsics::log2f32(n) } + } + + #[inline] + pub fn log2f64(n: f64) -> f64 { + unsafe { crate::intrinsics::log2f64(n) } + } + } +} + +// Solaris/Illumos requires a wrapper around log, log2, and log10 functions +// because of their non-standard behavior (e.g., log(-n) returns -Inf instead +// of expected NaN). +#[cfg(not(test))] +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +#[inline] +pub fn log_wrapper<F: Fn(f64) -> f64>(n: f64, log_fn: F) -> f64 { + if n.is_finite() { + if n > 0.0 { + log_fn(n) + } else if n == 0.0 { + f64::NEG_INFINITY // log(0) = -Inf + } else { + f64::NAN // log(-n) = NaN + } + } else if n.is_nan() { + n // log(NaN) = NaN + } else if n > 0.0 { + n // log(Inf) = Inf + } else { + f64::NAN // log(-Inf) = NaN + } +} + +#[cfg(not(test))] +#[cfg(not(any(target_os = "solaris", target_os = "illumos")))] +#[inline] +pub fn log_wrapper<F: Fn(f64) -> f64>(n: f64, log_fn: F) -> f64 { + log_fn(n) +} diff --git a/library/std/src/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs index 79624703a..79624703a 100644 --- a/library/std/src/personality/dwarf/eh.rs +++ b/library/std/src/sys/personality/dwarf/eh.rs diff --git a/library/std/src/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs index 652fbe95a..652fbe95a 100644 --- a/library/std/src/personality/dwarf/mod.rs +++ b/library/std/src/sys/personality/dwarf/mod.rs diff --git a/library/std/src/personality/dwarf/tests.rs b/library/std/src/sys/personality/dwarf/tests.rs index 1644f3708..1644f3708 100644 --- a/library/std/src/personality/dwarf/tests.rs +++ b/library/std/src/sys/personality/dwarf/tests.rs diff --git a/library/std/src/personality/emcc.rs b/library/std/src/sys/personality/emcc.rs index cb52ae89b..cb52ae89b 100644 --- a/library/std/src/personality/emcc.rs +++ b/library/std/src/sys/personality/emcc.rs diff --git a/library/std/src/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 6552d96ca..e477a0cd7 100644 --- a/library/std/src/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -59,9 +59,17 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 #[cfg(target_arch = "m68k")] const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1 -#[cfg(any(target_arch = "mips", target_arch = "mips64"))] +#[cfg(any( + target_arch = "mips", + target_arch = "mips32r6", + target_arch = "mips64", + target_arch = "mips64r6" +))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 +#[cfg(target_arch = "csky")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 diff --git a/library/std/src/personality.rs b/library/std/src/sys/personality/mod.rs index 386a399f5..386a399f5 100644 --- a/library/std/src/personality.rs +++ b/library/std/src/sys/personality/mod.rs diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs index 5da0257f3..86f4c7d3d 100644 --- a/library/std/src/sys/sgx/os.rs +++ b/library/std/src/sys/sgx/os.rs @@ -96,14 +96,61 @@ fn create_env_store() -> &'static EnvStore { unsafe { &*(ENV.load(Ordering::Relaxed) as *const EnvStore) } } -pub type Env = vec::IntoIter<(OsString, OsString)>; +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} pub fn env() -> Env { let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() }; - get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default().into_iter() + let iter = get_env_store() + .map(|env| clone_to_vec(&env.lock().unwrap())) + .unwrap_or_default() + .into_iter(); + Env { iter } } pub fn getenv(k: &OsStr) -> Option<OsString> { diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs index 1608b8cb6..7ac9d1d64 100644 --- a/library/std/src/sys/sgx/thread.rs +++ b/library/std/src/sys/sgx/thread.rs @@ -121,8 +121,16 @@ impl Thread { rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } + /// SGX should protect in-enclave data from the outside (attacker), + /// so there should be no data leakage to the OS, + /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. + /// + /// This is why the method is intentionally No-Op. pub fn set_name(_name: &CStr) { - // FIXME: could store this pointer in TLS somewhere + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic (target-agnostic) Rust thread code. + // This can be observed in the [`std::thread::tests::test_named_thread`] test, + // which succeeds as-is with the SGX target. } pub fn sleep(dur: Duration) { diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 6135921f0..9f4e66d62 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -81,10 +81,42 @@ pub fn current_exe() -> io::Result<PathBuf> { static ENV_LOCK: RwLock<()> = RwLock::new(()); +pub fn env_read_lock() -> impl Drop { + ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner) +} + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + impl !Send for Env {} impl !Sync for Env {} @@ -106,7 +138,7 @@ pub fn env() -> Env { } unsafe { - let _guard = ENV_LOCK.read(); + let _guard = env_read_lock(); let mut result = Vec::new(); if !environ.is_null() { while !(*environ).is_null() { @@ -140,17 +172,21 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - let s = run_with_cstr(k.as_bytes(), |k| { - let _guard = ENV_LOCK.read(); - Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) - }) - .ok()?; + run_with_cstr(k.as_bytes(), |k| { + let _guard = env_read_lock(); + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } + }) + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs index 2bf80d7a4..5346d2291 100644 --- a/library/std/src/sys/unix/cmath.rs +++ b/library/std/src/sys/unix/cmath.rs @@ -30,4 +30,8 @@ extern "C" { pub fn tanf(n: f32) -> f32; pub fn tanh(n: f64) -> f64; pub fn tanhf(n: f32) -> f32; + pub fn tgamma(n: f64) -> f64; + pub fn tgammaf(n: f32) -> f32; + pub fn lgamma_r(n: f64, s: &mut i32) -> f64; + pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index fbc7f04ce..a5604c92a 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -7,17 +7,6 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", - target_os = "nto", - target_os = "vita", -))] -use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -712,22 +701,10 @@ impl Iterator for ReadDir { // requires the full extent of *entry_ptr to be in bounds of the same // allocation, which is not necessarily the case here. // - // Absent any other way to obtain a pointer to `(*entry_ptr).d_name` - // legally in Rust analogously to how it would be done in C, we instead - // need to make our own non-libc allocation that conforms to the weird - // imaginary definition of dirent64, and use that for a field offset - // computation. + // Instead we must access fields individually through their offsets. macro_rules! offset_ptr { ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = { - let delusion = MaybeUninit::<dirent64>::uninit(); - let entry_ptr = delusion.as_ptr(); - unsafe { - ptr::addr_of!((*entry_ptr).$field) - .cast::<u8>() - .offset_from(entry_ptr.cast::<u8>()) - } - }; + const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; if true { // Cast to the same type determined by the else branch. $entry_ptr.byte_offset(OFFSET).cast::<_>() @@ -1227,6 +1204,7 @@ impl File { self.0.write_vectored_at(bufs, offset) } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs index 7d49bbdcb..4d17a1b00 100644 --- a/library/std/src/sys/unix/kernel_copy.rs +++ b/library/std/src/sys/unix/kernel_copy.rs @@ -89,6 +89,12 @@ enum FdMeta { NoneObtained, } +#[derive(PartialEq)] +enum FdHandle { + Input, + Output, +} + impl FdMeta { fn maybe_fifo(&self) -> bool { match self { @@ -114,12 +120,14 @@ impl FdMeta { } } - fn copy_file_range_candidate(&self) -> bool { + fn copy_file_range_candidate(&self, f: FdHandle) -> bool { match self { // copy_file_range will fail on empty procfs files. `read` can determine whether EOF has been reached // without extra cost and skip the write, thus there is no benefit in attempting copy_file_range - FdMeta::Metadata(meta) if meta.is_file() && meta.len() > 0 => true, - FdMeta::NoneObtained => true, + FdMeta::Metadata(meta) if f == FdHandle::Input && meta.is_file() && meta.len() > 0 => { + true + } + FdMeta::Metadata(meta) if f == FdHandle::Output && meta.is_file() => true, _ => false, } } @@ -197,7 +205,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> { written += flush()?; let max_write = reader.min_limit(); - if input_meta.copy_file_range_candidate() && output_meta.copy_file_range_candidate() { + if input_meta.copy_file_range_candidate(FdHandle::Input) + && output_meta.copy_file_range_candidate(FdHandle::Output) + { let result = copy_regular_files(readfd, writefd, max_write); result.update_take(reader); diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 326f1481e..77ef086f2 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -110,6 +110,11 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 { match errno() { libc::EINTR => continue, + #[cfg(target_vendor = "unikraft")] + libc::ENOSYS => { + // Not all configurations of Unikraft enable `LIBPOSIX_EVENT`. + break 'poll; + } libc::EINVAL | libc::EAGAIN | libc::ENOMEM => { // RLIMIT_NOFILE or temporary allocation failures // may be preventing use of poll(), fall back to fcntl @@ -165,7 +170,14 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] + #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", + // Unikraft's `signal` implementation is currently broken: + // https://github.com/unikraft/lib-musl/issues/57 + target_vendor = "unikraft", + )))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index a68c14758..57e1a36da 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -495,6 +495,34 @@ pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + impl !Send for Env {} impl !Sync for Env {} @@ -566,16 +594,21 @@ pub fn env() -> Env { pub fn getenv(k: &OsStr) -> Option<OsString> { // environment variables with a nul byte can't be set, so their value is // always None as well - let s = run_with_cstr(k.as_bytes(), |k| { + run_with_cstr(k.as_bytes(), |k| { let _guard = env_read_lock(); - Ok(unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char) + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } }) - .ok()?; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs index f7333fd5a..463b0a275 100644 --- a/library/std/src/sys/unix/os_str.rs +++ b/library/std/src/sys/unix/os_str.rs @@ -96,6 +96,16 @@ impl AsInner<[u8]> for Buf { } impl Buf { + #[inline] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self { + Self { inner: s } + } + pub fn from_string(s: String) -> Buf { Buf { inner: s.into_bytes() } } diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index e45c380a0..9931c2af2 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -235,7 +235,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(i64); impl ExitStatus { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 0ce93af66..3963e7f52 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -10,9 +10,6 @@ use core::ffi::NonZero_c_int; #[cfg(target_os = "linux")] use crate::os::linux::process::PidFd; -#[cfg(target_os = "linux")] -use crate::sys::weak::raw_syscall; - #[cfg(any( target_os = "macos", target_os = "watchos", @@ -91,6 +88,11 @@ impl Command { if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { return Ok((ret, ours)); } + + #[cfg(target_os = "linux")] + let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; + + #[cfg(not(target_os = "linux"))] let (input, output) = sys::pipe::anon_pipe()?; // Whatever happens after the fork is almost for sure going to touch or @@ -104,12 +106,16 @@ impl Command { // The child calls `mem::forget` to leak the lock, which is crucial because // releasing a lock is not async-signal-safe. let env_lock = sys::os::env_read_lock(); - let (pid, pidfd) = unsafe { self.do_fork()? }; + let pid = unsafe { self.do_fork()? }; if pid == 0 { crate::panic::always_abort(); mem::forget(env_lock); // avoid non-async-signal-safe unlocking drop(input); + #[cfg(target_os = "linux")] + if self.get_create_pidfd() { + self.send_pidfd(&output); + } let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) }; let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; let errno = errno.to_be_bytes(); @@ -133,6 +139,12 @@ impl Command { drop(env_lock); drop(output); + #[cfg(target_os = "linux")] + let pidfd = if self.get_create_pidfd() { self.recv_pidfd(&input) } else { -1 }; + + #[cfg(not(target_os = "linux"))] + let pidfd = -1; + // Safety: We obtained the pidfd from calling `clone3` with // `CLONE_PIDFD` so it's valid an otherwise unowned. let mut p = unsafe { Process::new(pid, pidfd) }; @@ -160,6 +172,7 @@ impl Command { } Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + // similarly SOCK_SEQPACKET messages should arrive whole assert!(p.wait().is_ok(), "wait() should either return Ok or panic"); panic!("short read on the CLOEXEC pipe") } @@ -185,20 +198,19 @@ impl Command { ); #[cfg(any(target_os = "tvos", target_os = "watchos"))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { return Err(Self::ERR_APPLE_TV_WATCH_NO_FORK_EXEC); } // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. #[cfg(not(any( - target_os = "linux", target_os = "watchos", target_os = "tvos", all(target_os = "nto", target_env = "nto71"), )))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { - cvt(libc::fork()).map(|res| (res, -1)) + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { + cvt(libc::fork()) } // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened @@ -206,7 +218,7 @@ impl Command { // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html #[cfg(all(target_os = "nto", target_env = "nto71"))] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + unsafe fn do_fork(&mut self) -> Result<pid_t, io::Error> { use crate::sys::os::errno; let mut delay = MIN_FORKSPAWN_SLEEP; @@ -229,91 +241,11 @@ impl Command { delay *= 2; continue; } else { - return cvt(r).map(|res| (res, -1)); + return cvt(r); } } } - // Attempts to fork the process. If successful, returns Ok((0, -1)) - // in the child, and Ok((child_pid, child_pidfd)) in the parent. - #[cfg(target_os = "linux")] - unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { - use crate::sync::atomic::{AtomicBool, Ordering}; - - static HAS_CLONE3: AtomicBool = AtomicBool::new(true); - const CLONE_PIDFD: u64 = 0x00001000; - - #[repr(C)] - struct clone_args { - flags: u64, - pidfd: u64, - child_tid: u64, - parent_tid: u64, - exit_signal: u64, - stack: u64, - stack_size: u64, - tls: u64, - set_tid: u64, - set_tid_size: u64, - cgroup: u64, - } - - raw_syscall! { - fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long - } - - // Bypassing libc for `clone3` can make further libc calls unsafe, - // so we use it sparingly for now. See #89522 for details. - // Some tools (e.g. sandboxing tools) may also expect `fork` - // rather than `clone3`. - let want_clone3_pidfd = self.get_create_pidfd(); - - // If we fail to create a pidfd for any reason, this will - // stay as -1, which indicates an error. - let mut pidfd: pid_t = -1; - - // Attempt to use the `clone3` syscall, which supports more arguments - // (in particular, the ability to create a pidfd). If this fails, - // we will fall through this block to a call to `fork()` - if want_clone3_pidfd && HAS_CLONE3.load(Ordering::Relaxed) { - let mut args = clone_args { - flags: CLONE_PIDFD, - pidfd: &mut pidfd as *mut pid_t as u64, - child_tid: 0, - parent_tid: 0, - exit_signal: libc::SIGCHLD as u64, - stack: 0, - stack_size: 0, - tls: 0, - set_tid: 0, - set_tid_size: 0, - cgroup: 0, - }; - - let args_ptr = &mut args as *mut clone_args; - let args_size = crate::mem::size_of::<clone_args>(); - - let res = cvt(clone3(args_ptr, args_size)); - match res { - Ok(n) => return Ok((n as pid_t, pidfd)), - Err(e) => match e.raw_os_error() { - // Multiple threads can race to execute this store, - // but that's fine - that just means that multiple threads - // will have tried and failed to execute the same syscall, - // with no other side effects. - Some(libc::ENOSYS) => HAS_CLONE3.store(false, Ordering::Relaxed), - // Fallback to fork if `EPERM` is returned. (e.g. blocked by seccomp) - Some(libc::EPERM) => {} - _ => return Err(e), - }, - } - } - - // Generally, we just call `fork`. If we get here after wanting `clone3`, - // then the syscall does not exist or we do not have permission to call it. - cvt(libc::fork()).map(|res| (res, pidfd)) - } - pub fn exec(&mut self, default: Stdio) -> io::Error { let envp = self.capture_env(); @@ -722,6 +654,115 @@ impl Command { Ok(Some(p)) } } + + #[cfg(target_os = "linux")] + fn send_pidfd(&self, sock: &crate::sys::net::Socket) { + use crate::io::IoSlice; + use crate::os::fd::RawFd; + use crate::sys::cvt_r; + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + let child_pid = libc::getpid(); + // pidfd_open sets CLOEXEC by default + let pidfd = libc::syscall(libc::SYS_pidfd_open, child_pid, 0); + + let fds: [c_int; 1] = [pidfd as RawFd]; + + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + + let mut cmsg: Cmsg = mem::zeroed(); + + // 0-length message to send through the socket so we can pass along the fd + let mut iov = [IoSlice::new(b"")]; + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; + msg.msg_control = &mut cmsg.buf as *mut _ as *mut _; + + // only attach cmsg if we successfully acquired the pidfd + if pidfd >= 0 { + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + (*hdr).cmsg_level = SOL_SOCKET; + (*hdr).cmsg_type = SCM_RIGHTS; + (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; + let data = CMSG_DATA(hdr); + crate::ptr::copy_nonoverlapping( + fds.as_ptr().cast::<u8>(), + data as *mut _, + SCM_MSG_LEN, + ); + } + + // we send the 0-length message even if we failed to acquire the pidfd + // so we get a consistent SEQPACKET order + match cvt_r(|| libc::sendmsg(sock.as_raw(), &msg, 0)) { + Ok(0) => {} + _ => rtabort!("failed to communicate with parent process"), + } + } + } + + #[cfg(target_os = "linux")] + fn recv_pidfd(&self, sock: &crate::sys::net::Socket) -> pid_t { + use crate::io::IoSliceMut; + use crate::sys::cvt_r; + + use libc::{CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, SCM_RIGHTS, SOL_SOCKET}; + + unsafe { + const SCM_MSG_LEN: usize = mem::size_of::<[c_int; 1]>(); + + #[repr(C)] + union Cmsg { + _buf: [u8; unsafe { CMSG_SPACE(SCM_MSG_LEN as u32) as usize }], + _align: libc::cmsghdr, + } + let mut cmsg: Cmsg = mem::zeroed(); + // 0-length read to get the fd + let mut iov = [IoSliceMut::new(&mut [])]; + + let mut msg: libc::msghdr = mem::zeroed(); + + msg.msg_iov = &mut iov as *mut _ as *mut _; + msg.msg_iovlen = 1; + msg.msg_controllen = mem::size_of::<Cmsg>() as _; + msg.msg_control = &mut cmsg as *mut _ as *mut _; + + match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, 0)) { + Err(_) => return -1, + Ok(_) => {} + } + + let hdr = CMSG_FIRSTHDR(&mut msg as *mut _ as *mut _); + if hdr.is_null() + || (*hdr).cmsg_level != SOL_SOCKET + || (*hdr).cmsg_type != SCM_RIGHTS + || (*hdr).cmsg_len != CMSG_LEN(SCM_MSG_LEN as _) as _ + { + return -1; + } + let data = CMSG_DATA(hdr); + + let mut fds = [-1 as c_int]; + + crate::ptr::copy_nonoverlapping( + data as *const _, + fds.as_mut_ptr().cast::<u8>(), + SCM_MSG_LEN, + ); + + fds[0] + } + } } //////////////////////////////////////////////////////////////////////////////// @@ -800,7 +841,7 @@ impl Process { // // This is not actually an "exit status" in Unix terminology. Rather, it is a "wait status". // See the discussion in comments and doc comments for `std::process::ExitStatus`. -#[derive(PartialEq, Eq, Clone, Copy)] +#[derive(PartialEq, Eq, Clone, Copy, Default)] pub struct ExitStatus(c_int); impl fmt::Debug for ExitStatus { diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs index e5e1f956b..6aa79e7f9 100644 --- a/library/std/src/sys/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/unix/process/process_unix/tests.rs @@ -60,3 +60,28 @@ fn test_command_fork_no_unwind() { || signal == libc::SIGSEGV ); } + +#[test] +#[cfg(target_os = "linux")] +fn test_command_pidfd() { + use crate::os::fd::RawFd; + use crate::os::linux::process::{ChildExt, CommandExt}; + use crate::process::Command; + + let our_pid = crate::process::id(); + let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; + let pidfd_open_available = if pidfd >= 0 { + unsafe { libc::close(pidfd as RawFd) }; + true + } else { + false + }; + + // always exercise creation attempts + let child = Command::new("echo").create_pidfd(true).spawn().unwrap(); + + // but only check if we know that the kernel supports pidfds + if pidfd_open_available { + assert!(child.pidfd().is_ok()) + } +} diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs index f28ca58d0..8e0b971af 100644 --- a/library/std/src/sys/unix/process/process_unsupported.rs +++ b/library/std/src/sys/unix/process/process_unsupported.rs @@ -55,7 +55,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c_int); impl ExitStatus { diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs index f70d3cb39..1ff2b2fb3 100644 --- a/library/std/src/sys/unix/process/process_vxworks.rs +++ b/library/std/src/sys/unix/process/process_vxworks.rs @@ -179,7 +179,7 @@ impl Process { } /// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c_int); impl ExitStatus { diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index d471be33e..fbf158f56 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -17,7 +17,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "openbsd"), - not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), not(target_os = "redox"), @@ -68,11 +67,25 @@ mod imp { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "freebsd")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + // FIXME: using the above when libary std's libc is updated + extern "C" { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" )))] fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false @@ -82,7 +95,8 @@ mod imp { target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" ))] fn getrandom_fill_bytes(v: &mut [u8]) -> bool { use crate::sync::atomic::{AtomicBool, Ordering}; @@ -222,7 +236,7 @@ mod imp { } } -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] +#[cfg(target_os = "netbsd")] mod imp { use crate::ptr; diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index a26f20795..97e75f1b5 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -54,6 +54,7 @@ impl io::Write for Stdout { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -81,6 +82,7 @@ impl io::Write for Stderr { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs index e150ae143..248b34829 100644 --- a/library/std/src/sys/unsupported/os.rs +++ b/library/std/src/sys/unsupported/os.rs @@ -65,10 +65,26 @@ pub fn current_exe() -> io::Result<PathBuf> { pub struct Env(!); +impl Env { + // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self(inner) = self; + match *inner {} + } +} + +impl fmt::Debug for Env { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(inner) = self; + match *inner {} + } +} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + let Self(inner) = self; + match *inner {} } } diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs index a494f2d6b..77b675aaa 100644 --- a/library/std/src/sys/unsupported/process.rs +++ b/library/std/src/sys/unsupported/process.rs @@ -99,58 +99,59 @@ impl fmt::Debug for Command { } } -pub struct ExitStatus(!); +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +#[non_exhaustive] +pub struct ExitStatus(); impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { - self.0 + Ok(()) } pub fn code(&self) -> Option<i32> { - self.0 + Some(0) } } -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - self.0 +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "<dummy exit status>") } } -impl Copy for ExitStatus {} +pub struct ExitStatusError(!); -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { +impl Clone for ExitStatusError { + fn clone(&self) -> ExitStatusError { self.0 } } -impl Eq for ExitStatus {} +impl Copy for ExitStatusError {} -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl PartialEq for ExitStatusError { + fn eq(&self, _other: &ExitStatusError) -> bool { self.0 } } -impl fmt::Display for ExitStatus { +impl Eq for ExitStatusError {} + +impl fmt::Debug for ExitStatusError { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatusError(ExitStatus); - impl Into<ExitStatus> for ExitStatusError { fn into(self) -> ExitStatus { - self.0.0 + self.0 } } impl ExitStatusError { pub fn code(self) -> Option<NonZeroI32> { - self.0.0 + self.0 } } diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 1b50c2ea6..d7295a799 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -16,14 +16,20 @@ pub struct WasiFd { fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { assert_eq!(mem::size_of::<IoSliceMut<'_>>(), mem::size_of::<wasi::Iovec>()); assert_eq!(mem::align_of::<IoSliceMut<'_>>(), mem::align_of::<wasi::Iovec>()); - // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout + // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. + // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. unsafe { mem::transmute(a) } } fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { assert_eq!(mem::size_of::<IoSlice<'_>>(), mem::size_of::<wasi::Ciovec>()); assert_eq!(mem::align_of::<IoSlice<'_>>(), mem::align_of::<wasi::Ciovec>()); - // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout + // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. + // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and + // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is + // guaranteed. unsafe { mem::transmute(a) } } diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index a22237080..98517da1d 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -29,8 +29,7 @@ pub mod fs; #[path = "../wasm/atomics/futex.rs"] pub mod futex; pub mod io; -#[path = "../unsupported/locks/mod.rs"] -pub mod locks; + pub mod net; pub mod os; #[path = "../unix/os_str.rs"] @@ -47,14 +46,27 @@ pub mod thread; pub mod thread_local_dtor; #[path = "../unsupported/thread_local_key.rs"] pub mod thread_local_key; -#[path = "../unsupported/thread_parking.rs"] -pub mod thread_parking; pub mod time; cfg_if::cfg_if! { - if #[cfg(not(target_feature = "atomics"))] { + if #[cfg(target_feature = "atomics")] { + #[path = "../unix/locks"] + pub mod locks { + #![allow(unsafe_op_in_unsafe_fn)] + mod futex_condvar; + mod futex_mutex; + mod futex_rwlock; + pub(crate) use futex_condvar::Condvar; + pub(crate) use futex_mutex::Mutex; + pub(crate) use futex_rwlock::RwLock; + } + } else { + #[path = "../unsupported/locks/mod.rs"] + pub mod locks; #[path = "../unsupported/once.rs"] pub mod once; + #[path = "../unsupported/thread_parking.rs"] + pub mod thread_parking; } } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index 9919dc708..d53bddd8e 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -142,10 +142,39 @@ impl StdError for JoinPathsError { pub fn current_exe() -> io::Result<PathBuf> { unsupported() } + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() + } +} + impl !Send for Env {} impl !Sync for Env {} @@ -196,16 +225,23 @@ pub fn env() -> Env { } pub fn getenv(k: &OsStr) -> Option<OsString> { - let s = run_with_cstr(k.as_bytes(), |k| unsafe { + // environment variables with a nul byte can't be set, so their value is + // always None as well + run_with_cstr(k.as_bytes(), |k| { let _guard = env_read_lock(); - Ok(libc::getenv(k.as_ptr()) as *const libc::c_char) + let v = unsafe { libc::getenv(k.as_ptr()) } as *const libc::c_char; + + if v.is_null() { + Ok(None) + } else { + // SAFETY: `v` cannot be mutated while executing this line since we've a read lock + let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec(); + + Ok(Some(OsStringExt::from_vec(bytes))) + } }) - .ok()?; - if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(unsafe { CStr::from_ptr(s) }.to_bytes().to_vec())) - } + .ok() + .flatten() } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { @@ -224,6 +260,11 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { }) } +#[allow(dead_code)] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + pub fn temp_dir() -> PathBuf { panic!("no filesystem on wasm") } diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs index e7a6ab4be..a0eefa881 100644 --- a/library/std/src/sys/wasi/thread.rs +++ b/library/std/src/sys/wasi/thread.rs @@ -1,5 +1,3 @@ -#![deny(unsafe_op_in_unsafe_fn)] - use crate::ffi::CStr; use crate::io; use crate::mem; @@ -7,14 +5,124 @@ use crate::num::NonZeroUsize; use crate::sys::unsupported; use crate::time::Duration; -pub struct Thread(!); +cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + use crate::cmp; + use crate::ptr; + use crate::sys::os; + // Add a few symbols not in upstream `libc` just yet. + mod libc { + pub use crate::ffi; + pub use crate::mem; + pub use libc::*; + + // defined in wasi-libc + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 + #[repr(C)] + union pthread_attr_union { + __i: [ffi::c_int; if mem::size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if mem::size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if mem::size_of::<ffi::c_long>() == 8 { 7 } else { 9 }], + } + + #[repr(C)] + pub struct pthread_attr_t { + __u: pthread_attr_union, + } + + #[allow(non_camel_case_types)] + pub type pthread_t = *mut ffi::c_void; + + extern "C" { + pub fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, + value: *mut ffi::c_void, + ) -> ffi::c_int; + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stack_size: libc::size_t, + ) -> ffi::c_int; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; + } + } + + pub struct Thread { + id: libc::pthread_t, + } + + impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } + } + } else { + pub struct Thread(!); + } +} pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { - unsupported() + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(libc::pthread_attr_init(&mut attr), 0); + + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); + + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); + } + }; + + let ret = libc::pthread_create(&mut native, &attr, thread_start, p 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. + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + + 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)); + 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 { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); + } + ptr::null_mut() + } + } + } else { + pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { + unsupported() + } + } } pub fn yield_now() { @@ -62,7 +170,19 @@ impl Thread { } pub fn join(self) { - self.0 + cfg_if::cfg_if! { + if #[cfg(target_feature = "atomics")] { + unsafe { + let ret = libc::pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } + } else { + self.0 + } + } } } diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 43ab8c7ee..1b2a86f3c 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -1,6 +1,6 @@ #![cfg(not(test))] -use libc::{c_double, c_float}; +use libc::{c_double, c_float, c_int}; extern "C" { pub fn acos(n: c_double) -> c_double; @@ -23,6 +23,10 @@ extern "C" { pub fn sinh(n: c_double) -> c_double; pub fn tan(n: c_double) -> c_double; pub fn tanh(n: c_double) -> c_double; + pub fn tgamma(n: c_double) -> c_double; + pub fn tgammaf(n: c_float) -> c_float; + pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double; + pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float; } pub use self::shims::*; diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 4fe95d411..e28dd4935 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -69,10 +69,7 @@ unsafe extern "C" fn init() { /// Helper macro for creating CStrs from literals and symbol names. macro_rules! ansi_str { - (sym $ident:ident) => {{ - #[allow(unused_unsafe)] - crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) - }}; + (sym $ident:ident) => {{ crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) }}; ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }}; } diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index d7adeb266..58afca088 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -25,10 +25,6 @@ pub fn errno() -> i32 { /// Gets a detailed string description for the given error number. pub fn error_string(mut errnum: i32) -> String { - // This value is calculated from the macro - // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT) - let langId = 0x0800 as c::DWORD; - let mut buf = [0 as c::WCHAR; 2048]; unsafe { @@ -56,13 +52,13 @@ pub fn error_string(mut errnum: i32) -> String { flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, module, errnum as c::DWORD, - langId, + 0, buf.as_mut_ptr(), buf.len() as c::DWORD, ptr::null(), ) as usize; if res == 0 { - // Sometimes FormatMessageW can fail e.g., system doesn't like langId, + // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, let fm_err = errno(); return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); } @@ -85,25 +81,69 @@ pub fn error_string(mut errnum: i32) -> String { pub struct Env { base: c::LPWCH, - cur: c::LPWCH, + iter: EnvIterator, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + iter: &'a EnvIterator, +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + let iter: EnvIterator = (*iter).clone(); + let mut list = f.debug_list(); + for (a, b) in iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +impl Env { + pub fn str_debug(&self) -> impl fmt::Debug + '_ { + let Self { base: _, iter } = self; + EnvStrDebug { iter } + } +} + +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { base: _, iter } = self; + f.debug_list().entries(iter.clone()).finish() + } } impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { + let Self { base: _, iter } = self; + iter.next() + } +} + +#[derive(Clone)] +struct EnvIterator(c::LPWCH); + +impl Iterator for EnvIterator { + type Item = (OsString, OsString); + + fn next(&mut self) -> Option<(OsString, OsString)> { + let Self(cur) = self; loop { unsafe { - if *self.cur == 0 { + if **cur == 0 { return None; } - let p = self.cur as *const u16; + let p = *cur as *const u16; let mut len = 0; while *p.add(len) != 0 { len += 1; } let s = slice::from_raw_parts(p, len); - self.cur = self.cur.add(len + 1); + *cur = cur.add(len + 1); // Windows allows environment variables to start with an equals // symbol (in any other position, this is the separator between @@ -137,7 +177,7 @@ pub fn env() -> Env { if ch.is_null() { panic!("failure getting env string from OS: {}", io::Error::last_os_error()); } - Env { base: ch, cur: ch } + Env { base: ch, iter: EnvIterator(ch) } } } diff --git a/library/std/src/sys/windows/os_str.rs b/library/std/src/sys/windows/os_str.rs index 16c4f55c6..4708657a9 100644 --- a/library/std/src/sys/windows/os_str.rs +++ b/library/std/src/sys/windows/os_str.rs @@ -63,6 +63,16 @@ impl fmt::Display for Slice { } impl Buf { + #[inline] + pub fn into_os_str_bytes(self) -> Vec<u8> { + self.inner.into_bytes() + } + + #[inline] + pub unsafe fn from_os_str_bytes_unchecked(s: Vec<u8>) -> Self { + Self { inner: Wtf8Buf::from_bytes_unchecked(s) } + } + pub fn with_capacity(capacity: usize) -> Buf { Buf { inner: Wtf8Buf::with_capacity(capacity) } } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index e3493cbb8..2dd0c67ac 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -652,7 +652,7 @@ impl Process { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] pub struct ExitStatus(c::DWORD); impl ExitStatus { diff --git a/library/std/src/sys/windows/thread_local_dtor.rs b/library/std/src/sys/windows/thread_local_dtor.rs index 9707a95df..cf542d2bf 100644 --- a/library/std/src/sys/windows/thread_local_dtor.rs +++ b/library/std/src/sys/windows/thread_local_dtor.rs @@ -4,29 +4,4 @@ #![unstable(feature = "thread_local_internals", issue = "none")] #![cfg(target_thread_local)] -// Using a per-thread list avoids the problems in synchronizing global state. -#[thread_local] -static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); - -// Ensure this can never be inlined because otherwise this may break in dylibs. -// See #44391. -#[inline(never)] -pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { - DESTRUCTORS.push((t, dtor)); -} - -#[inline(never)] // See comment above -/// Runs destructors. This should not be called until thread exit. -pub unsafe fn run_keyless_dtors() { - // Drop all the destructors. - // - // Note: While this is potentially an infinite loop, it *should* be - // the case that this loop always terminates because we provide the - // guarantee that a TLS key cannot be set after it is flagged for - // destruction. - while let Some((ptr, dtor)) = DESTRUCTORS.pop() { - (dtor)(ptr); - } - // We're done so free the memory. - DESTRUCTORS = Vec::new(); -} +pub use super::thread_local_key::register_keyless_dtor as register_dtor; diff --git a/library/std/src/sys/windows/thread_local_key.rs b/library/std/src/sys/windows/thread_local_key.rs index 17628b757..036d96596 100644 --- a/library/std/src/sys/windows/thread_local_key.rs +++ b/library/std/src/sys/windows/thread_local_key.rs @@ -1,7 +1,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{ - AtomicPtr, AtomicU32, + AtomicBool, AtomicPtr, AtomicU32, Ordering::{AcqRel, Acquire, Relaxed, Release}, }; use crate::sys::c; @@ -9,6 +9,41 @@ use crate::sys::c; #[cfg(test)] mod tests; +/// An optimization hint. The compiler is often smart enough to know if an atomic +/// is never set and can remove dead code based on that fact. +static HAS_DTORS: AtomicBool = AtomicBool::new(false); + +// Using a per-thread list avoids the problems in synchronizing global state. +#[thread_local] +#[cfg(target_thread_local)] +static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new(); + +// Ensure this can never be inlined because otherwise this may break in dylibs. +// See #44391. +#[inline(never)] +#[cfg(target_thread_local)] +pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + DESTRUCTORS.push((t, dtor)); + HAS_DTORS.store(true, Relaxed); +} + +#[inline(never)] // See comment above +#[cfg(target_thread_local)] +/// Runs destructors. This should not be called until thread exit. +unsafe fn run_keyless_dtors() { + // Drop all the destructors. + // + // Note: While this is potentially an infinite loop, it *should* be + // the case that this loop always terminates because we provide the + // guarantee that a TLS key cannot be set after it is flagged for + // destruction. + while let Some((ptr, dtor)) = DESTRUCTORS.pop() { + (dtor)(ptr); + } + // We're done so free the memory. + DESTRUCTORS = Vec::new(); +} + type Key = c::DWORD; type Dtor = unsafe extern "C" fn(*mut u8); @@ -156,6 +191,8 @@ static DTORS: AtomicPtr<StaticKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. unsafe fn register_dtor(key: &'static StaticKey) { + // Ensure this is never run when native thread locals are available. + assert_eq!(false, cfg!(target_thread_local)); let this = <*const StaticKey>::cast_mut(key); // Use acquire ordering to pass along the changes done by the previously // registered keys when we store the new head with release ordering. @@ -167,6 +204,7 @@ unsafe fn register_dtor(key: &'static StaticKey) { Err(new) => head = new, } } + HAS_DTORS.store(true, Release); } // ------------------------------------------------------------------------- @@ -240,10 +278,14 @@ pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c:: #[allow(dead_code, unused_variables)] unsafe extern "system" fn on_tls_callback(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID) { + if !HAS_DTORS.load(Acquire) { + return; + } if dwReason == c::DLL_THREAD_DETACH || dwReason == c::DLL_PROCESS_DETACH { + #[cfg(not(target_thread_local))] run_dtors(); #[cfg(target_thread_local)] - super::thread_local_dtor::run_keyless_dtors(); + run_keyless_dtors(); } // See comments above for what this is doing. Note that we don't need this diff --git a/library/std/src/sys/windows/thread_local_key/tests.rs b/library/std/src/sys/windows/thread_local_key/tests.rs index c95f383fb..c739f0caf 100644 --- a/library/std/src/sys/windows/thread_local_key/tests.rs +++ b/library/std/src/sys/windows/thread_local_key/tests.rs @@ -1,3 +1,7 @@ +// This file only tests the thread local key fallback. +// Windows targets with native thread local support do not use this. +#![cfg(not(target_thread_local))] + use super::StaticKey; use crate::ptr; diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index 6f020940d..84e2c5d8d 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -60,6 +60,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: bt_fmt.add_context()?; let mut idx = 0; let mut res = Ok(()); + let mut omitted_count: usize = 0; + let mut first_omit = true; // Start immediately if we're not using a short backtrace. let mut start = print_fmt != PrintFmt::Short; backtrace_rs::trace_unsynchronized(|frame| { @@ -85,10 +87,27 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: start = true; return; } + if !start { + omitted_count += 1; + } } } if start { + if omitted_count > 0 { + debug_assert!(print_fmt == PrintFmt::Short); + // only print the message between the middle of frames + if !first_omit { + let _ = writeln!( + bt_fmt.formatter(), + " [... omitted {} frame{} ...]", + omitted_count, + if omitted_count > 1 { "s" } else { "" } + ); + } + first_omit = false; + omitted_count = 0; + } res = bt_fmt.frame().symbol(frame, symbol); } }); diff --git a/library/std/src/sys_common/thread_info.rs b/library/std/src/sys_common/thread_info.rs index 38c9e5000..88d937a7d 100644 --- a/library/std/src/sys_common/thread_info.rs +++ b/library/std/src/sys_common/thread_info.rs @@ -1,5 +1,4 @@ #![allow(dead_code)] // stack_guard isn't used right now on all platforms -#![allow(unused_unsafe)] // thread_local with `const {}` triggers this liny use crate::cell::RefCell; use crate::sys::thread::guard::Guard; diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs index 15042fc3b..046674396 100644 --- a/library/std/src/sys_common/thread_parking/id.rs +++ b/library/std/src/sys_common/thread_parking/id.rs @@ -56,18 +56,14 @@ impl Parker { self.init_tid(); // Changes NOTIFIED to EMPTY and EMPTY to PARKED. - let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1); - if state == PARKED { + let state = self.state.fetch_sub(1, Acquire); + if state == EMPTY { // Loop to guard against spurious wakeups. - while state == PARKED { + // The state must be reset with acquire ordering to ensure that all + // calls to `unpark` synchronize with this thread. + while self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_err() { park(self.state.as_ptr().addr()); - state = self.state.load(Acquire); } - - // Since the state change has already been observed with acquire - // ordering, the state can be reset with a relaxed store instead - // of a swap. - self.state.store(EMPTY, Relaxed); } } @@ -78,8 +74,7 @@ impl Parker { if state == PARKED { park_timeout(dur, self.state.as_ptr().addr()); // Swap to ensure that we observe all state changes with acquire - // ordering, even if the state has been changed after the timeout - // occurred. + // ordering. self.state.swap(EMPTY, Acquire); } } diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index c9d3e13cf..67db5ebd8 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -182,6 +182,15 @@ impl Wtf8Buf { Wtf8Buf { bytes: Vec::with_capacity(capacity), is_known_utf8: true } } + /// Creates a WTF-8 string from a WTF-8 byte vec. + /// + /// Since the byte vec is not checked for valid WTF-8, this functions is + /// marked unsafe. + #[inline] + pub unsafe fn from_bytes_unchecked(value: Vec<u8>) -> Wtf8Buf { + Wtf8Buf { bytes: value, is_known_utf8: false } + } + /// Creates a WTF-8 string from a UTF-8 `String`. /// /// This takes ownership of the `String` and does not copy. @@ -402,6 +411,12 @@ impl Wtf8Buf { self.bytes.truncate(new_len) } + /// Consumes the WTF-8 string and tries to convert it to a vec of bytes. + #[inline] + pub fn into_bytes(self) -> Vec<u8> { + self.bytes + } + /// Consumes the WTF-8 string and tries to convert it to UTF-8. /// /// This does not copy the data. @@ -444,6 +459,7 @@ impl Wtf8Buf { /// Converts this `Wtf8Buf` into a boxed `Wtf8`. #[inline] pub fn into_box(self) -> Box<Wtf8> { + // SAFETY: relies on `Wtf8` being `repr(transparent)`. unsafe { mem::transmute(self.bytes.into_boxed_slice()) } } @@ -496,6 +512,7 @@ impl Extend<CodePoint> for Wtf8Buf { /// Similar to `&str`, but can additionally contain surrogate code points /// if they’re not in a surrogate pair. #[derive(Eq, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] pub struct Wtf8 { bytes: [u8], } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 1b86d898c..09994e47f 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -313,7 +313,6 @@ impl<T: 'static> LocalKey<Cell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::Cell; /// /// thread_local! { @@ -326,7 +325,7 @@ impl<T: 'static> LocalKey<Cell<T>> { /// /// assert_eq!(X.get(), 123); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn set(&'static self, value: T) { self.initialize_with(Cell::new(value), |value, cell| { if let Some(value) = value { @@ -351,7 +350,6 @@ impl<T: 'static> LocalKey<Cell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::Cell; /// /// thread_local! { @@ -360,7 +358,7 @@ impl<T: 'static> LocalKey<Cell<T>> { /// /// assert_eq!(X.get(), 1); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn get(&'static self) -> T where T: Copy, @@ -381,7 +379,6 @@ impl<T: 'static> LocalKey<Cell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::Cell; /// /// thread_local! { @@ -391,7 +388,7 @@ impl<T: 'static> LocalKey<Cell<T>> { /// assert_eq!(X.take(), Some(1)); /// assert_eq!(X.take(), None); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn take(&'static self) -> T where T: Default, @@ -412,7 +409,6 @@ impl<T: 'static> LocalKey<Cell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::Cell; /// /// thread_local! { @@ -422,7 +418,7 @@ impl<T: 'static> LocalKey<Cell<T>> { /// assert_eq!(X.replace(2), 1); /// assert_eq!(X.replace(3), 2); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn replace(&'static self, value: T) -> T { self.with(|cell| cell.replace(value)) } @@ -444,7 +440,6 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// # Example /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::RefCell; /// /// thread_local! { @@ -453,7 +448,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// /// X.with_borrow(|v| assert!(v.is_empty())); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn with_borrow<F, R>(&'static self, f: F) -> R where F: FnOnce(&T) -> R, @@ -476,7 +471,6 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// # Example /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::RefCell; /// /// thread_local! { @@ -487,7 +481,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// /// X.with_borrow(|v| assert_eq!(*v, vec![1])); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn with_borrow_mut<F, R>(&'static self, f: F) -> R where F: FnOnce(&mut T) -> R, @@ -511,7 +505,6 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::RefCell; /// /// thread_local! { @@ -524,7 +517,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3])); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn set(&'static self, value: T) { self.initialize_with(RefCell::new(value), |value, cell| { if let Some(value) = value { @@ -551,7 +544,6 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::RefCell; /// /// thread_local! { @@ -566,7 +558,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// /// X.with_borrow(|v| assert!(v.is_empty())); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn take(&'static self) -> T where T: Default, @@ -586,7 +578,6 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// # Examples /// /// ``` - /// #![feature(local_key_cell_methods)] /// use std::cell::RefCell; /// /// thread_local! { @@ -598,7 +589,7 @@ impl<T: 'static> LocalKey<RefCell<T>> { /// /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3])); /// ``` - #[unstable(feature = "local_key_cell_methods", issue = "92122")] + #[stable(feature = "local_key_cell_methods", since = "1.73.0")] pub fn replace(&'static self, value: T) -> T { self.with(|cell| cell.replace(value)) } diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs new file mode 100644 index 000000000..46dc9ff00 --- /dev/null +++ b/library/std/tests/process_spawning.rs @@ -0,0 +1,38 @@ +#![cfg(not(target_env="sgx"))] + +use std::env; +use std::fs; +use std::process; +use std::str; + +mod common; + +#[test] +fn issue_15149() { + // If we're the parent, copy our own binary to a new directory. + let my_path = env::current_exe().unwrap(); + + let temp = common::tmpdir(); + let child_dir = temp.join("issue-15140-child"); + fs::create_dir_all(&child_dir).unwrap(); + + let child_path = child_dir.join(&format!("mytest{}", env::consts::EXE_SUFFIX)); + fs::copy(&my_path, &child_path).unwrap(); + + // Append the new directory to our own PATH. + let path = { + let mut paths: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap()).collect(); + paths.push(child_dir.to_path_buf()); + env::join_paths(paths).unwrap() + }; + + let child_output = + process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + + assert!( + child_output.status.success(), + "child assertion failed\n child stdout:\n {}\n child stderr:\n {}", + str::from_utf8(&child_output.stdout).unwrap(), + str::from_utf8(&child_output.stderr).unwrap() + ); +} diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs new file mode 100644 index 000000000..2605664d2 --- /dev/null +++ b/library/std/tests/switch-stdout.rs @@ -0,0 +1,53 @@ +#![cfg(any(target_family = "unix", target_family = "windows"))] + +use std::fs::File; +use std::io::{Read, Write}; + +mod common; + +#[cfg(unix)] +fn switch_stdout_to(file: File) { + use std::os::unix::prelude::*; + + extern "C" { + fn dup2(old: i32, new: i32) -> i32; + } + + unsafe { + assert_eq!(dup2(file.as_raw_fd(), 1), 1); + } +} + +#[cfg(windows)] +fn switch_stdout_to(file: File) { + use std::os::windows::prelude::*; + + extern "system" { + fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32; + } + + const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32; + + unsafe { + let rc = SetStdHandle(STD_OUTPUT_HANDLE, file.into_raw_handle() as *mut _); + assert!(rc != 0); + } +} + +#[test] +fn switch_stdout() { + let temp = common::tmpdir(); + let path = temp.join("switch-stdout-output"); + let f = File::create(&path).unwrap(); + + let mut stdout = std::io::stdout(); + stdout.write(b"foo\n").unwrap(); + stdout.flush().unwrap(); + switch_stdout_to(f); + stdout.write(b"bar\n").unwrap(); + stdout.flush().unwrap(); + + let mut contents = String::new(); + File::open(&path).unwrap().read_to_string(&mut contents).unwrap(); + assert_eq!(contents, "bar\n"); +} |