diff options
Diffstat (limited to 'library/std/src')
97 files changed, 2470 insertions, 2945 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 15a7a770d1a..fc0fef620e3 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1875,12 +1875,7 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { } #[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: fmt::Debug, V: fmt::Debug> Error for OccupiedError<'a, K, V> { - #[allow(deprecated)] - fn description(&self) -> &str { - "key already exists" - } -} +impl<'a, K: Debug, V: Debug> Error for OccupiedError<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> { diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs index 889ed3c5380..6104a02c739 100644 --- a/library/std/src/collections/mod.rs +++ b/library/std/src/collections/mod.rs @@ -26,7 +26,7 @@ //! should be considered. Detailed discussions of strengths and weaknesses of //! individual collections can be found on their own documentation pages. //! -//! ### Use a `Vec` when: +//! ### Use a [`Vec`] when: //! * You want to collect items up to be processed or sent elsewhere later, and //! don't care about any properties of the actual values being stored. //! * You want a sequence of elements in a particular order, and will only be @@ -35,25 +35,25 @@ //! * You want a resizable array. //! * You want a heap-allocated array. //! -//! ### Use a `VecDeque` when: +//! ### Use a [`VecDeque`] when: //! * You want a [`Vec`] that supports efficient insertion at both ends of the //! sequence. //! * You want a queue. //! * You want a double-ended queue (deque). //! -//! ### Use a `LinkedList` when: +//! ### Use a [`LinkedList`] when: //! * You want a [`Vec`] or [`VecDeque`] of unknown size, and can't tolerate //! amortization. //! * You want to efficiently split and append lists. //! * You are *absolutely* certain you *really*, *truly*, want a doubly linked //! list. //! -//! ### Use a `HashMap` when: +//! ### Use a [`HashMap`] when: //! * You want to associate arbitrary keys with an arbitrary value. //! * You want a cache. //! * You want a map, with no extra functionality. //! -//! ### Use a `BTreeMap` when: +//! ### Use a [`BTreeMap`] when: //! * You want a map sorted by its keys. //! * You want to be able to get a range of entries on-demand. //! * You're interested in what the smallest or largest key-value pair is. @@ -65,7 +65,7 @@ //! * There is no meaningful value to associate with your keys. //! * You just want a set. //! -//! ### Use a `BinaryHeap` when: +//! ### Use a [`BinaryHeap`] when: //! //! * You want to store a bunch of elements, but only ever want to process the //! "biggest" or "most important" one at any given time. diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 9f17ff76445..e457cd61c75 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -296,15 +296,7 @@ impl fmt::Display for VarError { } #[stable(feature = "env", since = "1.0.0")] -impl Error for VarError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - VarError::NotPresent => "environment variable not found", - VarError::NotUnicode(..) => "environment variable was not valid unicode", - } - } -} +impl Error for VarError {} /// Sets the environment variable `key` to the value `value` for the currently running /// process. diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 8d7edc732af..1214490caad 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -137,7 +137,7 @@ impl OsString { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_unstable(feature = "const_pathbuf_osstring_new", issue = "141520")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] pub const fn new() -> OsString { OsString { inner: Buf::from_string(String::new()) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index a220a3f56e9..d9c9606fc1c 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3036,6 +3036,9 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// Entries for the current and parent directories (typically `.` and `..`) are /// skipped. /// +/// The order in which `read_dir` returns entries can change between calls. If reproducible +/// ordering is required, the entries should be explicitly sorted. +/// /// # Platform-specific behavior /// /// This function currently corresponds to the `opendir` function on Unix @@ -3119,7 +3122,7 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { /// On UNIX-like systems, this function will update the permission bits /// of the file pointed to by the symlink. /// -/// Note that this behavior can lead to privalage escalation vulnerabilities, +/// Note that this behavior can lead to privilege escalation vulnerabilities, /// where the ability to create a symlink in one directory allows you to /// cause the permissions of another file or directory to be modified. /// @@ -3156,6 +3159,25 @@ pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result fs_imp::set_permissions(path.as_ref(), perm.0) } +/// Set the permissions of a file, unless it is a symlink. +/// +/// Note that the non-final path elements are allowed to be symlinks. +/// +/// # Platform-specific behavior +/// +/// Currently unimplemented on Windows. +/// +/// On Unix platforms, this results in a [`FilesystemLoop`] error if the last element is a symlink. +/// +/// This behavior may change in the future. +/// +/// [`FilesystemLoop`]: crate::io::ErrorKind::FilesystemLoop +#[doc(alias = "chmod", alias = "SetFileAttributes")] +#[unstable(feature = "set_permissions_nofollow", issue = "141607")] +pub fn set_permissions_nofollow<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> { + fs_imp::set_permissions_nofollow(path.as_ref(), perm) +} + impl DirBuilder { /// Creates a new set of options with default mode/security settings for all /// platforms and also non-recursive. diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 574288e579e..9b600cd5575 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -122,7 +122,7 @@ impl Buffer { /// Remove bytes that have already been read from the buffer. pub fn backshift(&mut self) { - self.buf.copy_within(self.pos.., 0); + self.buf.copy_within(self.pos..self.filled, 0); self.filled -= self.pos; self.pos = 0; } diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 574eb83dc56..d569fed24c5 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -492,23 +492,15 @@ impl WriterPanicked { pub fn into_inner(self) -> Vec<u8> { self.buf } - - const DESCRIPTION: &'static str = - "BufWriter inner writer panicked, what data remains unwritten is not known"; } #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -impl error::Error for WriterPanicked { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - Self::DESCRIPTION - } -} +impl error::Error for WriterPanicked {} #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] impl fmt::Display for WriterPanicked { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", Self::DESCRIPTION) + "BufWriter inner writer panicked, what data remains unwritten is not known".fmt(f) } } diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs index 475d877528f..e36f2d92108 100644 --- a/library/std/src/io/buffered/mod.rs +++ b/library/std/src/io/buffered/mod.rs @@ -179,12 +179,7 @@ impl<W> From<IntoInnerError<W>> for Error { } #[stable(feature = "rust1", since = "1.0.0")] -impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - error::Error::description(self.error()) - } -} +impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> {} #[stable(feature = "rust1", since = "1.0.0")] impl<W> fmt::Display for IntoInnerError<W> { diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 15e962924ac..d060ad52897 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -63,10 +63,11 @@ where R: Read, W: Write, { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "android"))] { + cfg_select! { + any(target_os = "linux", target_os = "android") => { crate::sys::kernel_copy::copy_spec(reader, writer) - } else { + } + _ => { generic_copy(reader, writer) } } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 562fdbf4ff7..57a980d6acd 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -18,7 +18,7 @@ use crate::{error, fmt, result, sys}; /// This type is broadly used across [`std::io`] for any operation which may /// produce an error. /// -/// This typedef is generally used to avoid writing out [`io::Error`] directly and +/// This type alias is generally used to avoid writing out [`io::Error`] directly and /// is otherwise a direct mapping to [`Result`]. /// /// While usual Rust style is to import types directly, aliases of [`Result`] @@ -1049,15 +1049,6 @@ impl fmt::Display for Error { #[stable(feature = "rust1", since = "1.0.0")] impl error::Error for Error { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - match self.repr.data() { - ErrorData::Os(..) | ErrorData::Simple(..) => self.kind().as_str(), - ErrorData::SimpleMessage(msg) => msg.message, - ErrorData::Custom(c) => c.error.description(), - } - } - #[allow(deprecated)] fn cause(&self) -> Option<&dyn error::Error> { match self.repr.data() { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d351ee5e739..ff0e29e04c2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2461,7 +2461,7 @@ pub trait BufRead: Read { /// delimiter or EOF is found. /// /// If successful, this function will return the total number of bytes read, - /// including the delimiter byte. + /// including the delimiter byte if found. /// /// This is useful for efficiently skipping data such as NUL-terminated strings /// in binary file formats without buffering. @@ -2489,7 +2489,7 @@ pub trait BufRead: Read { /// ``` /// use std::io::{self, BufRead}; /// - /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0"); + /// let mut cursor = io::Cursor::new(b"Ferris\0Likes long walks on the beach\0Crustacean\0!"); /// /// // read name /// let mut name = Vec::new(); @@ -2509,6 +2509,11 @@ pub trait BufRead: Read { /// .expect("reading from cursor won't fail"); /// assert_eq!(num_bytes, 11); /// assert_eq!(animal, b"Crustacean\0"); + /// + /// // reach EOF + /// let num_bytes = cursor.skip_until(b'\0') + /// .expect("reading from cursor won't fail"); + /// assert_eq!(num_bytes, 1); /// ``` #[stable(feature = "bufread_skip_until", since = "1.83.0")] fn skip_until(&mut self, byte: u8) -> Result<usize> { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 07b38c65898..30a1b108817 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -15,7 +15,7 @@ //! //! If you already know the name of what you are looking for, the fastest way to //! find it is to use the <a href="#" onclick="window.searchState.focus();">search -//! bar</a> at the top of the page. +//! button</a> at the top of the page. //! //! Otherwise, you may want to jump to one of these useful sections: //! @@ -330,6 +330,7 @@ #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] +#![feature(cfg_select)] #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_cmp)] @@ -390,6 +391,7 @@ #![feature(try_with_capacity)] #![feature(unique_rc_arc)] #![feature(vec_into_raw_parts)] +#![feature(wtf8_internals)] // tidy-alphabetical-end // // Library features (unwind): @@ -428,11 +430,15 @@ // #![default_lib_allocator] +// The Rust prelude +// The compiler expects the prelude definition to be defined before it's use statement. +pub mod prelude; + // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. #[prelude_import] #[allow(unused)] -use prelude::rust_2021::*; +use prelude::rust_2024::*; // Access to Bencher, etc. #[cfg(test)] @@ -483,9 +489,6 @@ mod macros; #[macro_use] pub mod rt; -// The Rust prelude -pub mod prelude; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::any; #[stable(feature = "core_array", since = "1.35.0")] @@ -736,6 +739,14 @@ pub use core::{ unreachable, write, writeln, }; +// Re-export unstable derive macro defined through core. +#[unstable(feature = "derive_from", issue = "144889")] +/// Unstable module containing the unstable `From` derive macro. +pub mod from { + #[unstable(feature = "derive_from", issue = "144889")] + pub use core::from::From; +} + // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 035768a6fab..768fa77a5f8 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -1,14 +1,16 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( target_os = "linux", target_os = "android", target_os = "hurd", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", target_os = "haiku", target_os = "nto", - target_os = "cygwin"))] { + target_os = "cygwin", + ) => { use libc::MSG_NOSIGNAL; - } else { + } + _ => { const MSG_NOSIGNAL: core::ffi::c_int = 0x0; } } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 76e63a69e45..09429af06e3 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -4,8 +4,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -use cfg_if::cfg_if; - use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; @@ -13,17 +11,19 @@ use crate::sealed::Sealed; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; -cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { +cfg_select! { + any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita") => { type UserId = u16; type GroupId = u16; - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. // Only positive values should be used, see e.g. // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html type UserId = i32; type GroupId = i32; - } else { + } + _ => { type UserId = u32; type GroupId = u32; } diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 496443dbbc3..345d5b74285 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -53,12 +53,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +use alloc::wtf8::Wtf8Buf; + use crate::ffi::{OsStr, OsString}; +use crate::fmt; +use crate::iter::FusedIterator; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::sys_common::wtf8::EncodeWide; -use crate::sys_common::wtf8::Wtf8Buf; use crate::sys_common::{AsInner, FromInner}; /// Windows-specific extensions to [`OsString`]. @@ -130,6 +131,35 @@ pub trait OsStrExt: Sealed { impl OsStrExt for OsStr { #[inline] fn encode_wide(&self) -> EncodeWide<'_> { - self.as_inner().inner.encode_wide() + EncodeWide { inner: self.as_inner().inner.encode_wide() } + } +} + +/// Iterator returned by [`OsStrExt::encode_wide`]. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct EncodeWide<'a> { + inner: alloc::wtf8::EncodeWide<'a>, +} +#[stable(feature = "encode_wide_debug", since = "CURRENT_RUSTC_VERSION")] +impl fmt::Debug for EncodeWide<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for EncodeWide<'_> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option<u16> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.inner.size_hint() } } +#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")] +impl FusedIterator for EncodeWide<'_> {} diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 1c228914de9..28e972925e6 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -54,7 +54,7 @@ impl BorrowedSocket<'_> { /// /// # Safety /// - /// The resource pointed to by `raw` must remain open for the duration of + /// The resource pointed to by `socket` must remain open for the duration of /// the returned `BorrowedSocket`, and it must not have the value /// `INVALID_SOCKET`. #[inline] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 3b52804d6be..470d300d2d9 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1191,7 +1191,7 @@ impl PathBuf { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] - #[rustc_const_unstable(feature = "const_pathbuf_osstring_new", issue = "141520")] + #[rustc_const_stable(feature = "const_pathbuf_osstring_new", since = "CURRENT_RUSTC_VERSION")] pub const fn new() -> PathBuf { PathBuf { inner: OsString::new() } } @@ -2105,6 +2105,38 @@ impl PartialEq for PathBuf { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<str> for PathBuf { + #[inline] + fn eq(&self, other: &str) -> bool { + &*self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<PathBuf> for str { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<String> for PathBuf { + #[inline] + fn eq(&self, other: &String) -> bool { + **self == **other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<PathBuf> for String { + #[inline] + fn eq(&self, other: &PathBuf) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for PathBuf { fn hash<H: Hasher>(&self, h: &mut H) { @@ -3366,6 +3398,39 @@ impl PartialEq for Path { } } +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<str> for Path { + #[inline] + fn eq(&self, other: &str) -> bool { + let other: &OsStr = other.as_ref(); + self == other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<Path> for str { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<String> for Path { + #[inline] + fn eq(&self, other: &String) -> bool { + self == &*other + } +} + +#[stable(feature = "eq_str_for_path", since = "CURRENT_RUSTC_VERSION")] +impl cmp::PartialEq<Path> for String { + #[inline] + fn eq(&self, other: &Path) -> bool { + other == self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Path { fn hash<H: Hasher>(&self, h: &mut H) { @@ -3612,19 +3677,13 @@ impl_cmp_os_str!(<'a> Cow<'a, Path>, OsString); #[stable(since = "1.7.0", feature = "strip_prefix")] impl fmt::Display for StripPrefixError { - #[allow(deprecated, deprecated_in_future)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.description().fmt(f) + "prefix not found".fmt(f) } } #[stable(since = "1.7.0", feature = "strip_prefix")] -impl Error for StripPrefixError { - #[allow(deprecated)] - fn description(&self) -> &str { - "prefix not found" - } -} +impl Error for StripPrefixError {} #[unstable(feature = "normalize_lexically", issue = "134694")] impl fmt::Display for NormalizeError { diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index a40e29a772a..3231125f7a1 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -244,7 +244,11 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { #[inline] #[stable(feature = "lazy_cell", since = "1.80.0")] pub fn force(this: &LazyLock<T, F>) -> &T { - this.once.call_once(|| { + this.once.call_once_force(|state| { + if state.is_poisoned() { + panic_poisoned(); + } + // SAFETY: `call_once` only runs this closure once, ever. let data = unsafe { &mut *this.data.get() }; let f = unsafe { ManuallyDrop::take(&mut data.f) }; @@ -257,8 +261,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { // * the closure was called and initialized `value`. // * the closure was called and panicked, so this point is never reached. // * the closure was not called, but a previous call initialized `value`. - // * the closure was not called because the Once is poisoned, so this point - // is never reached. + // * the closure was not called because the Once is poisoned, which we handled above. // So `value` has definitely been initialized and will not be modified again. unsafe { &*(*this.data.get()).value } } diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index 0cbc61160f7..e3bcb149f64 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -23,14 +23,13 @@ use crate::ops::{Deref, DerefMut}; any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",), repr(align(128)) )] -// arm, mips, mips64, and riscv64 have 32-byte cache line size. +// arm, mips and mips64 have 32-byte cache line size. // // Sources: // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9 -// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7 #[cfg_attr( any( target_arch = "arm", @@ -38,7 +37,6 @@ use crate::ops::{Deref, DerefMut}; target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6", - target_arch = "riscv64", ), repr(align(32)) )] @@ -47,11 +45,12 @@ use crate::ops::{Deref, DerefMut}; // Sources: // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7 #[cfg_attr(target_arch = "s390x", repr(align(256)))] -// x86 and wasm have 64-byte cache line size. +// x86, wasm and riscv have 64-byte cache line size. // // Sources: // - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9 // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7 +// - https://github.com/golang/go/blob/5e31f78c8a4ed1b872ddc194f0cd1ae931b37d7e/src/internal/cpu/cpu_riscv64.go#L7 // // All others are assumed to have 64-byte cache line size. #[cfg_attr( @@ -64,7 +63,6 @@ use crate::ops::{Deref, DerefMut}; target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6", - target_arch = "riscv64", target_arch = "s390x", )), repr(align(64)) diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 41d1dd3ce67..f91c26aa22c 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -697,14 +697,14 @@ impl<T> SyncSender<T> { /// let sync_sender2 = sync_sender.clone(); /// /// // First thread owns sync_sender - /// thread::spawn(move || { + /// let handle1 = thread::spawn(move || { /// sync_sender.send(1).unwrap(); /// sync_sender.send(2).unwrap(); /// // Thread blocked /// }); /// /// // Second thread owns sync_sender2 - /// thread::spawn(move || { + /// let handle2 = thread::spawn(move || { /// // This will return an error and send /// // no message if the buffer is full /// let _ = sync_sender2.try_send(3); @@ -722,6 +722,10 @@ impl<T> SyncSender<T> { /// Ok(msg) => println!("message {msg} received"), /// Err(_) => println!("the third message was never sent"), /// } + /// + /// // Wait for threads to complete + /// handle1.join().unwrap(); + /// handle2.join().unwrap(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> { @@ -1104,12 +1108,7 @@ impl<T> fmt::Display for SendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> error::Error for SendError<T> { - #[allow(deprecated)] - fn description(&self) -> &str { - "sending on a closed channel" - } -} +impl<T> error::Error for SendError<T> {} #[stable(feature = "rust1", since = "1.0.0")] impl<T> fmt::Debug for TrySendError<T> { @@ -1132,15 +1131,7 @@ impl<T> fmt::Display for TrySendError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> error::Error for TrySendError<T> { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TrySendError::Full(..) => "sending on a full channel", - TrySendError::Disconnected(..) => "sending on a closed channel", - } - } -} +impl<T> error::Error for TrySendError<T> {} #[stable(feature = "mpsc_error_conversions", since = "1.24.0")] impl<T> From<SendError<T>> for TrySendError<T> { @@ -1164,12 +1155,7 @@ impl fmt::Display for RecvError { } #[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for RecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - "receiving on a closed channel" - } -} +impl error::Error for RecvError {} #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for TryRecvError { @@ -1182,15 +1168,7 @@ impl fmt::Display for TryRecvError { } #[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TryRecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TryRecvError::Empty => "receiving on an empty channel", - TryRecvError::Disconnected => "receiving on a closed channel", - } - } -} +impl error::Error for TryRecvError {} #[stable(feature = "mpsc_error_conversions", since = "1.24.0")] impl From<RecvError> for TryRecvError { @@ -1217,15 +1195,7 @@ impl fmt::Display for RecvTimeoutError { } #[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl error::Error for RecvTimeoutError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel", - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed", - } - } -} +impl error::Error for RecvTimeoutError {} #[stable(feature = "mpsc_error_conversions", since = "1.24.0")] impl From<RecvError> for RecvTimeoutError { diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs index 2bbf226dc2c..b3ae376e70d 100644 --- a/library/std/src/sync/nonpoison.rs +++ b/library/std/src/sync/nonpoison.rs @@ -33,5 +33,10 @@ impl fmt::Display for WouldBlock { pub use self::mutex::MappedMutexGuard; #[unstable(feature = "nonpoison_mutex", issue = "134645")] pub use self::mutex::{Mutex, MutexGuard}; +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; mod mutex; +mod rwlock; diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index b6861c78f00..fd1e671d7a3 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -100,7 +100,7 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex<T>, } -/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// A [`MutexGuard`] is not `Send` to maximize platform portability. /// /// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to /// release mutex locks on the same thread they were acquired. diff --git a/library/std/src/sync/nonpoison/rwlock.rs b/library/std/src/sync/nonpoison/rwlock.rs new file mode 100644 index 00000000000..eb0aef99cc1 --- /dev/null +++ b/library/std/src/sync/nonpoison/rwlock.rs @@ -0,0 +1,1081 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::{self, ManuallyDrop, forget}; +use crate::ops::{Deref, DerefMut}; +use crate::ptr::NonNull; +use crate::sync::nonpoison::{TryLockResult, WouldBlock}; +use crate::sys::sync as sys; + +/// A reader-writer lock that does not keep track of lock poisoning. +/// +/// For more information about reader-writer locks, check out the documentation for the poisoning +/// variant of this lock (which can be found at [`poison::RwLock`]). +/// +/// [`poison::RwLock`]: crate::sync::poison::RwLock +/// +/// # Examples +/// +/// ``` +/// #![feature(nonpoison_rwlock)] +/// +/// use std::sync::nonpoison::RwLock; +/// +/// let lock = RwLock::new(5); +/// +/// // many reader locks can be held at once +/// { +/// let r1 = lock.read(); +/// let r2 = lock.read(); +/// assert_eq!(*r1, 5); +/// assert_eq!(*r2, 5); +/// } // read locks are dropped at this point +/// +/// // only one write lock may be held, however +/// { +/// let mut w = lock.write(); +/// *w += 1; +/// assert_eq!(*w, 6); +/// } // write lock is dropped here +/// ``` +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLock")] +pub struct RwLock<T: ?Sized> { + /// The inner [`sys::RwLock`] that synchronizes thread access to the protected data. + inner: sys::RwLock, + /// The lock-protected data. + data: UnsafeCell<T>, +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Send> Send for RwLock<T> {} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Guards +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +/// +/// This structure is created by the [`read`] and [`try_read`] methods on +/// [`RwLock`]. +/// +/// [`read`]: RwLock::read +/// [`try_read`]: RwLock::try_read +#[must_use = "if unused the RwLock will immediately unlock"] +#[must_not_suspend = "holding a RwLockReadGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +#[clippy::has_significant_drop] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLockReadGuard")] +pub struct RwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `RwLockReadGuard` instance only holds + /// immutability until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also + /// covariant over `T`, just like we would have with `&T`. + data: NonNull<T>, + /// A reference to the internal [`sys::RwLock`] that we have read-locked. + inner_lock: &'rwlock sys::RwLock, +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> !Send for RwLockReadGuard<'_, T> {} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +/// +/// This structure is created by the [`write`] and [`try_write`] methods +/// on [`RwLock`]. +/// +/// [`write`]: RwLock::write +/// [`try_write`]: RwLock::try_write +#[must_use = "if unused the RwLock will immediately unlock"] +#[must_not_suspend = "holding a RwLockWriteGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Future's to not implement `Send`"] +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +#[clippy::has_significant_drop] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonRwLockWriteGuard")] +pub struct RwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A reference to the [`RwLock`] that we have write-locked. + lock: &'rwlock RwLock<T>, +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> !Send for RwLockWriteGuard<'_, T> {} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {} + +/// RAII structure used to release the shared read access of a lock when +/// dropped, which can point to a subfield of the protected data. +/// +/// This structure is created by the [`map`] and [`filter_map`] methods +/// on [`RwLockReadGuard`]. +/// +/// [`map`]: RwLockReadGuard::map +/// [`filter_map`]: RwLockReadGuard::filter_map +#[must_use = "if unused the RwLock will immediately unlock"] +#[must_not_suspend = "holding a MappedRwLockReadGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +#[clippy::has_significant_drop] +pub struct MappedRwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockReadGuard` instance only + /// holds immutability until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also + /// covariant over `T`, just like we would have with `&T`. + data: NonNull<T>, + /// A reference to the internal [`sys::RwLock`] that we have read-locked. + inner_lock: &'rwlock sys::RwLock, +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> !Send for MappedRwLockReadGuard<'_, T> {} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Sync> Sync for MappedRwLockReadGuard<'_, T> {} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped, which can point to a subfield of the protected data. +/// +/// This structure is created by the [`map`] and [`filter_map`] methods +/// on [`RwLockWriteGuard`]. +/// +/// [`map`]: RwLockWriteGuard::map +/// [`filter_map`]: RwLockWriteGuard::filter_map +#[must_use = "if unused the RwLock will immediately unlock"] +#[must_not_suspend = "holding a MappedRwLockWriteGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Future's to not implement `Send`"] +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +#[clippy::has_significant_drop] +pub struct MappedRwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockWriteGuard` instance only + /// holds uniquneness until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. + data: NonNull<T>, + /// `NonNull` is covariant over `T`, so we add a `PhantomData<&'rwlock mut T>` field here to + /// enforce the correct invariance over `T`. + _variance: PhantomData<&'rwlock mut T>, + /// A reference to the internal [`sys::RwLock`] that we have write-locked. + inner_lock: &'rwlock sys::RwLock, +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> !Send for MappedRwLockWriteGuard<'_, T> {} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +unsafe impl<T: ?Sized + Sync> Sync for MappedRwLockWriteGuard<'_, T> {} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Implementations +//////////////////////////////////////////////////////////////////////////////////////////////////// + +impl<T> RwLock<T> { + /// Creates a new instance of an `RwLock<T>` which is unlocked. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let lock = RwLock::new(5); + /// ``` + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + #[inline] + pub const fn new(t: T) -> RwLock<T> { + RwLock { inner: sys::RwLock::new(), data: UnsafeCell::new(t) } + } + + /// Returns the contained value by cloning it. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn get_cloned(&self) -> T + where + T: Clone, + { + self.read().clone() + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.get_cloned(), 7); + /// lock.set(11); + /// assert_eq!(lock.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn set(&self, value: T) { + if mem::needs_drop::<T>() { + // If the contained value has a non-trivial destructor, we + // call that destructor after the lock has been released. + drop(self.replace(value)) + } else { + *self.write() = value; + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let mut lock = RwLock::new(7); + /// + /// assert_eq!(lock.replace(11), 7); + /// assert_eq!(lock.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn replace(&self, value: T) -> T { + let mut guard = self.write(); + mem::replace(&mut *guard, value) + } +} + +impl<T: ?Sized> RwLock<T> { + /// Locks this `RwLock` with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// The calling thread will be blocked until there are no more writers which + /// hold the lock. There may be other readers currently inside the lock when + /// this method returns. This method does not provide any guarantees with + /// respect to the ordering of whether contentious readers or writers will + /// acquire the lock first. + /// + /// Returns an RAII guard which will release this thread's shared access + /// once it is dropped. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::Arc; + /// use std::sync::nonpoison::RwLock; + /// use std::thread; + /// + /// let lock = Arc::new(RwLock::new(1)); + /// let c_lock = Arc::clone(&lock); + /// + /// let n = lock.read(); + /// assert_eq!(*n, 1); + /// + /// thread::spawn(move || { + /// let r = c_lock.read(); + /// }).join().unwrap(); + /// ``` + #[inline] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn read(&self) -> RwLockReadGuard<'_, T> { + unsafe { + self.inner.read(); + RwLockReadGuard::new(self) + } + } + + /// Attempts to acquire this `RwLock` with shared read access. + /// + /// If the access could not be granted at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the shared access + /// when it is dropped. + /// + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. + /// + /// # Errors + /// + /// This function will return the [`WouldBlock`] error if the `RwLock` could + /// not be acquired because it was already locked exclusively. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// match lock.try_read() { + /// Ok(n) => assert_eq!(*n, 1), + /// Err(_) => unreachable!(), + /// }; + /// ``` + #[inline] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> { + unsafe { + if self.inner.try_read() { Ok(RwLockReadGuard::new(self)) } else { Err(WouldBlock) } + } + } + + /// Locks this `RwLock` with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// This function will not return while other writers or other readers + /// currently have access to the lock. + /// + /// Returns an RAII guard which will drop the write access of this `RwLock` + /// when dropped. + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// let mut n = lock.write(); + /// *n = 2; + /// + /// assert!(lock.try_read().is_err()); + /// ``` + #[inline] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn write(&self) -> RwLockWriteGuard<'_, T> { + unsafe { + self.inner.write(); + RwLockWriteGuard::new(self) + } + } + + /// Attempts to lock this `RwLock` with exclusive write access. + /// + /// If the lock could not be acquired at this time, then `Err` is returned. + /// Otherwise, an RAII guard is returned which will release the lock when + /// it is dropped. + /// + /// This function does not block. + /// + /// This function does not provide any guarantees with respect to the ordering + /// of whether contentious readers or writers will acquire the lock first. + /// + /// # Errors + /// + /// This function will return the [`WouldBlock`] error if the `RwLock` could + /// not be acquired because it was already locked. + /// + /// [`WouldBlock`]: WouldBlock + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let lock = RwLock::new(1); + /// + /// let n = lock.read(); + /// assert_eq!(*n, 1); + /// + /// assert!(lock.try_write().is_err()); + /// ``` + #[inline] + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> { + unsafe { + if self.inner.try_write() { Ok(RwLockWriteGuard::new(self)) } else { Err(WouldBlock) } + } + } + + /// Consumes this `RwLock`, returning the underlying data. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let lock = RwLock::new(String::new()); + /// { + /// let mut s = lock.write(); + /// *s = "modified".to_owned(); + /// } + /// assert_eq!(lock.into_inner(), "modified"); + /// ``` + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn into_inner(self) -> T + where + T: Sized, + { + self.data.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `RwLock` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no new locks can be acquired + /// while this reference exists. Note that this method does not clear any previously abandoned + /// locks (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// + /// use std::sync::nonpoison::RwLock; + /// + /// let mut lock = RwLock::new(0); + /// *lock.get_mut() = 10; + /// assert_eq!(*lock.read(), 10); + /// ``` + #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn get_mut(&mut self) -> &mut T { + self.data.get_mut() + } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the lock is dropped. + #[unstable(feature = "rwlock_data_ptr", issue = "140368")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("RwLock"); + match self.try_read() { + Ok(guard) => { + d.field("data", &&*guard); + } + Err(WouldBlock) => { + d.field("data", &format_args!("<locked>")); + } + } + d.finish_non_exhaustive() + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: Default> Default for RwLock<T> { + /// Creates a new `RwLock<T>`, with the `Default` value for T. + fn default() -> RwLock<T> { + RwLock::new(Default::default()) + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T> From<T> for RwLock<T> { + /// Creates a new instance of an `RwLock<T>` which is unlocked. + /// This is equivalent to [`RwLock::new`]. + fn from(t: T) -> Self { + RwLock::new(t) + } +} + +impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { + /// Creates a new instance of `RwLockReadGuard<T>` from a `RwLock<T>`. + /// + /// # Safety + /// + /// This function is safe if and only if the same thread has successfully and safely called + /// `lock.inner.read()`, `lock.inner.try_read()`, or `lock.inner.downgrade()` before + /// instantiating this object. + unsafe fn new(lock: &'rwlock RwLock<T>) -> RwLockReadGuard<'rwlock, T> { + RwLockReadGuard { + data: unsafe { NonNull::new_unchecked(lock.data.get()) }, + inner_lock: &lock.inner, + } + } + + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `RwLock` is already locked for reading, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RwLockReadGuard::map(...)`. A method would interfere with methods of + /// the same name on the contents of the `RwLockReadGuard` used through + /// `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> + where + F: FnOnce(&T) -> &U, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_ref() })); + let orig = ManuallyDrop::new(orig); + MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + } + + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `RwLock` is already locked for reading, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RwLockReadGuard::filter_map(...)`. A method would interfere with methods + /// of the same name on the contents of the `RwLockReadGuard` used through + /// `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'rwlock, U>, Self> + where + F: FnOnce(&T) -> Option<&U>, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_ref() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + } + None => Err(orig), + } + } +} + +impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { + /// Creates a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`. + /// + /// # Safety + /// + /// This function is safe if and only if the same thread has successfully and safely called + /// `lock.inner.write()`, `lock.inner.try_write()`, or `lock.inner.try_upgrade` before + /// instantiating this object. + unsafe fn new(lock: &'rwlock RwLock<T>) -> RwLockWriteGuard<'rwlock, T> { + RwLockWriteGuard { lock } + } + + /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. + /// + /// Since we have the `RwLockWriteGuard`, the [`RwLock`] must already be locked for writing, so + /// this method cannot fail. + /// + /// After downgrading, other readers will be allowed to read the protected data. + /// + /// # Examples + /// + /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// #![feature(rwlock_downgrade)] + /// + /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; + /// + /// let rw = RwLock::new(0); + /// + /// let mut write_guard = rw.write(); + /// *write_guard = 42; + /// + /// let read_guard = RwLockWriteGuard::downgrade(write_guard); + /// assert_eq!(42, *read_guard); + /// ``` + /// + /// `downgrade` will _atomically_ change the state of the [`RwLock`] from exclusive mode into + /// shared mode. This means that it is impossible for another writing thread to get in between a + /// thread calling `downgrade` and any reads it performs after downgrading. + /// + /// ``` + /// #![feature(nonpoison_rwlock)] + /// #![feature(rwlock_downgrade)] + /// + /// use std::sync::Arc; + /// use std::sync::nonpoison::{RwLock, RwLockWriteGuard}; + /// + /// let rw = Arc::new(RwLock::new(1)); + /// + /// // Put the lock in write mode. + /// let mut main_write_guard = rw.write(); + /// + /// let rw_clone = rw.clone(); + /// let evil_handle = std::thread::spawn(move || { + /// // This will not return until the main thread drops the `main_read_guard`. + /// let mut evil_guard = rw_clone.write(); + /// + /// assert_eq!(*evil_guard, 2); + /// *evil_guard = 3; + /// }); + /// + /// *main_write_guard = 2; + /// + /// // Atomically downgrade the write guard into a read guard. + /// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); + /// + /// // Since `downgrade` is atomic, the writer thread cannot have changed the protected data. + /// assert_eq!(*main_read_guard, 2, "`downgrade` was not atomic"); + /// # + /// # drop(main_read_guard); + /// # evil_handle.join().unwrap(); + /// # + /// # let final_check = rw.read(); + /// # assert_eq!(*final_check, 3); + /// ``` + #[unstable(feature = "rwlock_downgrade", issue = "128203")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { + let lock = s.lock; + + // We don't want to call the destructor since that calls `write_unlock`. + forget(s); + + // SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write + // mode, satisfying the `downgrade` contract. + unsafe { lock.inner.downgrade() }; + + // SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract. + unsafe { RwLockReadGuard::new(lock) } + } + + /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `RwLock` is already locked for writing, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RwLockWriteGuard::map(...)`. A method would interfere with methods of + /// the same name on the contents of the `RwLockWriteGuard` used through + /// `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); + MappedRwLockWriteGuard { data, inner_lock: &orig.lock.inner, _variance: PhantomData } + } + + /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `RwLock` is already locked for writing, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `RwLockWriteGuard::filter_map(...)`. A method would interfere with methods + /// of the same name on the contents of the `RwLockWriteGuard` used through + /// `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'rwlock, U>, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockWriteGuard { + data, + inner_lock: &orig.lock.inner, + _variance: PhantomData, + }) + } + None => Err(orig), + } + } +} + +impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, + /// e.g. an enum variant. + /// + /// The `RwLock` is already locked for reading, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedRwLockReadGuard::map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockReadGuard` + /// used through `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> + where + F: FnOnce(&T) -> &U, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_ref() })); + let orig = ManuallyDrop::new(orig); + MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + } + + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. + /// The original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `RwLock` is already locked for reading, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockReadGuard` + /// used through `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'rwlock, U>, Self> + where + F: FnOnce(&T) -> Option<&U>, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_ref() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + } + None => Err(orig), + } + } +} + +impl<'rwlock, T: ?Sized> MappedRwLockWriteGuard<'rwlock, T> { + /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, + /// e.g. an enum variant. + /// + /// The `RwLock` is already locked for writing, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedRwLockWriteGuard::map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockWriteGuard` + /// used through `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn map<U, F>(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_mut() })); + let orig = ManuallyDrop::new(orig); + MappedRwLockWriteGuard { data, inner_lock: orig.inner_lock, _variance: PhantomData } + } + + /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data. + /// The original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `RwLock` is already locked for writing, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedRwLockWriteGuard::filter_map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockWriteGuard` + /// used through `Deref`. + /// + /// # Panics + /// + /// If the closure panics, the guard will be dropped (unlocked). + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_rwlock", issue = "134645")] + pub fn filter_map<U, F>( + mut orig: Self, + f: F, + ) -> Result<MappedRwLockWriteGuard<'rwlock, U>, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockWriteGuard { + data, + inner_lock: orig.inner_lock, + _variance: PhantomData, + }) + } + None => Err(orig), + } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. + unsafe { + self.inner_lock.read_unlock(); + } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { + self.lock.inner.write_unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Drop for MappedRwLockReadGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { + self.inner_lock.read_unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Drop for MappedRwLockWriteGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { + self.inner_lock.write_unlock(); + } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { &*self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { &mut *self.lock.data.get() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Deref for MappedRwLockReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> Deref for MappedRwLockWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized> DerefMut for MappedRwLockWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_mut() } + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_rwlock", issue = "134645")] +impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 31889dcc10f..49a71b9ad10 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -263,12 +263,7 @@ impl<T> fmt::Display for PoisonError<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T> Error for PoisonError<T> { - #[allow(deprecated)] - fn description(&self) -> &str { - "poisoned lock: another task failed inside" - } -} +impl<T> Error for PoisonError<T> {} impl<T> PoisonError<T> { /// Creates a `PoisonError`. @@ -376,17 +371,6 @@ impl<T> fmt::Display for TryLockError<T> { #[stable(feature = "rust1", since = "1.0.0")] impl<T> Error for TryLockError<T> { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - match *self { - #[cfg(panic = "unwind")] - TryLockError::Poisoned(ref p) => p.description(), - #[cfg(not(panic = "unwind"))] - TryLockError::Poisoned(ref p) => match p._never {}, - TryLockError::WouldBlock => "try_lock failed because the operation would block", - } - } - #[allow(deprecated)] fn cause(&self) -> Option<&dyn Error> { match *self { diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 6205c4fa4ca..720c212c65c 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -279,7 +279,7 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } -/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// A [`MutexGuard`] is not `Send` to maximize platform portability. /// /// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to /// release mutex locks on the same thread they were acquired. diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 2c92602bc87..0a463f3f9c7 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -80,16 +80,24 @@ use crate::sys::sync as sys; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "RwLock")] pub struct RwLock<T: ?Sized> { + /// The inner [`sys::RwLock`] that synchronizes thread access to the protected data. inner: sys::RwLock, + /// A flag denoting if this `RwLock` has been poisoned. poison: poison::Flag, + /// The lock-protected data. data: UnsafeCell<T>, } #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Send for RwLock<T> {} + #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Guards +//////////////////////////////////////////////////////////////////////////////////////////////////// + /// RAII structure used to release the shared read access of a lock when /// dropped. /// @@ -105,13 +113,15 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} #[stable(feature = "rust1", since = "1.0.0")] #[clippy::has_significant_drop] #[cfg_attr(not(test), rustc_diagnostic_item = "RwLockReadGuard")] -pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { - // NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a - // `RwLockReadGuard` argument doesn't hold immutability for its whole scope, only until it drops. - // `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull` - // is preferable over `const* T` to allow for niche optimization. +pub struct RwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `RwLockReadGuard` instance only holds + /// immutability until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also + /// covariant over `T`, just like we would have with `&T`. data: NonNull<T>, - inner_lock: &'a sys::RwLock, + /// A reference to the internal [`sys::RwLock`] that we have read-locked. + inner_lock: &'rwlock sys::RwLock, } #[stable(feature = "rust1", since = "1.0.0")] @@ -135,8 +145,10 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockReadGuard<'_, T> {} #[stable(feature = "rust1", since = "1.0.0")] #[clippy::has_significant_drop] #[cfg_attr(not(test), rustc_diagnostic_item = "RwLockWriteGuard")] -pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { - lock: &'a RwLock<T>, +pub struct RwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A reference to the [`RwLock`] that we have write-locked. + lock: &'rwlock RwLock<T>, + /// The poison guard. See the [`poison`] module for more information. poison: poison::Guard, } @@ -160,13 +172,15 @@ unsafe impl<T: ?Sized + Sync> Sync for RwLockWriteGuard<'_, T> {} and cause Futures to not implement `Send`"] #[unstable(feature = "mapped_lock_guards", issue = "117108")] #[clippy::has_significant_drop] -pub struct MappedRwLockReadGuard<'a, T: ?Sized + 'a> { - // NB: we use a pointer instead of `&'a T` to avoid `noalias` violations, because a - // `MappedRwLockReadGuard` argument doesn't hold immutability for its whole scope, only until it drops. - // `NonNull` is also covariant over `T`, just like we would have with `&T`. `NonNull` - // is preferable over `const* T` to allow for niche optimization. +pub struct MappedRwLockReadGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockReadGuard` instance only + /// holds immutability until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. `NonNull` is also + /// covariant over `T`, just like we would have with `&T`. data: NonNull<T>, - inner_lock: &'a sys::RwLock, + /// A reference to the internal [`sys::RwLock`] that we have read-locked. + inner_lock: &'rwlock sys::RwLock, } #[unstable(feature = "mapped_lock_guards", issue = "117108")] @@ -189,16 +203,21 @@ unsafe impl<T: ?Sized + Sync> Sync for MappedRwLockReadGuard<'_, T> {} and cause Future's to not implement `Send`"] #[unstable(feature = "mapped_lock_guards", issue = "117108")] #[clippy::has_significant_drop] -pub struct MappedRwLockWriteGuard<'a, T: ?Sized + 'a> { - // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a - // `MappedRwLockWriteGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. - // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field - // below for the correct variance over `T` (invariance). +pub struct MappedRwLockWriteGuard<'rwlock, T: ?Sized + 'rwlock> { + /// A pointer to the data protected by the `RwLock`. Note that we use a pointer here instead of + /// `&'rwlock T` to avoid `noalias` violations, because a `MappedRwLockWriteGuard` instance only + /// holds uniquneness until it drops, not for its whole scope. + /// `NonNull` is preferable over `*const T` to allow for niche optimizations. data: NonNull<T>, - inner_lock: &'a sys::RwLock, - poison_flag: &'a poison::Flag, - poison: poison::Guard, - _variance: PhantomData<&'a mut T>, + /// `NonNull` is covariant over `T`, so we add a `PhantomData<&'rwlock mut T>` field here to + /// enforce the correct invariance over `T`. + _variance: PhantomData<&'rwlock mut T>, + /// A reference to the internal [`sys::RwLock`] that we have write-locked. + inner_lock: &'rwlock sys::RwLock, + /// A reference to the original `RwLock`'s poison state. + poison_flag: &'rwlock poison::Flag, + /// The poison guard. See the [`poison`] module for more information. + poison_guard: poison::Guard, } #[unstable(feature = "mapped_lock_guards", issue = "117108")] @@ -207,6 +226,10 @@ impl<T: ?Sized> !Send for MappedRwLockWriteGuard<'_, T> {} #[unstable(feature = "mapped_lock_guards", issue = "117108")] unsafe impl<T: ?Sized + Sync> Sync for MappedRwLockWriteGuard<'_, T> {} +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Implementations +//////////////////////////////////////////////////////////////////////////////////////////////////// + impl<T> RwLock<T> { /// Creates a new instance of an `RwLock<T>` which is unlocked. /// @@ -611,8 +634,8 @@ impl<T: ?Sized> RwLock<T> { /// /// Since this call borrows the `RwLock` mutably, no actual locking needs to /// take place -- the mutable borrow statically guarantees no new locks can be acquired - /// while this reference exists. Note that this method does not clear any previously abandoned locks - /// (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). + /// while this reference exists. Note that this method does not clear any previously abandoned + /// locks (e.g., via [`forget()`] on a [`RwLockReadGuard`] or [`RwLockWriteGuard`]). /// /// # Errors /// @@ -700,177 +723,7 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { inner_lock: &lock.inner, }) } -} - -impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - /// Creates a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`. - // SAFETY: if and only if `lock.inner.write()` (or `lock.inner.try_write()`) has been - // successfully called from the same thread before instantiating this object. - unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> { - poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard { lock, poison: guard }) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_debug", since = "1.16.0")] -impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "std_guard_impls", since = "1.20.0")] -impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockReadGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockWriteGuard<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { self.data.as_ref() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &*self.lock.data.get() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { &mut *self.lock.data.get() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized> Deref for MappedRwLockReadGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized> Deref for MappedRwLockWriteGuard<'_, T> { - type Target = T; - - fn deref(&self) -> &T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_ref() } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized> DerefMut for MappedRwLockWriteGuard<'_, T> { - fn deref_mut(&mut self) -> &mut T { - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { self.data.as_mut() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.lock.poison.done(&self.poison); - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. - unsafe { - self.lock.inner.write_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized> Drop for MappedRwLockReadGuard<'_, T> { - fn drop(&mut self) { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.read_unlock(); - } - } -} - -#[unstable(feature = "mapped_lock_guards", issue = "117108")] -impl<T: ?Sized> Drop for MappedRwLockWriteGuard<'_, T> { - fn drop(&mut self) { - self.poison_flag.done(&self.poison); - // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - unsafe { - self.inner_lock.write_unlock(); - } - } -} -impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, e.g. /// an enum variant. /// @@ -883,17 +736,18 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// /// # Panics /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be + /// poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U> + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> where F: FnOnce(&T) -> &U, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_ref() })); let orig = ManuallyDrop::new(orig); MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } @@ -912,17 +766,18 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { /// /// # Panics /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be + /// poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'rwlock, U>, Self> where F: FnOnce(&T) -> Option<&U>, U: ?Sized, { // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_ref() }) { Some(data) => { let data = NonNull::from(data); @@ -934,71 +789,95 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> { } } -impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> { - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, - /// e.g. an enum variant. - /// - /// The `RwLock` is already locked for reading, so this cannot fail. - /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. +impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { + /// Creates a new instance of `RwLockWriteGuard<T>` from a `RwLock<T>`. /// - /// # Panics + /// # Safety /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U> - where - F: FnOnce(&T) -> &U, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - let data = NonNull::from(f(unsafe { orig.data.as_ref() })); - let orig = ManuallyDrop::new(orig); - MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + /// This function is safe if and only if the same thread has successfully and safely called + /// `lock.inner.write()`, `lock.inner.try_write()`, or `lock.inner.try_upgrade` before + /// instantiating this object. + unsafe fn new(lock: &'rwlock RwLock<T>) -> LockResult<RwLockWriteGuard<'rwlock, T>> { + poison::map_result(lock.poison.guard(), |guard| RwLockWriteGuard { lock, poison: guard }) } - /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. - /// The original guard is returned as an `Err(...)` if the closure returns - /// `None`. + /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. /// - /// The `RwLock` is already locked for reading, so this cannot fail. + /// Since we have the `RwLockWriteGuard`, the [`RwLock`] must already be locked for writing, so + /// this method cannot fail. /// - /// This is an associated function that needs to be used as - /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with - /// methods of the same name on the contents of the `MappedRwLockReadGuard` - /// used through `Deref`. + /// After downgrading, other readers will be allowed to read the protected data. /// - /// # Panics + /// # Examples /// - /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be poisoned. - #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self> - where - F: FnOnce(&T) -> Option<&U>, - U: ?Sized, - { - // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard - // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. - match f(unsafe { orig.data.as_ref() }) { - Some(data) => { - let data = NonNull::from(data); - let orig = ManuallyDrop::new(orig); - Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) - } - None => Err(orig), - } + /// `downgrade` takes ownership of the `RwLockWriteGuard` and returns a [`RwLockReadGuard`]. + /// + /// ``` + /// #![feature(rwlock_downgrade)] + /// + /// use std::sync::{RwLock, RwLockWriteGuard}; + /// + /// let rw = RwLock::new(0); + /// + /// let mut write_guard = rw.write().unwrap(); + /// *write_guard = 42; + /// + /// let read_guard = RwLockWriteGuard::downgrade(write_guard); + /// assert_eq!(42, *read_guard); + /// ``` + /// + /// `downgrade` will _atomically_ change the state of the [`RwLock`] from exclusive mode into + /// shared mode. This means that it is impossible for another writing thread to get in between a + /// thread calling `downgrade` and any reads it performs after downgrading. + /// + /// ``` + /// #![feature(rwlock_downgrade)] + /// + /// use std::sync::{Arc, RwLock, RwLockWriteGuard}; + /// + /// let rw = Arc::new(RwLock::new(1)); + /// + /// // Put the lock in write mode. + /// let mut main_write_guard = rw.write().unwrap(); + /// + /// let rw_clone = rw.clone(); + /// let evil_handle = std::thread::spawn(move || { + /// // This will not return until the main thread drops the `main_read_guard`. + /// let mut evil_guard = rw_clone.write().unwrap(); + /// + /// assert_eq!(*evil_guard, 2); + /// *evil_guard = 3; + /// }); + /// + /// *main_write_guard = 2; + /// + /// // Atomically downgrade the write guard into a read guard. + /// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); + /// + /// // Since `downgrade` is atomic, the writer thread cannot have changed the protected data. + /// assert_eq!(*main_read_guard, 2, "`downgrade` was not atomic"); + /// # + /// # drop(main_read_guard); + /// # evil_handle.join().unwrap(); + /// # + /// # let final_check = rw.read().unwrap(); + /// # assert_eq!(*final_check, 3); + /// ``` + #[unstable(feature = "rwlock_downgrade", issue = "128203")] + pub fn downgrade(s: Self) -> RwLockReadGuard<'rwlock, T> { + let lock = s.lock; + + // We don't want to call the destructor since that calls `write_unlock`. + forget(s); + + // SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write + // mode, satisfying the `downgrade` contract. + unsafe { lock.inner.downgrade() }; + + // SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract. + unsafe { RwLockReadGuard::new(lock).unwrap_or_else(PoisonError::into_inner) } } -} -impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, e.g. /// an enum variant. /// @@ -1013,22 +892,22 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U> + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> where F: FnOnce(&mut T) -> &mut U, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); let orig = ManuallyDrop::new(orig); MappedRwLockWriteGuard { data, inner_lock: &orig.lock.inner, poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), + poison_guard: orig.poison.clone(), _variance: PhantomData, } } @@ -1048,15 +927,15 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'rwlock, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. match f(unsafe { &mut *orig.lock.data.get() }) { Some(data) => { let data = NonNull::from(data); @@ -1065,78 +944,82 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> { data, inner_lock: &orig.lock.inner, poison_flag: &orig.lock.poison, - poison: orig.poison.clone(), + poison_guard: orig.poison.clone(), _variance: PhantomData, }) } None => Err(orig), } } +} - /// Downgrades a write-locked `RwLockWriteGuard` into a read-locked [`RwLockReadGuard`]. - /// - /// This method will atomically change the state of the [`RwLock`] from exclusive mode into - /// shared mode. This means that it is impossible for a writing thread to get in between a - /// thread calling `downgrade` and the same thread reading whatever it wrote while it had the - /// [`RwLock`] in write mode. - /// - /// Note that since we have the `RwLockWriteGuard`, we know that the [`RwLock`] is already - /// locked for writing, so this method cannot fail. - /// - /// # Example - /// - /// ``` - /// #![feature(rwlock_downgrade)] - /// use std::sync::{Arc, RwLock, RwLockWriteGuard}; - /// - /// // The inner value starts as 0. - /// let rw = Arc::new(RwLock::new(0)); +impl<'rwlock, T: ?Sized> MappedRwLockReadGuard<'rwlock, T> { + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data, + /// e.g. an enum variant. /// - /// // Put the lock in write mode. - /// let mut main_write_guard = rw.write().unwrap(); + /// The `RwLock` is already locked for reading, so this cannot fail. /// - /// let evil = rw.clone(); - /// let handle = std::thread::spawn(move || { - /// // This will not return until the main thread drops the `main_read_guard`. - /// let mut evil_guard = evil.write().unwrap(); + /// This is an associated function that needs to be used as + /// `MappedRwLockReadGuard::map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockReadGuard` + /// used through `Deref`. /// - /// assert_eq!(*evil_guard, 1); - /// *evil_guard = 2; - /// }); + /// # Panics /// - /// // After spawning the writer thread, set the inner value to 1. - /// *main_write_guard = 1; + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be + /// poisoned. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'rwlock, U> + where + F: FnOnce(&T) -> &U, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_ref() })); + let orig = ManuallyDrop::new(orig); + MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock } + } + + /// Makes a [`MappedRwLockReadGuard`] for a component of the borrowed data. + /// The original guard is returned as an `Err(...)` if the closure returns + /// `None`. /// - /// // Atomically downgrade the write guard into a read guard. - /// let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); + /// The `RwLock` is already locked for reading, so this cannot fail. /// - /// // Since `downgrade` is atomic, the writer thread cannot have set the inner value to 2. - /// assert_eq!(*main_read_guard, 1, "`downgrade` was not atomic"); + /// This is an associated function that needs to be used as + /// `MappedRwLockReadGuard::filter_map(...)`. A method would interfere with + /// methods of the same name on the contents of the `MappedRwLockReadGuard` + /// used through `Deref`. /// - /// // Clean up everything now - /// drop(main_read_guard); - /// handle.join().unwrap(); + /// # Panics /// - /// let final_check = rw.read().unwrap(); - /// assert_eq!(*final_check, 2); - /// ``` - #[unstable(feature = "rwlock_downgrade", issue = "128203")] - pub fn downgrade(s: Self) -> RwLockReadGuard<'a, T> { - let lock = s.lock; - - // We don't want to call the destructor since that calls `write_unlock`. - forget(s); - - // SAFETY: We take ownership of a write guard, so we must already have the `RwLock` in write - // mode, satisfying the `downgrade` contract. - unsafe { lock.inner.downgrade() }; - - // SAFETY: We have just successfully called `downgrade`, so we fulfill the safety contract. - unsafe { RwLockReadGuard::new(lock).unwrap_or_else(PoisonError::into_inner) } + /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will not be + /// poisoned. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'rwlock, U>, Self> + where + F: FnOnce(&T) -> Option<&U>, + U: ?Sized, + { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_ref() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedRwLockReadGuard { data, inner_lock: &orig.inner_lock }) + } + None => Err(orig), + } } } -impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { +impl<'rwlock, T: ?Sized> MappedRwLockWriteGuard<'rwlock, T> { /// Makes a [`MappedRwLockWriteGuard`] for a component of the borrowed data, /// e.g. an enum variant. /// @@ -1151,22 +1034,22 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn map<U, F>(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'a, U> + pub fn map<U, F>(mut orig: Self, f: F) -> MappedRwLockWriteGuard<'rwlock, U> where F: FnOnce(&mut T) -> &mut U, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. let data = NonNull::from(f(unsafe { orig.data.as_mut() })); let orig = ManuallyDrop::new(orig); MappedRwLockWriteGuard { data, inner_lock: orig.inner_lock, poison_flag: orig.poison_flag, - poison: orig.poison.clone(), + poison_guard: orig.poison_guard.clone(), _variance: PhantomData, } } @@ -1186,15 +1069,18 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { /// /// If the closure panics, the guard will be dropped (unlocked) and the RwLock will be poisoned. #[unstable(feature = "mapped_lock_guards", issue = "117108")] - pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self> + pub fn filter_map<U, F>( + mut orig: Self, + f: F, + ) -> Result<MappedRwLockWriteGuard<'rwlock, U>, Self> where F: FnOnce(&mut T) -> Option<&mut U>, U: ?Sized, { // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard // was created, and have been upheld throughout `map` and/or `filter_map`. - // The signature of the closure guarantees that it will not "leak" the lifetime of the reference - // passed to it. If the closure panics, the guard will be dropped. + // The signature of the closure guarantees that it will not "leak" the lifetime of the + // reference passed to it. If the closure panics, the guard will be dropped. match f(unsafe { orig.data.as_mut() }) { Some(data) => { let data = NonNull::from(data); @@ -1203,7 +1089,7 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { data, inner_lock: orig.inner_lock, poison_flag: orig.poison_flag, - poison: orig.poison.clone(), + poison_guard: orig.poison_guard.clone(), _variance: PhantomData, }) } @@ -1211,3 +1097,162 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> { } } } + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. + unsafe { + self.inner_lock.read_unlock(); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: ?Sized> Drop for RwLockWriteGuard<'_, T> { + fn drop(&mut self) { + self.lock.poison.done(&self.poison); + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { + self.lock.inner.write_unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized> Drop for MappedRwLockReadGuard<'_, T> { + fn drop(&mut self) { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { + self.inner_lock.read_unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized> Drop for MappedRwLockWriteGuard<'_, T> { + fn drop(&mut self) { + self.poison_flag.done(&self.poison_guard); + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { + self.inner_lock.write_unlock(); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when created. + unsafe { self.data.as_ref() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: ?Sized> Deref for RwLockWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { &*self.lock.data.get() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when created. + unsafe { &mut *self.lock.data.get() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized> Deref for MappedRwLockReadGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockReadGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized> Deref for MappedRwLockWriteGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized> DerefMut for MappedRwLockWriteGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + // SAFETY: the conditions of `RwLockWriteGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + unsafe { self.data.as_mut() } + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[stable(feature = "std_guard_impls", since = "1.20.0")] +impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[stable(feature = "std_debug", since = "1.16.0")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[stable(feature = "std_guard_impls", since = "1.20.0")] +impl<T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockReadGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedRwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl<T: ?Sized + fmt::Display> fmt::Display for MappedRwLockWriteGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 727252f03a2..4140718560c 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,5 +1,3 @@ -use cfg_if::cfg_if; - use crate::cell::UnsafeCell; use crate::fmt; use crate::ops::Deref; @@ -87,8 +85,8 @@ pub struct ReentrantLock<T: ?Sized> { data: T, } -cfg_if!( - if #[cfg(target_has_atomic = "64")] { +cfg_select!( + target_has_atomic = "64" => { use crate::sync::atomic::{Atomic, AtomicU64, Ordering::Relaxed}; struct Tid(Atomic<u64>); @@ -110,7 +108,8 @@ cfg_if!( self.0.store(value, Relaxed); } } - } else { + } + _ => { /// Returns the address of a TLS variable. This is guaranteed to /// be unique across all currently alive threads. fn tls_addr() -> usize { diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index f3af1f7f599..6d4b09494a3 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -68,29 +68,37 @@ unsafe fn realloc_fallback( } } -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( target_family = "unix", target_os = "wasi", target_os = "teeos", target_os = "trusty", - ))] { + ) => { mod unix; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; - } else if #[cfg(target_family = "wasm")] { + } + target_family = "wasm" => { mod wasm; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; } } diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index a7ac4117ec9..3d369b08abc 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -58,18 +58,16 @@ unsafe impl GlobalAlloc for System { } } -cfg_if::cfg_if! { +cfg_select! { // We use posix_memalign wherever possible, but some targets have very incomplete POSIX coverage // so we need a fallback for those. - if #[cfg(any( - target_os = "horizon", - target_os = "vita", - ))] { + any(target_os = "horizon", target_os = "vita") => { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } } - } else { + } + _ => { #[inline] #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index c8fab992a88..48e2fdd4ecc 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -16,12 +16,15 @@ //! The crate itself provides a global allocator which on wasm has no //! synchronization as there are no threads! -// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint -#![allow(static_mut_refs)] +use core::cell::SyncUnsafeCell; use crate::alloc::{GlobalAlloc, Layout, System}; -static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); +struct SyncDlmalloc(dlmalloc::Dlmalloc); +unsafe impl Sync for SyncDlmalloc {} + +static DLMALLOC: SyncUnsafeCell<SyncDlmalloc> = + SyncUnsafeCell::new(SyncDlmalloc(dlmalloc::Dlmalloc::new())); #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { @@ -30,7 +33,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling malloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } } #[inline] @@ -38,7 +41,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling calloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } } #[inline] @@ -46,7 +49,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling free() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } + unsafe { (*DLMALLOC.get()).0.free(ptr, layout.size(), layout.align()) } } #[inline] @@ -54,7 +57,7 @@ unsafe impl GlobalAlloc for System { // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. // Calling realloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } + unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } } } diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs index aa14c8b650d..b6f464161ee 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -1,13 +1,15 @@ #![forbid(unsafe_op_in_unsafe_fn)] -cfg_if::cfg_if! { - if #[cfg(unix)] { +cfg_select! { + unix => { mod unix; pub use unix::{AnonPipe, pipe}; - } else if #[cfg(windows)] { + } + windows => { mod windows; pub use windows::{AnonPipe, pipe}; - } else { + } + _ => { mod unsupported; pub use unsupported::{AnonPipe, pipe}; } diff --git a/library/std/src/sys/args/mod.rs b/library/std/src/sys/args/mod.rs index 0011f55dc14..c9627322276 100644 --- a/library/std/src/sys/args/mod.rs +++ b/library/std/src/sys/args/mod.rs @@ -12,32 +12,39 @@ ))] mod common; -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))), target_os = "hermit", - ))] { + ) => { mod unix; pub use unix::*; - } else if #[cfg(target_family = "windows")] { + } + target_family = "windows" => { mod windows; pub use windows::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::*; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::*; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; pub use zkvm::*; - } else { + } + _ => { mod unsupported; pub use unsupported::*; } diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 272d0fa4d1a..57682207e07 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -113,7 +113,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: res = bt_fmt.frame().symbol(frame, symbol); } }); - #[cfg(target_os = "nto")] + #[cfg(all(target_os = "nto", any(target_env = "nto70", target_env = "nto71")))] if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { if !hit && print { use crate::backtrace_rs::SymbolName; diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 299ce1a6ff0..1592218ead8 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -45,69 +45,70 @@ unsafe extern "C" { pub safe fn lgammaf128_r(n: f128, s: &mut i32) -> f128; pub safe fn erff128(n: f128) -> f128; pub safe fn erfcf128(n: f128) -> f128; - - cfg_if::cfg_if! { - if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { - pub safe fn acosf(n: f32) -> f32; - pub safe fn asinf(n: f32) -> f32; - pub safe fn atan2f(a: f32, b: f32) -> f32; - pub safe fn atanf(n: f32) -> f32; - pub safe fn coshf(n: f32) -> f32; - pub safe fn sinhf(n: f32) -> f32; - pub safe fn tanf(n: f32) -> f32; - pub safe fn tanhf(n: f32) -> f32; - }} } -// On AIX, we don't have lgammaf_r only the f64 version, so we can -// use the f64 version lgamma_r -#[cfg(target_os = "aix")] -pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 { - lgamma_r(n.into(), s) as f32 -} +cfg_select! { + all(target_os = "windows", target_env = "msvc", target_arch = "x86") => { + // On 32-bit x86 MSVC these functions aren't defined, so we just define shims + // which promote everything to f64, perform the calculation, and then demote + // back to f32. While not precisely correct should be "correct enough" for now. + #[inline] + pub fn acosf(n: f32) -> f32 { + f64::acos(n as f64) as f32 + } -// On 32-bit x86 MSVC these functions aren't defined, so we just define shims -// which promote everything to f64, perform the calculation, and then demote -// back to f32. While not precisely correct should be "correct enough" for now. -cfg_if::cfg_if! { -if #[cfg(all(target_os = "windows", target_env = "msvc", target_arch = "x86"))] { - #[inline] - pub fn acosf(n: f32) -> f32 { - f64::acos(n as f64) as f32 - } + #[inline] + pub fn asinf(n: f32) -> f32 { + f64::asin(n as f64) as f32 + } - #[inline] - pub fn asinf(n: f32) -> f32 { - f64::asin(n as f64) as f32 - } + #[inline] + pub fn atan2f(n: f32, b: f32) -> f32 { + f64::atan2(n as f64, b as f64) as f32 + } - #[inline] - pub fn atan2f(n: f32, b: f32) -> f32 { - f64::atan2(n as f64, b as f64) as f32 - } + #[inline] + pub fn atanf(n: f32) -> f32 { + f64::atan(n as f64) as f32 + } - #[inline] - pub fn atanf(n: f32) -> f32 { - f64::atan(n as f64) as f32 - } + #[inline] + pub fn coshf(n: f32) -> f32 { + f64::cosh(n as f64) as f32 + } - #[inline] - pub fn coshf(n: f32) -> f32 { - f64::cosh(n as f64) as f32 - } + #[inline] + pub fn sinhf(n: f32) -> f32 { + f64::sinh(n as f64) as f32 + } - #[inline] - pub fn sinhf(n: f32) -> f32 { - f64::sinh(n as f64) as f32 - } + #[inline] + pub fn tanf(n: f32) -> f32 { + f64::tan(n as f64) as f32 + } - #[inline] - pub fn tanf(n: f32) -> f32 { - f64::tan(n as f64) as f32 + #[inline] + pub fn tanhf(n: f32) -> f32 { + f64::tanh(n as f64) as f32 + } } - - #[inline] - pub fn tanhf(n: f32) -> f32 { - f64::tanh(n as f64) as f32 + _ => { + unsafe extern "C" { + pub safe fn acosf(n: f32) -> f32; + pub safe fn asinf(n: f32) -> f32; + pub safe fn atan2f(a: f32, b: f32) -> f32; + pub safe fn atanf(n: f32) -> f32; + pub safe fn coshf(n: f32) -> f32; + pub safe fn sinhf(n: f32) -> f32; + pub safe fn tanf(n: f32) -> f32; + pub safe fn tanhf(n: f32) -> f32; + } } -}} +} + +// On AIX, we don't have lgammaf_r only the f64 version, so we can +// use the f64 version lgamma_r +#[cfg(target_os = "aix")] +pub fn lgammaf_r(n: f32, s: &mut i32) -> f32 { + lgamma_r(n.into(), s) as f32 +} diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs index d81ff875c83..f211a9fc86b 100644 --- a/library/std/src/sys/env/mod.rs +++ b/library/std/src/sys/env/mod.rs @@ -13,35 +13,44 @@ ))] mod common; -cfg_if::cfg_if! { - if #[cfg(target_family = "unix")] { +cfg_select! { + target_family = "unix" => { mod unix; pub use unix::*; - } else if #[cfg(target_family = "windows")] { + } + target_family = "windows" => { mod windows; pub use windows::*; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; pub use hermit::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use solid::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::*; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::*; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; pub use zkvm::*; - } else { + } + _ => { mod unsupported; pub use unsupported::*; } diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs index 3719f9db51e..1327cbc3263 100644 --- a/library/std/src/sys/env/wasi.rs +++ b/library/std/src/sys/env/wasi.rs @@ -7,8 +7,8 @@ use crate::os::wasi::prelude::*; use crate::sys::common::small_c_string::run_with_cstr; use crate::sys::pal::os::{cvt, libc}; -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { +cfg_select! { + target_feature = "atomics" => { // Access to the environment must be protected by a lock in multi-threaded scenarios. use crate::sync::{PoisonError, RwLock}; static ENV_LOCK: RwLock<()> = RwLock::new(()); @@ -18,7 +18,8 @@ cfg_if::cfg_if! { pub fn env_write_lock() -> impl Drop { ENV_LOCK.write().unwrap_or_else(PoisonError::into_inner) } - } else { + } + _ => { // No need for a lock if we are single-threaded. pub fn env_read_lock() -> impl Drop { Box::new(()) diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index bd70d178244..00b91842e9d 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -1,5 +1,5 @@ -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { +cfg_select! { + target_os = "linux" => { /// Mitigation for <https://github.com/rust-lang/rust/issues/126600> /// /// On glibc, `libc::exit` has been observed to not always be thread-safe. @@ -56,7 +56,8 @@ cfg_if::cfg_if! { } } } - } else { + } + _ => { /// Mitigation for <https://github.com/rust-lang/rust/issues/126600> /// /// Mitigation is ***NOT*** implemented on this platform, either because this platform diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index e0f5eab6951..7cb9dd1cba9 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -2,18 +2,22 @@ #![forbid(unsafe_op_in_unsafe_fn)] -cfg_if::cfg_if! { - if #[cfg(target_family = "unix")] { +cfg_select! { + target_family = "unix" => { mod unix; pub use unix::*; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; pub use hermit::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::*; } + _ => {} } diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index cdca73cdca1..a12f692e754 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -37,10 +37,10 @@ pub struct FileDesc(OwnedFd); // // On Apple targets however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size -// larger than or equal to INT_MAX. To handle both of these the read -// size is capped on both platforms. +// larger than INT_MAX. To handle both of these the read size is capped on +// both platforms. const READ_LIMIT: usize = if cfg!(target_vendor = "apple") { - libc::c_int::MAX as usize - 1 + libc::c_int::MAX as usize } else { libc::ssize_t::MAX as usize }; diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index d55e28074fe..0276bf6e64c 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -5,8 +5,8 @@ use crate::path::{Path, PathBuf}; pub mod common; -cfg_if::cfg_if! { - if #[cfg(target_family = "unix")] { +cfg_select! { + target_family = "unix" => { mod unix; use unix as imp; pub use unix::{chown, fchown, lchown, mkfifo}; @@ -16,24 +16,30 @@ cfg_if::cfg_if! { #[cfg(any(target_os = "linux", target_os = "android"))] pub(crate) use unix::CachedFileMetadata; use crate::sys::common::small_c_string::run_path_with_cstr as with_native_path; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; use windows as imp; pub use windows::{symlink_inner, junction_point}; use crate::sys::path::with_native_path; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; use hermit as imp; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; use solid as imp; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; use uefi as imp; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; use wasi as imp; - } else { + } + _ => { mod unsupported; use unsupported as imp; } @@ -108,6 +114,30 @@ pub fn set_permissions(path: &Path, perm: FilePermissions) -> io::Result<()> { with_native_path(path, &|path| imp::set_perm(path, perm.clone())) } +#[cfg(unix)] +pub fn set_permissions_nofollow(path: &Path, perm: crate::fs::Permissions) -> io::Result<()> { + use crate::fs::OpenOptions; + + let mut options = OpenOptions::new(); + + // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. + // Their filesystems do not have symbolic links, so no special handling is required. + #[cfg(not(any(target_os = "espidf", target_os = "horizon")))] + { + use crate::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_NOFOLLOW); + } + + options.open(path)?.set_permissions(perm) +} + +#[cfg(not(unix))] +pub fn set_permissions_nofollow(_path: &Path, _perm: crate::fs::Permissions) -> io::Result<()> { + crate::unimplemented!( + "`set_permissions_nofollow` is currently only implemented on Unix platforms" + ) +} + pub fn canonicalize(path: &Path) -> io::Result<PathBuf> { with_native_path(path, &imp::canonicalize) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b310db2dac4..0d710a4b2a6 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -101,10 +101,11 @@ pub struct File(FileDesc); // https://github.com/rust-lang/rust/pull/67774 macro_rules! cfg_has_statx { ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => { - cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + cfg_select! { + all(target_os = "linux", target_env = "gnu") => { $($then_tt)* - } else { + } + _ => { $($else_tt)* } } @@ -1263,6 +1264,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", ))] pub fn lock(&self) -> io::Result<()> { @@ -1275,6 +1278,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", )))] pub fn lock(&self) -> io::Result<()> { @@ -1286,6 +1291,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", ))] pub fn lock_shared(&self) -> io::Result<()> { @@ -1298,6 +1305,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", )))] pub fn lock_shared(&self) -> io::Result<()> { @@ -1309,6 +1318,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", ))] pub fn try_lock(&self) -> Result<(), TryLockError> { @@ -1329,6 +1340,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", )))] pub fn try_lock(&self) -> Result<(), TryLockError> { @@ -1343,6 +1356,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", ))] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { @@ -1363,6 +1378,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", )))] pub fn try_lock_shared(&self) -> Result<(), TryLockError> { @@ -1377,6 +1394,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", ))] pub fn unlock(&self) -> io::Result<()> { @@ -1389,6 +1408,8 @@ impl File { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", target_vendor = "apple", )))] pub fn unlock(&self) -> io::Result<()> { @@ -1505,8 +1526,8 @@ impl File { )), None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; - cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] { + cfg_select! { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. @@ -1515,7 +1536,8 @@ impl File { io::ErrorKind::Unsupported, "setting file times not supported", )) - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3]; let mut num_times = 0; let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; @@ -1543,7 +1565,8 @@ impl File { 0 ) })?; Ok(()) - } else if #[cfg(target_os = "android")] { + } + target_os = "android" => { let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; // futimens requires Android API level 19 cvt(unsafe { @@ -1559,7 +1582,8 @@ impl File { } })?; Ok(()) - } else { + } + _ => { #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] { use crate::sys::{time::__timespec64, weak::weak}; @@ -1677,13 +1701,14 @@ impl fmt::Debug for File { let mut buf = vec![0; libc::PATH_MAX as usize]; let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; if n == -1 { - cfg_if::cfg_if! { - if #[cfg(target_os = "netbsd")] { + cfg_select! { + target_os = "netbsd" => { // fallback to procfs as last resort let mut p = PathBuf::from("/proc/self/fd"); p.push(&fd.to_string()); return run_path_with_cstr(&p, &readlink).ok() - } else { + } + _ => { return None; } } @@ -1884,15 +1909,16 @@ pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> { } pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] { + cfg_select! { + any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70") => { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. // Android has `linkat` on newer versions, but we happen to know `link` // always has the correct behavior, so it's here as well. cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; - } else { + } + _ => { // Where we can, use `linkat` instead of `link`; see the comment above // this one for details on why. cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index ae75f4d97b4..fe8ec1dbb73 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -1,20 +1,24 @@ #![forbid(unsafe_op_in_unsafe_fn)] mod io_slice { - cfg_if::cfg_if! { - if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty"))] { + cfg_select! { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty") => { mod iovec; pub use iovec::*; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub use windows::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::*; - } else { + } + _ => { mod unsupported; pub use unsupported::*; } @@ -22,17 +26,20 @@ mod io_slice { } mod is_terminal { - cfg_if::cfg_if! { - if #[cfg(any(target_family = "unix", target_os = "wasi"))] { + cfg_select! { + any(target_family = "unix", target_os = "wasi") => { mod isatty; pub use isatty::*; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub use windows::*; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; pub use hermit::*; - } else { + } + _ => { mod unsupported; pub use unsupported::*; } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8ec0a0e3302..6324c1a232a 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,7 +1,7 @@ #![allow(unsafe_op_in_unsafe_fn)] /// The configure builtins provides runtime support compiler-builtin features -/// which require dynamic intialization to work as expected, e.g. aarch64 +/// which require dynamic initialization to work as expected, e.g. aarch64 /// outline-atomics. mod configure_builtins; diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 242df10bc32..2389fd1bcb6 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -452,12 +452,7 @@ pub struct NonIpSockAddr { host: String, } -impl error::Error for NonIpSockAddr { - #[allow(deprecated)] - fn description(&self) -> &str { - "Failed to convert address to SocketAddr" - } -} +impl error::Error for NonIpSockAddr {} impl fmt::Display for NonIpSockAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket.rs index 7301bde6881..aa83ed65d4c 100644 --- a/library/std/src/sys/net/connection/socket.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -9,29 +9,34 @@ use crate::sys_common::{AsInner, FromInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; -cfg_if::cfg_if! { - if #[cfg(target_os = "hermit")] { +cfg_select! { + target_os = "hermit" => { mod hermit; pub use hermit::*; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use solid::*; - } else if #[cfg(target_family = "unix")] { + } + target_family = "unix" => { mod unix; pub use unix::*; - } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + } + all(target_os = "wasi", target_env = "p2") => { mod wasip2; pub use wasip2::*; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub use windows::*; } + _ => {} } use netc as c; -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", @@ -43,39 +48,44 @@ cfg_if::cfg_if! { target_os = "nto", target_os = "nuttx", target_vendor = "apple", - ))] { + ) => { use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; - } else { + } + _ => { use c::IPV6_ADD_MEMBERSHIP; use c::IPV6_DROP_MEMBERSHIP; } } -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( target_os = "linux", target_os = "android", target_os = "hurd", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", target_os = "haiku", target_os = "nto", - target_os = "cygwin"))] { + target_os = "cygwin", + ) => { use libc::MSG_NOSIGNAL; - } else { + } + _ => { const MSG_NOSIGNAL: c_int = 0x0; } } -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", - target_os = "nto"))] { + target_os = "nto", + ) => { use crate::ffi::c_uchar; type IpV4MultiCastType = c_uchar; - } else { + } + _ => { type IpV4MultiCastType = c_int; } } @@ -523,17 +533,19 @@ impl TcpListener { let (addr, len) = socket_addr_to_c(addr); cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - cfg_if::cfg_if! { - if #[cfg(target_os = "horizon")] { + cfg_select! { + target_os = "horizon" => { // The 3DS doesn't support a big connection backlog. Sometimes // it allows up to about 37, but other times it doesn't even // accept 32. There may be a global limitation causing this. let backlog = 20; - } else if #[cfg(target_os = "haiku")] { + } + target_os = "haiku" => { // Haiku does not support a queue length > 32 // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 let backlog = 32; - } else { + } + _ => { // The default for all other platforms let backlog = 128; } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index cc111f3521b..8b5970d1494 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -12,10 +12,11 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; -cfg_if::cfg_if! { - if #[cfg(target_vendor = "apple")] { +cfg_select! { + target_vendor = "apple" => { use libc::SO_LINGER_SEC as SO_LINGER; - } else { + } + _ => { use libc::SO_LINGER; } } @@ -72,8 +73,8 @@ impl Socket { pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { unsafe { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", @@ -85,7 +86,7 @@ impl Socket { target_os = "cygwin", target_os = "nto", target_os = "solaris", - ))] { + ) => { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as // CLOEXEC. On Linux this was added in 2.6.27. @@ -98,7 +99,8 @@ impl Socket { setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; Ok(socket) - } else { + } + _ => { let fd = cvt(libc::socket(fam, ty, 0))?; let fd = FileDesc::from_raw_fd(fd); fd.set_cloexec()?; @@ -120,8 +122,8 @@ impl Socket { unsafe { let mut fds = [0, 0]; - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", @@ -132,11 +134,12 @@ impl Socket { target_os = "openbsd", target_os = "cygwin", target_os = "nto", - ))] { + ) => { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) - } else { + } + _ => { cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; let a = FileDesc::from_raw_fd(fds[0]); let b = FileDesc::from_raw_fd(fds[1]); @@ -250,8 +253,8 @@ impl Socket { // atomically set the CLOEXEC flag is to use the `accept4` syscall on // platforms that support it. On Linux, this was added in 2.6.28, // glibc 2.10 and musl 0.9.5. - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", @@ -261,12 +264,13 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", - ))] { + ) => { unsafe { let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; Ok(Socket(FileDesc::from_raw_fd(fd))) } - } else { + } + _ => { unsafe { let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; let fd = FileDesc::from_raw_fd(fd); diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index 646679a1cc8..5df1fe138ab 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -1,36 +1,41 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_family = "unix", not(target_os = "l4re")), target_os = "windows", target_os = "hermit", all(target_os = "wasi", target_env = "p2"), target_os = "solid_asp3", - ))] { + ) => { mod connection { mod socket; pub use socket::*; } - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod connection { mod sgx; pub use sgx::*; } - } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + } + all(target_os = "wasi", target_env = "p1") => { mod connection { mod wasip1; pub use wasip1::*; } - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod connection { mod xous; pub use xous::*; } - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod connection { mod uefi; pub use uefi::*; } - } else { + } + _ => { mod connection { mod unsupported; pub use unsupported::*; diff --git a/library/std/src/sys/os_str/mod.rs b/library/std/src/sys/os_str/mod.rs index 345e661586d..65c90d88049 100644 --- a/library/std/src/sys/os_str/mod.rs +++ b/library/std/src/sys/os_str/mod.rs @@ -1,13 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "windows", - target_os = "uefi", - ))] { +cfg_select! { + any(target_os = "windows", target_os = "uefi") => { mod wtf8; pub use wtf8::{Buf, Slice}; - } else { + } + _ => { mod bytes; pub use bytes::{Buf, Slice}; } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index bbc704ebf86..96da891874e 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,12 +1,12 @@ //! The underlying OsString/OsStr implementation on Windows is a //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. +use alloc::wtf8::{Wtf8, Wtf8Buf}; use core::clone::CloneToUninit; use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::wtf8::{Wtf8, Wtf8Buf, check_utf8_boundary}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; @@ -220,7 +220,9 @@ impl Buf { /// trailing surrogate half. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { - self.inner.extend_from_slice(other); + unsafe { + self.inner.extend_from_slice_unchecked(other); + } } } @@ -238,7 +240,7 @@ impl Slice { #[track_caller] #[inline] pub fn check_public_boundary(&self, index: usize) { - check_utf8_boundary(&self.inner, index); + self.inner.check_utf8_boundary(index); } #[inline] diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index a998c3165e5..0fe713a503b 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -1,5 +1,4 @@ use super::hermit_abi; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; @@ -52,12 +51,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on hermit yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index fbefc62ac88..9376f5249f1 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -24,60 +24,70 @@ pub mod common; -cfg_if::cfg_if! { - if #[cfg(unix)] { +cfg_select! { + unix => { mod unix; pub use self::unix::*; - } else if #[cfg(windows)] { + } + windows => { mod windows; pub use self::windows::*; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use self::solid::*; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; pub use self::hermit::*; - } else if #[cfg(target_os = "trusty")] { + } + target_os = "trusty" => { mod trusty; pub use self::trusty::*; - } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + } + all(target_os = "wasi", target_env = "p2") => { mod wasip2; pub use self::wasip2::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use self::wasi::*; - } else if #[cfg(target_family = "wasm")] { + } + target_family = "wasm" => { mod wasm; pub use self::wasm::*; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use self::xous::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use self::uefi::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use self::sgx::*; - } else if #[cfg(target_os = "teeos")] { + } + target_os = "teeos" => { mod teeos; pub use self::teeos::*; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; pub use self::zkvm::*; - } else { + } + _ => { mod unsupported; pub use self::unsupported::*; } } -cfg_if::cfg_if! { +pub const FULL_BACKTRACE_DEFAULT: bool = cfg_select! { // Fuchsia components default to full backtrace. - if #[cfg(target_os = "fuchsia")] { - pub const FULL_BACKTRACE_DEFAULT: bool = true; - } else { - pub const FULL_BACKTRACE_DEFAULT: bool = false; - } -} + target_os = "fuchsia" => true, + _ => false, +}; #[cfg(not(target_os = "uefi"))] pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 6e43a79ddec..4a297b6823f 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -59,8 +59,7 @@ pub fn sgx_ineffective<T>(v: T) -> crate::io::Result<T> { #[inline] pub fn is_interrupted(code: i32) -> bool { - use fortanix_sgx_abi::Error; - code == Error::Interrupted as _ + code == fortanix_sgx_abi::Error::Interrupted as _ } pub fn decode_error_kind(code: i32) -> ErrorKind { diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 70f838679c9..28d79963ac8 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,11 +1,10 @@ use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; -use crate::{fmt, io, str}; +use crate::{fmt, io}; pub fn errno() -> i32 { RESULT_SUCCESS @@ -59,12 +58,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported in SGX yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index 8f5976b0592..cb6e2cbceae 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,5 +1,4 @@ use super::{error, itron, unsupported}; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; use crate::{fmt, io}; @@ -58,12 +57,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index 03f3c72b022..512b3e2885b 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use super::unsupported; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::path::PathBuf; use crate::{fmt, io, path}; @@ -62,12 +61,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index bfd4dc81cb4..aae6cb9e064 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -2,7 +2,6 @@ use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use super::{RawOsError, helpers, unsupported_err}; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::uefi; @@ -122,7 +121,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError {} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { let protocol = helpers::image_handle_protocol::<device_path::Protocol>( diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index df5611b2ddd..36ce3f7ef96 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -188,7 +188,7 @@ pub(crate) mod system_time_internal { Duration::new(epoch, t.nanosecond) } - /// This algorithm is a modifed version of the one described in the post: + /// This algorithm is a modified version of the one described in the post: /// https://howardhinnant.github.io/date_algorithms.html#clive_from_days /// /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX @@ -197,7 +197,7 @@ pub(crate) mod system_time_internal { // Check timzone validity assert!(timezone <= 1440 && timezone >= -1440); - // FIXME(#126043): use checked_sub_signed once stablized + // FIXME(#126043): use checked_sub_signed once stabilized let secs = dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap(); @@ -296,12 +296,9 @@ pub(crate) mod instant_internal { } pub fn platform_specific() -> Option<Instant> { - cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { - timestamp_rdtsc().map(Instant) - } else { - None - } + cfg_select! { + any(target_arch = "x86_64", target_arch = "x86") => timestamp_rdtsc().map(Instant), + _ => None, } } diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index c23278bdf5e..265067d84d5 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -46,8 +46,8 @@ pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) } let r = unsafe { - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { + cfg_select! { + target_os = "freebsd" => { // FreeBSD doesn't have futex(), but it has // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly // identical. It supports absolute timeouts through a flag @@ -66,7 +66,8 @@ pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) crate::ptr::without_provenance_mut(umtx_timeout_size), umtx_timeout_ptr as *mut _, ) - } else if #[cfg(any(target_os = "linux", target_os = "android"))] { + } + any(target_os = "linux", target_os = "android") => { // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an // absolute time rather than a relative time. libc::syscall( @@ -78,7 +79,8 @@ pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET. !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT. ) - } else { + } + _ => { compile_error!("unknown target_os"); } } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ba9e14b8009..aef7ab55088 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -59,6 +59,30 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn sanitize_standard_fds() { + #[allow(dead_code, unused_variables, unused_mut)] + let mut opened_devnull = -1; + #[allow(dead_code, unused_variables, unused_mut)] + let mut open_devnull = || { + #[cfg(not(all(target_os = "linux", target_env = "gnu")))] + use libc::open; + #[cfg(all(target_os = "linux", target_env = "gnu"))] + use libc::open64 as open; + + if opened_devnull != -1 { + if libc::dup(opened_devnull) != -1 { + return; + } + } + opened_devnull = open(c"/dev/null".as_ptr(), libc::O_RDWR, 0); + if opened_devnull == -1 { + // If the stream is closed but we failed to reopen it, abort the + // process. Otherwise we wouldn't preserve the safety of + // operations on the corresponding Rust object Stdin, Stdout, or + // Stderr. + libc::abort(); + } + }; + // fast path with a single syscall for systems with poll() #[cfg(not(any( miri, @@ -74,11 +98,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_vendor = "apple", )))] 'poll: { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; let pfds: &mut [_] = &mut [ libc::pollfd { fd: 0, events: 0, revents: 0 }, @@ -106,13 +125,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } return; } @@ -129,21 +142,10 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vita", )))] { - #[cfg(not(all(target_os = "linux", target_env = "gnu")))] - use libc::open as open64; - #[cfg(all(target_os = "linux", target_env = "gnu"))] - use libc::open64; - use crate::sys::os::errno; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64(c"/dev/null".as_ptr(), libc::O_RDWR, 0) == -1 { - // If the stream is closed but we failed to reopen it, abort the - // process. Otherwise we wouldn't preserve the safety of - // operations on the corresponding Rust object Stdin, Stdout, or - // Stderr. - libc::abort(); - } + open_devnull(); } } } @@ -366,31 +368,36 @@ pub fn abort_internal() -> ! { unsafe { libc::abort() } } -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { +cfg_select! { + target_os = "android" => { #[link(name = "dl", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "dl", cfg(not(target_feature = "crt-static")))] #[link(name = "log", cfg(not(target_feature = "crt-static")))] unsafe extern "C" {} - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { #[link(name = "execinfo")] #[link(name = "pthread")] unsafe extern "C" {} - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { #[link(name = "pthread")] #[link(name = "rt")] unsafe extern "C" {} - } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd", target_os = "cygwin"))] { + } + any(target_os = "dragonfly", target_os = "openbsd", target_os = "cygwin") => { #[link(name = "pthread")] unsafe extern "C" {} - } else if #[cfg(target_os = "solaris")] { + } + target_os = "solaris" => { #[link(name = "socket")] #[link(name = "posix4")] #[link(name = "pthread")] #[link(name = "resolv")] unsafe extern "C" {} - } else if #[cfg(target_os = "illumos")] { + } + target_os = "illumos" => { #[link(name = "socket")] #[link(name = "posix4")] #[link(name = "pthread")] @@ -399,24 +406,29 @@ cfg_if::cfg_if! { // Use libumem for the (malloc-compatible) allocator #[link(name = "umem")] unsafe extern "C" {} - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { // Link to `libSystem.dylib`. // // Don't get confused by the presence of `System.framework`, // it is a deprecated wrapper over the dynamic library. #[link(name = "System")] unsafe extern "C" {} - } else if #[cfg(target_os = "fuchsia")] { + } + target_os = "fuchsia" => { #[link(name = "zircon")] #[link(name = "fdio")] unsafe extern "C" {} - } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { + } + all(target_os = "linux", target_env = "uclibc") => { #[link(name = "dl")] unsafe extern "C" {} - } else if #[cfg(target_os = "vita")] { + } + target_os = "vita" => { #[link(name = "pthread", kind = "static", modifiers = "-bundle")] unsafe extern "C" {} } + _ => {} } #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 0e68313cc3e..81275afa707 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -7,7 +7,6 @@ mod tests; use libc::{c_char, c_int, c_void}; -use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; @@ -17,13 +16,10 @@ use crate::{fmt, io, iter, mem, ptr, slice, str}; const TMPBUF_SZ: usize = 128; -cfg_if::cfg_if! { - if #[cfg(target_os = "redox")] { - const PATH_SEPARATOR: u8 = b';'; - } else { - const PATH_SEPARATOR: u8 = b':'; - } -} +const PATH_SEPARATOR: u8 = cfg_select! { + target_os = "redox" => b';', + _ => b':', +}; unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] @@ -251,12 +247,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to join paths" - } -} +impl crate::error::Error for JoinPathsError {} #[cfg(target_os = "aix")] pub fn current_exe() -> io::Result<PathBuf> { @@ -620,14 +611,10 @@ fn darwin_temp_dir() -> PathBuf { pub fn temp_dir() -> PathBuf { crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { - cfg_if::cfg_if! { - if #[cfg(all(target_vendor = "apple", not(miri)))] { - darwin_temp_dir() - } else if #[cfg(target_os = "android")] { - PathBuf::from("/data/local/tmp") - } else { - PathBuf::from("/tmp") - } + cfg_select! { + all(target_vendor = "apple", not(miri)) => darwin_temp_dir(), + target_os = "android" => PathBuf::from("/data/local/tmp"), + _ => PathBuf::from("/tmp"), } }) } diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 55510153dc8..6b0cd14da4f 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -18,8 +18,8 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { // The only known way right now to create atomically set the CLOEXEC flag is // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 // and musl 0.9.3, and some other targets also have it. - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", @@ -29,12 +29,13 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "openbsd", target_os = "cygwin", target_os = "redox" - ))] { + ) => { unsafe { cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1])))) } - } else { + } + _ => { unsafe { cvt(libc::pipe(fds.as_mut_ptr()))?; diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 36e53e7cadc..3389b8c0c8a 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -77,7 +77,18 @@ impl Thread { 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(attr.as_mut_ptr(), stack_size), 0); + + // Some libc implementations, e.g. musl, place an upper bound + // on the stack size, in which case we can only gracefully return + // an error here. + if libc::pthread_attr_setstacksize(attr.as_mut_ptr(), stack_size) != 0 { + assert_eq!(libc::pthread_attr_destroy(attr.as_mut_ptr()), 0); + drop(Box::from_raw(data)); + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + "invalid stack size" + )); + } } }; } @@ -140,12 +151,13 @@ impl Thread { ))] pub fn set_name(name: &CStr) { unsafe { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "cygwin"))] { + cfg_select! { + any(target_os = "linux", target_os = "cygwin") => { // Linux and Cygwin limits the allowed length of the name. const TASK_COMM_LEN: usize = 16; let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - } else { + } + _ => { // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. } }; @@ -404,9 +416,9 @@ pub(crate) fn current_os_id() -> Option<u64> { // // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed // for process inspection (debuggers, trace, `top`, etc.). - cfg_if::cfg_if! { + cfg_select! { // Most platforms have a function returning a `pid_t` or int, which is an `i32`. - if #[cfg(any(target_os = "android", target_os = "linux"))] { + any(target_os = "android", target_os = "linux") => { use crate::sys::weak::syscall; // `libc::gettid` is only available on glibc 2.30+, but the syscall is available @@ -416,28 +428,34 @@ pub(crate) fn current_os_id() -> Option<u64> { // SAFETY: FFI call with no preconditions. let id: libc::pid_t = unsafe { gettid() }; Some(id as u64) - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { // SAFETY: FFI call with no preconditions. let id: libc::pid_t = unsafe { libc::gettid() }; Some(id as u64) - } else if #[cfg(target_os = "openbsd")] { + } + target_os = "openbsd" => { // SAFETY: FFI call with no preconditions. let id: libc::pid_t = unsafe { libc::getthrid() }; Some(id as u64) - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { // SAFETY: FFI call with no preconditions. let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; Some(id as u64) - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { // SAFETY: FFI call with no preconditions. let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; Some(id as u64) - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + } + any(target_os = "illumos", target_os = "solaris") => { // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. // SAFETY: FFI call with no preconditions. let id: libc::pthread_t = unsafe { libc::pthread_self() }; Some(id as u64) - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. let mut id = 0u64; // SAFETY: `thread_id` is a valid pointer, no other preconditions. @@ -447,10 +465,9 @@ pub(crate) fn current_os_id() -> Option<u64> { } else { None } - } else { - // Other platforms don't have an OS thread ID or don't have a way to access it. - None } + // Other platforms don't have an OS thread ID or don't have a way to access it. + _ => None, } } @@ -472,8 +489,8 @@ fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_W } pub fn available_parallelism() -> io::Result<NonZero<usize>> { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "android", target_os = "emscripten", target_os = "fuchsia", @@ -482,7 +499,7 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { target_os = "aix", target_vendor = "apple", target_os = "cygwin", - ))] { + ) => { #[allow(unused_assignments)] #[allow(unused_mut)] let mut quota = usize::MAX; @@ -516,12 +533,13 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { Ok(unsafe { NonZero::new_unchecked(count) }) } } - } else if #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "netbsd", - ))] { + } + any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + ) => { use crate::ptr; #[cfg(target_os = "freebsd")] @@ -596,7 +614,8 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { } Ok(unsafe { NonZero::new_unchecked(cpus as usize) }) - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { unsafe { use libc::_syspage_ptr; if _syspage_ptr.is_null() { @@ -607,13 +626,15 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { .ok_or(io::Error::UNKNOWN_THREAD_COUNT) } } - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + } + any(target_os = "solaris", target_os = "illumos") => { let mut cpus = 0u32; if unsafe { libc::pset_info(libc::PS_MYID, core::ptr::null_mut(), &mut cpus, core::ptr::null_mut()) } != 0 { return Err(io::Error::UNKNOWN_THREAD_COUNT); } Ok(unsafe { NonZero::new_unchecked(cpus as usize) }) - } else if #[cfg(target_os = "haiku")] { + } + target_os = "haiku" => { // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus` // `get_system_info` calls then `smp_get_num_cpus` unsafe { @@ -626,7 +647,8 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { Ok(NonZero::new_unchecked(sinfo.cpu_count as usize)) } - } else if #[cfg(target_os = "vxworks")] { + } + target_os = "vxworks" => { // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF // expectations than the actual cores availability. unsafe extern "C" { @@ -638,7 +660,8 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { let set = vxCpuEnabledGet(); Ok(NonZero::new_unchecked(set.count_ones() as usize)) } - } else { + } + _ => { // FIXME: implement on Redox, l4re Err(io::const_error!(io::ErrorKind::Unsupported, "getting the number of hardware threads is not supported on the target platform")) } diff --git a/library/std/src/sys/pal/unsupported/os.rs b/library/std/src/sys/pal/unsupported/os.rs index a8ef97ecf67..13d2a2044f4 100644 --- a/library/std/src/sys/pal/unsupported/os.rs +++ b/library/std/src/sys/pal/unsupported/os.rs @@ -1,5 +1,4 @@ use super::unsupported; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; @@ -51,12 +50,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index 672cf70d1a5..151ba254ec4 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::error::Error as StdError; use crate::ffi::{CStr, OsStr, OsString}; use crate::marker::PhantomData; use crate::os::wasi::prelude::*; @@ -105,12 +104,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on wasm yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 4755e2ef5da..e062b49bd7a 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -5,8 +5,8 @@ use crate::num::NonZero; use crate::time::{Duration, Instant}; use crate::{io, mem}; -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { +cfg_select! { + target_feature = "atomics" => { use crate::cmp; use crate::ptr; use crate::sys::os; @@ -62,7 +62,8 @@ cfg_if::cfg_if! { debug_assert_eq!(ret, 0); } } - } else { + } + _ => { pub struct Thread(!); } } @@ -71,8 +72,8 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { + cfg_select! { + target_feature = "atomics" => { pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; @@ -119,7 +120,8 @@ impl Thread { ptr::null_mut() } } - } else { + } + _ => { pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { crate::sys::unsupported() } @@ -180,14 +182,15 @@ impl Thread { } pub fn join(self) { - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { + cfg_select! { + target_feature = "atomics" => { let id = mem::ManuallyDrop::new(self).id; let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; if ret != 0 { rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); } - } else { + } + _ => { self.0 } } @@ -199,14 +202,13 @@ pub(crate) fn current_os_id() -> Option<u64> { } pub fn available_parallelism() -> io::Result<NonZero<usize>> { - cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { + cfg_select! { + target_feature = "atomics" => { match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { -1 => Err(io::Error::last_os_error()), cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), } - } else { - crate::sys::unsupported() } + _ => crate::sys::unsupported(), } } diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 37cb46a8f6b..346c9ff88c9 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,13 +23,14 @@ pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; -cfg_if::cfg_if! { - if #[cfg(target_feature = "atomics")] { +cfg_select! { + target_feature = "atomics" => { #[path = "atomics/futex.rs"] pub mod futex; #[path = "atomics/thread.rs"] pub mod thread; - } else { + } + _ => { #[path = "../unsupported/thread.rs"] pub mod thread; } diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index edac5262a4e..25c1a82cc42 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -95,11 +95,8 @@ pub struct MOUNT_POINT_REPARSE_BUFFER { } // Desktop specific functions & types -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { - pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; -} -} +#[cfg(not(target_vendor = "uwp"))] +pub const EXCEPTION_CONTINUE_SEARCH: i32 = 0; // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. #[cfg(not(target_vendor = "win7"))] @@ -230,12 +227,13 @@ compat_fn_with_fallback! { } } -cfg_if::cfg_if! { - if #[cfg(target_vendor = "uwp")] { +cfg_select! { + target_vendor = "uwp" => { windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); windows_targets::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); } + _ => {} } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c8e4dca4781..abc1c19827f 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2158,6 +2158,7 @@ GetCurrentDirectoryW GetCurrentProcess GetCurrentProcessId GetCurrentThread +GetCurrentThreadId GetEnvironmentStringsW GetEnvironmentVariableW GetExitCodeProcess @@ -2185,7 +2186,6 @@ GetSystemInfo GetSystemTimeAsFileTime GetSystemTimePreciseAsFileTime GetTempPathW -GetThreadId GetUserProfileDirectoryW GetWindowsDirectoryW HANDLE diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 45a273d241a..989a1246650 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -38,6 +38,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferle windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn GetCurrentThreadId() -> u32); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR); windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL); @@ -61,7 +62,6 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : * windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); -windows_targets::link!("kernel32.dll" "system" fn GetThreadId(thread : HANDLE) -> u32); windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 8f54e2376eb..10ad4541bed 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,10 +22,11 @@ pub mod os; pub mod pipe; pub mod thread; pub mod time; -cfg_if::cfg_if! { - if #[cfg(not(target_vendor = "uwp"))] { +cfg_select! { + not(target_vendor = "uwp") => { pub mod stack_overflow; - } else { + } + _ => { pub mod stack_overflow_uwp; pub use self::stack_overflow_uwp as stack_overflow; } @@ -337,14 +338,17 @@ pub fn dur2timeout(dur: Duration) -> u32 { #[cfg(not(miri))] // inline assembly does not work in Miri pub fn abort_internal() -> ! { unsafe { - cfg_if::cfg_if! { - if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + cfg_select! { + any(target_arch = "x86", target_arch = "x86_64") => { core::arch::asm!("int $$0x29", in("ecx") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] { + } + all(target_arch = "arm", target_feature = "thumb-mode") => { core::arch::asm!(".inst 0xDEFB", in("r0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] { + } + any(target_arch = "aarch64", target_arch = "arm64ec") => { core::arch::asm!("brk 0xF003", in("x0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack)); - } else { + } + _ => { core::intrinsics::abort(); } } diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index f331282d2d7..1b3c80c079b 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -8,7 +8,6 @@ mod tests; use super::api; #[cfg(not(target_vendor = "uwp"))] use super::api::WinError; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; @@ -162,12 +161,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to join paths" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { super::fill_utf16_buf( diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c708da5af12..b0e38220a2d 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -129,7 +129,7 @@ impl Thread { pub(crate) fn current_os_id() -> Option<u64> { // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetThreadId(c::GetCurrentThread()) }; + let id: u32 = unsafe { c::GetCurrentThreadId() }; // A return value of 0 indicates failed lookup. if id == 0 { None } else { Some(id.into()) } diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index d612a27d2bd..d9b8418e6c3 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,5 +1,4 @@ use super::unsupported; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; @@ -110,12 +109,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/pal/zkvm/os.rs b/library/std/src/sys/pal/zkvm/os.rs index a8ef97ecf67..13d2a2044f4 100644 --- a/library/std/src/sys/pal/zkvm/os.rs +++ b/library/std/src/sys/pal/zkvm/os.rs @@ -1,5 +1,4 @@ use super::unsupported; -use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; @@ -51,12 +50,7 @@ impl fmt::Display for JoinPathsError { } } -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "not supported on this platform yet" - } -} +impl crate::error::Error for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { unsupported() diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index a4ff4338cf5..254683bc83f 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -1,22 +1,27 @@ -cfg_if::cfg_if! { - if #[cfg(target_os = "windows")] { +cfg_select! { + target_os = "windows" => { mod windows; mod windows_prefix; pub use windows::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod unsupported_backslash; pub use unsupported_backslash::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::*; - } else if #[cfg(target_os = "cygwin")] { + } + target_os = "cygwin" => { mod cygwin; mod windows_prefix; pub use cygwin::*; - } else { + } + _ => { mod unix; pub use unix::*; } diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 75e793f18b8..019d5629d6d 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -93,12 +93,12 @@ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c -cfg_if::cfg_if! { - if #[cfg(all( +cfg_select! { + all( target_arch = "arm", not(target_vendor = "apple"), not(target_os = "netbsd"), - ))] { + ) => { /// personality fn called by [ARM EHABI][armeabi-eh] /// /// 32-bit ARM on iOS/tvOS/watchOS does not use ARM EHABI, it uses @@ -202,7 +202,8 @@ cfg_if::cfg_if! { } } } - } else { + } + _ => { /// Default personality routine, which is used directly on most targets /// and indirectly on Windows x86_64 and AArch64 via SEH. unsafe extern "C" fn rust_eh_personality_impl( @@ -247,11 +248,11 @@ cfg_if::cfg_if! { } } - cfg_if::cfg_if! { - if #[cfg(any( - all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"), - target_os = "cygwin", - ))] { + cfg_select! { + any( + all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"), + target_os = "cygwin", + ) => { /// personality fn called by [Windows Structured Exception Handling][windows-eh] /// /// On x86_64 and AArch64 MinGW targets, the unwinding mechanism is SEH, @@ -279,7 +280,8 @@ cfg_if::cfg_if! { ) } } - } else { + } + _ => { /// personality fn called by [Itanium C++ ABI Exception Handling][itanium-eh] /// /// The personality routine for most non-Windows targets. This will be called by diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 2e1d2e53a29..158e44e1764 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -13,10 +13,11 @@ mod dwarf; #[cfg(not(any(test, doctest)))] -cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { +cfg_select! { + target_os = "emscripten" => { mod emcc; - } else if #[cfg(any(target_env = "msvc", target_family = "wasm"))] { + } + any(target_env = "msvc", target_family = "wasm") => { // This is required by the compiler to exist (e.g., it's a lang item), // but it's never actually called by the compiler because // __CxxFrameHandler3 (msvc) / __gxx_wasm_personality_v0 (wasm) is the @@ -26,16 +27,18 @@ cfg_if::cfg_if! { fn rust_eh_personality() { core::intrinsics::abort() } - } else if #[cfg(any( + } + any( all(target_family = "windows", target_env = "gnu"), target_os = "psp", target_os = "xous", target_os = "solid_asp3", all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), - ))] { + ) => { mod gcc; - } else { + } + _ => { // Targets that don't support unwinding. // - os=none ("bare metal" targets) // - os=uefi diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 91c7005a328..9ef5496e57a 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -1,14 +1,17 @@ -cfg_if::cfg_if! { - if #[cfg(target_family = "unix")] { +cfg_select! { + target_family = "unix" => { mod unix; use unix as imp; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; use windows as imp; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; use uefi as imp; - } else { + } + _ => { mod unsupported; use unsupported as imp; } diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 6219be60caf..ea45b08e90a 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -20,12 +20,14 @@ use crate::{fmt, io}; mod cstring_array; -cfg_if::cfg_if! { - if #[cfg(target_os = "fuchsia")] { +cfg_select! { + target_os = "fuchsia" => { // fuchsia doesn't have /dev/null - } else if #[cfg(target_os = "vxworks")] { + } + target_os = "vxworks" => { const DEV_NULL: &CStr = c"/null"; - } else { + } + _ => { const DEV_NULL: &CStr = c"/dev/null"; } } @@ -35,8 +37,8 @@ cfg_if::cfg_if! { // to support older Android version (independent of libc version). // The following implementations are based on // https://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h -cfg_if::cfg_if! { - if #[cfg(target_os = "android")] { +cfg_select! { + target_os = "android" => { #[allow(dead_code)] pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int { set.write_bytes(0u8, 1); @@ -69,7 +71,8 @@ cfg_if::cfg_if! { raw[bit / LONG_BIT] |= 1 << (bit % LONG_BIT); return 0; } - } else { + } + _ => { #[allow(unused_imports)] pub use libc::{sigemptyset, sigaddset}; } diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index ee8fd8b2ca3..b4cf060fba9 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -1,18 +1,21 @@ #[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] mod common; -cfg_if::cfg_if! { - if #[cfg(target_os = "fuchsia")] { +cfg_select! { + target_os = "fuchsia" => { mod fuchsia; use fuchsia as imp; - } else if #[cfg(target_os = "vxworks")] { + } + target_os = "vxworks" => { mod vxworks; use vxworks as imp; - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { + } + any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx") => { mod unsupported; use unsupported as imp; pub use unsupported::output; - } else { + } + _ => { mod unix; use unix as imp; } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 5d13d6da185..11d48878727 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -18,8 +18,8 @@ use crate::sys::cvt; use crate::sys::pal::linux::pidfd::PidFd; use crate::{fmt, mem, sys}; -cfg_if::cfg_if! { - if #[cfg(target_os = "nto")] { +cfg_select! { + target_os = "nto" => { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -43,6 +43,7 @@ cfg_if::cfg_if! { // Maximum duration of sleeping before giving up and returning an error const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); } + _ => {} } //////////////////////////////////////////////////////////////////////////////// @@ -465,8 +466,8 @@ impl Command { return Ok(None); } - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { use crate::sys::weak::weak; weak!( @@ -526,7 +527,8 @@ impl Command { } core::assert_matches::debug_assert_matches!(support, SPAWN | NO); } - } else { + } + _ => { if self.get_create_pidfd() { unreachable!("only implemented on linux") } @@ -746,10 +748,11 @@ impl Command { } if self.get_setsid() { - cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + cfg_select! { + all(target_os = "linux", target_env = "gnu") => { flags |= libc::POSIX_SPAWN_SETSID; - } else { + } + _ => { return Ok(None); } } diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index fc85797dcc2..a7fbae86099 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -1,16 +1,19 @@ -cfg_if::cfg_if! { +cfg_select! { // Tier 1 - if #[cfg(any(target_os = "linux", target_os = "android"))] { + any(target_os = "linux", target_os = "android") => { mod linux; pub use linux::{fill_bytes, hashmap_random_keys}; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub use windows::fill_bytes; - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { mod apple; pub use apple::fill_bytes; // Others, in alphabetical ordering. - } else if #[cfg(any( + } + any( target_os = "dragonfly", target_os = "freebsd", target_os = "haiku", @@ -21,69 +24,86 @@ cfg_if::cfg_if! { target_os = "solaris", target_os = "vita", target_os = "nuttx", - ))] { + ) => { mod arc4random; pub use arc4random::fill_bytes; - } else if #[cfg(target_os = "emscripten")] { + } + target_os = "emscripten" => { mod getentropy; pub use getentropy::fill_bytes; - } else if #[cfg(target_os = "espidf")] { + } + target_os = "espidf" => { mod espidf; pub use espidf::fill_bytes; - } else if #[cfg(target_os = "fuchsia")] { + } + target_os = "fuchsia" => { mod fuchsia; pub use fuchsia::fill_bytes; - } else if #[cfg(target_os = "hermit")] { + } + target_os = "hermit" => { mod hermit; pub use hermit::fill_bytes; - } else if #[cfg(any(target_os = "horizon", target_os = "cygwin"))] { + } + any(target_os = "horizon", target_os = "cygwin") => { // FIXME(horizon): add arc4random_buf to shim-3ds mod getrandom; pub use getrandom::fill_bytes; - } else if #[cfg(any( + } + any( target_os = "aix", target_os = "hurd", target_os = "l4re", target_os = "nto", - ))] { + ) => { mod unix_legacy; pub use unix_legacy::fill_bytes; - } else if #[cfg(target_os = "redox")] { + } + target_os = "redox" => { mod redox; pub use redox::fill_bytes; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::fill_bytes; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use solid::fill_bytes; - } else if #[cfg(target_os = "teeos")] { + } + target_os = "teeos" => { mod teeos; pub use teeos::fill_bytes; - } else if #[cfg(target_os = "trusty")] { + } + target_os = "trusty" => { mod trusty; pub use trusty::fill_bytes; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::fill_bytes; - } else if #[cfg(target_os = "vxworks")] { + } + target_os = "vxworks" => { mod vxworks; pub use vxworks::fill_bytes; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::fill_bytes; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; pub use zkvm::fill_bytes; - } else if #[cfg(any( + } + any( all(target_family = "wasm", target_os = "unknown"), target_os = "xous", - ))] { + ) => { // FIXME: finally remove std support for wasm32-unknown-unknown // FIXME: add random data generation to xous mod unsupported; pub use unsupported::{fill_bytes, hashmap_random_keys}; } + _ => {} } #[cfg(not(any( diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs index 4a71d32fffe..697933f197b 100644 --- a/library/std/src/sys/random/uefi.rs +++ b/library/std/src/sys/random/uefi.rs @@ -55,12 +55,13 @@ mod rng_protocol { /// Port from [getrandom](https://github.com/rust-random/getrandom/blob/master/src/backends/rdrand.rs) #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] mod rdrand { - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { + cfg_select! { + target_arch = "x86_64" => { use crate::arch::x86_64 as arch; use arch::_rdrand64_step as rdrand_step; type Word = u64; - } else if #[cfg(target_arch = "x86")] { + } + target_arch = "x86" => { use crate::arch::x86 as arch; use arch::_rdrand32_step as rdrand_step; type Word = u32; diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 336d4c8527d..314f226f07b 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -1,40 +1,47 @@ #![forbid(unsafe_op_in_unsafe_fn)] -cfg_if::cfg_if! { - if #[cfg(any( - target_family = "unix", - target_os = "hermit" - ))] { +cfg_select! { + any(target_family = "unix", target_os = "hermit") => { mod unix; pub use unix::*; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub use windows::*; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::*; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use solid::*; - } else if #[cfg(target_os = "teeos")] { + } + target_os = "teeos" => { mod teeos; pub use teeos::*; - } else if #[cfg(target_os = "trusty")] { + } + target_os = "trusty" => { mod trusty; pub use trusty::*; - } else if #[cfg(target_os = "uefi")] { + } + target_os = "uefi" => { mod uefi; pub use uefi::*; - } else if #[cfg(target_os = "wasi")] { + } + target_os = "wasi" => { mod wasi; pub use wasi::*; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::*; - } else if #[cfg(target_os = "zkvm")] { + } + target_os = "zkvm" => { mod zkvm; pub use zkvm::*; - } else { + } + _ => { mod unsupported; pub use unsupported::*; } diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index d0c998a5597..cb67d273759 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -1,5 +1,5 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_os = "windows", not(target_vendor="win7")), target_os = "linux", target_os = "android", @@ -9,28 +9,34 @@ cfg_if::cfg_if! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", - ))] { + ) => { mod futex; pub use futex::Condvar; - } else if #[cfg(any( + } + any( target_family = "unix", target_os = "teeos", - ))] { + ) => { mod pthread; pub use pthread::Condvar; - } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { + } + all(target_os = "windows", target_vendor = "win7") => { mod windows7; pub use windows7::Condvar; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::Condvar; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod itron; pub use itron::Condvar; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::Condvar; - } else { + } + _ => { mod no_threads; pub use no_threads::Condvar; } diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index 360df3fc4b5..c885b0eabae 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -1,5 +1,5 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", @@ -8,31 +8,38 @@ cfg_if::cfg_if! { target_os = "dragonfly", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", - ))] { + ) => { mod futex; pub use futex::Mutex; - } else if #[cfg(target_os = "fuchsia")] { + } + target_os = "fuchsia" => { mod fuchsia; pub use fuchsia::Mutex; - } else if #[cfg(any( + } + any( target_family = "unix", target_os = "teeos", - ))] { + ) => { mod pthread; pub use pthread::Mutex; - } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { + } + all(target_os = "windows", target_vendor = "win7") => { mod windows7; pub use windows7::{Mutex, raw}; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; pub use sgx::Mutex; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod itron; pub use itron::Mutex; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::Mutex; - } else { + } + _ => { mod no_threads; pub use no_threads::Mutex; } diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index 0e38937b121..8adeb1f259d 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -7,8 +7,8 @@ // This also gives us the opportunity to optimize the implementation a bit which // should help the fast path on call sites. -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_os = "windows", not(target_vendor="win7")), target_os = "linux", target_os = "android", @@ -18,19 +18,21 @@ cfg_if::cfg_if! { target_os = "dragonfly", target_os = "fuchsia", target_os = "hermit", - ))] { + ) => { mod futex; pub use futex::{Once, OnceState}; - } else if #[cfg(any( + } + any( windows, target_family = "unix", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", target_os = "xous", - ))] { + ) => { mod queue; pub use queue::{Once, OnceState}; - } else { + } + _ => { mod no_threads; pub use no_threads::{Once, OnceState}; } diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 70ba6bf38ef..82f1dd18dee 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -1,5 +1,5 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", @@ -9,24 +9,28 @@ cfg_if::cfg_if! { target_os = "fuchsia", all(target_family = "wasm", target_feature = "atomics"), target_os = "hermit", - ))] { + ) => { mod futex; pub use futex::RwLock; - } else if #[cfg(any( + } + any( target_family = "unix", all(target_os = "windows", target_vendor = "win7"), all(target_vendor = "fortanix", target_env = "sgx"), target_os = "xous", - ))] { + ) => { mod queue; pub use queue::RwLock; - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub use solid::RwLock; - } else if #[cfg(target_os = "teeos")] { + } + target_os = "teeos" => { mod teeos; pub use teeos::RwLock; - } else { + } + _ => { mod no_threads; pub use no_threads::RwLock; } diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index f4d8fa0a58c..b9fb27b4eef 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -1,5 +1,5 @@ -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", @@ -9,30 +9,36 @@ cfg_if::cfg_if! { target_os = "dragonfly", target_os = "fuchsia", target_os = "hermit", - ))] { + ) => { mod futex; pub use futex::Parker; - } else if #[cfg(any( + } + any( target_os = "netbsd", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", - ))] { + ) => { mod id; pub use id::Parker; - } else if #[cfg(target_vendor = "win7")] { + } + target_vendor = "win7" => { mod windows7; pub use windows7::Parker; - } else if #[cfg(all(target_vendor = "apple", not(miri)))] { + } + all(target_vendor = "apple", not(miri)) => { // Doesn't work in Miri, see <https://github.com/rust-lang/miri/issues/2589>. mod darwin; pub use darwin::Parker; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod xous; pub use xous::Parker; - } else if #[cfg(target_family = "unix")] { + } + target_family = "unix" => { mod pthread; pub use pthread::Parker; - } else { + } + _ => { mod unsupported; pub use unsupported::Parker; } diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 9fafac3aa5b..cff74857c47 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -23,21 +23,23 @@ issue = "none" )] -cfg_if::cfg_if! { - if #[cfg(any( +cfg_select! { + any( all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi", target_os = "zkvm", target_os = "trusty", - ))] { + ) => { mod no_threads; pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner}; pub(crate) use no_threads::{LocalPointer, local_pointer}; - } else if #[cfg(target_thread_local)] { + } + target_thread_local => { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; pub(crate) use native::{LocalPointer, local_pointer}; - } else { + } + _ => { mod os; pub use os::{Storage, thread_local_inner}; pub(crate) use os::{LocalPointer, local_pointer}; @@ -53,8 +55,8 @@ cfg_if::cfg_if! { /// single callback that runs all of the destructors in the list. #[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics")))))] pub(crate) mod destructors { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "android", target_os = "fuchsia", @@ -62,12 +64,13 @@ pub(crate) mod destructors { target_os = "hurd", target_os = "netbsd", target_os = "dragonfly" - ))] { + ) => { mod linux_like; mod list; pub(super) use linux_like::register; pub(super) use list::run; - } else { + } + _ => { mod list; pub(super) use list::register; pub(crate) use list::run; @@ -79,21 +82,23 @@ pub(crate) mod destructors { /// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` /// should ensure that these functions are called at the right times. pub(crate) mod guard { - cfg_if::cfg_if! { - if #[cfg(all(target_thread_local, target_vendor = "apple"))] { + cfg_select! { + all(target_thread_local, target_vendor = "apple") => { mod apple; pub(crate) use apple::enable; - } else if #[cfg(target_os = "windows")] { + } + target_os = "windows" => { mod windows; pub(crate) use windows::enable; - } else if #[cfg(any( + } + any( all(target_family = "wasm", not( all(target_os = "wasi", target_env = "p1", target_feature = "atomics") )), target_os = "uefi", target_os = "zkvm", target_os = "trusty", - ))] { + ) => { pub(crate) fn enable() { // FIXME: Right now there is no concept of "thread exit" on // wasm, but this is likely going to show up at some point in @@ -107,17 +112,20 @@ pub(crate) mod guard { #[allow(unused)] use crate::rt::thread_cleanup; } - } else if #[cfg(any( + } + any( target_os = "hermit", target_os = "xous", - ))] { + ) => { // `std` is the only runtime, so it just calls the destructor functions // itself when the time comes. pub(crate) fn enable() {} - } else if #[cfg(target_os = "solid_asp3")] { + } + target_os = "solid_asp3" => { mod solid; pub(crate) use solid::enable; - } else { + } + _ => { mod key; pub(crate) use key::enable; } @@ -131,8 +139,8 @@ pub(crate) mod guard { /// reference an entry in a thread-local table. This then associates each key /// with a pointer which we can get and set to store our data. pub(crate) mod key { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( all( not(target_vendor = "apple"), not(target_family = "wasm"), @@ -141,7 +149,7 @@ pub(crate) mod key { all(not(target_thread_local), target_vendor = "apple"), target_os = "teeos", all(target_os = "wasi", target_env = "p1", target_feature = "atomics"), - ))] { + ) => { mod racy; mod unix; #[cfg(test)] @@ -151,12 +159,14 @@ pub(crate) mod key { #[cfg(any(not(target_thread_local), test))] pub(super) use unix::get; use unix::{create, destroy}; - } else if #[cfg(all(not(target_thread_local), target_os = "windows"))] { + } + all(not(target_thread_local), target_os = "windows") => { #[cfg(test)] mod tests; mod windows; pub(super) use windows::{Key, LazyKey, get, run_dtors, set}; - } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + } + all(target_vendor = "fortanix", target_env = "sgx") => { mod racy; mod sgx; #[cfg(test)] @@ -164,7 +174,8 @@ pub(crate) mod key { pub(super) use racy::LazyKey; pub(super) use sgx::{Key, get, set}; use sgx::{create, destroy}; - } else if #[cfg(target_os = "xous")] { + } + target_os = "xous" => { mod racy; #[cfg(test)] mod tests; @@ -174,6 +185,7 @@ pub(crate) mod key { pub(super) use xous::{Key, get, set}; use xous::{create, destroy}; } + _ => {} } } diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index cce88d936b7..ec45c723e0d 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,7 +21,6 @@ mod tests; pub mod wstr; -pub mod wtf8; // common error constructors @@ -51,17 +50,16 @@ pub trait FromInner<Inner> { fn from_inner(inner: Inner) -> Self; } -// Computes (value*numer)/denom without overflow, as long as both -// (numer*denom) and the overall result fit into i64 (which is the case -// for our time conversions). +// Computes (value*numerator)/denom without overflow, as long as both (numerator*denom) and the +// overall result fit into i64 (which is the case for our time conversions). #[allow(dead_code)] // not used on all platforms -pub fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { +pub fn mul_div_u64(value: u64, numerator: u64, denom: u64) -> u64 { let q = value / denom; let r = value % denom; // Decompose value as (value/denom*denom + value%denom), - // substitute into (value*numer)/denom and simplify. - // r < denom, so (denom*numer) is the upper bound of (r*numer) - q * numer + r * numer / denom + // substitute into (value*numerator)/denom and simplify. + // r < denom, so (denom*numerator) is the upper bound of (r*numerator) + q * numerator + r * numerator / denom } pub fn ignore_notfound<T>(result: crate::io::Result<T>) -> crate::io::Result<()> { diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs deleted file mode 100644 index 50bde88b5a4..00000000000 --- a/library/std/src/sys_common/wtf8.rs +++ /dev/null @@ -1,1083 +0,0 @@ -//! Implementation of [the WTF-8 encoding](https://simonsapin.github.io/wtf-8/). -//! -//! This library uses Rust’s type system to maintain -//! [well-formedness](https://simonsapin.github.io/wtf-8/#well-formed), -//! like the `String` and `&str` types do for UTF-8. -//! -//! Since [WTF-8 must not be used -//! for interchange](https://simonsapin.github.io/wtf-8/#intended-audience), -//! this library deliberately does not provide access to the underlying bytes -//! of WTF-8 strings, -//! nor can it decode WTF-8 from arbitrary bytes. -//! WTF-8 strings can be obtained from UTF-8, UTF-16, or code points. - -// this module is imported from @SimonSapin's repo and has tons of dead code on -// unix (it's mostly used on windows), so don't worry about dead code here. -#![allow(dead_code)] - -#[cfg(test)] -mod tests; - -use core::char::{MAX_LEN_UTF8, MAX_LEN_UTF16, encode_utf8_raw, encode_utf16_raw}; -use core::clone::CloneToUninit; -use core::str::next_code_point; - -use crate::borrow::Cow; -use crate::collections::TryReserveError; -use crate::hash::{Hash, Hasher}; -use crate::iter::FusedIterator; -use crate::rc::Rc; -use crate::sync::Arc; -use crate::sys_common::AsInner; -use crate::{fmt, mem, ops, slice, str}; - -const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}"; - -/// A Unicode code point: from U+0000 to U+10FFFF. -/// -/// Compares with the `char` type, -/// which represents a Unicode scalar value: -/// a code point that is not a surrogate (U+D800 to U+DFFF). -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy)] -pub struct CodePoint { - value: u32, -} - -/// Format the code point as `U+` followed by four to six hexadecimal digits. -/// Example: `U+1F4A9` -impl fmt::Debug for CodePoint { - #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "U+{:04X}", self.value) - } -} - -impl CodePoint { - /// Unsafely creates a new `CodePoint` without checking the value. - /// - /// Only use when `value` is known to be less than or equal to 0x10FFFF. - #[inline] - pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint { - CodePoint { value } - } - - /// Creates a new `CodePoint` if the value is a valid code point. - /// - /// Returns `None` if `value` is above 0x10FFFF. - #[inline] - pub fn from_u32(value: u32) -> Option<CodePoint> { - match value { - 0..=0x10FFFF => Some(CodePoint { value }), - _ => None, - } - } - - /// Creates a new `CodePoint` from a `char`. - /// - /// Since all Unicode scalar values are code points, this always succeeds. - #[inline] - pub fn from_char(value: char) -> CodePoint { - CodePoint { value: value as u32 } - } - - /// Returns the numeric value of the code point. - #[inline] - pub fn to_u32(&self) -> u32 { - self.value - } - - /// Returns the numeric value of the code point if it is a leading surrogate. - #[inline] - pub fn to_lead_surrogate(&self) -> Option<u16> { - match self.value { - lead @ 0xD800..=0xDBFF => Some(lead as u16), - _ => None, - } - } - - /// Returns the numeric value of the code point if it is a trailing surrogate. - #[inline] - pub fn to_trail_surrogate(&self) -> Option<u16> { - match self.value { - trail @ 0xDC00..=0xDFFF => Some(trail as u16), - _ => None, - } - } - - /// Optionally returns a Unicode scalar value for the code point. - /// - /// Returns `None` if the code point is a surrogate (from U+D800 to U+DFFF). - #[inline] - pub fn to_char(&self) -> Option<char> { - match self.value { - 0xD800..=0xDFFF => None, - _ => Some(unsafe { char::from_u32_unchecked(self.value) }), - } - } - - /// Returns a Unicode scalar value for the code point. - /// - /// Returns `'\u{FFFD}'` (the replacement character “�”) - /// if the code point is a surrogate (from U+D800 to U+DFFF). - #[inline] - pub fn to_char_lossy(&self) -> char { - self.to_char().unwrap_or('\u{FFFD}') - } -} - -/// An owned, growable string of well-formed WTF-8 data. -/// -/// Similar to `String`, but can additionally contain surrogate code points -/// if they’re not in a surrogate pair. -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)] -pub struct Wtf8Buf { - bytes: Vec<u8>, - - /// Do we know that `bytes` holds a valid UTF-8 encoding? We can easily - /// know this if we're constructed from a `String` or `&str`. - /// - /// It is possible for `bytes` to have valid UTF-8 without this being - /// set, such as when we're concatenating `&Wtf8`'s and surrogates become - /// paired, as we don't bother to rescan the entire string. - is_known_utf8: bool, -} - -impl ops::Deref for Wtf8Buf { - type Target = Wtf8; - - fn deref(&self) -> &Wtf8 { - self.as_slice() - } -} - -impl ops::DerefMut for Wtf8Buf { - fn deref_mut(&mut self) -> &mut Wtf8 { - self.as_mut_slice() - } -} - -/// Formats the string in double quotes, with characters escaped according to -/// [`char::escape_debug`] and unpaired surrogates represented as `\u{xxxx}`, -/// where each `x` is a hexadecimal digit. -/// -/// For example, the code units [U+0061, U+D800, U+000A] are formatted as -/// `"a\u{D800}\n"`. -impl fmt::Debug for Wtf8Buf { - #[inline] - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&**self, formatter) - } -} - -/// Formats the string with unpaired surrogates substituted with the replacement -/// character, U+FFFD. -impl fmt::Display for Wtf8Buf { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(s) = self.as_known_utf8() { - fmt::Display::fmt(s, formatter) - } else { - fmt::Display::fmt(&**self, formatter) - } - } -} - -impl Wtf8Buf { - /// Creates a new, empty WTF-8 string. - #[inline] - pub fn new() -> Wtf8Buf { - Wtf8Buf { bytes: Vec::new(), is_known_utf8: true } - } - - /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes. - #[inline] - pub fn with_capacity(capacity: usize) -> 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 function 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. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub const fn from_string(string: String) -> Wtf8Buf { - Wtf8Buf { bytes: string.into_bytes(), is_known_utf8: true } - } - - /// Creates a WTF-8 string from a UTF-8 `&str` slice. - /// - /// This copies the content of the slice. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub fn from_str(s: &str) -> Wtf8Buf { - Wtf8Buf { bytes: s.as_bytes().to_vec(), is_known_utf8: true } - } - - pub fn clear(&mut self) { - self.bytes.clear(); - self.is_known_utf8 = true; - } - - /// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units. - /// - /// This is lossless: calling `.encode_wide()` on the resulting string - /// will always return the original code units. - pub fn from_wide(v: &[u16]) -> Wtf8Buf { - let mut string = Wtf8Buf::with_capacity(v.len()); - for item in char::decode_utf16(v.iter().cloned()) { - match item { - Ok(ch) => string.push_char(ch), - Err(surrogate) => { - let surrogate = surrogate.unpaired_surrogate(); - // Surrogates are known to be in the code point range. - let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) }; - // The string will now contain an unpaired surrogate. - string.is_known_utf8 = false; - // Skip the WTF-8 concatenation check, - // surrogate pairs are already decoded by decode_utf16 - string.push_code_point_unchecked(code_point); - } - } - } - string - } - - /// Appends the given `char` to the end of this string. - /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check. - /// Copied from String::push. - fn push_code_point_unchecked(&mut self, code_point: CodePoint) { - let mut bytes = [0; MAX_LEN_UTF8]; - let bytes = encode_utf8_raw(code_point.value, &mut bytes); - self.bytes.extend_from_slice(bytes) - } - - #[inline] - pub fn as_slice(&self) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(&self.bytes) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut Wtf8 { - // Safety: `Wtf8` doesn't expose any way to mutate the bytes that would - // cause them to change from well-formed UTF-8 to ill-formed UTF-8, - // which would break the assumptions of the `is_known_utf8` field. - unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) } - } - - /// Converts the string to UTF-8 without validation, if it was created from - /// valid UTF-8. - #[inline] - fn as_known_utf8(&self) -> Option<&str> { - if self.is_known_utf8 { - // SAFETY: The buffer is known to be valid UTF-8. - Some(unsafe { str::from_utf8_unchecked(self.as_bytes()) }) - } else { - None - } - } - - /// Reserves capacity for at least `additional` more bytes to be inserted - /// in the given `Wtf8Buf`. - /// The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` bytes. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.bytes.reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more bytes to be - /// inserted in the given `Wtf8Buf`. The `Wtf8Buf` may reserve more space to - /// avoid frequent reallocations. After calling `try_reserve`, capacity will - /// be greater than or equal to `self.len() + additional`. Does nothing if - /// capacity is already sufficient. This method preserves the contents even - /// if an error occurs. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - #[inline] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.bytes.try_reserve(additional) - } - - #[inline] - pub fn reserve_exact(&mut self, additional: usize) { - self.bytes.reserve_exact(additional) - } - - /// Tries to reserve the minimum capacity for exactly `additional` more - /// bytes to be inserted in the given `Wtf8Buf`. After calling - /// `try_reserve_exact`, capacity will be greater than or equal to - /// `self.len() + additional` if it returns `Ok(())`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the `Wtf8Buf` more space than it - /// requests. Therefore, capacity can not be relied upon to be precisely - /// minimal. Prefer [`try_reserve`] if future insertions are expected. - /// - /// [`try_reserve`]: Wtf8Buf::try_reserve - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - #[inline] - pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.bytes.try_reserve_exact(additional) - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.bytes.shrink_to_fit() - } - - #[inline] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.bytes.shrink_to(min_capacity) - } - - #[inline] - pub fn leak<'a>(self) -> &'a mut Wtf8 { - unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) } - } - - /// Returns the number of bytes that this string buffer can hold without reallocating. - #[inline] - pub fn capacity(&self) -> usize { - self.bytes.capacity() - } - - /// Append a UTF-8 slice at the end of the string. - #[inline] - pub fn push_str(&mut self, other: &str) { - self.bytes.extend_from_slice(other.as_bytes()) - } - - /// Append a WTF-8 slice at the end of the string. - /// - /// This replaces newly paired surrogates at the boundary - /// with a supplementary code point, - /// like concatenating ill-formed UTF-16 strings effectively would. - #[inline] - pub fn push_wtf8(&mut self, other: &Wtf8) { - match ((&*self).final_lead_surrogate(), other.initial_trail_surrogate()) { - // Replace newly paired surrogates by a supplementary code point. - (Some(lead), Some(trail)) => { - let len_without_lead_surrogate = self.len() - 3; - self.bytes.truncate(len_without_lead_surrogate); - let other_without_trail_surrogate = &other.bytes[3..]; - // 4 bytes for the supplementary code point - self.bytes.reserve(4 + other_without_trail_surrogate.len()); - self.push_char(decode_surrogate_pair(lead, trail)); - self.bytes.extend_from_slice(other_without_trail_surrogate); - } - _ => { - // If we'll be pushing a string containing a surrogate, we may - // no longer have UTF-8. - if self.is_known_utf8 && other.next_surrogate(0).is_some() { - self.is_known_utf8 = false; - } - - self.bytes.extend_from_slice(&other.bytes); - } - } - } - - /// Append a Unicode scalar value at the end of the string. - #[inline] - pub fn push_char(&mut self, c: char) { - self.push_code_point_unchecked(CodePoint::from_char(c)) - } - - /// Append a code point at the end of the string. - /// - /// This replaces newly paired surrogates at the boundary - /// with a supplementary code point, - /// like concatenating ill-formed UTF-16 strings effectively would. - #[inline] - pub fn push(&mut self, code_point: CodePoint) { - if let Some(trail) = code_point.to_trail_surrogate() { - if let Some(lead) = (&*self).final_lead_surrogate() { - let len_without_lead_surrogate = self.len() - 3; - self.bytes.truncate(len_without_lead_surrogate); - self.push_char(decode_surrogate_pair(lead, trail)); - return; - } - - // We're pushing a trailing surrogate. - self.is_known_utf8 = false; - } else if code_point.to_lead_surrogate().is_some() { - // We're pushing a leading surrogate. - self.is_known_utf8 = false; - } - - // No newly paired surrogates at the boundary. - self.push_code_point_unchecked(code_point) - } - - /// Shortens a string to the specified length. - /// - /// # Panics - /// - /// Panics if `new_len` > current length, - /// or if `new_len` is not a code point boundary. - #[inline] - pub fn truncate(&mut self, new_len: usize) { - assert!(is_code_point_boundary(self, new_len)); - 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. - /// - /// If the contents are not well-formed UTF-8 - /// (that is, if the string contains surrogates), - /// the original WTF-8 string is returned instead. - pub fn into_string(self) -> Result<String, Wtf8Buf> { - if self.is_known_utf8 || self.next_surrogate(0).is_none() { - Ok(unsafe { String::from_utf8_unchecked(self.bytes) }) - } else { - Err(self) - } - } - - /// Consumes the WTF-8 string and converts it lossily to UTF-8. - /// - /// This does not copy the data (but may overwrite parts of it in place). - /// - /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”) - pub fn into_string_lossy(mut self) -> String { - if !self.is_known_utf8 { - let mut pos = 0; - while let Some((surrogate_pos, _)) = self.next_surrogate(pos) { - pos = surrogate_pos + 3; - // Surrogates and the replacement character are all 3 bytes, so - // they can substituted in-place. - self.bytes[surrogate_pos..pos] - .copy_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - } - } - unsafe { String::from_utf8_unchecked(self.bytes) } - } - - /// 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()) } - } - - /// Converts a `Box<Wtf8>` into a `Wtf8Buf`. - pub fn from_box(boxed: Box<Wtf8>) -> Wtf8Buf { - let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) }; - Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false } - } - - /// Provides plumbing to core `Vec::extend_from_slice`. - /// More well behaving alternative to allowing outer types - /// full mutable access to the core `Vec`. - #[inline] - pub(crate) fn extend_from_slice(&mut self, other: &[u8]) { - self.bytes.extend_from_slice(other); - self.is_known_utf8 = false; - } -} - -/// Creates a new WTF-8 string from an iterator of code points. -/// -/// This replaces surrogate code point pairs with supplementary code points, -/// like concatenating ill-formed UTF-16 strings effectively would. -impl FromIterator<CodePoint> for Wtf8Buf { - fn from_iter<T: IntoIterator<Item = CodePoint>>(iter: T) -> Wtf8Buf { - let mut string = Wtf8Buf::new(); - string.extend(iter); - string - } -} - -/// Append code points from an iterator to the string. -/// -/// This replaces surrogate code point pairs with supplementary code points, -/// like concatenating ill-formed UTF-16 strings effectively would. -impl Extend<CodePoint> for Wtf8Buf { - fn extend<T: IntoIterator<Item = CodePoint>>(&mut self, iter: T) { - let iterator = iter.into_iter(); - let (low, _high) = iterator.size_hint(); - // Lower bound of one byte per code point (ASCII only) - self.bytes.reserve(low); - iterator.for_each(move |code_point| self.push(code_point)); - } - - #[inline] - fn extend_one(&mut self, code_point: CodePoint) { - self.push(code_point); - } - - #[inline] - fn extend_reserve(&mut self, additional: usize) { - // Lower bound of one byte per code point (ASCII only) - self.bytes.reserve(additional); - } -} - -/// A borrowed slice of well-formed WTF-8 data. -/// -/// 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], -} - -impl AsInner<[u8]> for Wtf8 { - #[inline] - fn as_inner(&self) -> &[u8] { - &self.bytes - } -} - -/// Formats the string in double quotes, with characters escaped according to -/// [`char::escape_debug`] and unpaired surrogates represented as `\u{xxxx}`, -/// where each `x` is a hexadecimal digit. -impl fmt::Debug for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result { - use crate::fmt::Write; - for c in s.chars().flat_map(|c| c.escape_debug()) { - f.write_char(c)? - } - Ok(()) - } - - formatter.write_str("\"")?; - let mut pos = 0; - while let Some((surrogate_pos, surrogate)) = self.next_surrogate(pos) { - write_str_escaped(formatter, unsafe { - str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos]) - })?; - write!(formatter, "\\u{{{:x}}}", surrogate)?; - pos = surrogate_pos + 3; - } - write_str_escaped(formatter, unsafe { str::from_utf8_unchecked(&self.bytes[pos..]) })?; - formatter.write_str("\"") - } -} - -/// Formats the string with unpaired surrogates substituted with the replacement -/// character, U+FFFD. -impl fmt::Display for Wtf8 { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - let wtf8_bytes = &self.bytes; - let mut pos = 0; - loop { - match self.next_surrogate(pos) { - Some((surrogate_pos, _)) => { - formatter.write_str(unsafe { - str::from_utf8_unchecked(&wtf8_bytes[pos..surrogate_pos]) - })?; - formatter.write_str(UTF8_REPLACEMENT_CHARACTER)?; - pos = surrogate_pos + 3; - } - None => { - let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) }; - if pos == 0 { return s.fmt(formatter) } else { return formatter.write_str(s) } - } - } - } - } -} - -impl Wtf8 { - /// Creates a WTF-8 slice from a UTF-8 `&str` slice. - /// - /// Since WTF-8 is a superset of UTF-8, this always succeeds. - #[inline] - pub fn from_str(value: &str) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(value.as_bytes()) } - } - - /// Creates a WTF-8 slice from a WTF-8 byte slice. - /// - /// Since the byte slice is not checked for valid WTF-8, this functions is - /// marked unsafe. - #[inline] - pub unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 { - // SAFETY: start with &[u8], end with fancy &[u8] - unsafe { &*(value as *const [u8] as *const Wtf8) } - } - - /// Creates a mutable WTF-8 slice from a mutable WTF-8 byte slice. - /// - /// Since the byte slice is not checked for valid WTF-8, this functions is - /// marked unsafe. - #[inline] - unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 { - // SAFETY: start with &mut [u8], end with fancy &mut [u8] - unsafe { &mut *(value as *mut [u8] as *mut Wtf8) } - } - - /// Returns the length, in WTF-8 bytes. - #[inline] - pub fn len(&self) -> usize { - self.bytes.len() - } - - #[inline] - pub fn is_empty(&self) -> bool { - self.bytes.is_empty() - } - - /// Returns the code point at `position` if it is in the ASCII range, - /// or `b'\xFF'` otherwise. - /// - /// # Panics - /// - /// Panics if `position` is beyond the end of the string. - #[inline] - pub fn ascii_byte_at(&self, position: usize) -> u8 { - match self.bytes[position] { - ascii_byte @ 0x00..=0x7F => ascii_byte, - _ => 0xFF, - } - } - - /// Returns an iterator for the string’s code points. - #[inline] - pub fn code_points(&self) -> Wtf8CodePoints<'_> { - Wtf8CodePoints { bytes: self.bytes.iter() } - } - - /// Access raw bytes of WTF-8 data - #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.bytes - } - - /// Tries to convert the string to UTF-8 and return a `&str` slice. - /// - /// Returns `None` if the string contains surrogates. - /// - /// This does not copy the data. - #[inline] - pub fn as_str(&self) -> Result<&str, str::Utf8Error> { - str::from_utf8(&self.bytes) - } - - /// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`. - pub fn to_owned(&self) -> Wtf8Buf { - Wtf8Buf { bytes: self.bytes.to_vec(), is_known_utf8: false } - } - - /// Lossily converts the string to UTF-8. - /// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8. - /// - /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”). - /// - /// This only copies the data if necessary (if it contains any surrogate). - pub fn to_string_lossy(&self) -> Cow<'_, str> { - let Some((surrogate_pos, _)) = self.next_surrogate(0) else { - return Cow::Borrowed(unsafe { str::from_utf8_unchecked(&self.bytes) }); - }; - let wtf8_bytes = &self.bytes; - let mut utf8_bytes = Vec::with_capacity(self.len()); - utf8_bytes.extend_from_slice(&wtf8_bytes[..surrogate_pos]); - utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - let mut pos = surrogate_pos + 3; - loop { - match self.next_surrogate(pos) { - Some((surrogate_pos, _)) => { - utf8_bytes.extend_from_slice(&wtf8_bytes[pos..surrogate_pos]); - utf8_bytes.extend_from_slice(UTF8_REPLACEMENT_CHARACTER.as_bytes()); - pos = surrogate_pos + 3; - } - None => { - utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]); - return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) }); - } - } - } - } - - /// Converts the WTF-8 string to potentially ill-formed UTF-16 - /// and return an iterator of 16-bit code units. - /// - /// This is lossless: - /// calling `Wtf8Buf::from_ill_formed_utf16` on the resulting code units - /// would always return the original WTF-8 string. - #[inline] - pub fn encode_wide(&self) -> EncodeWide<'_> { - EncodeWide { code_points: self.code_points(), extra: 0 } - } - - #[inline] - fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> { - let mut iter = self.bytes[pos..].iter(); - loop { - let b = *iter.next()?; - if b < 0x80 { - pos += 1; - } else if b < 0xE0 { - iter.next(); - pos += 2; - } else if b == 0xED { - match (iter.next(), iter.next()) { - (Some(&b2), Some(&b3)) if b2 >= 0xA0 => { - return Some((pos, decode_surrogate(b2, b3))); - } - _ => pos += 3, - } - } else if b < 0xF0 { - iter.next(); - iter.next(); - pos += 3; - } else { - iter.next(); - iter.next(); - iter.next(); - pos += 4; - } - } - } - - #[inline] - fn final_lead_surrogate(&self) -> Option<u16> { - match self.bytes { - [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)), - _ => None, - } - } - - #[inline] - fn initial_trail_surrogate(&self) -> Option<u16> { - match self.bytes { - [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)), - _ => None, - } - } - - pub fn clone_into(&self, buf: &mut Wtf8Buf) { - buf.is_known_utf8 = false; - self.bytes.clone_into(&mut buf.bytes); - } - - /// Boxes this `Wtf8`. - #[inline] - pub fn into_box(&self) -> Box<Wtf8> { - let boxed: Box<[u8]> = self.bytes.into(); - unsafe { mem::transmute(boxed) } - } - - /// Creates a boxed, empty `Wtf8`. - pub fn empty_box() -> Box<Wtf8> { - let boxed: Box<[u8]> = Default::default(); - unsafe { mem::transmute(boxed) } - } - - #[inline] - pub fn into_arc(&self) -> Arc<Wtf8> { - let arc: Arc<[u8]> = Arc::from(&self.bytes); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) } - } - - #[inline] - pub fn into_rc(&self) -> Rc<Wtf8> { - let rc: Rc<[u8]> = Rc::from(&self.bytes); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) } - } - - #[inline] - pub fn make_ascii_lowercase(&mut self) { - self.bytes.make_ascii_lowercase() - } - - #[inline] - pub fn make_ascii_uppercase(&mut self) { - self.bytes.make_ascii_uppercase() - } - - #[inline] - pub fn to_ascii_lowercase(&self) -> Wtf8Buf { - Wtf8Buf { bytes: self.bytes.to_ascii_lowercase(), is_known_utf8: false } - } - - #[inline] - pub fn to_ascii_uppercase(&self) -> Wtf8Buf { - Wtf8Buf { bytes: self.bytes.to_ascii_uppercase(), is_known_utf8: false } - } - - #[inline] - pub fn is_ascii(&self) -> bool { - self.bytes.is_ascii() - } - - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { - self.bytes.eq_ignore_ascii_case(&other.bytes) - } -} - -/// Returns a slice of the given string for the byte range \[`begin`..`end`). -/// -/// # Panics -/// -/// Panics when `begin` and `end` do not point to code point boundaries, -/// or point beyond the end of the string. -impl ops::Index<ops::Range<usize>> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::Range<usize>) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if range.start <= range.end - && is_code_point_boundary(self, range.start) - && is_code_point_boundary(self, range.end) - { - unsafe { slice_unchecked(self, range.start, range.end) } - } else { - slice_error_fail(self, range.start, range.end) - } - } -} - -/// Returns a slice of the given string from byte `begin` to its end. -/// -/// # Panics -/// -/// Panics when `begin` is not at a code point boundary, -/// or is beyond the end of the string. -impl ops::Index<ops::RangeFrom<usize>> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::RangeFrom<usize>) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if is_code_point_boundary(self, range.start) { - unsafe { slice_unchecked(self, range.start, self.len()) } - } else { - slice_error_fail(self, range.start, self.len()) - } - } -} - -/// Returns a slice of the given string from its beginning to byte `end`. -/// -/// # Panics -/// -/// Panics when `end` is not at a code point boundary, -/// or is beyond the end of the string. -impl ops::Index<ops::RangeTo<usize>> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, range: ops::RangeTo<usize>) -> &Wtf8 { - // is_code_point_boundary checks that the index is in [0, .len()] - if is_code_point_boundary(self, range.end) { - unsafe { slice_unchecked(self, 0, range.end) } - } else { - slice_error_fail(self, 0, range.end) - } - } -} - -impl ops::Index<ops::RangeFull> for Wtf8 { - type Output = Wtf8; - - #[inline] - fn index(&self, _range: ops::RangeFull) -> &Wtf8 { - self - } -} - -#[inline] -fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 { - // The first byte is assumed to be 0xED - 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F -} - -#[inline] -fn decode_surrogate_pair(lead: u16, trail: u16) -> char { - let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32); - unsafe { char::from_u32_unchecked(code_point) } -} - -/// Copied from str::is_char_boundary -#[inline] -pub fn is_code_point_boundary(slice: &Wtf8, index: usize) -> bool { - if index == 0 { - return true; - } - match slice.bytes.get(index) { - None => index == slice.len(), - Some(&b) => (b as i8) >= -0x40, - } -} - -/// Verify that `index` is at the edge of either a valid UTF-8 codepoint -/// (i.e. a codepoint that's not a surrogate) or of the whole string. -/// -/// These are the cases currently permitted by `OsStr::slice_encoded_bytes`. -/// Splitting between surrogates is valid as far as WTF-8 is concerned, but -/// we do not permit it in the public API because WTF-8 is considered an -/// implementation detail. -#[track_caller] -#[inline] -pub fn check_utf8_boundary(slice: &Wtf8, index: usize) { - if index == 0 { - return; - } - match slice.bytes.get(index) { - Some(0xED) => (), // Might be a surrogate - Some(&b) if (b as i8) >= -0x40 => return, - Some(_) => panic!("byte index {index} is not a codepoint boundary"), - None if index == slice.len() => return, - None => panic!("byte index {index} is out of bounds"), - } - if slice.bytes[index + 1] >= 0xA0 { - // There's a surrogate after index. Now check before index. - if index >= 3 && slice.bytes[index - 3] == 0xED && slice.bytes[index - 2] >= 0xA0 { - panic!("byte index {index} lies between surrogate codepoints"); - } - } -} - -/// Copied from core::str::raw::slice_unchecked -#[inline] -pub unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 { - // SAFETY: memory layout of a &[u8] and &Wtf8 are the same - unsafe { - let len = end - begin; - let start = s.as_bytes().as_ptr().add(begin); - Wtf8::from_bytes_unchecked(slice::from_raw_parts(start, len)) - } -} - -/// Copied from core::str::raw::slice_error_fail -#[inline(never)] -pub fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! { - assert!(begin <= end); - panic!("index {begin} and/or {end} in `{s:?}` do not lie on character boundary"); -} - -/// Iterator for the code points of a WTF-8 string. -/// -/// Created with the method `.code_points()`. -#[derive(Clone)] -pub struct Wtf8CodePoints<'a> { - bytes: slice::Iter<'a, u8>, -} - -impl Iterator for Wtf8CodePoints<'_> { - type Item = CodePoint; - - #[inline] - fn next(&mut self) -> Option<CodePoint> { - // SAFETY: `self.bytes` has been created from a WTF-8 string - unsafe { next_code_point(&mut self.bytes).map(|c| CodePoint { value: c }) } - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let len = self.bytes.len(); - (len.saturating_add(3) / 4, Some(len)) - } -} - -/// Generates a wide character sequence for potentially ill-formed UTF-16. -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct EncodeWide<'a> { - code_points: Wtf8CodePoints<'a>, - extra: u16, -} - -// Copied from libunicode/u_str.rs -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for EncodeWide<'_> { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option<u16> { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; MAX_LEN_UTF16]; - self.code_points.next().map(|code_point| { - let n = encode_utf16_raw(code_point.value, &mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - let (low, high) = self.code_points.size_hint(); - let ext = (self.extra != 0) as usize; - // every code point gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low + ext, high.and_then(|n| n.checked_mul(2)).and_then(|n| n.checked_add(ext))) - } -} - -#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")] -impl FusedIterator for EncodeWide<'_> {} - -impl Hash for CodePoint { - #[inline] - fn hash<H: Hasher>(&self, state: &mut H) { - self.value.hash(state) - } -} - -impl Hash for Wtf8Buf { - #[inline] - fn hash<H: Hasher>(&self, state: &mut H) { - state.write(&self.bytes); - 0xfeu8.hash(state) - } -} - -impl Hash for Wtf8 { - #[inline] - fn hash<H: Hasher>(&self, state: &mut H) { - state.write(&self.bytes); - 0xfeu8.hash(state) - } -} - -#[unstable(feature = "clone_to_uninit", issue = "126799")] -unsafe impl CloneToUninit for Wtf8 { - #[inline] - #[cfg_attr(debug_assertions, track_caller)] - unsafe fn clone_to_uninit(&self, dst: *mut u8) { - // SAFETY: we're just a transparent wrapper around [u8] - unsafe { self.bytes.clone_to_uninit(dst) } - } -} diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs deleted file mode 100644 index b57c99a8452..00000000000 --- a/library/std/src/sys_common/wtf8/tests.rs +++ /dev/null @@ -1,751 +0,0 @@ -use super::*; - -#[test] -fn code_point_from_u32() { - assert!(CodePoint::from_u32(0).is_some()); - assert!(CodePoint::from_u32(0xD800).is_some()); - assert!(CodePoint::from_u32(0x10FFFF).is_some()); - assert!(CodePoint::from_u32(0x110000).is_none()); -} - -#[test] -fn code_point_to_u32() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0).to_u32(), 0); - assert_eq!(c(0xD800).to_u32(), 0xD800); - assert_eq!(c(0x10FFFF).to_u32(), 0x10FFFF); -} - -#[test] -fn code_point_to_lead_surrogate() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0).to_lead_surrogate(), None); - assert_eq!(c(0xE9).to_lead_surrogate(), None); - assert_eq!(c(0xD800).to_lead_surrogate(), Some(0xD800)); - assert_eq!(c(0xDBFF).to_lead_surrogate(), Some(0xDBFF)); - assert_eq!(c(0xDC00).to_lead_surrogate(), None); - assert_eq!(c(0xDFFF).to_lead_surrogate(), None); - assert_eq!(c(0x1F4A9).to_lead_surrogate(), None); - assert_eq!(c(0x10FFFF).to_lead_surrogate(), None); -} - -#[test] -fn code_point_to_trail_surrogate() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0).to_trail_surrogate(), None); - assert_eq!(c(0xE9).to_trail_surrogate(), None); - assert_eq!(c(0xD800).to_trail_surrogate(), None); - assert_eq!(c(0xDBFF).to_trail_surrogate(), None); - assert_eq!(c(0xDC00).to_trail_surrogate(), Some(0xDC00)); - assert_eq!(c(0xDFFF).to_trail_surrogate(), Some(0xDFFF)); - assert_eq!(c(0x1F4A9).to_trail_surrogate(), None); - assert_eq!(c(0x10FFFF).to_trail_surrogate(), None); -} - -#[test] -fn code_point_from_char() { - assert_eq!(CodePoint::from_char('a').to_u32(), 0x61); - assert_eq!(CodePoint::from_char('💩').to_u32(), 0x1F4A9); -} - -#[test] -fn code_point_to_string() { - assert_eq!(format!("{:?}", CodePoint::from_char('a')), "U+0061"); - assert_eq!(format!("{:?}", CodePoint::from_char('💩')), "U+1F4A9"); -} - -#[test] -fn code_point_to_char() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0x61).to_char(), Some('a')); - assert_eq!(c(0x1F4A9).to_char(), Some('💩')); - assert_eq!(c(0xD800).to_char(), None); -} - -#[test] -fn code_point_to_char_lossy() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - assert_eq!(c(0x61).to_char_lossy(), 'a'); - assert_eq!(c(0x1F4A9).to_char_lossy(), '💩'); - assert_eq!(c(0xD800).to_char_lossy(), '\u{FFFD}'); -} - -#[test] -fn wtf8buf_new() { - assert_eq!(Wtf8Buf::new().bytes, b""); -} - -#[test] -fn wtf8buf_from_str() { - assert_eq!(Wtf8Buf::from_str("").bytes, b""); - assert_eq!(Wtf8Buf::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); -} - -#[test] -fn wtf8buf_from_string() { - assert_eq!(Wtf8Buf::from_string(String::from("")).bytes, b""); - assert_eq!(Wtf8Buf::from_string(String::from("aé 💩")).bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); -} - -#[test] -fn wtf8buf_from_wide() { - let buf = Wtf8Buf::from_wide(&[]); - assert_eq!(buf.bytes, b""); - assert!(buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xDCA9]); - assert_eq!(buf.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - assert!(buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9]); - assert_eq!(buf.bytes, b"a\xC3\xA9 \xED\xA0\xBD\xF0\x9F\x92\xA9"); - assert!(!buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0xD800]); - assert_eq!(buf.bytes, b"\xED\xA0\x80"); - assert!(!buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0xDBFF]); - assert_eq!(buf.bytes, b"\xED\xAF\xBF"); - assert!(!buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0xDC00]); - assert_eq!(buf.bytes, b"\xED\xB0\x80"); - assert!(!buf.is_known_utf8); - - let buf = Wtf8Buf::from_wide(&[0xDFFF]); - assert_eq!(buf.bytes, b"\xED\xBF\xBF"); - assert!(!buf.is_known_utf8); -} - -#[test] -fn wtf8buf_push_str() { - let mut string = Wtf8Buf::new(); - assert_eq!(string.bytes, b""); - assert!(string.is_known_utf8); - - string.push_str("aé 💩"); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - assert!(string.is_known_utf8); -} - -#[test] -fn wtf8buf_push_char() { - let mut string = Wtf8Buf::from_str("aé "); - assert_eq!(string.bytes, b"a\xC3\xA9 "); - assert!(string.is_known_utf8); - - string.push_char('💩'); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - assert!(string.is_known_utf8); -} - -#[test] -fn wtf8buf_push() { - let mut string = Wtf8Buf::from_str("aé "); - assert_eq!(string.bytes, b"a\xC3\xA9 "); - assert!(string.is_known_utf8); - - string.push(CodePoint::from_char('💩')); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - assert!(string.is_known_utf8); - - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - - let mut string = Wtf8Buf::new(); - string.push(c(0xD83D)); // lead - assert!(!string.is_known_utf8); - string.push(c(0xDCA9)); // trail - assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! - - let mut string = Wtf8Buf::new(); - string.push(c(0xD83D)); // lead - assert!(!string.is_known_utf8); - string.push(c(0x20)); // not surrogate - string.push(c(0xDCA9)); // trail - assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD800)); // lead - assert!(!string.is_known_utf8); - string.push(c(0xDBFF)); // lead - assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD800)); // lead - assert!(!string.is_known_utf8); - string.push(c(0xE000)); // not surrogate - assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xD7FF)); // not surrogate - assert!(string.is_known_utf8); - string.push(c(0xDC00)); // trail - assert!(!string.is_known_utf8); - assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0x61)); // not surrogate, < 3 bytes - assert!(string.is_known_utf8); - string.push(c(0xDC00)); // trail - assert!(!string.is_known_utf8); - assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); - - let mut string = Wtf8Buf::new(); - string.push(c(0xDC00)); // trail - assert!(!string.is_known_utf8); - assert_eq!(string.bytes, b"\xED\xB0\x80"); -} - -#[test] -fn wtf8buf_push_wtf8() { - let mut string = Wtf8Buf::from_str("aé"); - assert_eq!(string.bytes, b"a\xC3\xA9"); - string.push_wtf8(Wtf8::from_str(" 💩")); - assert_eq!(string.bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); - assert!(string.is_known_utf8); - - fn w(v: &[u8]) -> &Wtf8 { - unsafe { Wtf8::from_bytes_unchecked(v) } - } - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\xBD")); // lead - string.push_wtf8(w(b"\xED\xB2\xA9")); // trail - assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); // Magic! - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\xBD")); // lead - string.push_wtf8(w(b" ")); // not surrogate - string.push_wtf8(w(b"\xED\xB2\xA9")); // trail - assert_eq!(string.bytes, b"\xED\xA0\xBD \xED\xB2\xA9"); - assert!(!string.is_known_utf8); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\x80")); // lead - string.push_wtf8(w(b"\xED\xAF\xBF")); // lead - assert_eq!(string.bytes, b"\xED\xA0\x80\xED\xAF\xBF"); - assert!(!string.is_known_utf8); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xA0\x80")); // lead - string.push_wtf8(w(b"\xEE\x80\x80")); // not surrogate - assert_eq!(string.bytes, b"\xED\xA0\x80\xEE\x80\x80"); - assert!(!string.is_known_utf8); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\x9F\xBF")); // not surrogate - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\xED\x9F\xBF\xED\xB0\x80"); - assert!(!string.is_known_utf8); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"a")); // not surrogate, < 3 bytes - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\x61\xED\xB0\x80"); - assert!(!string.is_known_utf8); - - let mut string = Wtf8Buf::new(); - string.push_wtf8(w(b"\xED\xB0\x80")); // trail - assert_eq!(string.bytes, b"\xED\xB0\x80"); - assert!(!string.is_known_utf8); -} - -#[test] -fn wtf8buf_truncate() { - let mut string = Wtf8Buf::from_str("aé"); - assert!(string.is_known_utf8); - - string.truncate(3); - assert_eq!(string.bytes, b"a\xC3\xA9"); - assert!(string.is_known_utf8); - - string.truncate(1); - assert_eq!(string.bytes, b"a"); - assert!(string.is_known_utf8); - - string.truncate(0); - assert_eq!(string.bytes, b""); - assert!(string.is_known_utf8); -} - -#[test] -fn wtf8buf_truncate_around_non_bmp() { - let mut string = Wtf8Buf::from_str("💩"); - assert!(string.is_known_utf8); - - string.truncate(4); - assert_eq!(string.bytes, b"\xF0\x9F\x92\xA9"); - assert!(string.is_known_utf8); - - string.truncate(0); - assert_eq!(string.bytes, b""); - assert!(string.is_known_utf8); -} - -#[test] -#[should_panic] -fn wtf8buf_truncate_fail_code_point_boundary() { - let mut string = Wtf8Buf::from_str("aé"); - string.truncate(2); -} - -#[test] -#[should_panic] -fn wtf8buf_truncate_fail_longer() { - let mut string = Wtf8Buf::from_str("aé"); - string.truncate(4); -} - -#[test] -#[should_panic] -fn wtf8buf_truncate_splitting_non_bmp3() { - let mut string = Wtf8Buf::from_str("💩"); - assert!(string.is_known_utf8); - string.truncate(3); -} - -#[test] -#[should_panic] -fn wtf8buf_truncate_splitting_non_bmp2() { - let mut string = Wtf8Buf::from_str("💩"); - assert!(string.is_known_utf8); - string.truncate(2); -} - -#[test] -#[should_panic] -fn wtf8buf_truncate_splitting_non_bmp1() { - let mut string = Wtf8Buf::from_str("💩"); - assert!(string.is_known_utf8); - string.truncate(1); -} - -#[test] -fn wtf8buf_into_string() { - let mut string = Wtf8Buf::from_str("aé 💩"); - assert!(string.is_known_utf8); - assert_eq!(string.clone().into_string(), Ok(String::from("aé 💩"))); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert!(!string.is_known_utf8); - assert_eq!(string.clone().into_string(), Err(string)); -} - -#[test] -fn wtf8buf_into_string_lossy() { - let mut string = Wtf8Buf::from_str("aé 💩"); - assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩")); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(string.clone().into_string_lossy(), String::from("aé 💩�")); -} - -#[test] -fn wtf8buf_from_iterator() { - fn f(values: &[u32]) -> Wtf8Buf { - values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::<Wtf8Buf>() - } - assert_eq!( - f(&[0x61, 0xE9, 0x20, 0x1F4A9]), - Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } - ); - - assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!( - f(&[0xD83D, 0x20, 0xDCA9]), - Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD800, 0xDBFF]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD800, 0xE000]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0xD7FF, 0xDC00]), - Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - f(&[0x61, 0xDC00]), - Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }); -} - -#[test] -fn wtf8buf_extend() { - fn e(initial: &[u32], extended: &[u32]) -> Wtf8Buf { - fn c(value: &u32) -> CodePoint { - CodePoint::from_u32(*value).unwrap() - } - let mut string = initial.iter().map(c).collect::<Wtf8Buf>(); - string.extend(extended.iter().map(c)); - string - } - - assert_eq!( - e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), - Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } - ); - - assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!( - e(&[0xD83D, 0x20], &[0xDCA9]), - Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD800], &[0xDBFF]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD800], &[0xE000]), - Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0xD7FF], &[0xDC00]), - Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[0x61], &[0xDC00]), - Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); - assert_eq!( - e(&[], &[0xDC00]), - Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false } - ); -} - -#[test] -fn wtf8buf_show() { - let mut string = Wtf8Buf::from_str("a\té \u{7f}💩\r"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!(format!("{string:?}"), "\"a\\té \\u{7f}\u{1f4a9}\\r\\u{d800}\""); -} - -#[test] -fn wtf8buf_as_slice() { - assert_eq!(Wtf8Buf::from_str("aé").as_slice(), Wtf8::from_str("aé")); -} - -#[test] -fn wtf8buf_show_str() { - let text = "a\té 💩\r"; - let string = Wtf8Buf::from_str(text); - assert_eq!(format!("{text:?}"), format!("{string:?}")); -} - -#[test] -fn wtf8_from_str() { - assert_eq!(&Wtf8::from_str("").bytes, b""); - assert_eq!(&Wtf8::from_str("aé 💩").bytes, b"a\xC3\xA9 \xF0\x9F\x92\xA9"); -} - -#[test] -fn wtf8_len() { - assert_eq!(Wtf8::from_str("").len(), 0); - assert_eq!(Wtf8::from_str("aé 💩").len(), 8); -} - -#[test] -fn wtf8_slice() { - assert_eq!(&Wtf8::from_str("aé 💩")[1..4].bytes, b"\xC3\xA9 "); -} - -#[test] -#[should_panic] -fn wtf8_slice_not_code_point_boundary() { - let _ = &Wtf8::from_str("aé 💩")[2..4]; -} - -#[test] -fn wtf8_slice_from() { - assert_eq!(&Wtf8::from_str("aé 💩")[1..].bytes, b"\xC3\xA9 \xF0\x9F\x92\xA9"); -} - -#[test] -#[should_panic] -fn wtf8_slice_from_not_code_point_boundary() { - let _ = &Wtf8::from_str("aé 💩")[2..]; -} - -#[test] -fn wtf8_slice_to() { - assert_eq!(&Wtf8::from_str("aé 💩")[..4].bytes, b"a\xC3\xA9 "); -} - -#[test] -#[should_panic] -fn wtf8_slice_to_not_code_point_boundary() { - let _ = &Wtf8::from_str("aé 💩")[5..]; -} - -#[test] -fn wtf8_ascii_byte_at() { - let slice = Wtf8::from_str("aé 💩"); - assert_eq!(slice.ascii_byte_at(0), b'a'); - assert_eq!(slice.ascii_byte_at(1), b'\xFF'); - assert_eq!(slice.ascii_byte_at(2), b'\xFF'); - assert_eq!(slice.ascii_byte_at(3), b' '); - assert_eq!(slice.ascii_byte_at(4), b'\xFF'); -} - -#[test] -fn wtf8_code_points() { - fn c(value: u32) -> CodePoint { - CodePoint::from_u32(value).unwrap() - } - fn cp(string: &Wtf8Buf) -> Vec<Option<char>> { - string.code_points().map(|c| c.to_char()).collect::<Vec<_>>() - } - let mut string = Wtf8Buf::from_str("é "); - assert_eq!(cp(&string), [Some('é'), Some(' ')]); - string.push(c(0xD83D)); - assert_eq!(cp(&string), [Some('é'), Some(' '), None]); - string.push(c(0xDCA9)); - assert_eq!(cp(&string), [Some('é'), Some(' '), Some('💩')]); -} - -#[test] -fn wtf8_as_str() { - assert_eq!(Wtf8::from_str("").as_str(), Ok("")); - assert_eq!(Wtf8::from_str("aé 💩").as_str(), Ok("aé 💩")); - let mut string = Wtf8Buf::new(); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert!(string.as_str().is_err()); -} - -#[test] -fn wtf8_to_string_lossy() { - assert_eq!(Wtf8::from_str("").to_string_lossy(), Cow::Borrowed("")); - assert_eq!(Wtf8::from_str("aé 💩").to_string_lossy(), Cow::Borrowed("aé 💩")); - let mut string = Wtf8Buf::from_str("aé 💩"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - let expected: Cow<'_, str> = Cow::Owned(String::from("aé 💩�")); - assert_eq!(string.to_string_lossy(), expected); -} - -#[test] -fn wtf8_display() { - fn d(b: &[u8]) -> String { - (&unsafe { Wtf8::from_bytes_unchecked(b) }).to_string() - } - - assert_eq!("", d("".as_bytes())); - assert_eq!("aé 💩", d("aé 💩".as_bytes())); - - let mut string = Wtf8Buf::from_str("aé 💩"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - assert_eq!("aé 💩�", d(string.as_inner())); -} - -#[test] -fn wtf8_encode_wide() { - let mut string = Wtf8Buf::from_str("aé "); - string.push(CodePoint::from_u32(0xD83D).unwrap()); - string.push_char('💩'); - assert_eq!( - string.encode_wide().collect::<Vec<_>>(), - vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] - ); -} - -#[test] -fn wtf8_encode_wide_size_hint() { - let string = Wtf8Buf::from_str("\u{12345}"); - let mut iter = string.encode_wide(); - assert_eq!((1, Some(8)), iter.size_hint()); - iter.next().unwrap(); - assert_eq!((1, Some(1)), iter.size_hint()); - iter.next().unwrap(); - assert_eq!((0, Some(0)), iter.size_hint()); - assert!(iter.next().is_none()); -} - -#[test] -fn wtf8_clone_into() { - let mut string = Wtf8Buf::new(); - Wtf8::from_str("green").clone_into(&mut string); - assert_eq!(string.bytes, b"green"); - - let mut string = Wtf8Buf::from_str("green"); - Wtf8::from_str("").clone_into(&mut string); - assert_eq!(string.bytes, b""); - - let mut string = Wtf8Buf::from_str("red"); - Wtf8::from_str("green").clone_into(&mut string); - assert_eq!(string.bytes, b"green"); - - let mut string = Wtf8Buf::from_str("green"); - Wtf8::from_str("red").clone_into(&mut string); - assert_eq!(string.bytes, b"red"); - - let mut string = Wtf8Buf::from_str("green"); - assert!(string.is_known_utf8); - unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").clone_into(&mut string) }; - assert_eq!(string.bytes, b"\xED\xA0\x80"); - assert!(!string.is_known_utf8); -} - -#[test] -fn wtf8_to_ascii_lowercase() { - let lowercase = Wtf8::from_str("").to_ascii_lowercase(); - assert_eq!(lowercase.bytes, b""); - - let lowercase = Wtf8::from_str("GrEeN gRaPeS! 🍇").to_ascii_lowercase(); - assert_eq!(lowercase.bytes, b"green grapes! \xf0\x9f\x8d\x87"); - - let lowercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_ascii_lowercase() }; - assert_eq!(lowercase.bytes, b"\xED\xA0\x80"); - assert!(!lowercase.is_known_utf8); -} - -#[test] -fn wtf8_to_ascii_uppercase() { - let uppercase = Wtf8::from_str("").to_ascii_uppercase(); - assert_eq!(uppercase.bytes, b""); - - let uppercase = Wtf8::from_str("GrEeN gRaPeS! 🍇").to_ascii_uppercase(); - assert_eq!(uppercase.bytes, b"GREEN GRAPES! \xf0\x9f\x8d\x87"); - - let uppercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_ascii_uppercase() }; - assert_eq!(uppercase.bytes, b"\xED\xA0\x80"); - assert!(!uppercase.is_known_utf8); -} - -#[test] -fn wtf8_make_ascii_lowercase() { - let mut lowercase = Wtf8Buf::from_str(""); - lowercase.make_ascii_lowercase(); - assert_eq!(lowercase.bytes, b""); - - let mut lowercase = Wtf8Buf::from_str("GrEeN gRaPeS! 🍇"); - lowercase.make_ascii_lowercase(); - assert_eq!(lowercase.bytes, b"green grapes! \xf0\x9f\x8d\x87"); - - let mut lowercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; - lowercase.make_ascii_lowercase(); - assert_eq!(lowercase.bytes, b"\xED\xA0\x80"); - assert!(!lowercase.is_known_utf8); -} - -#[test] -fn wtf8_make_ascii_uppercase() { - let mut uppercase = Wtf8Buf::from_str(""); - uppercase.make_ascii_uppercase(); - assert_eq!(uppercase.bytes, b""); - - let mut uppercase = Wtf8Buf::from_str("GrEeN gRaPeS! 🍇"); - uppercase.make_ascii_uppercase(); - assert_eq!(uppercase.bytes, b"GREEN GRAPES! \xf0\x9f\x8d\x87"); - - let mut uppercase = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; - uppercase.make_ascii_uppercase(); - assert_eq!(uppercase.bytes, b"\xED\xA0\x80"); - assert!(!uppercase.is_known_utf8); -} - -#[test] -fn wtf8_to_owned() { - let string = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; - assert_eq!(string.bytes, b"\xED\xA0\x80"); - assert!(!string.is_known_utf8); -} - -#[test] -fn wtf8_valid_utf8_boundaries() { - let mut string = Wtf8Buf::from_str("aé 💩"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - string.push(CodePoint::from_u32(0xD800).unwrap()); - check_utf8_boundary(&string, 0); - check_utf8_boundary(&string, 1); - check_utf8_boundary(&string, 3); - check_utf8_boundary(&string, 4); - check_utf8_boundary(&string, 8); - check_utf8_boundary(&string, 14); - assert_eq!(string.len(), 14); - - string.push_char('a'); - check_utf8_boundary(&string, 14); - check_utf8_boundary(&string, 15); - - let mut string = Wtf8Buf::from_str("a"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - check_utf8_boundary(&string, 1); - - let mut string = Wtf8Buf::from_str("\u{D7FF}"); - string.push(CodePoint::from_u32(0xD800).unwrap()); - check_utf8_boundary(&string, 3); - - let mut string = Wtf8Buf::new(); - string.push(CodePoint::from_u32(0xD800).unwrap()); - string.push_char('\u{D7FF}'); - check_utf8_boundary(&string, 3); -} - -#[test] -#[should_panic(expected = "byte index 4 is out of bounds")] -fn wtf8_utf8_boundary_out_of_bounds() { - let string = Wtf8::from_str("aé"); - check_utf8_boundary(&string, 4); -} - -#[test] -#[should_panic(expected = "byte index 1 is not a codepoint boundary")] -fn wtf8_utf8_boundary_inside_codepoint() { - let string = Wtf8::from_str("é"); - check_utf8_boundary(&string, 1); -} - -#[test] -#[should_panic(expected = "byte index 1 is not a codepoint boundary")] -fn wtf8_utf8_boundary_inside_surrogate() { - let mut string = Wtf8Buf::new(); - string.push(CodePoint::from_u32(0xD800).unwrap()); - check_utf8_boundary(&string, 1); -} - -#[test] -#[should_panic(expected = "byte index 3 lies between surrogate codepoints")] -fn wtf8_utf8_boundary_between_surrogates() { - let mut string = Wtf8Buf::new(); - string.push(CodePoint::from_u32(0xD800).unwrap()); - string.push(CodePoint::from_u32(0xD800).unwrap()); - check_utf8_boundary(&string, 3); -} - -#[test] -fn wobbled_wtf8_plus_bytes_isnt_utf8() { - let mut string: Wtf8Buf = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; - assert!(!string.is_known_utf8); - string.extend_from_slice(b"some utf-8"); - assert!(!string.is_known_utf8); -} - -#[test] -fn wobbled_wtf8_plus_str_isnt_utf8() { - let mut string: Wtf8Buf = unsafe { Wtf8::from_bytes_unchecked(b"\xED\xA0\x80").to_owned() }; - assert!(!string.is_known_utf8); - string.push_str("some utf-8"); - assert!(!string.is_known_utf8); -} - -#[test] -fn unwobbly_wtf8_plus_utf8_is_utf8() { - let mut string: Wtf8Buf = Wtf8Buf::from_str("hello world"); - assert!(string.is_known_utf8); - string.push_str("some utf-8"); - assert!(string.is_known_utf8); -} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 5c879903526..7da1621da45 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -18,8 +18,8 @@ local_pointer! { pub(super) mod id { use super::*; - cfg_if::cfg_if! { - if #[cfg(target_thread_local)] { + cfg_select! { + target_thread_local => { use crate::cell::Cell; #[thread_local] @@ -34,7 +34,8 @@ pub(super) mod id { pub(super) fn set(id: ThreadId) { ID.set(Some(id)) } - } else if #[cfg(target_pointer_width = "16")] { + } + target_pointer_width = "16" => { local_pointer! { static ID0; static ID16; @@ -59,7 +60,8 @@ pub(super) mod id { ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); ID48.set(ptr::without_provenance_mut((val >> 48) as usize)); } - } else if #[cfg(target_pointer_width = "32")] { + } + target_pointer_width = "32" => { local_pointer! { static ID0; static ID32; @@ -78,7 +80,8 @@ pub(super) mod id { ID0.set(ptr::without_provenance_mut(val as usize)); ID32.set(ptr::without_provenance_mut((val >> 32) as usize)); } - } else { + } + _ => { local_pointer! { static ID; } diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 0ad014ccd3e..797feeb2bbb 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -8,8 +8,8 @@ use crate::fmt; /// A thread local storage (TLS) key which owns its contents. /// -/// This key uses the fastest possible implementation available to it for the -/// target platform. It is instantiated with the [`thread_local!`] macro and the +/// This key uses the fastest implementation available on the target platform. +/// It is instantiated with the [`thread_local!`] macro and the /// primary method is the [`with`] method, though there are helpers to make /// working with [`Cell`] types easier. /// @@ -24,10 +24,10 @@ use crate::fmt; /// [`with`]) within a thread, and values that implement [`Drop`] get /// destructed when a thread exits. Some platform-specific caveats apply, which /// are explained below. -/// Note that, should the destructor panics, the whole process will be [aborted]. +/// Note that if the destructor panics, the whole process will be [aborted]. /// /// A `LocalKey`'s initializer cannot recursively depend on itself. Using a -/// `LocalKey` in this way may cause panics, aborts or infinite recursion on +/// `LocalKey` in this way may cause panics, aborts, or infinite recursion on /// the first call to `with`. /// /// [aborted]: crate::process::abort diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 292323d0118..b6059c28cec 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1212,8 +1212,8 @@ impl ThreadId { panic!("failed to generate unique thread ID: bitspace exhausted") } - cfg_if::cfg_if! { - if #[cfg(target_has_atomic = "64")] { + cfg_select! { + target_has_atomic = "64" => { use crate::sync::atomic::{Atomic, AtomicU64}; static COUNTER: Atomic<u64> = AtomicU64::new(0); @@ -1229,7 +1229,8 @@ impl ThreadId { Err(id) => last = id, } } - } else { + } + _ => { use crate::sync::{Mutex, PoisonError}; static COUNTER: Mutex<u64> = Mutex::new(0); @@ -1318,8 +1319,8 @@ use thread_name_string::ThreadNameString; /// Note however that this also means that the name reported in pre-main functions /// will be incorrect, but that's just something we have to live with. pub(crate) mod main_thread { - cfg_if::cfg_if! { - if #[cfg(target_has_atomic = "64")] { + cfg_select! { + target_has_atomic = "64" => { use super::ThreadId; use crate::sync::atomic::{Atomic, AtomicU64}; use crate::sync::atomic::Ordering::Relaxed; @@ -1335,7 +1336,8 @@ pub(crate) mod main_thread { pub(crate) unsafe fn set(id: ThreadId) { MAIN.store(id.as_u64().get(), Relaxed) } - } else { + } + _ => { use super::ThreadId; use crate::mem::MaybeUninit; use crate::sync::atomic::{Atomic, AtomicBool}; diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 07bb41f1496..84fbb4c2fe4 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -717,12 +717,7 @@ impl SystemTimeError { } #[stable(feature = "time2", since = "1.8.0")] -impl Error for SystemTimeError { - #[allow(deprecated)] - fn description(&self) -> &str { - "other time was not earlier than self" - } -} +impl Error for SystemTimeError {} #[stable(feature = "time2", since = "1.8.0")] impl fmt::Display for SystemTimeError { |
