summaryrefslogtreecommitdiffstats
path: root/library/std/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /library/std/src
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/alloc.rs1
-rw-r--r--library/std/src/backtrace.rs59
-rw-r--r--library/std/src/backtrace/tests.rs13
-rw-r--r--library/std/src/collections/hash/map.rs6
-rw-r--r--library/std/src/collections/hash/set.rs11
-rw-r--r--library/std/src/env.rs13
-rw-r--r--library/std/src/env/tests.rs20
-rw-r--r--library/std/src/error.rs16
-rw-r--r--library/std/src/error/tests.rs4
-rw-r--r--library/std/src/f32.rs57
-rw-r--r--library/std/src/f32/tests.rs32
-rw-r--r--library/std/src/f64.rs83
-rw-r--r--library/std/src/f64/tests.rs32
-rw-r--r--library/std/src/ffi/mod.rs2
-rw-r--r--library/std/src/ffi/os_str.rs73
-rw-r--r--library/std/src/fs.rs100
-rw-r--r--library/std/src/fs/tests.rs2
-rw-r--r--library/std/src/io/copy.rs81
-rw-r--r--library/std/src/io/copy/tests.rs38
-rw-r--r--library/std/src/io/readbuf.rs8
-rw-r--r--library/std/src/io/util.rs100
-rw-r--r--library/std/src/io/util/tests.rs13
-rw-r--r--library/std/src/lib.rs20
-rw-r--r--library/std/src/net/tcp.rs2
-rw-r--r--library/std/src/os/android/raw.rs107
-rw-r--r--library/std/src/os/l4re/raw.rs1
-rw-r--r--library/std/src/os/linux/raw.rs4
-rw-r--r--library/std/src/os/raw/mod.rs5
-rw-r--r--library/std/src/os/unix/fs.rs40
-rw-r--r--library/std/src/os/unix/net/stream.rs1
-rw-r--r--library/std/src/os/unix/net/tests.rs18
-rw-r--r--library/std/src/panicking.rs6
-rw-r--r--library/std/src/path.rs40
-rw-r--r--library/std/src/path/tests.rs50
-rw-r--r--library/std/src/process.rs19
-rw-r--r--library/std/src/sync/barrier.rs7
-rw-r--r--library/std/src/sync/condvar.rs18
-rw-r--r--library/std/src/sync/lazy_lock.rs37
-rw-r--r--library/std/src/sync/mpmc/utils.rs4
-rw-r--r--library/std/src/sync/mpsc/mod.rs5
-rw-r--r--library/std/src/sync/mutex.rs8
-rw-r--r--library/std/src/sync/once_lock.rs8
-rw-r--r--library/std/src/sync/rwlock.rs8
-rw-r--r--library/std/src/sys/common/alloc.rs3
-rw-r--r--library/std/src/sys/common/thread_local/fast_local.rs4
-rw-r--r--library/std/src/sys/common/thread_local/os_local.rs5
-rw-r--r--library/std/src/sys/common/thread_local/static_local.rs4
-rw-r--r--library/std/src/sys/hermit/fs.rs1
-rw-r--r--library/std/src/sys/hermit/os.rs28
-rw-r--r--library/std/src/sys/hermit/thread.rs3
-rw-r--r--library/std/src/sys/hermit/time.rs4
-rw-r--r--library/std/src/sys/mod.rs50
-rw-r--r--library/std/src/sys/personality/dwarf/eh.rs (renamed from library/std/src/personality/dwarf/eh.rs)0
-rw-r--r--library/std/src/sys/personality/dwarf/mod.rs (renamed from library/std/src/personality/dwarf/mod.rs)0
-rw-r--r--library/std/src/sys/personality/dwarf/tests.rs (renamed from library/std/src/personality/dwarf/tests.rs)0
-rw-r--r--library/std/src/sys/personality/emcc.rs (renamed from library/std/src/personality/emcc.rs)0
-rw-r--r--library/std/src/sys/personality/gcc.rs (renamed from library/std/src/personality/gcc.rs)10
-rw-r--r--library/std/src/sys/personality/mod.rs (renamed from library/std/src/personality.rs)0
-rw-r--r--library/std/src/sys/sgx/os.rs51
-rw-r--r--library/std/src/sys/sgx/thread.rs10
-rw-r--r--library/std/src/sys/solid/os.rs58
-rw-r--r--library/std/src/sys/unix/cmath.rs4
-rw-r--r--library/std/src/sys/unix/fs.rs28
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs18
-rw-r--r--library/std/src/sys/unix/mod.rs14
-rw-r--r--library/std/src/sys/unix/os.rs49
-rw-r--r--library/std/src/sys/unix/os_str.rs10
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs2
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs223
-rw-r--r--library/std/src/sys/unix/process/process_unix/tests.rs25
-rw-r--r--library/std/src/sys/unix/process/process_unsupported.rs2
-rw-r--r--library/std/src/sys/unix/process/process_vxworks.rs2
-rw-r--r--library/std/src/sys/unix/rand.rs22
-rw-r--r--library/std/src/sys/unix/stdio.rs2
-rw-r--r--library/std/src/sys/unsupported/os.rs18
-rw-r--r--library/std/src/sys/unsupported/process.rs37
-rw-r--r--library/std/src/sys/wasi/fd.rs10
-rw-r--r--library/std/src/sys/wasi/mod.rs22
-rw-r--r--library/std/src/sys/wasi/os.rs57
-rw-r--r--library/std/src/sys/wasi/thread.rs132
-rw-r--r--library/std/src/sys/windows/cmath.rs6
-rw-r--r--library/std/src/sys/windows/compat.rs5
-rw-r--r--library/std/src/sys/windows/os.rs62
-rw-r--r--library/std/src/sys/windows/os_str.rs10
-rw-r--r--library/std/src/sys/windows/process.rs2
-rw-r--r--library/std/src/sys/windows/thread_local_dtor.rs27
-rw-r--r--library/std/src/sys/windows/thread_local_key.rs46
-rw-r--r--library/std/src/sys/windows/thread_local_key/tests.rs4
-rw-r--r--library/std/src/sys_common/backtrace.rs19
-rw-r--r--library/std/src/sys_common/thread_info.rs1
-rw-r--r--library/std/src/sys_common/thread_parking/id.rs17
-rw-r--r--library/std/src/sys_common/wtf8.rs17
-rw-r--r--library/std/src/thread/local.rs27
93 files changed, 1748 insertions, 575 deletions
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))
}